summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/interface-changes.rst2
-rw-r--r--DOCS/man/vo.rst21
-rw-r--r--libmpv/opengl_cb.h6
-rw-r--r--video/out/vo_opengl_cb.c172
4 files changed, 61 insertions, 140 deletions
diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst
index fa041fc534..b81312f8e9 100644
--- a/DOCS/interface-changes.rst
+++ b/DOCS/interface-changes.rst
@@ -19,6 +19,8 @@ Interface changes
::
+ --- mpv 0.13.0 ---
+ - remove VO opengl-cb frame queue suboptions (no replacement)
--- mpv 0.12.0 ---
- remove --use-text-osd (useless; fontconfig isn't a requirement anymore,
and text rendering is also lazily initialized)
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst
index 6d90046ea2..f59b9cbea7 100644
--- a/DOCS/man/vo.rst
+++ b/DOCS/man/vo.rst
@@ -1089,27 +1089,6 @@ Available video output drivers are:
``opengl-cb``
For use with libmpv direct OpenGL embedding; useless in any other contexts.
(See ``<mpv/opengl_cb.h>``.)
- Usually, ``opengl-cb`` renders frames asynchronously by client and this
- can cause some frame drops. In order to provide a way to handle this
- situation, ``opengl-cb`` has its own frame queue and calls update callback
- more frequently if the queue is not empty regardless of existence of new frame.
- Once the queue is filled, ``opengl-cb`` drops frames automatically.
-
- With default options, ``opengl-cb`` renders only the latest frame and drops
- all frames handed over while waiting render function after update callback.
-
- ``frame-queue-size=<1..100>``
- The maximum count of frames which the frame queue can hold (default: 1)
-
- ``frame-drop-mode=<pop|clear|block>``
- Select the behavior when the frame queue is full.
-
- pop
- Drop the oldest frame in the frame queue.
- clear
- Drop all frames in the frame queue.
- block
- Wait for a short time, behave like ``clear`` on timeout. (default)
This also supports many of the suboptions the ``opengl`` VO has. Run
``mpv --vo=opengl-cb:help`` for a list.
diff --git a/libmpv/opengl_cb.h b/libmpv/opengl_cb.h
index 112f254a59..d117213c53 100644
--- a/libmpv/opengl_cb.h
+++ b/libmpv/opengl_cb.h
@@ -242,7 +242,7 @@ int mpv_opengl_cb_init_gl(mpv_opengl_cb_context *ctx, const char *exts,
* @param h Height of the framebuffer. Same as with the w parameter, except
* that this parameter can be negative. In this case, the video
* frame will be rendered flipped.
- * @return the number of left frames in the internal queue to be rendered
+ * @return 0
*/
int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int w, int h);
@@ -263,10 +263,14 @@ int mpv_opengl_cb_render(mpv_opengl_cb_context *ctx, int fbo, int vp[4]);
* Tell the renderer that a frame was flipped at the given time. This is
* optional, but can help the player to achieve better timing.
*
+ * Note that calling this at least once informs libmpv that you will use this
+ * function. If you use it inconsistently, expect bad video playback.
+ *
* If this is called while no video or no OpenGL is initialized, it is ignored.
*
* @param time The mpv time (using mpv_get_time_us()) at which the flip call
* returned. If 0 is passed, mpv_get_time_us() is used instead.
+ * Currently, this parameter is ignored.
* @return error code
*/
int mpv_opengl_cb_report_flip(mpv_opengl_cb_context *ctx, int64_t time);
diff --git a/video/out/vo_opengl_cb.c b/video/out/vo_opengl_cb.c
index 29b22939cf..15faa36c88 100644
--- a/video/out/vo_opengl_cb.c
+++ b/video/out/vo_opengl_cb.c
@@ -35,12 +35,17 @@
* 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.
+ *
+ * Locking hierarchy:
+ * - the libmpv user can mix openglcb and normal API; thus openglcb API
+ * functions can wait on the core, but not the reverse
+ * - the core does blocking calls into the VO thread, thus the VO functions
+ * can't wait on the user calling the API functions
+ * - to make video timing work like it should, the VO thread waits on the
+ * openglcb API user anyway, and the (unlikely) deadlock is avoided with
+ * a timeout
*/
-#define FRAME_DROP_POP 0 // drop the oldest frame in queue
-#define FRAME_DROP_CLEAR 1 // drop all frames in queue
-#define FRAME_DROP_BLOCK 2
-
struct vo_priv {
struct vo *vo;
@@ -49,8 +54,6 @@ struct vo_priv {
// Immutable after VO init
int use_gl_debug;
struct gl_video_opts *renderer_opts;
- int frame_queue_size;
- int frame_drop_mode;
};
struct mpv_opengl_cb_context {
@@ -64,9 +67,10 @@ struct mpv_opengl_cb_context {
bool initialized;
mpv_opengl_cb_update_fn update_cb;
void *update_cb_ctx;
- struct vo_frame *waiting_frame;
- struct vo_frame **frame_queue;
- int queued_frames;
+ struct vo_frame *next_frame; // next frame to draw
+ int64_t present_count; // incremented when next frame can be shown
+ int64_t expected_flip_count; // next vsync event for next_frame
+ int64_t flip_count;
struct vo_frame *cur_frame;
struct mp_image_params img_params;
bool reconfigured, reset;
@@ -80,8 +84,6 @@ struct mpv_opengl_cb_context {
struct m_config *new_opts_cfg;
bool eq_changed;
struct mp_csp_equalizer eq;
- int64_t recent_flip;
- bool frozen; // libmpv user is not redrawing frames
struct vo *active;
int hwdec_api;
@@ -96,68 +98,9 @@ struct mpv_opengl_cb_context {
static void update(struct vo_priv *p);
-// all queue manipulation functions shold be called under locked state
-
-static struct vo_frame *frame_queue_pop(struct mpv_opengl_cb_context *ctx)
-{
- if (ctx->queued_frames == 0)
- return NULL;
- struct vo_frame *ret = ctx->frame_queue[0];
- MP_TARRAY_REMOVE_AT(ctx->frame_queue, ctx->queued_frames, 0);
- pthread_cond_broadcast(&ctx->wakeup);
- return ret;
-}
-
-static void frame_queue_drop(struct mpv_opengl_cb_context *ctx)
-{
- struct vo_frame *frame = frame_queue_pop(ctx);
- if (frame) {
- talloc_free(frame);
- if (ctx->active)
- vo_increment_drop_count(ctx->active, 1);
- pthread_cond_broadcast(&ctx->wakeup);
- }
-}
-
-static void frame_queue_clear(struct mpv_opengl_cb_context *ctx)
-{
- for (int i = 0; i < ctx->queued_frames; i++)
- talloc_free(ctx->frame_queue[i]);
- talloc_free(ctx->frame_queue);
- ctx->frame_queue = NULL;
- ctx->queued_frames = 0;
- pthread_cond_broadcast(&ctx->wakeup);
-}
-
-static void frame_queue_drop_all(struct mpv_opengl_cb_context *ctx)
-{
- int frames = ctx->queued_frames;
- frame_queue_clear(ctx);
- if (ctx->active && frames > 0)
- vo_increment_drop_count(ctx->active, frames);
- pthread_cond_broadcast(&ctx->wakeup);
-}
-
-static void frame_queue_push(struct mpv_opengl_cb_context *ctx,
- struct vo_frame *frame)
-{
- MP_TARRAY_APPEND(ctx, ctx->frame_queue, ctx->queued_frames, frame);
- pthread_cond_broadcast(&ctx->wakeup);
-}
-
-static void frame_queue_shrink(struct mpv_opengl_cb_context *ctx, int size)
-{
- pthread_cond_broadcast(&ctx->wakeup);
- while (ctx->queued_frames > size)
- frame_queue_drop(ctx);
-}
-
static void forget_frames(struct mpv_opengl_cb_context *ctx, bool all)
{
pthread_cond_broadcast(&ctx->wakeup);
- frame_queue_clear(ctx);
- talloc_free(ctx->waiting_frame);
- ctx->waiting_frame = NULL;
if (all) {
talloc_free(ctx->cur_frame);
ctx->cur_frame = NULL;
@@ -294,7 +237,6 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
struct vo *vo = ctx->active;
ctx->force_update |= ctx->reconfigured;
- ctx->frozen = false;
if (ctx->vp_w != vp_w || ctx->vp_h != vp_h)
ctx->force_update = true;
@@ -326,7 +268,6 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
gl_video_configure_queue(ctx->renderer, vo);
ctx->gl->debug_context = opts->use_gl_debug;
gl_video_set_debug(ctx->renderer, opts->use_gl_debug);
- frame_queue_shrink(ctx, opts->frame_queue_size);
}
}
ctx->reconfigured = false;
@@ -346,8 +287,12 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
}
ctx->eq_changed = false;
- struct vo_frame *frame = frame_queue_pop(ctx);
+ struct vo_frame *frame = ctx->next_frame;
+ int64_t wait_present_count = ctx->present_count;
if (frame) {
+ ctx->next_frame = NULL;
+ wait_present_count += 1;
+ pthread_cond_signal(&ctx->wakeup);
talloc_free(ctx->cur_frame);
ctx->cur_frame = vo_frame_ref(frame);
} else {
@@ -371,12 +316,11 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
talloc_free(frame);
pthread_mutex_lock(&ctx->lock);
- const int left = ctx->queued_frames;
- if (vo && left > 0)
- update(vo->priv);
+ while (wait_present_count > ctx->present_count)
+ pthread_cond_wait(&ctx->wakeup, &ctx->lock);
pthread_mutex_unlock(&ctx->lock);
- return left;
+ return 0;
}
int mpv_opengl_cb_report_flip(mpv_opengl_cb_context *ctx, int64_t time)
@@ -384,7 +328,8 @@ int mpv_opengl_cb_report_flip(mpv_opengl_cb_context *ctx, int64_t time)
MP_STATS(ctx, "glcb-reportflip");
pthread_mutex_lock(&ctx->lock);
- ctx->recent_flip = time > 0 ? time : mp_time_us();
+ ctx->flip_count += 1;
+ pthread_cond_signal(&ctx->wakeup);
pthread_mutex_unlock(&ctx->lock);
return 0;
@@ -402,38 +347,49 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
struct vo_priv *p = vo->priv;
pthread_mutex_lock(&p->ctx->lock);
- talloc_free(p->ctx->waiting_frame);
- p->ctx->waiting_frame = vo_frame_ref(frame);
+ assert(!p->ctx->next_frame);
+ p->ctx->next_frame = vo_frame_ref(frame);
+ p->ctx->expected_flip_count = p->ctx->flip_count + 1;
+ update(p);
pthread_mutex_unlock(&p->ctx->lock);
}
static void flip_page(struct vo *vo)
{
struct vo_priv *p = vo->priv;
+ struct timespec ts = mp_rel_time_to_timespec(0.2);
pthread_mutex_lock(&p->ctx->lock);
- while (p->ctx->queued_frames >= p->frame_queue_size) {
- switch (p->frame_drop_mode) {
- case FRAME_DROP_CLEAR:
- frame_queue_drop_all(p->ctx);
+
+ // Wait until frame was rendered
+ while (p->ctx->next_frame) {
+ if (pthread_cond_timedwait(&p->ctx->wakeup, &p->ctx->lock, &ts))
break;
- case FRAME_DROP_POP:
- frame_queue_shrink(p->ctx, p->frame_queue_size - 1);
+ }
+
+ // Unblock mpv_opengl_cb_draw().
+ p->ctx->present_count += 1;
+ pthread_cond_signal(&p->ctx->wakeup);
+
+ // Wait until frame was presented
+ while (p->ctx->expected_flip_count > p->ctx->flip_count) {
+ // mpv_opengl_cb_report_flip() is declared as optional API.
+ // Assume the user calls it consistently _if_ it's called at all.
+ if (!p->ctx->flip_count)
break;
- case FRAME_DROP_BLOCK: ;
- struct timespec ts = mp_rel_time_to_timespec(0.2);
- if (p->ctx->frozen ||
- pthread_cond_timedwait(&p->ctx->wakeup, &p->ctx->lock, &ts))
- {
- frame_queue_drop_all(p->ctx);
- p->ctx->frozen = true;
- }
+ if (pthread_cond_timedwait(&p->ctx->wakeup, &p->ctx->lock, &ts))
break;
- }
}
- frame_queue_push(p->ctx, p->ctx->waiting_frame);
- p->ctx->waiting_frame = NULL;
- update(p);
+
+ // The API user is not reacting, or is being unusually slow => drop.
+ if (p->ctx->next_frame) {
+ talloc_free(p->ctx->next_frame);
+ p->ctx->next_frame = NULL;
+ p->ctx->present_count += 1;
+ pthread_cond_signal(&p->ctx->wakeup);
+ vo_increment_drop_count(vo, 1);
+ }
+
pthread_mutex_unlock(&p->ctx->lock);
}
@@ -466,11 +422,6 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
#define OPT_BASE_STRUCT struct vo_priv
static const struct m_option change_opts[] = {
OPT_FLAG("debug", use_gl_debug, 0),
- OPT_INTRANGE("frame-queue-size", frame_queue_size, 0, 1, 100, OPTDEF_INT(2)),
- OPT_CHOICE("frame-drop-mode", frame_drop_mode, 0,
- ({"pop", FRAME_DROP_POP},
- {"clear", FRAME_DROP_CLEAR},
- {"block", FRAME_DROP_BLOCK})),
OPT_SUBSTRUCT("", renderer_opts, gl_video_conf, 0),
{0}
};
@@ -553,16 +504,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
*arg = p->ctx ? &p->ctx->hwdec_info : NULL;
return true;
}
- case VOCTRL_GET_RECENT_FLIP_TIME: {
- int r = VO_FALSE;
- pthread_mutex_lock(&p->ctx->lock);
- if (p->ctx->recent_flip) {
- *(int64_t *)data = p->ctx->recent_flip;
- r = VO_TRUE;
- }
- pthread_mutex_unlock(&p->ctx->lock);
- return r;
- }
}
return VO_NOTIMPL;
@@ -611,11 +552,6 @@ static int preinit(struct vo *vo)
#define OPT_BASE_STRUCT struct vo_priv
static const struct m_option options[] = {
OPT_FLAG("debug", use_gl_debug, 0),
- OPT_INTRANGE("frame-queue-size", frame_queue_size, 0, 1, 100, OPTDEF_INT(2)),
- OPT_CHOICE("frame-drop-mode", frame_drop_mode, 0,
- ({"pop", FRAME_DROP_POP},
- {"clear", FRAME_DROP_CLEAR},
- {"block", FRAME_DROP_BLOCK}), OPTDEF_INT(FRAME_DROP_BLOCK)),
OPT_SUBSTRUCT("", renderer_opts, gl_video_conf, 0),
{0},
};