diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst index 9055ca53a2..827fa79063 100644 --- a/DOCS/interface-changes.rst +++ b/DOCS/interface-changes.rst @@ -44,6 +44,7 @@ Interface changes - change fallback deinterlace to bwdif - add the command `load-config-file` - add the command `load-input-conf` + - remove `--vo=rpi`, `--gpu-context=rpi`, and `--hwdec=mmal` --- mpv 0.37.0 --- - `--save-position-on-quit` and its associated commands now store state files in %LOCALAPPDATA% instead of %APPDATA% directory by default on Windows. diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index e887e25ba7..e3b49b4087 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -1315,8 +1315,6 @@ Video :mediacodec: requires ``--vo=gpu --gpu-context=android`` or ``--vo=mediacodec_embed`` (Android only) :mediacodec-copy: copies video back to system RAM (Android only) - :mmal: requires ``--vo=gpu`` (Raspberry Pi only - default if available) - :mmal-copy: copies video back to system RAM (Raspberry Pi only) :cuda: requires ``--vo=gpu`` (Any platform CUDA is available) :cuda-copy: copies video back to system RAM (Any platform CUDA is available) :crystalhd: copies video back to system RAM (Any platform supported by hardware) @@ -1411,9 +1409,6 @@ Video affect this additionally. This can give incorrect results even with completely ordinary video sources. - ``rpi`` always uses the hardware overlay renderer, even with - ``--vo=gpu``. - ``mediacodec`` is not safe. It forces RGB conversion (not with ``-copy``) and how well it handles non-standard colorspaces is not known. In the rare cases where 10-bit is supported the bit depth of the output diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst index 5ee4eaa14f..2c9390114d 100644 --- a/DOCS/man/vo.rst +++ b/DOCS/man/vo.rst @@ -567,30 +567,6 @@ Available video output drivers are: This also supports many of the options the ``gpu`` VO has, depending on the backend. -``rpi`` (Raspberry Pi) - Native video output on the Raspberry Pi using the MMAL API. - - The following global options are supported by this video output: - - ``--rpi-display=`` - Select the display number on which the video overlay should be shown - (default: 0). - - ``--rpi-layer=`` - Select the dispmanx layer on which the video overlay should be shown - (default: -10). Note that mpv will also use the 2 layers above the - selected layer, to handle the window background and OSD. Actual video - rendering will happen on the layer above the selected layer. - - ``--rpi-background=`` - Whether to render a black background behind the video (default: no). - Normally it's better to kill the console framebuffer instead, which - gives better performance. - - ``--rpi-osd=`` - Enabled by default. If disabled with ``no``, no OSD layer is created. - This also means there will be no subtitles rendered. - ``drm`` (Direct Rendering Manager) Video output driver using Kernel Mode Setting / Direct Rendering Manager. Should be used when one doesn't want to install full-blown graphical diff --git a/meson.build b/meson.build index 8a96176d85..8c7730f6b8 100644 --- a/meson.build +++ b/meson.build @@ -1219,16 +1219,7 @@ if plain_gl.allowed() features += {'gl': true} endif -rpi = dependency('/opt/vc/lib/pkgconfig/brcmegl.pc', 'brcmegl', required: get_option('rpi')) -features += {'rpi': gl_allowed and rpi.found()} -if features['rpi'] - dependencies += rpi - features += {'gl': true} - sources += files('video/out/opengl/context_rpi.c') -endif - -features += {'egl-helpers': features['egl'] or egl_android.found() or - egl_angle_win32.allowed() or features['rpi']} +features += {'egl-helpers': features['egl'] or egl_android.found() or egl_angle_win32.allowed()} if features['egl-helpers'] sources += files('video/out/opengl/egl_helpers.c') endif @@ -1377,18 +1368,6 @@ if features['ios-gl'] sources += files('video/out/hwdec/hwdec_ios_gl.m') endif -rpi_mmal_opt = get_option('rpi-mmal').require( - features['rpi'], - error_message: 'rpi was not found!', -) -rpi_mmal = dependency('/opt/vc/lib/pkgconfig/mmal.pc', 'mmal', required: rpi_mmal_opt) -features += {'rpi-mmal': rpi_mmal.found()} -if features['rpi-mmal'] - dependencies += rpi_mmal - sources += files('video/out/opengl/hwdec_rpi.c', - 'video/out/vo_rpi.c') -endif - libva = dependency('libva', version: '>= 1.1.0', required: get_option('vaapi')) vaapi_drm = dependency('libva-drm', version: '>= 1.1.0', diff --git a/meson_options.txt b/meson_options.txt index a5454e97ef..cded6a8e61 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -74,7 +74,6 @@ option('gl-dxinterop', type: 'feature', value: 'auto', description: 'OpenGL/Dire option('gl-win32', type: 'feature', value: 'auto', description: 'OpenGL Win32 Backend') option('gl-x11', type: 'feature', value: 'disabled', description: 'OpenGL X11/GLX (deprecated/legacy)') option('jpeg', type: 'feature', value: 'auto', description: 'JPEG support') -option('rpi', type: 'feature', value: 'disabled', description: 'Raspberry Pi support') option('sdl2-video', type: 'feature', value: 'auto', description: 'SDL2 video output') option('shaderc', type: 'feature', value: 'auto', description: 'libshaderc SPIR-V compiler') option('sixel', type: 'feature', value:'auto', description: 'Sixel') @@ -100,7 +99,6 @@ option('d3d-hwaccel', type: 'feature', value: 'auto', description: 'D3D11VA hwac option('d3d9-hwaccel', type: 'feature', value: 'auto', description: 'DXVA2 hwaccel') option('gl-dxinterop-d3d9', type: 'feature', value: 'auto', description: 'OpenGL/DirectX Interop Backend DXVA2 interop') option('ios-gl', type: 'feature', value: 'auto', description: 'iOS OpenGL ES hardware decoding interop support') -option('rpi-mmal', type: 'feature', value: 'auto', description: 'Raspberry Pi MMAL hwaccel') option('videotoolbox-gl', type: 'feature', value: 'auto', description: 'Videotoolbox with OpenGL') option('videotoolbox-pl', type: 'feature', value: 'auto', description: 'Videotoolbox with libplacebo') option('vulkan-interop', type: 'feature', value: 'auto', description: 'Vulkan graphics interop') diff --git a/video/out/gpu/context.c b/video/out/gpu/context.c index 41ec2a4107..d842ddda91 100644 --- a/video/out/gpu/context.c +++ b/video/out/gpu/context.c @@ -41,7 +41,6 @@ extern const struct ra_ctx_fns ra_ctx_wayland_egl; extern const struct ra_ctx_fns ra_ctx_wgl; extern const struct ra_ctx_fns ra_ctx_angle; extern const struct ra_ctx_fns ra_ctx_dxgl; -extern const struct ra_ctx_fns ra_ctx_rpi; extern const struct ra_ctx_fns ra_ctx_android; /* Vulkan */ @@ -67,9 +66,6 @@ static const struct ra_ctx_fns *contexts[] = { #if HAVE_EGL_ANDROID &ra_ctx_android, #endif -#if HAVE_RPI - &ra_ctx_rpi, -#endif #if HAVE_EGL_ANGLE_WIN32 &ra_ctx_angle, #endif diff --git a/video/out/gpu/hwdec.c b/video/out/gpu/hwdec.c index c8098f3aad..c7d817c63a 100644 --- a/video/out/gpu/hwdec.c +++ b/video/out/gpu/hwdec.c @@ -34,7 +34,6 @@ extern const struct ra_hwdec_driver ra_hwdec_dxva2gldx; extern const struct ra_hwdec_driver ra_hwdec_d3d11va; extern const struct ra_hwdec_driver ra_hwdec_dxva2dxgi; extern const struct ra_hwdec_driver ra_hwdec_cuda; -extern const struct ra_hwdec_driver ra_hwdec_rpi_overlay; extern const struct ra_hwdec_driver ra_hwdec_drmprime; extern const struct ra_hwdec_driver ra_hwdec_drmprime_overlay; extern const struct ra_hwdec_driver ra_hwdec_aimagereader; @@ -70,9 +69,6 @@ const struct ra_hwdec_driver *const ra_hwdec_drivers[] = { #if HAVE_VDPAU_GL_X11 &ra_hwdec_vdpau, #endif -#if HAVE_RPI_MMAL - &ra_hwdec_rpi_overlay, -#endif #if HAVE_DRM &ra_hwdec_drmprime, &ra_hwdec_drmprime_overlay, diff --git a/video/out/opengl/context_rpi.c b/video/out/opengl/context_rpi.c deleted file mode 100644 index 0b6babbd1f..0000000000 --- a/video/out/opengl/context_rpi.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see . - */ - -#include -#include -#include - -#include - -#include -#include - -#include "common/common.h" -#include "video/out/win_state.h" -#include "context.h" -#include "egl_helpers.h" - -struct priv { - struct GL gl; - DISPMANX_DISPLAY_HANDLE_T display; - DISPMANX_ELEMENT_HANDLE_T window; - DISPMANX_UPDATE_HANDLE_T update; - EGLDisplay egl_display; - EGLConfig egl_config; - EGLContext egl_context; - EGLSurface egl_surface; - // yep, the API keeps a pointer to it - EGL_DISPMANX_WINDOW_T egl_window; - int x, y, w, h; - double display_fps; - atomic_int reload_display; - int win_params[4]; -}; - -static void tv_callback(void *callback_data, uint32_t reason, uint32_t param1, - uint32_t param2) -{ - struct ra_ctx *ctx = callback_data; - struct priv *p = ctx->priv; - atomic_store(&p->reload_display, true); - vo_wakeup(ctx->vo); -} - -static void destroy_dispmanx(struct ra_ctx *ctx) -{ - struct priv *p = ctx->priv; - - if (p->egl_surface) { - eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT); - eglDestroySurface(p->egl_display, p->egl_surface); - p->egl_surface = EGL_NO_SURFACE; - } - - if (p->window) - vc_dispmanx_element_remove(p->update, p->window); - p->window = 0; - if (p->display) - vc_dispmanx_display_close(p->display); - p->display = 0; - if (p->update) - vc_dispmanx_update_submit_sync(p->update); - p->update = 0; -} - -static void rpi_uninit(struct ra_ctx *ctx) -{ - struct priv *p = ctx->priv; - ra_gl_ctx_uninit(ctx); - - vc_tv_unregister_callback_full(tv_callback, ctx); - - destroy_dispmanx(ctx); - - if (p->egl_context) - eglDestroyContext(p->egl_display, p->egl_context); - p->egl_context = EGL_NO_CONTEXT; - eglReleaseThread(); - p->egl_display = EGL_NO_DISPLAY; -} - -static bool recreate_dispmanx(struct ra_ctx *ctx) -{ - struct priv *p = ctx->priv; - int display_nr = 0; - int layer = 0; - - MP_VERBOSE(ctx, "Recreating DISPMANX state...\n"); - - destroy_dispmanx(ctx); - - p->display = vc_dispmanx_display_open(display_nr); - p->update = vc_dispmanx_update_start(0); - if (!p->display || !p->update) { - MP_FATAL(ctx, "Could not get DISPMANX objects.\n"); - goto fail; - } - - uint32_t dispw, disph; - if (graphics_get_display_size(0, &dispw, &disph) < 0) { - MP_FATAL(ctx, "Could not get display size.\n"); - goto fail; - } - p->w = dispw; - p->h = disph; - - if (ctx->vo->opts->fullscreen) { - p->x = p->y = 0; - } else { - struct vo_win_geometry geo; - struct mp_rect screenrc = {0, 0, p->w, p->h}; - - vo_calc_window_geometry(ctx->vo, &screenrc, &geo); - - mp_rect_intersection(&geo.win, &screenrc); - - p->x = geo.win.x0; - p->y = geo.win.y0; - p->w = geo.win.x1 - geo.win.x0; - p->h = geo.win.y1 - geo.win.y0; - } - - // dispmanx is like a neanderthal version of Wayland - you can add an - // overlay any place on the screen. - VC_RECT_T dst = {.x = p->x, .y = p->y, .width = p->w, .height = p->h}; - VC_RECT_T src = {.width = p->w << 16, .height = p->h << 16}; - VC_DISPMANX_ALPHA_T alpha = { - .flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, - .opacity = 0xFF, - }; - p->window = vc_dispmanx_element_add(p->update, p->display, layer, &dst, 0, - &src, DISPMANX_PROTECTION_NONE, &alpha, - 0, 0); - if (!p->window) { - MP_FATAL(ctx, "Could not add DISPMANX element.\n"); - goto fail; - } - - vc_dispmanx_update_submit_sync(p->update); - p->update = vc_dispmanx_update_start(0); - - p->egl_window = (EGL_DISPMANX_WINDOW_T){ - .element = p->window, - .width = p->w, - .height = p->h, - }; - p->egl_surface = eglCreateWindowSurface(p->egl_display, p->egl_config, - &p->egl_window, NULL); - - if (p->egl_surface == EGL_NO_SURFACE) { - MP_FATAL(ctx, "Could not create EGL surface!\n"); - goto fail; - } - - if (!eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface, - p->egl_context)) - { - MP_FATAL(ctx, "Failed to set context!\n"); - goto fail; - } - - p->display_fps = 0; - TV_GET_STATE_RESP_T tvstate; - TV_DISPLAY_STATE_T tvstate_disp; - if (!vc_tv_get_state(&tvstate) && !vc_tv_get_display_state(&tvstate_disp)) { - if (tvstate_disp.state & (VC_HDMI_HDMI | VC_HDMI_DVI)) { - p->display_fps = tvstate_disp.display.hdmi.frame_rate; - - HDMI_PROPERTY_PARAM_T param = { - .property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE, - }; - if (!vc_tv_hdmi_get_property(¶m) && - param.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC) - p->display_fps = p->display_fps / 1.001; - } else { - p->display_fps = tvstate_disp.display.sdtv.frame_rate; - } - } - - p->win_params[0] = display_nr; - p->win_params[1] = layer; - p->win_params[2] = p->x; - p->win_params[3] = p->y; - - ctx->vo->dwidth = p->w; - ctx->vo->dheight = p->h; - if (ctx->swapchain) - ra_gl_ctx_resize(ctx->swapchain, p->w, p->h, 0); - - ctx->vo->want_redraw = true; - - vo_event(ctx->vo, VO_EVENT_WIN_STATE); - return true; - -fail: - destroy_dispmanx(ctx); - return false; -} - -static void rpi_swap_buffers(struct ra_ctx *ctx) -{ - struct priv *p = ctx->priv; - eglSwapBuffers(p->egl_display, p->egl_surface); -} - -static bool rpi_init(struct ra_ctx *ctx) -{ - struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); - - bcm_host_init(); - - vc_tv_register_callback(tv_callback, ctx); - - p->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (!eglInitialize(p->egl_display, NULL, NULL)) { - MP_FATAL(ctx, "EGL failed to initialize.\n"); - goto fail; - } - - if (!mpegl_create_context(ctx, p->egl_display, &p->egl_context, &p->egl_config)) - goto fail; - - if (!recreate_dispmanx(ctx)) - goto fail; - - mpegl_load_functions(&p->gl, ctx->log); - - struct ra_gl_ctx_params params = { - .swap_buffers = rpi_swap_buffers, - }; - - if (!ra_gl_ctx_init(ctx, &p->gl, params)) - goto fail; - - ra_add_native_resource(ctx->ra, "MPV_RPI_WINDOW", p->win_params); - - ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, 0); - return true; - -fail: - rpi_uninit(ctx); - return false; -} - -static bool rpi_reconfig(struct ra_ctx *ctx) -{ - return recreate_dispmanx(ctx); -} - -static struct mp_image *take_screenshot(struct ra_ctx *ctx) -{ - struct priv *p = ctx->priv; - - if (!p->display) - return NULL; - - struct mp_image *img = mp_image_alloc(IMGFMT_BGR0, p->w, p->h); - if (!img) - return NULL; - - DISPMANX_RESOURCE_HANDLE_T resource = - vc_dispmanx_resource_create(VC_IMAGE_ARGB8888, - img->w | ((img->w * 4) << 16), img->h, - &(int32_t){0}); - if (!resource) - goto fail; - - if (vc_dispmanx_snapshot(p->display, resource, 0)) - goto fail; - - VC_RECT_T rc = {.width = img->w, .height = img->h}; - if (vc_dispmanx_resource_read_data(resource, &rc, img->planes[0], img->stride[0])) - goto fail; - - vc_dispmanx_resource_delete(resource); - return img; - -fail: - vc_dispmanx_resource_delete(resource); - talloc_free(img); - return NULL; -} - -static int rpi_control(struct ra_ctx *ctx, int *events, int request, void *arg) -{ - struct priv *p = ctx->priv; - - switch (request) { - case VOCTRL_SCREENSHOT_WIN: - *(struct mp_image **)arg = take_screenshot(ctx); - return VO_TRUE; - case VOCTRL_CHECK_EVENTS: - if (atomic_fetch_and(&p->reload_display, 0)) { - MP_WARN(ctx, "Recovering from display mode switch...\n"); - recreate_dispmanx(ctx); - } - return VO_TRUE; - case VOCTRL_GET_DISPLAY_FPS: - *(double *)arg = p->display_fps; - return VO_TRUE; - } - - return VO_NOTIMPL; -} - -const struct ra_ctx_fns ra_ctx_rpi = { - .type = "opengl", - .name = "rpi", - .reconfig = rpi_reconfig, - .control = rpi_control, - .init = rpi_init, - .uninit = rpi_uninit, -}; diff --git a/video/out/opengl/hwdec_rpi.c b/video/out/opengl/hwdec_rpi.c deleted file mode 100644 index b90fae571d..0000000000 --- a/video/out/opengl/hwdec_rpi.c +++ /dev/null @@ -1,384 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see . - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include "common/common.h" -#include "common/msg.h" -#include "video/mp_image.h" -#include "video/out/gpu/hwdec.h" - -#include "common.h" - -struct priv { - struct mp_log *log; - - struct mp_image_params params; - - MMAL_COMPONENT_T *renderer; - bool renderer_enabled; - - // for RAM input - MMAL_POOL_T *swpool; - - struct mp_image *current_frame; - - struct mp_rect src, dst; - int cur_window[4]; // raw user params -}; - -// Magic alignments (in pixels) expected by the MMAL internals. -#define ALIGN_W 32 -#define ALIGN_H 16 - -// Make mpi point to buffer, assuming MMAL_ENCODING_I420. -// buffer can be NULL. -// Return the required buffer space. -static size_t layout_buffer(struct mp_image *mpi, MMAL_BUFFER_HEADER_T *buffer, - struct mp_image_params *params) -{ - assert(params->imgfmt == IMGFMT_420P); - mp_image_set_params(mpi, params); - int w = MP_ALIGN_UP(params->w, ALIGN_W); - int h = MP_ALIGN_UP(params->h, ALIGN_H); - uint8_t *cur = buffer ? buffer->data : NULL; - size_t size = 0; - for (int i = 0; i < 3; i++) { - int div = i ? 2 : 1; - mpi->planes[i] = cur; - mpi->stride[i] = w / div; - size_t plane_size = h / div * mpi->stride[i]; - if (cur) - cur += plane_size; - size += plane_size; - } - return size; -} - -static MMAL_FOURCC_T map_csp(enum pl_color_system csp) -{ - switch (csp) { - case PL_COLOR_SYSTEM_BT_601: return MMAL_COLOR_SPACE_ITUR_BT601; - case PL_COLOR_SYSTEM_BT_709: return MMAL_COLOR_SPACE_ITUR_BT709; - case PL_COLOR_SYSTEM_SMPTE_240M: return MMAL_COLOR_SPACE_SMPTE240M; - default: return MMAL_COLOR_SPACE_UNKNOWN; - } -} - -static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) -{ - mmal_buffer_header_release(buffer); -} - -static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) -{ - struct mp_image *mpi = buffer->user_data; - talloc_free(mpi); -} - -static void disable_renderer(struct ra_hwdec *hw) -{ - struct priv *p = hw->priv; - - if (p->renderer_enabled) { - mmal_port_disable(p->renderer->control); - mmal_port_disable(p->renderer->input[0]); - - mmal_port_flush(p->renderer->control); - mmal_port_flush(p->renderer->input[0]); - - mmal_component_disable(p->renderer); - } - mmal_pool_destroy(p->swpool); - p->swpool = NULL; - p->renderer_enabled = false; -} - -// check_window_only: assume params and dst/src rc are unchanged -static void update_overlay(struct ra_hwdec *hw, bool check_window_only) -{ - struct priv *p = hw->priv; - MMAL_PORT_T *input = p->renderer->input[0]; - struct mp_rect src = p->src; - struct mp_rect dst = p->dst; - - int defs[4] = {0, 0, 0, 0}; - int *z = ra_get_native_resource(hw->ra_ctx->ra, "MPV_RPI_WINDOW"); - if (!z) - z = defs; - - // As documented in the libmpv openglcb headers. - int display = z[0]; - int layer = z[1]; - int x = z[2]; - int y = z[3]; - - if (check_window_only && memcmp(z, p->cur_window, sizeof(p->cur_window)) == 0) - return; - - memcpy(p->cur_window, z, sizeof(p->cur_window)); - - int rotate[] = {MMAL_DISPLAY_ROT0, - MMAL_DISPLAY_ROT90, - MMAL_DISPLAY_ROT180, - MMAL_DISPLAY_ROT270}; - - int src_w = src.x1 - src.x0, src_h = src.y1 - src.y0, - dst_w = dst.x1 - dst.x0, dst_h = dst.y1 - dst.y0; - int p_x, p_y; - av_reduce(&p_x, &p_y, dst_w * src_h, src_w * dst_h, 16000); - MMAL_DISPLAYREGION_T dr = { - .hdr = { .id = MMAL_PARAMETER_DISPLAYREGION, - .size = sizeof(MMAL_DISPLAYREGION_T), }, - .src_rect = { .x = src.x0, .y = src.y0, - .width = src_w, .height = src_h }, - .dest_rect = { .x = dst.x0 + x, .y = dst.y0 + y, - .width = dst_w, .height = dst_h }, - .layer = layer - 1, // under the GL layer - .display_num = display, - .pixel_x = p_x, - .pixel_y = p_y, - .transform = rotate[p->params.rotate / 90], - .fullscreen = 0, - .set = MMAL_DISPLAY_SET_SRC_RECT | MMAL_DISPLAY_SET_DEST_RECT | - MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_NUM | - MMAL_DISPLAY_SET_PIXEL | MMAL_DISPLAY_SET_TRANSFORM | - MMAL_DISPLAY_SET_FULLSCREEN, - }; - - if (p->params.rotate % 180 == 90) { - MPSWAP(int, dr.src_rect.x, dr.src_rect.y); - MPSWAP(int, dr.src_rect.width, dr.src_rect.height); - } - - if (mmal_port_parameter_set(input, &dr.hdr)) - MP_WARN(p, "could not set video rectangle\n"); -} - -static int enable_renderer(struct ra_hwdec *hw) -{ - struct priv *p = hw->priv; - MMAL_PORT_T *input = p->renderer->input[0]; - struct mp_image_params *params = &p->params; - - if (p->renderer_enabled) - return 0; - - if (!params->imgfmt) - return -1; - - bool opaque = params->imgfmt == IMGFMT_MMAL; - - input->format->encoding = opaque ? MMAL_ENCODING_OPAQUE : MMAL_ENCODING_I420; - input->format->es->video.width = MP_ALIGN_UP(params->w, ALIGN_W); - input->format->es->video.height = MP_ALIGN_UP(params->h, ALIGN_H); - input->format->es->video.crop = (MMAL_RECT_T){0, 0, params->w, params->h}; - input->format->es->video.par = (MMAL_RATIONAL_T){params->p_w, params->p_h}; - input->format->es->video.color_space = map_csp(params->repr.sys); - - if (mmal_port_format_commit(input)) - return -1; - - input->buffer_num = MPMAX(input->buffer_num_min, - input->buffer_num_recommended) + 3; - input->buffer_size = MPMAX(input->buffer_size_min, - input->buffer_size_recommended); - - if (!opaque) { - size_t size = layout_buffer(&(struct mp_image){0}, NULL, params); - if (input->buffer_size != size) { - MP_FATAL(hw, "We disagree with MMAL about buffer sizes.\n"); - return -1; - } - - p->swpool = mmal_pool_create(input->buffer_num, input->buffer_size); - if (!p->swpool) { - MP_FATAL(hw, "Could not allocate buffer pool.\n"); - return -1; - } - } - - update_overlay(hw, false); - - p->renderer_enabled = true; - - if (mmal_port_enable(p->renderer->control, control_port_cb)) - return -1; - - if (mmal_port_enable(input, input_port_cb)) - return -1; - - if (mmal_component_enable(p->renderer)) { - MP_FATAL(hw, "Failed to enable video renderer.\n"); - return -1; - } - - return 0; -} - -static void free_mmal_buffer(void *arg) -{ - MMAL_BUFFER_HEADER_T *buffer = arg; - mmal_buffer_header_release(buffer); -} - -static struct mp_image *upload(struct ra_hwdec *hw, struct mp_image *hw_image) -{ - struct priv *p = hw->priv; - - MMAL_BUFFER_HEADER_T *buffer = mmal_queue_wait(p->swpool->queue); - if (!buffer) { - MP_ERR(hw, "Can't allocate buffer.\n"); - return NULL; - } - mmal_buffer_header_reset(buffer); - - struct mp_image *new_ref = mp_image_new_custom_ref(NULL, buffer, - free_mmal_buffer); - if (!new_ref) { - mmal_buffer_header_release(buffer); - MP_ERR(hw, "Out of memory.\n"); - return NULL; - } - - mp_image_setfmt(new_ref, IMGFMT_MMAL); - new_ref->planes[3] = (void *)buffer; - - struct mp_image dmpi = {0}; - buffer->length = layout_buffer(&dmpi, buffer, &p->params); - mp_image_copy(&dmpi, hw_image); - - return new_ref; -} - -static int overlay_frame(struct ra_hwdec *hw, struct mp_image *hw_image, - struct mp_rect *src, struct mp_rect *dst, bool newframe) -{ - struct priv *p = hw->priv; - - if (hw_image && !mp_image_params_equal(&p->params, &hw_image->params)) { - p->params = hw_image->params; - - disable_renderer(hw); - mp_image_unrefp(&p->current_frame); - - if (enable_renderer(hw) < 0) - return -1; - } - - if (hw_image && p->current_frame && !newframe) { - if (!mp_rect_equals(&p->src, src) ||mp_rect_equals(&p->dst, dst)) { - p->src = *src; - p->dst = *dst; - update_overlay(hw, false); - } - return 0; // don't reupload - } - - mp_image_unrefp(&p->current_frame); - - if (!hw_image) { - disable_renderer(hw); - return 0; - } - - if (enable_renderer(hw) < 0) - return -1; - - update_overlay(hw, true); - - struct mp_image *mpi = NULL; - if (hw_image->imgfmt == IMGFMT_MMAL) { - mpi = mp_image_new_ref(hw_image); - } else { - mpi = upload(hw, hw_image); - } - - if (!mpi) { - disable_renderer(hw); - return -1; - } - - MMAL_BUFFER_HEADER_T *ref = (void *)mpi->planes[3]; - - // Assume this field is free for use by us. - ref->user_data = mpi; - - if (mmal_port_send_buffer(p->renderer->input[0], ref)) { - MP_ERR(hw, "could not queue picture!\n"); - talloc_free(mpi); - return -1; - } - - return 0; -} - -static void destroy(struct ra_hwdec *hw) -{ - struct priv *p = hw->priv; - - disable_renderer(hw); - - if (p->renderer) - mmal_component_release(p->renderer); - - mmal_vc_deinit(); -} - -static int create(struct ra_hwdec *hw) -{ - struct priv *p = hw->priv; - p->log = hw->log; - - bcm_host_init(); - - if (mmal_vc_init()) { - MP_FATAL(hw, "Could not initialize MMAL.\n"); - return -1; - } - - if (mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &p->renderer)) - { - MP_FATAL(hw, "Could not create MMAL renderer.\n"); - mmal_vc_deinit(); - return -1; - } - - return 0; -} - -const struct ra_hwdec_driver ra_hwdec_rpi_overlay = { - .name = "rpi-overlay", - .priv_size = sizeof(struct priv), - .imgfmts = {IMGFMT_MMAL, IMGFMT_420P, 0}, - .init = create, - .overlay_frame = overlay_frame, - .uninit = destroy, -}; diff --git a/video/out/vo.c b/video/out/vo.c index ce93c66810..aa98bc8aea 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -63,7 +63,6 @@ extern const struct vo_driver video_out_sdl; extern const struct vo_driver video_out_vaapi; extern const struct vo_driver video_out_dmabuf_wayland; extern const struct vo_driver video_out_wlshm; -extern const struct vo_driver video_out_rpi; extern const struct vo_driver video_out_tct; extern const struct vo_driver video_out_sixel; extern const struct vo_driver video_out_kitty; @@ -110,9 +109,6 @@ static const struct vo_driver *const video_out_drivers[] = #if HAVE_DRM &video_out_drm, #endif -#if HAVE_RPI_MMAL - &video_out_rpi, -#endif #if HAVE_SIXEL &video_out_sixel, #endif diff --git a/video/out/vo_rpi.c b/video/out/vo_rpi.c deleted file mode 100644 index 7fd65979b0..0000000000 --- a/video/out/vo_rpi.c +++ /dev/null @@ -1,938 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see . - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "common/common.h" -#include "common/msg.h" -#include "opengl/common.h" -#include "options/m_config.h" -#include "osdep/timer.h" -#include "vo.h" -#include "win_state.h" -#include "video/mp_image.h" -#include "sub/osd.h" - -#include "opengl/ra_gl.h" -#include "gpu/video.h" - -struct mp_egl_rpi { - struct mp_log *log; - struct GL *gl; - struct ra *ra; - EGLDisplay egl_display; - EGLConfig egl_config; - EGLContext egl_context; - EGLSurface egl_surface; - // yep, the API keeps a pointer to it - EGL_DISPMANX_WINDOW_T egl_window; -}; - -struct priv { - DISPMANX_DISPLAY_HANDLE_T display; - DISPMANX_ELEMENT_HANDLE_T window; - DISPMANX_ELEMENT_HANDLE_T osd_overlay; - DISPMANX_UPDATE_HANDLE_T update; - uint32_t w, h; - uint32_t x, y; - double display_fps; - - double osd_pts; - struct mp_osd_res osd_res; - struct m_config_cache *opts_cache; - - struct mp_egl_rpi egl; - struct gl_video *gl_video; - struct mpgl_osd *osd; - - MMAL_COMPONENT_T *renderer; - bool renderer_enabled; - - bool display_synced, skip_osd; - struct mp_image *next_image; - - // for RAM input - MMAL_POOL_T *swpool; - - mp_mutex display_mutex; - mp_cond display_cond; - int64_t vsync_counter; - bool reload_display; - - int background_layer; - int video_layer; - int osd_layer; - - int display_nr; - int layer; - bool background; - bool enable_osd; -}; - -// Magic alignments (in pixels) expected by the MMAL internals. -#define ALIGN_W 32 -#define ALIGN_H 16 - -static void recreate_renderer(struct vo *vo); - -static void *get_proc_address(const GLubyte *name) -{ - void *p = eglGetProcAddress(name); - // EGL 1.4 (supported by the RPI firmware) does not necessarily return - // function pointers for core functions. - if (!p) { - void *h = dlopen("/opt/vc/lib/libbrcmGLESv2.so", RTLD_LAZY); - if (h) { - p = dlsym(h, name); - dlclose(h); - } - } - return p; -} - -static EGLConfig select_fb_config_egl(struct mp_egl_rpi *p) -{ - EGLint attributes[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_DEPTH_SIZE, 0, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_NONE - }; - - EGLint config_count; - EGLConfig config; - - eglChooseConfig(p->egl_display, attributes, &config, 1, &config_count); - - if (!config_count) { - MP_FATAL(p, "Could find EGL configuration!\n"); - return NULL; - } - - return config; -} - -static void mp_egl_rpi_destroy(struct mp_egl_rpi *p) -{ - if (p->egl_display) { - eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT); - } - if (p->egl_surface) - eglDestroySurface(p->egl_display, p->egl_surface); - if (p->egl_context) - eglDestroyContext(p->egl_display, p->egl_context); - p->egl_context = EGL_NO_CONTEXT; - eglReleaseThread(); - p->egl_display = EGL_NO_DISPLAY; - talloc_free(p->gl); - p->gl = NULL; -} - -static int mp_egl_rpi_init(struct mp_egl_rpi *p, DISPMANX_ELEMENT_HANDLE_T window, - int w, int h) -{ - p->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (!eglInitialize(p->egl_display, NULL, NULL)) { - MP_FATAL(p, "EGL failed to initialize.\n"); - goto fail; - } - - eglBindAPI(EGL_OPENGL_ES_API); - - EGLConfig config = select_fb_config_egl(p); - if (!config) - goto fail; - - p->egl_window = (EGL_DISPMANX_WINDOW_T){ - .element = window, - .width = w, - .height = h, - }; - p->egl_surface = eglCreateWindowSurface(p->egl_display, config, - &p->egl_window, NULL); - - if (p->egl_surface == EGL_NO_SURFACE) { - MP_FATAL(p, "Could not create EGL surface!\n"); - goto fail; - } - - EGLint context_attributes[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - p->egl_context = eglCreateContext(p->egl_display, config, - EGL_NO_CONTEXT, context_attributes); - - if (p->egl_context == EGL_NO_CONTEXT) { - MP_FATAL(p, "Could not create EGL context!\n"); - goto fail; - } - - eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface, - p->egl_context); - - p->gl = talloc_zero(NULL, struct GL); - - const char *exts = eglQueryString(p->egl_display, EGL_EXTENSIONS); - mpgl_load_functions(p->gl, get_proc_address, exts, p->log); - - if (!p->gl->version && !p->gl->es) - goto fail; - - p->ra = ra_create_gl(p->gl, p->log); - if (!p->ra) - goto fail; - - return 0; - -fail: - mp_egl_rpi_destroy(p); - return -1; -} - -// Make mpi point to buffer, assuming MMAL_ENCODING_I420. -// buffer can be NULL. -// Return the required buffer space. -static size_t layout_buffer(struct mp_image *mpi, MMAL_BUFFER_HEADER_T *buffer, - struct mp_image_params *params) -{ - assert(params->imgfmt == IMGFMT_420P); - mp_image_set_params(mpi, params); - int w = MP_ALIGN_UP(params->w, ALIGN_W); - int h = MP_ALIGN_UP(params->h, ALIGN_H); - uint8_t *cur = buffer ? buffer->data : NULL; - size_t size = 0; - for (int i = 0; i < 3; i++) { - int div = i ? 2 : 1; - mpi->planes[i] = cur; - mpi->stride[i] = w / div; - size_t plane_size = h / div * mpi->stride[i]; - if (cur) - cur += plane_size; - size += plane_size; - } - return size; -} - -static void update_osd(struct vo *vo) -{ - struct priv *p = vo->priv; - if (!p->enable_osd) - return; - - if (!gl_video_check_osd_change(p->gl_video, &p->osd_res, p->osd_pts)) { - p->skip_osd = true; - return; - } - - MP_STATS(vo, "start rpi_osd"); - - struct vo_frame frame = {0}; - struct ra_fbo target = { - .tex = ra_create_wrapped_fb(p->egl.ra, 0, p->osd_res.w, p->osd_res.h), - .flip = true, - }; - gl_video_set_osd_pts(p->gl_video, p->osd_pts); - gl_video_render_frame(p->gl_video, &frame, &target, RENDER_FRAME_DEF); - ra_tex_free(p->egl.ra, &target.tex); - - MP_STATS(vo, "stop rpi_osd"); -} - -static void resize(struct vo *vo) -{ - struct priv *p = vo->priv; - MMAL_PORT_T *input = p->renderer->input[0]; - - struct mp_rect src, dst; - - vo_get_src_dst_rects(vo, &src, &dst, &p->osd_res); - - int rotate[] = {MMAL_DISPLAY_ROT0, - MMAL_DISPLAY_ROT90, - MMAL_DISPLAY_ROT180, - MMAL_DISPLAY_ROT270}; - - - int src_w = src.x1 - src.x0, src_h = src.y1 - src.y0, - dst_w = dst.x1 - dst.x0, dst_h = dst.y1 - dst.y0; - int p_x, p_y; - av_reduce(&p_x, &p_y, dst_w * src_h, src_w * dst_h, 16000); - MMAL_DISPLAYREGION_T dr = { - .hdr = { .id = MMAL_PARAMETER_DISPLAYREGION, - .size = sizeof(MMAL_DISPLAYREGION_T), }, - .src_rect = { .x = src.x0, .y = src.y0, .width = src_w, .height = src_h }, - .dest_rect = { .x = dst.x0 + p->x, .y = dst.y0 + p->y, - .width = dst_w, .height = dst_h }, - .layer = p->video_layer, - .display_num = p->display_nr, - .pixel_x = p_x, - .pixel_y = p_y, - .transform = rotate[vo->params ? vo->params->rotate / 90 : 0], - .fullscreen = vo->opts->fullscreen, - .set = MMAL_DISPLAY_SET_SRC_RECT | MMAL_DISPLAY_SET_DEST_RECT | - MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_NUM | - MMAL_DISPLAY_SET_PIXEL | MMAL_DISPLAY_SET_TRANSFORM | - MMAL_DISPLAY_SET_FULLSCREEN, - }; - - if (vo->params && (vo->params->rotate % 180) == 90) { - MPSWAP(int, dr.src_rect.x, dr.src_rect.y); - MPSWAP(int, dr.src_rect.width, dr.src_rect.height); - } - - if (mmal_port_parameter_set(input, &dr.hdr)) - MP_WARN(vo, "could not set video rectangle\n"); - - if (p->gl_video) - gl_video_resize(p->gl_video, &src, &dst, &p->osd_res); -} - -static void destroy_overlays(struct vo *vo) -{ - struct priv *p = vo->priv; - - if (p->window) - vc_dispmanx_element_remove(p->update, p->window); - p->window = 0; - - gl_video_uninit(p->gl_video); - p->gl_video = NULL; - ra_free(&p->egl.ra); - mp_egl_rpi_destroy(&p->egl); - - if (p->osd_overlay) - vc_dispmanx_element_remove(p->update, p->osd_overlay); - p->osd_overlay = 0; -} - -static int update_display_size(struct vo *vo) -{ - struct priv *p = vo->priv; - - uint32_t n_w = 0, n_h = 0; - if (graphics_get_display_size(0, &n_w, &n_h) < 0) { - MP_FATAL(vo, "Could not get display size.\n"); - return -1; - } - - if (p->w == n_w && p->h == n_h) - return 0; - - p->w = n_w; - p->h = n_h; - - MP_VERBOSE(vo, "Display size: %dx%d\n", p->w, p->h); - - return 0; -} - -static int create_overlays(struct vo *vo) -{ - struct priv *p = vo->priv; - destroy_overlays(vo); - - if (!p->display) - return -1; - - if (vo->opts->fullscreen && p->background) { - // Use the whole screen. - VC_RECT_T dst = {.width = p->w, .height = p->h}; - VC_RECT_T src = {.width = 1 << 16, .height = 1 << 16}; - VC_DISPMANX_ALPHA_T alpha = { - .flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, - .opacity = 0xFF, - }; - - p->window = vc_dispmanx_element_add(p->update, p->display, - p->background_layer, - &dst, 0, &src, - DISPMANX_PROTECTION_NONE, - &alpha, 0, 0); - if (!p->window) { - MP_FATAL(vo, "Could not add DISPMANX element.\n"); - return -1; - } - } - - if (p->enable_osd) { - VC_RECT_T dst = {.x = p->x, .y = p->y, - .width = p->osd_res.w, .height = p->osd_res.h}; - VC_RECT_T src = {.width = p->osd_res.w << 16, .height = p->osd_res.h << 16}; - VC_DISPMANX_ALPHA_T alpha = { - .flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE, - .opacity = 0xFF, - }; - p->osd_overlay = vc_dispmanx_element_add(p->update, p->display, - p->osd_layer, - &dst, 0, &src, - DISPMANX_PROTECTION_NONE, - &alpha, 0, 0); - if (!p->osd_overlay) { - MP_FATAL(vo, "Could not add DISPMANX element.\n"); - return -1; - } - - if (mp_egl_rpi_init(&p->egl, p->osd_overlay, - p->osd_res.w, p->osd_res.h) < 0) - { - MP_FATAL(vo, "EGL/GLES initialization for OSD renderer failed.\n"); - return -1; - } - p->gl_video = gl_video_init(p->egl.ra, vo->log, vo->global); - gl_video_set_clear_color(p->gl_video, (struct m_color){.a = 0}); - gl_video_set_osd_source(p->gl_video, vo->osd); - } - - p->display_fps = 0; - TV_GET_STATE_RESP_T tvstate; - TV_DISPLAY_STATE_T tvstate_disp; - if (!vc_tv_get_state(&tvstate) && !vc_tv_get_display_state(&tvstate_disp)) { - if (tvstate_disp.state & (VC_HDMI_HDMI | VC_HDMI_DVI)) { - p->display_fps = tvstate_disp.display.hdmi.frame_rate; - - HDMI_PROPERTY_PARAM_T param = { - .property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE, - }; - if (!vc_tv_hdmi_get_property(¶m) && - param.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC) - p->display_fps = p->display_fps / 1.001; - } else { - p->display_fps = tvstate_disp.display.sdtv.frame_rate; - } - } - - resize(vo); - - vo_event(vo, VO_EVENT_WIN_STATE); - - vc_dispmanx_update_submit_sync(p->update); - p->update = vc_dispmanx_update_start(10); - - return 0; -} - -static int set_geometry(struct vo *vo) -{ - struct priv *p = vo->priv; - - if (vo->opts->fullscreen) { - vo->dwidth = p->w; - vo->dheight = p->h; - p->x = p->y = 0; - } else { - struct vo_win_geometry geo; - struct mp_rect screenrc = {0, 0, p->w, p->h}; - - vo_calc_window_geometry(vo, &screenrc, &geo); - vo_apply_window_geometry(vo, &geo); - - p->x = geo.win.x0; - p->y = geo.win.y0; - } - - resize(vo); - - if (create_overlays(vo) < 0) - return -1; - - return 0; -} - -static void wait_next_vsync(struct vo *vo) -{ - struct priv *p = vo->priv; - mp_mutex_lock(&p->display_mutex); - int64_t end = mp_time_ns() + MP_TIME_MS_TO_NS(50); - int64_t old = p->vsync_counter; - while (old == p->vsync_counter && !p->reload_display) { - if (mp_cond_timedwait_until(&p->display_cond, &p->display_mutex, end)) - break; - } - mp_mutex_unlock(&p->display_mutex); -} - -static void flip_page(struct vo *vo) -{ - struct priv *p = vo->priv; - - if (!p->renderer_enabled) - return; - - struct mp_image *mpi = p->next_image; - p->next_image = NULL; - - // For OSD - if (!p->skip_osd && p->egl.gl) - eglSwapBuffers(p->egl.egl_display, p->egl.egl_surface); - p->skip_osd = false; - - if (mpi) { - MMAL_PORT_T *input = p->renderer->input[0]; - MMAL_BUFFER_HEADER_T *ref = (void *)mpi->planes[3]; - - // Assume this field is free for use by us. - ref->user_data = mpi; - - if (mmal_port_send_buffer(input, ref)) { - MP_ERR(vo, "could not queue picture!\n"); - talloc_free(mpi); - } - } - - if (p->display_synced) - wait_next_vsync(vo); -} - -static void free_mmal_buffer(void *arg) -{ - MMAL_BUFFER_HEADER_T *buffer = arg; - mmal_buffer_header_release(buffer); -} - -static void draw_frame(struct vo *vo, struct vo_frame *frame) -{ - struct priv *p = vo->priv; - - if (!p->renderer_enabled) - return; - - mp_image_t *mpi = NULL; - if (!frame->redraw && !frame->repeat) - mpi = mp_image_new_ref(frame->current); - - talloc_free(p->next_image); - p->next_image = NULL; - - if (mpi) - p->osd_pts = mpi->pts; - - // Redraw only if the OSD has meaningfully changed, which we assume it - // hasn't when a frame is merely repeated for display sync. - p->skip_osd = !frame->redraw && frame->repeat; - - if (!p->skip_osd && p->egl.gl) - update_osd(vo); - - p->display_synced = frame->display_synced; - - if (mpi && mpi->imgfmt != IMGFMT_MMAL) { - MMAL_BUFFER_HEADER_T *buffer = mmal_queue_wait(p->swpool->queue); - if (!buffer) { - talloc_free(mpi); - MP_ERR(vo, "Can't allocate buffer.\n"); - return; - } - mmal_buffer_header_reset(buffer); - - struct mp_image *new_ref = mp_image_new_custom_ref(NULL, buffer, - free_mmal_buffer); - if (!new_ref) { - mmal_buffer_header_release(buffer); - talloc_free(mpi); - MP_ERR(vo, "Out of memory.\n"); - return; - } - - mp_image_setfmt(new_ref, IMGFMT_MMAL); - new_ref->planes[3] = (void *)buffer; - - struct mp_image dmpi = {0}; - buffer->length = layout_buffer(&dmpi, buffer, vo->params); - mp_image_copy(&dmpi, mpi); - - talloc_free(mpi); - mpi = new_ref; - } - - p->next_image = mpi; -} - -static int query_format(struct vo *vo, int format) -{ - return format == IMGFMT_MMAL || format == IMGFMT_420P; -} - -static MMAL_FOURCC_T map_csp(enum pl_color_system csp) -{ - switch (csp) { - case PL_COLOR_SYSTEM_BT_601: return MMAL_COLOR_SPACE_ITUR_BT601; - case PL_COLOR_SYSTEM_BT_709: return MMAL_COLOR_SPACE_ITUR_BT709; - case PL_COLOR_SYSTEM_SMPTE_240M: return MMAL_COLOR_SPACE_SMPTE240M; - default: return MMAL_COLOR_SPACE_UNKNOWN; - } -} - -static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) -{ - mmal_buffer_header_release(buffer); -} - -static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) -{ - struct mp_image *mpi = buffer->user_data; - talloc_free(mpi); -} - -static void disable_renderer(struct vo *vo) -{ - struct priv *p = vo->priv; - - if (p->renderer_enabled) { - mmal_port_disable(p->renderer->control); - mmal_port_disable(p->renderer->input[0]); - - mmal_port_flush(p->renderer->control); - mmal_port_flush(p->renderer->input[0]); - - mmal_component_disable(p->renderer); - } - mmal_pool_destroy(p->swpool); - p->swpool = NULL; - p->renderer_enabled = false; -} - -static int reconfig(struct vo *vo, struct mp_image_params *params) -{ - struct priv *p = vo->priv; - MMAL_PORT_T *input = p->renderer->input[0]; - bool opaque = params->imgfmt == IMGFMT_MMAL; - - if (!p->display) - return -1; - - disable_renderer(vo); - - input->format->encoding = opaque ? MMAL_ENCODING_OPAQUE : MMAL_ENCODING_I420; - input->format->es->video.width = MP_ALIGN_UP(params->w, ALIGN_W); - input->format->es->video.height = MP_ALIGN_UP(params->h, ALIGN_H); - input->format->es->video.crop = (MMAL_RECT_T){0, 0, params->w, params->h}; - input->format->es->video.par = (MMAL_RATIONAL_T){params->p_w, params->p_h}; - input->format->es->video.color_space = map_csp(params->repr.sys); - - if (mmal_port_format_commit(input)) - return -1; - - input->buffer_num = MPMAX(input->buffer_num_min, - input->buffer_num_recommended) + 3; - input->buffer_size = MPMAX(input->buffer_size_min, - input->buffer_size_recommended); - - if (!opaque) { - size_t size = layout_buffer(&(struct mp_image){0}, NULL, params); - if (input->buffer_size != size) { - MP_FATAL(vo, "We disagree with MMAL about buffer sizes.\n"); - return -1; - } - - p->swpool = mmal_pool_create(input->buffer_num, input->buffer_size); - if (!p->swpool) { - MP_FATAL(vo, "Could not allocate buffer pool.\n"); - return -1; - } - } - - if (set_geometry(vo) < 0) - return -1; - - p->renderer_enabled = true; - - if (mmal_port_enable(p->renderer->control, control_port_cb)) - return -1; - - if (mmal_port_enable(input, input_port_cb)) - return -1; - - if (mmal_component_enable(p->renderer)) { - MP_FATAL(vo, "Failed to enable video renderer.\n"); - return -1; - } - - resize(vo); - - return 0; -} - -static struct mp_image *take_screenshot(struct vo *vo) -{ - struct priv *p = vo->priv; - - if (!p->display) - return NULL; - - struct mp_image *img = mp_image_alloc(IMGFMT_BGR0, p->w, p->h); - if (!img) - return NULL; - - DISPMANX_RESOURCE_HANDLE_T resource = - vc_dispmanx_resource_create(VC_IMAGE_ARGB8888, - img->w | ((img->w * 4) << 16), img->h, - &(int32_t){0}); - if (!resource) - goto fail; - - if (vc_dispmanx_snapshot(p->display, resource, 0)) - goto fail; - - VC_RECT_T rc = {.width = img->w, .height = img->h}; - if (vc_dispmanx_resource_read_data(resource, &rc, img->planes[0], img->stride[0])) - goto fail; - - vc_dispmanx_resource_delete(resource); - return img; - -fail: - vc_dispmanx_resource_delete(resource); - talloc_free(img); - return NULL; -} - -static void set_fullscreen(struct vo *vo) { - struct priv *p = vo->priv; - - if (p->renderer_enabled) - set_geometry(vo); - vo->want_redraw = true; -} - -static int control(struct vo *vo, uint32_t request, void *data) -{ - struct priv *p = vo->priv; - - switch (request) { - case VOCTRL_VO_OPTS_CHANGED: { - void *opt; - while (m_config_cache_get_next_changed(p->opts_cache, &opt)) { - struct mp_vo_opts *opts = p->opts_cache->opts; - if (&opts->fullscreen == opt) - set_fullscreen(vo); - } - return VO_TRUE; - } - case VOCTRL_SET_PANSCAN: - if (p->renderer_enabled) - resize(vo); - vo->want_redraw = true; - return VO_TRUE; - case VOCTRL_REDRAW_FRAME: - update_osd(vo); - return VO_TRUE; - case VOCTRL_SCREENSHOT_WIN: - *(struct mp_image **)data = take_screenshot(vo); - return VO_TRUE; - case VOCTRL_CHECK_EVENTS: { - mp_mutex_lock(&p->display_mutex); - bool reload_required = p->reload_display; - p->reload_display = false; - mp_mutex_unlock(&p->display_mutex); - if (reload_required) - recreate_renderer(vo); - return VO_TRUE; - } - case VOCTRL_GET_DISPLAY_FPS: - *(double *)data = p->display_fps; - return VO_TRUE; - case VOCTRL_GET_DISPLAY_RES: - ((int *)data)[0] = p->w; - ((int *)data)[1] = p->h; - return VO_TRUE; - } - - return VO_NOTIMPL; -} - -static void tv_callback(void *callback_data, uint32_t reason, uint32_t param1, - uint32_t param2) -{ - struct vo *vo = callback_data; - struct priv *p = vo->priv; - mp_mutex_lock(&p->display_mutex); - p->reload_display = true; - mp_cond_signal(&p->display_cond); - mp_mutex_unlock(&p->display_mutex); - vo_wakeup(vo); -} - -static void vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *arg) -{ - struct vo *vo = arg; - struct priv *p = vo->priv; - mp_mutex_lock(&p->display_mutex); - p->vsync_counter += 1; - mp_cond_signal(&p->display_cond); - mp_mutex_unlock(&p->display_mutex); -} - -static void destroy_dispmanx(struct vo *vo) -{ - struct priv *p = vo->priv; - - disable_renderer(vo); - destroy_overlays(vo); - - if (p->update) - vc_dispmanx_update_submit_sync(p->update); - p->update = 0; - - if (p->display) { - vc_dispmanx_vsync_callback(p->display, NULL, NULL); - vc_dispmanx_display_close(p->display); - } - p->display = 0; -} - -static int recreate_dispmanx(struct vo *vo) -{ - struct priv *p = vo->priv; - - p->display = vc_dispmanx_display_open(p->display_nr); - p->update = vc_dispmanx_update_start(0); - if (!p->display || !p->update) { - MP_FATAL(vo, "Could not get DISPMANX objects.\n"); - if (p->display) - vc_dispmanx_display_close(p->display); - p->display = 0; - p->update = 0; - return -1; - } - - update_display_size(vo); - - vc_dispmanx_vsync_callback(p->display, vsync_callback, vo); - - return 0; -} - -static void recreate_renderer(struct vo *vo) -{ - MP_WARN(vo, "Recreating renderer after display change.\n"); - - destroy_dispmanx(vo); - recreate_dispmanx(vo); - - if (vo->params) { - if (reconfig(vo, vo->params) < 0) - MP_FATAL(vo, "Recreation failed.\n"); - } -} - -static void uninit(struct vo *vo) -{ - struct priv *p = vo->priv; - - vc_tv_unregister_callback_full(tv_callback, vo); - - talloc_free(p->next_image); - - destroy_dispmanx(vo); - - if (p->renderer) - mmal_component_release(p->renderer); - - mmal_vc_deinit(); - - mp_cond_destroy(&p->display_cond); - mp_mutex_destroy(&p->display_mutex); -} - -static int preinit(struct vo *vo) -{ - struct priv *p = vo->priv; - - p->background_layer = p->layer; - p->video_layer = p->layer + 1; - p->osd_layer = p->layer + 2; - - p->egl.log = vo->log; - - bcm_host_init(); - - if (mmal_vc_init()) { - MP_FATAL(vo, "Could not initialize MMAL.\n"); - return -1; - } - - mp_mutex_init(&p->display_mutex); - mp_cond_init(&p->display_cond); - - p->opts_cache = m_config_cache_alloc(p, vo->global, &vo_sub_opts); - - if (recreate_dispmanx(vo) < 0) - goto fail; - - if (update_display_size(vo) < 0) - goto fail; - - if (mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &p->renderer)) - { - MP_FATAL(vo, "Could not create MMAL renderer.\n"); - goto fail; - } - - vc_tv_register_callback(tv_callback, vo); - - return 0; - -fail: - uninit(vo); - return -1; -} - -#define OPT_BASE_STRUCT struct priv -static const struct m_option options[] = { - {"display", OPT_INT(display_nr)}, - {"layer", OPT_INT(layer), OPTDEF_INT(-10)}, - {"background", OPT_BOOL(background)}, - {"osd", OPT_BOOL(enable_osd), OPTDEF_INT(1)}, - {0}, -}; - -const struct vo_driver video_out_rpi = { - .description = "Raspberry Pi (MMAL)", - .name = "rpi", - .caps = VO_CAP_ROTATE90, - .preinit = preinit, - .query_format = query_format, - .reconfig = reconfig, - .control = control, - .draw_frame = draw_frame, - .flip_page = flip_page, - .uninit = uninit, - .priv_size = sizeof(struct priv), - .options = options, - .options_prefix = "rpi", -};