summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
Diffstat (limited to 'video')
-rw-r--r--video/out/gl_common.c24
-rw-r--r--video/out/gl_common.h2
-rw-r--r--video/out/gl_hwdec.c15
-rw-r--r--video/out/gl_hwdec.h2
-rw-r--r--video/out/gl_hwdec_vaglx.c4
-rw-r--r--video/out/gl_hwdec_vdpau.c4
-rw-r--r--video/out/gl_video.c29
-rw-r--r--video/out/gl_video.h5
-rw-r--r--video/out/vo.c2
-rw-r--r--video/out/vo.h2
-rw-r--r--video/out/vo_opengl.c4
-rw-r--r--video/out/vo_opengl_cb.c370
12 files changed, 445 insertions, 18 deletions
diff --git a/video/out/gl_common.c b/video/out/gl_common.c
index 1f005934d4..dddb11a53e 100644
--- a/video/out/gl_common.c
+++ b/video/out/gl_common.c
@@ -474,15 +474,15 @@ static const struct gl_functions gl_functions[] = {
// log: used to output messages
// Note: if you create a CONTEXT_FORWARD_COMPATIBLE_BIT_ARB with OpenGL 3.0,
// you must append "GL_ARB_compatibility" to ext2.
-void mpgl_load_functions(GL *gl, void *(*getProcAddress)(const GLubyte *),
- const char *ext2, struct mp_log *log)
+void mpgl_load_functions2(GL *gl, void *(*get_fn)(void *ctx, const char *n),
+ void *fn_ctx, const char *ext2, struct mp_log *log)
{
talloc_free_children(gl);
*gl = (GL) {
.extensions = talloc_strdup(gl, ext2 ? ext2 : ""),
};
- gl->GetString = getProcAddress ? getProcAddress("glGetString") : NULL;
+ gl->GetString = get_fn(fn_ctx, "glGetString");
if (!gl->GetString) {
mp_err(log, "Can't load OpenGL functions.\n");
return;
@@ -508,8 +508,8 @@ void mpgl_load_functions(GL *gl, void *(*getProcAddress)(const GLubyte *),
bool has_legacy = false;
if (gl->version >= MPGL_VER(3, 0)) {
- gl->GetStringi = getProcAddress("glGetStringi");
- gl->GetIntegerv = getProcAddress("glGetIntegerv");
+ gl->GetStringi = get_fn(fn_ctx, "glGetStringi");
+ gl->GetIntegerv = get_fn(fn_ctx, "glGetIntegerv");
if (!(gl->GetStringi && gl->GetIntegerv))
return;
@@ -571,7 +571,7 @@ void mpgl_load_functions(GL *gl, void *(*getProcAddress)(const GLubyte *),
const struct gl_function *fn = &section->functions[i];
void *ptr = NULL;
for (int x = 0; fn->funcnames[x]; x++) {
- ptr = getProcAddress((const GLubyte *)fn->funcnames[x]);
+ ptr = get_fn(fn_ctx, fn->funcnames[x]);
if (ptr)
break;
}
@@ -620,6 +620,18 @@ void mpgl_load_functions(GL *gl, void *(*getProcAddress)(const GLubyte *),
list_features(gl->mpgl_caps, log, MSGL_V, false);
}
+static void *get_procaddr_wrapper(void *ctx, const char *name)
+{
+ void *(*getProcAddress)(const GLubyte *) = ctx;
+ return getProcAddress ? getProcAddress((const GLubyte*)name) : NULL;
+}
+
+void mpgl_load_functions(GL *gl, void *(*getProcAddress)(const GLubyte *),
+ const char *ext2, struct mp_log *log)
+{
+ mpgl_load_functions2(gl, get_procaddr_wrapper, getProcAddress, ext2, log);
+}
+
/**
* \brief return the number of bytes per pixel for the given format
* \param format OpenGL format
diff --git a/video/out/gl_common.h b/video/out/gl_common.h
index 38d952ae1b..951d2efefb 100644
--- a/video/out/gl_common.h
+++ b/video/out/gl_common.h
@@ -168,6 +168,8 @@ void mpgl_set_backend_wayland(MPGLContext *ctx);
void mpgl_load_functions(GL *gl, void *(*getProcAddress)(const GLubyte *),
const char *ext2, struct mp_log *log);
+void mpgl_load_functions2(GL *gl, void *(*get_fn)(void *ctx, const char *n),
+ void *fn_ctx, const char *ext2, struct mp_log *log);
// print a multi line string with line numbers (e.g. for shader sources)
// log, lev: module and log level, as in mp_msg()
diff --git a/video/out/gl_hwdec.c b/video/out/gl_hwdec.c
index 92f0ad095e..3bab1c1e9c 100644
--- a/video/out/gl_hwdec.c
+++ b/video/out/gl_hwdec.c
@@ -38,18 +38,19 @@ static const struct gl_hwdec_driver *const mpgl_hwdec_drivers[] = {
#if HAVE_VAAPI_GLX
&gl_hwdec_vaglx,
#endif
-#if HAVE_VDA_GL
- &gl_hwdec_vda,
-#endif
#if HAVE_VDPAU_GL_X11
&gl_hwdec_vdpau,
#endif
+#if HAVE_VDA_GL
+ &gl_hwdec_vda,
+#endif
NULL
};
static struct gl_hwdec *load_hwdec_driver(struct mp_log *log, GL *gl,
const struct gl_hwdec_driver *drv,
- struct mp_hwdec_info *info)
+ struct mp_hwdec_info *info,
+ bool is_auto)
{
struct gl_hwdec *hwdec = talloc(NULL, struct gl_hwdec);
*hwdec = (struct gl_hwdec) {
@@ -58,6 +59,7 @@ static struct gl_hwdec *load_hwdec_driver(struct mp_log *log, GL *gl,
.gl = gl,
.info = info,
.gl_texture_target = GL_TEXTURE_2D,
+ .reject_emulated = is_auto,
};
if (hwdec->driver->create(hwdec) < 0) {
talloc_free(hwdec);
@@ -71,10 +73,11 @@ struct gl_hwdec *gl_hwdec_load_api(struct mp_log *log, GL *gl,
const char *api_name,
struct mp_hwdec_info *info)
{
+ bool is_auto = api_name && strcmp(api_name, "auto") == 0;
for (int n = 0; mpgl_hwdec_drivers[n]; n++) {
const struct gl_hwdec_driver *drv = mpgl_hwdec_drivers[n];
- if (api_name && strcmp(drv->api_name, api_name) == 0) {
- struct gl_hwdec *r = load_hwdec_driver(log, gl, drv, info);
+ if (is_auto || (api_name && strcmp(drv->api_name, api_name) == 0)) {
+ struct gl_hwdec *r = load_hwdec_driver(log, gl, drv, info, is_auto);
if (r)
return r;
}
diff --git a/video/out/gl_hwdec.h b/video/out/gl_hwdec.h
index 5c70f7fd0d..ea10ac08f8 100644
--- a/video/out/gl_hwdec.h
+++ b/video/out/gl_hwdec.h
@@ -13,6 +13,8 @@ struct gl_hwdec {
struct mp_hwdec_info *info;
// For free use by hwdec driver
void *priv;
+ // For working around the vdpau vs. vaapi mess.
+ bool reject_emulated;
// hwdec backends must set this to an IMGFMT_ that has an equivalent
// internal representation in gl_video.c as the hardware texture.
// It's used to build the rendering chain, and also as screenshot format.
diff --git a/video/out/gl_hwdec_vaglx.c b/video/out/gl_hwdec_vaglx.c
index d93fa6253e..555e61aec3 100644
--- a/video/out/gl_hwdec_vaglx.c
+++ b/video/out/gl_hwdec_vaglx.c
@@ -80,6 +80,10 @@ static int create(struct gl_hwdec *hw)
vaTerminate(p->display);
return -1;
}
+ if (hw->reject_emulated && va_guess_if_emulated(p->ctx)) {
+ destroy(hw);
+ return -1;
+ }
hw->info->vaapi_ctx = p->ctx;
hw->converted_imgfmt = IMGFMT_RGB0;
return 0;
diff --git a/video/out/gl_hwdec_vdpau.c b/video/out/gl_hwdec_vdpau.c
index 7a68ddd83f..c59d97bc1b 100644
--- a/video/out/gl_hwdec_vdpau.c
+++ b/video/out/gl_hwdec_vdpau.c
@@ -111,6 +111,10 @@ static int create(struct gl_hwdec *hw)
return -1;
p->vdp_surface = VDP_INVALID_HANDLE;
p->mixer = mp_vdpau_mixer_create(p->ctx, hw->log);
+ if (hw->reject_emulated && mp_vdpau_guess_if_emulated(p->ctx)) {
+ destroy(hw);
+ return -1;
+ }
hw->info->vdpau_ctx = p->ctx;
hw->converted_imgfmt = IMGFMT_RGB0;
return 0;
diff --git a/video/out/gl_video.c b/video/out/gl_video.c
index eef5dfc467..9a762118c3 100644
--- a/video/out/gl_video.c
+++ b/video/out/gl_video.c
@@ -190,6 +190,7 @@ struct gl_video {
struct mp_rect dst_rect; // video rectangle on output window
struct mp_osd_res osd_rect; // OSD size/margins
int vp_x, vp_y, vp_w, vp_h; // GL viewport
+ bool vp_vflipped;
int frames_rendered;
@@ -574,7 +575,10 @@ static void update_uniforms(struct gl_video *p, GLuint program)
loc = gl->GetUniformLocation(program, "transform");
if (loc >= 0 && p->vp_w > 0 && p->vp_h > 0) {
float matrix[3][3];
- matrix_ortho2d(matrix, 0, p->vp_w, p->vp_h, 0);
+ int vvp[2] = {p->vp_h, 0};
+ if (p->vp_vflipped)
+ MPSWAP(int, vvp[0], vvp[1]);
+ matrix_ortho2d(matrix, 0, p->vp_w, vvp[0], vvp[1]);
gl->UniformMatrix3fv(loc, 1, GL_FALSE, &matrix[0][0]);
}
@@ -1786,7 +1790,7 @@ static void check_resize(struct gl_video *p)
void gl_video_resize(struct gl_video *p, struct mp_rect *window,
struct mp_rect *src, struct mp_rect *dst,
- struct mp_osd_res *osd)
+ struct mp_osd_res *osd, bool vflip)
{
p->src_rect = *src;
p->src_rect_rot = *src;
@@ -1803,6 +1807,7 @@ void gl_video_resize(struct gl_video *p, struct mp_rect *window,
p->vp_w = window->x1 - window->x0;
p->vp_h = window->y1 - window->y0;
+ p->vp_vflipped = vflip;
check_resize(p);
}
@@ -2188,7 +2193,7 @@ static int init_gl(struct gl_video *p)
gl->BindBuffer(GL_ARRAY_BUFFER, 0);
- gl->ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ gl_video_set_gl_state(p);
debug_check_gl(p, "after init_gl");
@@ -2214,6 +2219,24 @@ void gl_video_uninit(struct gl_video *p)
talloc_free(p);
}
+void gl_video_set_gl_state(struct gl_video *p)
+{
+ GL *gl = p->gl;
+
+ gl->ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ gl->ActiveTexture(GL_TEXTURE0);
+}
+
+void gl_video_unset_gl_state(struct gl_video *p)
+{
+ GL *gl = p->gl;
+
+ gl->PixelStorei(GL_PACK_ROW_LENGTH, 0);
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ gl->PixelStorei(GL_PACK_ALIGNMENT, 4);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
+}
+
// dest = src.<w> (always using 4 components)
static void packed_fmt_swizzle(char w[5], const struct packed_fmt_entry *fmt)
{
diff --git a/video/out/gl_video.h b/video/out/gl_video.h
index bba24e364a..3d733c8a80 100644
--- a/video/out/gl_video.h
+++ b/video/out/gl_video.h
@@ -69,7 +69,7 @@ void gl_video_render_frame(struct gl_video *p, int fbo);
struct mp_image *gl_video_download_image(struct gl_video *p);
void gl_video_resize(struct gl_video *p, struct mp_rect *window,
struct mp_rect *src, struct mp_rect *dst,
- struct mp_osd_res *osd);
+ struct mp_osd_res *osd, bool vflip);
void gl_video_get_colorspace(struct gl_video *p, struct mp_image_params *params);
bool gl_video_set_equalizer(struct gl_video *p, const char *name, int val);
bool gl_video_get_equalizer(struct gl_video *p, const char *name, int *val);
@@ -77,6 +77,9 @@ bool gl_video_get_equalizer(struct gl_video *p, const char *name, int *val);
void gl_video_set_debug(struct gl_video *p, bool enable);
void gl_video_resize_redraw(struct gl_video *p, int w, int h);
+void gl_video_set_gl_state(struct gl_video *p);
+void gl_video_unset_gl_state(struct gl_video *p);
+
struct gl_hwdec;
void gl_video_set_hwdec(struct gl_video *p, struct gl_hwdec *hwdec);
diff --git a/video/out/vo.c b/video/out/vo.c
index 518841007f..710f2f28ce 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -55,6 +55,7 @@ extern const struct vo_driver video_out_xv;
extern const struct vo_driver video_out_opengl;
extern const struct vo_driver video_out_opengl_hq;
extern const struct vo_driver video_out_opengl_old;
+extern const struct vo_driver video_out_opengl_cb;
extern const struct vo_driver video_out_null;
extern const struct vo_driver video_out_image;
extern const struct vo_driver video_out_lavc;
@@ -103,6 +104,7 @@ const struct vo_driver *const video_out_drivers[] =
#endif
#if HAVE_GL
&video_out_opengl_hq,
+ &video_out_opengl_cb,
#endif
#if HAVE_WAYLAND
&video_out_wayland,
diff --git a/video/out/vo.h b/video/out/vo.h
index f54e2c1a55..8b47d04cfe 100644
--- a/video/out/vo.h
+++ b/video/out/vo.h
@@ -105,6 +105,8 @@ enum mp_voctrl {
VOCTRL_GET_RECENT_FLIP_TIME, // int64_t* (using mp_time_us())
VOCTRL_GET_PREF_DEINT, // int*
+
+ VOCTRL_SET_LIBMPV_OPENGL_CB_CONTEXT,// struct mpv_opengl_cb_context*
};
// VOCTRL_SET_EQUALIZER
diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c
index 2043a4cd3c..567f83be97 100644
--- a/video/out/vo_opengl.c
+++ b/video/out/vo_opengl.c
@@ -96,7 +96,7 @@ static void resize(struct gl_priv *p)
struct mp_osd_res osd;
vo_get_src_dst_rects(vo, &src, &dst, &osd);
- gl_video_resize(p->renderer, &wnd, &src, &dst, &osd);
+ gl_video_resize(p->renderer, &wnd, &src, &dst, &osd, false);
vo->want_redraw = true;
}
@@ -447,7 +447,7 @@ err_out:
}
#define OPT_BASE_STRUCT struct gl_priv
-const struct m_option options[] = {
+static const struct m_option options[] = {
OPT_FLAG("glfinish", use_glFinish, 0),
OPT_FLAG("waitvsync", waitvsync, 0),
OPT_INT("swapinterval", swap_interval, 0, OPTDEF_INT(1)),
diff --git a/video/out/vo_opengl_cb.c b/video/out/vo_opengl_cb.c
new file mode 100644
index 0000000000..09649c3b4b
--- /dev/null
+++ b/video/out/vo_opengl_cb.c
@@ -0,0 +1,370 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <pthread.h>
+#include <assert.h>
+
+#include "config.h"
+
+#include "talloc.h"
+#include "common/common.h"
+#include "misc/bstr.h"
+#include "common/msg.h"
+#include "options/m_config.h"
+#include "options/options.h"
+#include "aspect.h"
+#include "vo.h"
+#include "video/vfcap.h"
+#include "video/mp_image.h"
+#include "sub/osd.h"
+
+#include "common/global.h"
+#include "player/client.h"
+
+#include "gl_common.h"
+#include "gl_video.h"
+#include "gl_hwdec.h"
+
+#include "video/decode/lavc.h" // HWDEC_* values
+
+#include "libmpv/opengl_cb.h"
+
+/*
+ * mpv_opengl_cb_context is created by the host application - the host application
+ * can access it any time, even if the VO is destroyed (or not created yet).
+ * The OpenGL object allows initializing the renderer etc. The VO object is only
+ * here to transfer the video frames somehow.
+ */
+
+struct vo_priv {
+ struct vo *vo;
+
+ struct mpv_opengl_cb_context *ctx;
+};
+
+struct mpv_opengl_cb_context {
+ struct mp_log *log;
+
+ pthread_mutex_t lock;
+
+ // --- Protected by lock
+ mpv_opengl_cb_update_fn update_cb;
+ void *update_cb_ctx;
+ struct mp_image *next_frame;
+ struct mp_image_params img_params;
+ struct mp_image_params *new_params;
+ struct mp_rect wnd;
+ bool flip;
+ bool force_update;
+ bool imgfmt_supported[IMGFMT_END - IMGFMT_START];
+ struct mp_vo_opts vo_opts;
+
+ // --- All of these can only be accessed from the thread where the host
+ // application's OpenGL context is current - i.e. only while the
+ // host application is calling certain mpv_opengl_cb_* APIs.
+ GL *gl;
+ struct gl_video *renderer;
+ struct gl_hwdec *hwdec;
+
+ // --- Immutable or semi-threadsafe.
+
+ struct osd_state *osd;
+ struct mp_hwdec_info hwdec_info;
+ const char *hwapi;
+
+ struct vo *active;
+};
+
+struct mpv_opengl_cb_context *mp_opengl_create(struct mpv_global *g,
+ struct osd_state *osd)
+{
+ mpv_opengl_cb_context *ctx = talloc_zero(NULL, mpv_opengl_cb_context);
+ ctx->log = mp_log_new(ctx, g->log, "opengl-cb");
+ pthread_mutex_init(&ctx->lock, NULL);
+
+ ctx->gl = talloc_zero(ctx, GL);
+
+ ctx->osd = osd;
+
+ switch (g->opts->hwdec_api) {
+ case HWDEC_AUTO: ctx->hwapi = "auto"; break;
+ case HWDEC_VDPAU: ctx->hwapi = "vdpau"; break;
+ case HWDEC_VDA: ctx->hwapi = "vda"; break;
+ case HWDEC_VAAPI: ctx->hwapi = "vaapi"; break;
+ default: ctx->hwapi = "";
+ }
+
+ return ctx;
+}
+
+// To be called from VO thread, with p->ctx->lock held.
+static void copy_vo_opts(struct vo *vo)
+{
+ struct vo_priv *p = vo->priv;
+
+ // We're being lazy: none of the options we need use dynamic data, so
+ // copy the struct with an assignment.
+ // Just remove all the dynamic data to avoid confusion.
+ struct mp_vo_opts opts = *vo->opts;
+ opts.video_driver_list = opts.vo_defs = NULL;
+ opts.winname = NULL;
+ opts.sws_opts = NULL;
+ p->ctx->vo_opts = opts;
+}
+
+void mpv_opengl_cb_set_update_callback(struct mpv_opengl_cb_context *ctx,
+ mpv_opengl_cb_update_fn callback,
+ void *callback_ctx)
+{
+ pthread_mutex_lock(&ctx->lock);
+ ctx->update_cb = callback;
+ ctx->update_cb_ctx = callback_ctx;
+ pthread_mutex_unlock(&ctx->lock);
+}
+
+int mpv_opengl_cb_init_gl(struct mpv_opengl_cb_context *ctx, const char *exts,
+ mpv_opengl_cb_get_proc_address_fn get_proc_address,
+ void *get_proc_address_ctx)
+{
+ if (ctx->renderer)
+ return MPV_ERROR_INVALID_PARAMETER;
+
+ mpgl_load_functions2(ctx->gl, get_proc_address, get_proc_address_ctx,
+ exts, ctx->log);
+ int caps = MPGL_CAP_GL21 | MPGL_CAP_TEX_RG;
+ if ((ctx->gl->mpgl_caps & caps) != caps) {
+ MP_FATAL(ctx, "Missing OpenGL features.\n");
+ return MPV_ERROR_UNSUPPORTED;
+ }
+ ctx->renderer = gl_video_init(ctx->gl, ctx->log, ctx->osd);
+ ctx->hwdec = gl_hwdec_load_api(ctx->log, ctx->gl, ctx->hwapi, &ctx->hwdec_info);
+ gl_video_set_hwdec(ctx->renderer, ctx->hwdec);
+
+ pthread_mutex_lock(&ctx->lock);
+ for (int n = IMGFMT_START; n < IMGFMT_END; n++) {
+ ctx->imgfmt_supported[n - IMGFMT_START] =
+ gl_video_check_format(ctx->renderer, n);
+ }
+ pthread_mutex_unlock(&ctx->lock);
+
+ gl_video_unset_gl_state(ctx->renderer);
+ return 0;
+}
+
+int mpv_opengl_cb_uninit_gl(struct mpv_opengl_cb_context *ctx)
+{
+ gl_video_uninit(ctx->renderer);
+ ctx->renderer = NULL;
+ gl_hwdec_uninit(ctx->hwdec);
+ ctx->hwdec = NULL;
+ talloc_free(ctx->gl);
+ ctx->gl = NULL;
+ return 0;
+}
+
+int mpv_opengl_cb_render(struct mpv_opengl_cb_context *ctx, int fbo, int vp[4])
+{
+ assert(ctx->renderer);
+
+ gl_video_set_gl_state(ctx->renderer);
+
+ pthread_mutex_lock(&ctx->lock);
+
+ struct vo *vo = ctx->active;
+
+ struct mp_image_params *new_params = ctx->new_params;
+ ctx->new_params = NULL;
+ if (new_params) {
+ ctx->img_params = *new_params;
+ ctx->force_update = true;
+ }
+
+ int h = vp[3];
+ bool flip = h < 0 && h > INT_MIN;
+ if (flip)
+ h = -h;
+ struct mp_rect wnd = {vp[0], vp[1], vp[0] + vp[2], vp[1] + h};
+ if (wnd.x0 != ctx->wnd.x0 || wnd.y0 != ctx->wnd.y0 ||
+ wnd.x1 != ctx->wnd.x1 || wnd.y1 != ctx->wnd.y1 ||
+ ctx->flip != flip)
+ ctx->force_update = true;
+
+ if (ctx->force_update && vo) {
+ ctx->force_update = false;
+ ctx->wnd = wnd;
+
+ struct mp_rect src, dst;
+ struct mp_osd_res osd;
+ mp_get_src_dst_rects(ctx->log, &ctx->vo_opts, vo->driver->caps,
+ &ctx->img_params, wnd.x1 - wnd.x0, wnd.y1 - wnd.y0,
+ 1.0, &src, &dst, &osd);
+
+ gl_video_resize(ctx->renderer, &wnd, &src, &dst, &osd, !ctx->flip);
+ }
+
+ if (new_params) {
+ gl_video_config(ctx->renderer, new_params);
+ talloc_free(new_params);
+ }
+
+ struct mp_image *mpi = ctx->next_frame;
+ ctx->next_frame = NULL;
+
+ pthread_mutex_unlock(&ctx->lock);
+
+ if (mpi)
+ gl_video_upload_image(ctx->renderer, mpi);
+
+ gl_video_render_frame(ctx->renderer, fbo);
+
+ gl_video_unset_gl_state(ctx->renderer);
+
+ return 0;
+}
+
+static void draw_image(struct vo *vo, mp_image_t *mpi)
+{
+ struct vo_priv *p = vo->priv;
+ if (p->ctx) {
+ pthread_mutex_lock(&p->ctx->lock);
+ mp_image_setrefp(&p->ctx->next_frame, mpi);
+ pthread_mutex_unlock(&p->ctx->lock);
+ }
+ talloc_free(mpi);
+}
+
+static void flip_page(struct vo *vo)
+{
+ struct vo_priv *p = vo->priv;
+ if (p->ctx) {
+ pthread_mutex_lock(&p->ctx->lock);
+ if (p->ctx->update_cb)
+ p->ctx->update_cb(p->ctx->update_cb_ctx);
+ pthread_mutex_unlock(&p->ctx->lock);
+ }
+}
+
+static int query_format(struct vo *vo, uint32_t format)
+{
+ struct vo_priv *p = vo->priv;
+
+ bool ok = false;
+ if (p->ctx) {
+ pthread_mutex_lock(&p->ctx->lock);
+ if (format >= IMGFMT_START && format < IMGFMT_END)
+ ok = p->ctx->imgfmt_supported[format - IMGFMT_START];
+ pthread_mutex_unlock(&p->ctx->lock);
+ }
+ return ok ? VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW : 0;
+}
+
+static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
+{
+ struct vo_priv *p = vo->priv;
+
+ if (p->ctx) {
+ pthread_mutex_lock(&p->ctx->lock);
+ mp_image_unrefp(&p->ctx->next_frame);
+ talloc_free(p->ctx->new_params);
+ p->ctx->new_params = talloc_memdup(NULL, params, sizeof(*params));
+ pthread_mutex_unlock(&p->ctx->lock);
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct vo_priv *p = vo->priv;
+
+ switch (request) {
+ case VOCTRL_SET_LIBMPV_OPENGL_CB_CONTEXT: {
+ if (p->ctx)
+ return VO_FALSE;
+ struct mpv_opengl_cb_context *nctx = data;
+ if (nctx) {
+ pthread_mutex_lock(&nctx->lock);
+ if (nctx->active) {
+ MP_FATAL(vo, "There is already a VO using the OpenGL context.\n");
+ } else {
+ nctx->active = vo;
+ p->ctx = nctx;
+ assert(vo->osd == p->ctx->osd);
+ copy_vo_opts(vo);
+ }
+ pthread_mutex_unlock(&nctx->lock);
+ }
+ return VO_TRUE;
+ }
+ case VOCTRL_GET_PANSCAN:
+ return VO_TRUE;
+ case VOCTRL_SET_PANSCAN:
+ case VOCTRL_REDRAW_FRAME:
+ pthread_mutex_lock(&p->ctx->lock);
+ copy_vo_opts(vo);
+ p->ctx->force_update = true;
+ if (p->ctx->update_cb)
+ p->ctx->update_cb(p->ctx->update_cb_ctx);
+ pthread_mutex_unlock(&p->ctx->lock);
+ return VO_TRUE;
+ case VOCTRL_GET_HWDEC_INFO: {
+ // Warning: in theory, the API user could destroy the OpenGL context
+ // while the decoder uses the hwdec thing, and bad things would
+ // happen. Currently, the API user is told not to do this.
+ struct mp_hwdec_info **arg = data;
+ *arg = p->ctx ? &p->ctx->hwdec_info : NULL;
+ return true;
+ }
+ }
+
+ return VO_NOTIMPL;
+}
+
+static void uninit(struct vo *vo)
+{
+ struct vo_priv *p = vo->priv;
+
+ if (p->ctx) {
+ pthread_mutex_lock(&p->ctx->lock);
+ mp_image_unrefp(&p->ctx->next_frame);
+ talloc_free(p->ctx->new_params);
+ p->ctx->new_params = NULL;
+ p->ctx->active = NULL;
+ pthread_mutex_unlock(&p->ctx->lock);
+ }
+}
+
+static int preinit(struct vo *vo)
+{
+ struct vo_priv *p = vo->priv;
+ p->vo = vo;
+ // Currently, there's no video timing in the API, and it's questionable
+ // how API users would make use of it too.
+ vo_set_flip_queue_offset(vo, 0);
+ return 0;
+}
+
+#define OPT_BASE_STRUCT struct gl_priv
+static const struct m_option options[] = {
+ {0},
+};
+
+const struct vo_driver video_out_opengl_cb = {
+ .description = "OpenGL Callbacks for libmpv",
+ .name = "opengl-cb",
+ .caps = VO_CAP_ROTATE90,
+ .preinit = preinit,
+ .query_format = query_format,
+ .reconfig = reconfig,
+ .control = control,
+ .draw_image = draw_image,
+ .flip_page = flip_page,
+ .uninit = uninit,
+ .priv_size = sizeof(struct vo_priv),
+ .options = options,
+};