summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/options.rst1
-rw-r--r--DOCS/man/vo.rst13
-rwxr-xr-xold-configure3
-rw-r--r--options/options.c7
-rw-r--r--video/decode/lavc.h2
-rw-r--r--video/decode/rpi.c56
-rw-r--r--video/decode/vd_lavc.c9
-rw-r--r--video/fmt-conversion.c3
-rw-r--r--video/hwdec.h1
-rw-r--r--video/img_format.h1
-rw-r--r--video/out/gl_common.c3
-rw-r--r--video/out/gl_common.h1
-rw-r--r--video/out/gl_rpi.c219
-rw-r--r--video/out/vo.c4
-rw-r--r--video/out/vo_rpi.c567
-rw-r--r--wscript44
-rw-r--r--wscript_build.py3
17 files changed, 930 insertions, 7 deletions
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index 763e0c832a..45e312eea7 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -555,6 +555,7 @@ Video
:vaapi-copy: copies video back into system RAM (Linux with Intel GPUs only)
:vda: requires ``--vo=opengl`` (OS X only)
:dxva2-copy: copies video back to system RAM (Windows only)
+ :rpi: requires ``--vo=rpi`` (Raspberry Pi only - default if available)
``auto`` tries to automatically enable hardware decoding using the first
available method. This still depends what VO you are using. For example,
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst
index 7ac4cfe367..d0fe3c070a 100644
--- a/DOCS/man/vo.rst
+++ b/DOCS/man/vo.rst
@@ -866,3 +866,16 @@ Available video output drivers are:
``mpv --vo=opengl-cb:help`` for a list.
This also supports the ``vo_cmdline`` command.
+
+``rpi`` (Raspberry Pi)
+ Native video output on the Raspberry Pi using the MMAL API.
+
+ ``display=<number>``
+ Select the display number on which the video overlay should be shown
+ (default: 0).
+
+ ``layer=<number>``
+ 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.
diff --git a/old-configure b/old-configure
index 11c2e3ff90..d4b4f31436 100755
--- a/old-configure
+++ b/old-configure
@@ -932,6 +932,9 @@ cat > $TMPC << EOF
#define HAVE_NETBSD_THREAD_NAME 0
#define HAVE_DXVA2_HWACCEL 0
#define HAVE_FCHMOD 0
+#define HAVE_RPI 0
+#define HAVE_RPI_GLES 0
+#define HAVE_AV_PIX_FMT_MMAL 0
#ifdef __OpenBSD__
#define DEFAULT_CDROM_DEVICE "/dev/rcd0c"
diff --git a/options/options.c b/options/options.c
index 20876c0e4a..80b2c2bc5d 100644
--- a/options/options.c
+++ b/options/options.c
@@ -292,7 +292,8 @@ const m_option_t mp_opts[] = {
{"vda", 2},
{"vaapi", 4},
{"vaapi-copy", 5},
- {"dxva2-copy", 6})),
+ {"dxva2-copy", 6},
+ {"rpi", 7})),
OPT_STRING("hwdec-codecs", hwdec_codecs, 0),
OPT_SUBSTRUCT("sws", vo.sws_opts, sws_conf, 0),
@@ -797,6 +798,10 @@ const struct MPOpts mp_default_opts = {
.mf_fps = 1.0,
+#if HAVE_RPI
+ .hwdec_api = -1,
+#endif
+
.display_tags = (char **)(const char*[]){
"Artist", "Album", "Album_Artist", "Comment", "Composer", "Genre",
"Performer", "Title", "Track", "icy-title",
diff --git a/video/decode/lavc.h b/video/decode/lavc.h
index 85cf49abab..eaddd4abc2 100644
--- a/video/decode/lavc.h
+++ b/video/decode/lavc.h
@@ -52,6 +52,8 @@ struct vd_lavc_hwdec {
// For horrible Intel shit-drivers only
void (*lock)(struct lavc_ctx *ctx);
void (*unlock)(struct lavc_ctx *ctx);
+ // Optional; if a special hardware decoder is needed (instead of "hwaccel").
+ const char *(*get_codec)(struct lavc_ctx *ctx);
};
enum {
diff --git a/video/decode/rpi.c b/video/decode/rpi.c
new file mode 100644
index 0000000000..44a550fe2e
--- /dev/null
+++ b/video/decode/rpi.c
@@ -0,0 +1,56 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "lavc.h"
+#include "common/common.h"
+
+static int init_decoder(struct lavc_ctx *ctx, int fmt, int w, int h)
+{
+ return 0;
+}
+
+static void uninit(struct lavc_ctx *ctx)
+{
+}
+
+static int init(struct lavc_ctx *ctx)
+{
+ return 0;
+}
+
+static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
+ const char *decoder)
+{
+ if (strcmp(decoder, "h264") != 0)
+ return HWDEC_ERR_NO_CODEC;
+ return 0;
+}
+
+static const char *get_codec(struct lavc_ctx *ctx)
+{
+ return "h264_mmal";
+}
+
+const struct vd_lavc_hwdec mp_vd_lavc_rpi = {
+ .type = HWDEC_RPI,
+ .image_format = IMGFMT_MMAL,
+ .probe = probe,
+ .init = init,
+ .uninit = uninit,
+ .init_decoder = init_decoder,
+ .get_codec = get_codec,
+};
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index eecc130208..b328d24102 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -121,8 +121,12 @@ const struct vd_lavc_hwdec mp_vd_lavc_vda;
const struct vd_lavc_hwdec mp_vd_lavc_vaapi;
const struct vd_lavc_hwdec mp_vd_lavc_vaapi_copy;
const struct vd_lavc_hwdec mp_vd_lavc_dxva2_copy;
+const struct vd_lavc_hwdec mp_vd_lavc_rpi;
static const struct vd_lavc_hwdec *const hwdec_list[] = {
+#if HAVE_RPI
+ &mp_vd_lavc_rpi,
+#endif
#if HAVE_VDPAU_HWACCEL
&mp_vd_lavc_vdpau,
#endif
@@ -303,6 +307,8 @@ static int init(struct dec_video *vd, const char *decoder)
if (hwdec) {
ctx->software_fallback_decoder = talloc_strdup(ctx, decoder);
+ if (hwdec->get_codec)
+ decoder = hwdec->get_codec(ctx);
MP_INFO(vd, "Using hardware decoding.\n");
} else if (vd->opts->hwdec_api != HWDEC_NONE) {
MP_INFO(vd, "Using software decoding.\n");
@@ -370,7 +376,8 @@ static void init_avctx(struct dec_video *vd, const char *decoder,
if (ctx->hwdec) {
avctx->thread_count = 1;
avctx->get_format = get_format_hwdec;
- avctx->get_buffer2 = get_buffer2_hwdec;
+ if (ctx->hwdec->allocate_image)
+ avctx->get_buffer2 = get_buffer2_hwdec;
if (ctx->hwdec->init(ctx) < 0)
goto error;
} else {
diff --git a/video/fmt-conversion.c b/video/fmt-conversion.c
index 9ed9777736..2288512011 100644
--- a/video/fmt-conversion.c
+++ b/video/fmt-conversion.c
@@ -124,6 +124,9 @@ static const struct {
#endif
{IMGFMT_VAAPI, AV_PIX_FMT_VAAPI_VLD},
{IMGFMT_DXVA2, AV_PIX_FMT_DXVA2_VLD},
+#if HAVE_AV_PIX_FMT_MMAL
+ {IMGFMT_MMAL, AV_PIX_FMT_MMAL},
+#endif
{0, AV_PIX_FMT_NONE}
};
diff --git a/video/hwdec.h b/video/hwdec.h
index 0574e5c562..b04b7c519b 100644
--- a/video/hwdec.h
+++ b/video/hwdec.h
@@ -12,6 +12,7 @@ enum hwdec_type {
HWDEC_VAAPI = 4,
HWDEC_VAAPI_COPY = 5,
HWDEC_DXVA2_COPY = 6,
+ HWDEC_RPI = 7,
};
struct mp_hwdec_ctx {
diff --git a/video/img_format.h b/video/img_format.h
index e734185045..c6c2886909 100644
--- a/video/img_format.h
+++ b/video/img_format.h
@@ -206,6 +206,7 @@ enum mp_imgfmt {
IMGFMT_VDA,
IMGFMT_VAAPI,
IMGFMT_DXVA2, // IDirect3DSurface9 (NV12)
+ IMGFMT_MMAL, // MMAL_BUFFER_HEADER_T
// Generic pass-through of AV_PIX_FMT_*. Used for formats which don't have
// a corresponding IMGFMT_ value.
diff --git a/video/out/gl_common.c b/video/out/gl_common.c
index 1470227b5c..7e8ed7ce39 100644
--- a/video/out/gl_common.c
+++ b/video/out/gl_common.c
@@ -529,6 +529,9 @@ struct backend {
};
static const struct backend backends[] = {
+#if HAVE_RPI_GLES
+ {"rpi", mpgl_set_backend_rpi},
+#endif
#if HAVE_GL_COCOA
{"cocoa", mpgl_set_backend_cocoa},
#endif
diff --git a/video/out/gl_common.h b/video/out/gl_common.h
index 1d33682f3e..2e25d331c7 100644
--- a/video/out/gl_common.h
+++ b/video/out/gl_common.h
@@ -137,6 +137,7 @@ void mpgl_set_backend_x11es(MPGLContext *ctx);
void mpgl_set_backend_x11egl(MPGLContext *ctx);
void mpgl_set_backend_x11egles(MPGLContext *ctx);
void mpgl_set_backend_wayland(MPGLContext *ctx);
+void mpgl_set_backend_rpi(MPGLContext *ctx);
void mpgl_load_functions(GL *gl, void *(*getProcAddress)(const GLubyte *),
const char *ext2, struct mp_log *log);
diff --git a/video/out/gl_rpi.c b/video/out/gl_rpi.c
new file mode 100644
index 0000000000..f82046e899
--- /dev/null
+++ b/video/out/gl_rpi.c
@@ -0,0 +1,219 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * You can alternatively redistribute this file 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.
+ */
+
+#include <stddef.h>
+#include <assert.h>
+
+#include <bcm_host.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include "common/common.h"
+#include "x11_common.h"
+#include "gl_common.h"
+
+struct priv {
+ EGLDisplay egl_display;
+ EGLContext egl_context;
+ EGLSurface egl_surface;
+ DISPMANX_DISPLAY_HANDLE_T display;
+ DISPMANX_ELEMENT_HANDLE_T window;
+ DISPMANX_UPDATE_HANDLE_T update;
+ // yep, the API keeps a pointer to it
+ EGL_DISPMANX_WINDOW_T egl_window;
+ int w, h;
+};
+
+static void *get_proc_address(const GLubyte *name)
+{
+ void *p = eglGetProcAddress(name);
+ // It looks like eglGetProcAddress() should work even for builtin
+ // functions, but it doesn't work at least with RPI/Broadcom crap.
+ // (EGL 1.4, which current RPI firmware pretends to support, definitely
+ // is required to return non-extension functions.)
+ if (!p) {
+ void *h = dlopen("/opt/vc/lib/libGLESv2.so", RTLD_LAZY);
+ if (h) {
+ p = dlsym(h, name);
+ dlclose(h);
+ }
+ }
+ return p;
+}
+
+static EGLConfig select_fb_config_egl(struct MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+
+ 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(ctx->vo, "Could find EGL configuration!\n");
+ return NULL;
+ }
+
+ return config;
+}
+
+static bool sc_config_window(struct MPGLContext *ctx, int flags)
+{
+ struct priv *p = ctx->priv;
+ struct vo *vo = ctx->vo;
+
+ if (p->egl_context) {
+ vo->dwidth = p->w;
+ vo->dheight = p->h;
+ return true;
+ }
+
+ bcm_host_init();
+
+ p->display = vc_dispmanx_display_open(0);
+ p->update = vc_dispmanx_update_start(0);
+ if (!p->display || !p->update) {
+ MP_FATAL(ctx->vo, "Could not get DISPMANX objects.\n");
+ return false;
+ }
+
+ uint32_t w, h;
+ if (graphics_get_display_size(0, &w, &h) < 0) {
+ MP_FATAL(ctx->vo, "Could not get display size.\n");
+ return false;
+ }
+
+ // dispmanx is like a neanderthal version of Wayland - you can add an
+ // overlay any place on the screen. Just use the whole screen.
+ VC_RECT_T dst = {.width = w, .height = h};
+ VC_RECT_T src = {.width = w << 16, .height = 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, 1, &dst, 0,
+ &src, DISPMANX_PROTECTION_NONE, &alpha, 0, 0);
+ if (!p->window) {
+ MP_FATAL(ctx->vo, "Could not add DISPMANX element.\n");
+ return false;
+ }
+
+ vc_dispmanx_update_submit_sync(p->update);
+
+ p->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (!eglInitialize(p->egl_display, NULL, NULL)) {
+ MP_FATAL(ctx->vo, "EGL failed to initialize.\n");
+ return false;
+ }
+
+ eglBindAPI(EGL_OPENGL_ES_API);
+
+ EGLConfig config = select_fb_config_egl(ctx);
+ if (!config)
+ return false;
+
+ p->egl_window = (EGL_DISPMANX_WINDOW_T){.element = p->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(ctx->vo, "Could not create EGL surface!\n");
+ return false;
+ }
+
+ 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(ctx->vo, "Could not create EGL context!\n");
+ return false;
+ }
+
+ eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface,
+ p->egl_context);
+
+ const char *exts = eglQueryString(p->egl_display, EGL_EXTENSIONS);
+ mpgl_load_functions(ctx->gl, get_proc_address, exts, vo->log);
+
+ vo->dwidth = p->w = w;
+ vo->dheight = p->h = h;
+
+ return true;
+}
+
+static void sc_releaseGlContext(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ if (p->egl_context) {
+ eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+ eglDestroyContext(p->egl_display, p->egl_context);
+ }
+ p->egl_context = EGL_NO_CONTEXT;
+ eglTerminate(p->egl_display);
+ vc_dispmanx_display_close(p->display);
+}
+
+static void sc_swapGlBuffers(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ eglSwapBuffers(p->egl_display, p->egl_surface);
+}
+
+static int sc_vo_init(struct vo *vo)
+{
+ return 1;
+}
+
+static void sc_vo_uninit(struct vo *vo)
+{
+}
+
+static int sc_vo_control(struct vo *vo, int *events, int request, void *arg)
+{
+ return VO_NOTIMPL;
+}
+
+void mpgl_set_backend_rpi(MPGLContext *ctx)
+{
+ ctx->priv = talloc_zero(ctx, struct priv);
+ ctx->config_window = sc_config_window;
+ ctx->releaseGlContext = sc_releaseGlContext;
+ ctx->swapGlBuffers = sc_swapGlBuffers;
+ ctx->vo_init = sc_vo_init;
+ ctx->vo_uninit = sc_vo_uninit;
+ ctx->vo_control = sc_vo_control;
+}
diff --git a/video/out/vo.c b/video/out/vo.c
index a99e9f69f4..e9b1cbfc1d 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -63,9 +63,13 @@ extern const struct vo_driver video_out_direct3d_shaders;
extern const struct vo_driver video_out_sdl;
extern const struct vo_driver video_out_vaapi;
extern const struct vo_driver video_out_wayland;
+extern const struct vo_driver video_out_rpi;
const struct vo_driver *const video_out_drivers[] =
{
+#if HAVE_RPI
+ &video_out_rpi,
+#endif
#if HAVE_GL
&video_out_opengl,
#endif
diff --git a/video/out/vo_rpi.c b/video/out/vo_rpi.c
new file mode 100644
index 0000000000..e197477987
--- /dev/null
+++ b/video/out/vo_rpi.c
@@ -0,0 +1,567 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <stdbool.h>
+#include <assert.h>
+
+#include <bcm_host.h>
+#include <interface/mmal/mmal.h>
+#include <interface/mmal/util/mmal_util.h>
+#include <interface/mmal/util/mmal_default_components.h>
+
+#include <libavutil/rational.h>
+
+#include "common/common.h"
+#include "common/msg.h"
+#include "options/m_config.h"
+#include "vo.h"
+#include "video/mp_image.h"
+#include "sub/osd.h"
+#include "sub/img_convert.h"
+
+// In theory, the number of RGBA subbitmaps the OSD code could give us is
+// unlimited; but in practice there will be rarely many elements.
+#define MAX_OSD_ELEMS MP_SUB_BB_LIST_MAX
+
+struct osd_elem {
+ DISPMANX_RESOURCE_HANDLE_T resource;
+ DISPMANX_ELEMENT_HANDLE_T element;
+};
+
+struct osd_part {
+ struct osd_elem elems[MAX_OSD_ELEMS];
+ int num_elems;
+ int change_id;
+ bool needed;
+};
+
+struct priv {
+ DISPMANX_DISPLAY_HANDLE_T display;
+ DISPMANX_ELEMENT_HANDLE_T window;
+ DISPMANX_UPDATE_HANDLE_T update;
+ uint32_t w, h;
+
+ struct osd_part osd_parts[MAX_OSD_PARTS];
+ double osd_pts;
+ struct mp_osd_res osd_res;
+
+ MMAL_COMPONENT_T *renderer;
+ bool renderer_enabled;
+
+ struct mp_image *next_image;
+
+ // for RAM input
+ MMAL_POOL_T *swpool;
+
+ int background_layer;
+ int video_layer;
+ int osd_layer;
+
+ int display_nr;
+ int layer;
+};
+
+// 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 void wipe_osd_part(struct vo *vo, struct osd_part *part)
+{
+ struct priv *p = vo->priv;
+
+ for (int n = 0; n < part->num_elems; n++) {
+ vc_dispmanx_element_remove(p->update, part->elems[n].element);
+ vc_dispmanx_resource_delete(part->elems[n].resource);
+ }
+ part->num_elems = 0;
+ part->change_id = -1;
+}
+
+static void wipe_osd(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ for (int x = 0; x < MAX_OSD_PARTS; x++)
+ wipe_osd_part(vo, &p->osd_parts[x]);
+}
+
+static int add_element(struct vo *vo, struct osd_part *part, int index,
+ struct sub_bitmap *sub)
+{
+ struct priv *p = vo->priv;
+ VC_IMAGE_TYPE_T format = VC_IMAGE_ARGB8888; // assuming RPI is always LE
+
+ struct osd_elem *elem = &part->elems[index];
+ *elem = (struct osd_elem){0};
+
+ // I have no idea why stride must be passed in such a hacky way. It's not
+ // documented. Other software does it too. Other software claims aligning
+ // the width and "probably" the height is required too, but for me it works
+ // just fine without on rpi2. (See Weston's rpi renderer.)
+ elem->resource = vc_dispmanx_resource_create(format,
+ sub->w | (sub->stride << 16),
+ sub->h,
+ &(int32_t){0});
+ if (!elem->resource) {
+ MP_ERR(vo, "Could not create %dx%d sub-bitmap\n", sub->w, sub->h);
+ return -1;
+ }
+
+ VC_RECT_T rc = {.width = sub->w, .height = sub->h};
+ vc_dispmanx_resource_write_data(elem->resource, format,
+ sub->stride, sub->bitmap, &rc);
+ VC_RECT_T src = {.width = sub->w << 16, .height = sub->h << 16};
+ VC_RECT_T dst = {.x = sub->x, .y = sub->y, .width = sub->dw, .height = sub->dh};
+ VC_DISPMANX_ALPHA_T alpha = {
+ .flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_PREMULT,
+ .opacity = 0xFF,
+ };
+ elem->element = vc_dispmanx_element_add(p->update, p->display, p->osd_layer,
+ &dst, elem->resource, &src,
+ DISPMANX_PROTECTION_NONE,
+ &alpha, 0, 0);
+ if (!elem->element) {
+ MP_ERR(vo, "Could not create sub-bitmap element\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void osd_draw_cb(void *ctx, struct sub_bitmaps *imgs)
+{
+ struct vo *vo = ctx;
+ struct priv *p = vo->priv;
+ struct osd_part *part = &p->osd_parts[imgs->render_index];
+
+ part->needed = true;
+
+ if (imgs->change_id == part->change_id)
+ return;
+
+ wipe_osd_part(vo, part);
+ part->change_id = imgs->change_id;
+
+ for (int n = 0; n < imgs->num_parts; n++) {
+ if (part->num_elems == MAX_OSD_ELEMS) {
+ MP_ERR(vo, "Too many OSD elements.\n");
+ break;
+ }
+ int index = part->num_elems++;
+ if (add_element(vo, part, index, &imgs->parts[n]) < 0)
+ break;
+ }
+}
+
+static void update_osd(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ for (int x = 0; x < MAX_OSD_PARTS; x++)
+ p->osd_parts[x].needed = false;
+
+ static const bool formats[SUBBITMAP_COUNT] = {[SUBBITMAP_RGBA] = true};
+ osd_draw(vo->osd, p->osd_res, p->osd_pts, 0, formats, osd_draw_cb, vo);
+
+ for (int x = 0; x < MAX_OSD_PARTS; x++) {
+ struct osd_part *part = &p->osd_parts[x];
+ if (!part->needed)
+ wipe_osd_part(vo, part);
+ }
+}
+
+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);
+
+ MMAL_DISPLAYREGION_T dr = {
+ .hdr = { .id = MMAL_PARAMETER_DISPLAYREGION,
+ .size = sizeof(MMAL_DISPLAYREGION_T), },
+ .src_rect = { .x = src.x0, .y = src.y0,
+ .width = src.x1 - src.x0, .height = src.y1 - src.y0, },
+ .dest_rect = { .x = dst.x0, .y = dst.y0,
+ .width = dst.x1 - dst.x0, .height = dst.y1 - dst.y0, },
+ .layer = p->video_layer,
+ .display_num = p->display_nr,
+ .set = MMAL_DISPLAY_SET_SRC_RECT | MMAL_DISPLAY_SET_DEST_RECT |
+ MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_NUM,
+ };
+
+ if (mmal_port_parameter_set(input, &dr.hdr))
+ MP_WARN(vo, "could not set video rectangle\n");
+}
+
+static void flip_page(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ struct mp_image *mpi = p->next_image;
+ p->next_image = NULL;
+
+ // For OSD
+ vc_dispmanx_update_submit_sync(p->update);
+ p->update = vc_dispmanx_update_start(10);
+
+ 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);
+ }
+ }
+}
+
+static void free_mmal_buffer(void *arg)
+{
+ MMAL_BUFFER_HEADER_T *buffer = arg;
+ mmal_buffer_header_release(buffer);
+}
+
+static void draw_image(struct vo *vo, mp_image_t *mpi)
+{
+ struct priv *p = vo->priv;
+
+ talloc_free(p->next_image);
+ p->next_image = NULL;
+
+ p->osd_pts = mpi->pts;
+ update_osd(vo);
+
+ if (vo->params->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(&(struct mp_image){0},
+ 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 mp_csp csp)
+{
+ switch (csp) {
+ case MP_CSP_BT_601: return MMAL_COLOR_SPACE_ITUR_BT601;
+ case MP_CSP_BT_709: return MMAL_COLOR_SPACE_ITUR_BT709;
+ case MP_CSP_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, int flags)
+{
+ struct priv *p = vo->priv;
+ MMAL_PORT_T *input = p->renderer->input[0];
+ bool opaque = params->imgfmt == IMGFMT_MMAL;
+
+ vo->dwidth = p->w;
+ vo->dheight = p->h;
+
+ disable_renderer(vo);
+
+ AVRational dr = {params->d_w, params->d_h};
+ AVRational ir = {params->w, params->h};
+ AVRational par = av_div_q(dr, ir);
+
+ 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){par.num, par.den};
+ input->format->es->video.color_space = map_csp(params->colorspace);
+
+ 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;
+ }
+ }
+
+ resize(vo);
+
+ 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;
+ }
+
+ return 0;
+}
+
+static struct mp_image *take_screenshot(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ struct mp_image *img = mp_image_alloc(IMGFMT_BGRA, 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 control(struct vo *vo, uint32_t request, void *data)
+{
+ struct priv *p = vo->priv;
+
+ switch (request) {
+ case VOCTRL_GET_