summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2016-09-12 16:39:12 +0200
committerwm4 <wm4@nowhere>2016-09-12 20:03:55 +0200
commitb261e1e782cfa12b70b4601b136e07df16bdd651 (patch)
tree9870de0d8cf4efcc424580c0e95cc78ca06322db
parent9a873cc53bbfd98ce9668d91ad7437d5d0ca6283 (diff)
downloadmpv-b261e1e782cfa12b70b4601b136e07df16bdd651.tar.bz2
mpv-b261e1e782cfa12b70b4601b136e07df16bdd651.tar.xz
vo_opengl: rpi: merge vo_rpi features
Since vo_rpi is going to be deprecated, better port its features to the vo_opengl backend. The most tricky part is the fact that recreating dispmanx elements will conflict with the GL context. Fortunately, RPI's EGL support is reasonably compliant, and we can transplant the context to newly created dispmanx elements, making this much easier. This means unlike vo_rpi, the GL state will actually not be recreated.
-rw-r--r--video/out/opengl/context_rpi.c274
-rw-r--r--video/out/opengl/context_rpi.h1
2 files changed, 255 insertions, 20 deletions
diff --git a/video/out/opengl/context_rpi.c b/video/out/opengl/context_rpi.c
index 5a257a741c..c51a3f1319 100644
--- a/video/out/opengl/context_rpi.c
+++ b/video/out/opengl/context_rpi.c
@@ -19,6 +19,8 @@
#include <assert.h>
#include "common/common.h"
+#include "osdep/atomic.h"
+#include "video/out/win_state.h"
#include "context.h"
#include "context_rpi.h"
@@ -138,30 +140,139 @@ void mp_egl_rpi_destroy(struct mp_egl_rpi *p)
p->gl = NULL;
}
+static int mp_egl_rpi_init_base(struct mp_egl_rpi *p)
+{
+ 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);
+
+ p->egl_config = select_fb_config_egl(p);
+ if (!p->egl_config)
+ goto fail;
+
+ EGLint context_attributes[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+ p->egl_context = eglCreateContext(p->egl_display, p->egl_config,
+ EGL_NO_CONTEXT, context_attributes);
+
+ if (p->egl_context == EGL_NO_CONTEXT) {
+ MP_FATAL(p, "Could not create EGL context!\n");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ mp_egl_rpi_destroy(p);
+ return -1;
+}
+
+static void mp_egl_rpi_destroy_base(struct mp_egl_rpi *p)
+{
+ 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 int mp_egl_rpi_init_window(struct mp_egl_rpi *p,
+ DISPMANX_ELEMENT_HANDLE_T window,
+ int w, int h)
+{
+ p->egl_window = (EGL_DISPMANX_WINDOW_T){
+ .element = window,
+ .width = w,
+ .height = h,
+ };
+ p->egl_surface = eglCreateWindowSurface(p->egl_display, p->egl_config,
+ &p->egl_window, NULL);
+
+ if (p->egl_surface == EGL_NO_SURFACE) {
+ MP_FATAL(p, "Could not create EGL surface!\n");
+ return -1;
+ }
+
+ if (!eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface,
+ p->egl_context))
+ {
+ MP_FATAL(p, "Failed to set context!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void mp_egl_rpi_destroy_window(struct mp_egl_rpi *p)
+{
+ 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;
+ }
+}
+
struct priv {
DISPMANX_DISPLAY_HANDLE_T display;
DISPMANX_ELEMENT_HANDLE_T window;
DISPMANX_UPDATE_HANDLE_T update;
struct mp_egl_rpi egl;
- int w, h;
+ int x, y, w, h;
+ double display_fps;
+ atomic_bool reload_display;
+ int win_params[4];
};
-static void rpi_uninit(MPGLContext *ctx)
+static void tv_callback(void *callback_data, uint32_t reason, uint32_t param1,
+ uint32_t param2)
+{
+ struct MPGLContext *ctx = callback_data;
+ struct priv *p = ctx->priv;
+ atomic_store(&p->reload_display, true);
+ vo_wakeup(ctx->vo);
+}
+
+static void destroy_dispmanx(struct MPGLContext *ctx)
{
struct priv *p = ctx->priv;
- mp_egl_rpi_destroy(&p->egl);
+
+ mp_egl_rpi_destroy_window(&p->egl);
+ 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 int rpi_init(struct MPGLContext *ctx, int flags)
+static void rpi_uninit(MPGLContext *ctx)
{
struct priv *p = ctx->priv;
- struct vo *vo = ctx->vo;
- p->egl.log = vo->log;
+ vc_tv_unregister_callback_full(tv_callback, ctx);
- bcm_host_init();
+ mp_egl_rpi_destroy_base(&p->egl);
+
+ destroy_dispmanx(ctx);
+}
+
+static int recreate_dispmanx(struct MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+
+ MP_VERBOSE(ctx->vo, "Recreating DISPMANX state...\n");
+
+ destroy_dispmanx(ctx);
p->display = vc_dispmanx_display_open(0);
p->update = vc_dispmanx_update_start(0);
@@ -170,16 +281,34 @@ static int rpi_init(struct MPGLContext *ctx, int flags)
goto fail;
}
- uint32_t w, h;
- if (graphics_get_display_size(0, &w, &h) < 0) {
+ uint32_t dispw, disph;
+ if (graphics_get_display_size(0, &dispw, &disph) < 0) {
MP_FATAL(ctx->vo, "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. Just use the whole screen.
- VC_RECT_T dst = {.width = w, .height = h};
- VC_RECT_T src = {.width = w << 16, .height = h << 16};
+ // 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_FROM_SOURCE,
.opacity = 0xFF,
@@ -192,38 +321,143 @@ static int rpi_init(struct MPGLContext *ctx, int flags)
}
vc_dispmanx_update_submit_sync(p->update);
+ p->update = vc_dispmanx_update_start(0);
- if (mp_egl_rpi_init(&p->egl, p->window, w, h) < 0)
+ if (mp_egl_rpi_init_window(&p->egl, p->window, p->w, p->h) < 0)
goto fail;
- ctx->gl = p->egl.gl;
+ 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(&param) &&
+ 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[2] = p->x;
+ p->win_params[3] = p->y;
+
+ ctx->vo->dwidth = p->w;
+ ctx->vo->dheight = p->h;
- vo->dwidth = p->w = w;
- vo->dheight = p->h = h;
+ ctx->vo->want_redraw = true;
+ vo_event(ctx->vo, VO_EVENT_WIN_STATE);
return 0;
fail:
- rpi_uninit(ctx);
+ destroy_dispmanx(ctx);
return -1;
}
-static int rpi_reconfig(struct MPGLContext *ctx)
+static int rpi_init(struct MPGLContext *ctx, int flags)
{
struct priv *p = ctx->priv;
- ctx->vo->dwidth = p->w;
- ctx->vo->dheight = p->h;
+
+ bcm_host_init();
+
+ vc_tv_register_callback(tv_callback, ctx);
+
+ p->egl.log = ctx->vo->log;
+ if (mp_egl_rpi_init_base(&p->egl) < 0) {
+ rpi_uninit(ctx);
+ return -1;
+ }
+
+ if (recreate_dispmanx(ctx) < 0) {
+ rpi_uninit(ctx);
+ return -1;
+ }
+
+ ctx->gl = talloc_zero(ctx, GL);
+
+ const char *exts = eglQueryString(p->egl.egl_display, EGL_EXTENSIONS);
+ mpgl_load_functions(ctx->gl, get_proc_address, exts, p->egl.log);
+
+ ctx->native_display_type = "MPV_RPI_WINDOW";
+ ctx->native_display = p->win_params;
+
return 0;
}
+static int rpi_reconfig(struct MPGLContext *ctx)
+{
+ return recreate_dispmanx(ctx);
+}
+
static void rpi_swap_buffers(MPGLContext *ctx)
{
struct priv *p = ctx->priv;
eglSwapBuffers(p->egl.egl_display, p->egl.egl_surface);
}
+static struct mp_image *take_screenshot(struct MPGLContext *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(MPGLContext *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 true;
+ case VOCTRL_FULLSCREEN:
+ recreate_dispmanx(ctx);
+ return VO_TRUE;
+ case VOCTRL_CHECK_EVENTS:
+ if (atomic_fetch_and(&p->reload_display, 0)) {
+ MP_WARN(ctx->vo, "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;
}
diff --git a/video/out/opengl/context_rpi.h b/video/out/opengl/context_rpi.h
index c2c6dd0dc4..60bd9c72d2 100644
--- a/video/out/opengl/context_rpi.h
+++ b/video/out/opengl/context_rpi.h
@@ -9,6 +9,7 @@ struct mp_egl_rpi {
struct mp_log *log;
struct GL *gl;
EGLDisplay egl_display;
+ EGLConfig egl_config;
EGLContext egl_context;
EGLSurface egl_surface;
// yep, the API keeps a pointer to it