diff options
Diffstat (limited to 'video')
-rw-r--r-- | video/out/gl_common.c | 24 | ||||
-rw-r--r-- | video/out/gl_common.h | 2 | ||||
-rw-r--r-- | video/out/gl_hwdec.c | 15 | ||||
-rw-r--r-- | video/out/gl_hwdec.h | 2 | ||||
-rw-r--r-- | video/out/gl_hwdec_vaglx.c | 4 | ||||
-rw-r--r-- | video/out/gl_hwdec_vdpau.c | 4 | ||||
-rw-r--r-- | video/out/gl_video.c | 29 | ||||
-rw-r--r-- | video/out/gl_video.h | 5 | ||||
-rw-r--r-- | video/out/vo.c | 2 | ||||
-rw-r--r-- | video/out/vo.h | 2 | ||||
-rw-r--r-- | video/out/vo_opengl.c | 4 | ||||
-rw-r--r-- | video/out/vo_opengl_cb.c | 370 |
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 = §ion->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, +}; |