summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/client-api-changes.rst1
-rw-r--r--libmpv/client.h23
-rw-r--r--libmpv/mpv.def1
-rw-r--r--libmpv/render.h105
-rw-r--r--player/client.c31
-rw-r--r--player/client.h3
-rw-r--r--video/out/libmpv.h3
-rw-r--r--video/out/vo_libmpv.c132
8 files changed, 269 insertions, 30 deletions
diff --git a/DOCS/client-api-changes.rst b/DOCS/client-api-changes.rst
index a451f6e032..32244a6e37 100644
--- a/DOCS/client-api-changes.rst
+++ b/DOCS/client-api-changes.rst
@@ -33,6 +33,7 @@ API changes
::
--- mpv 0.29.0 ---
+ 1.101 - add MPV_RENDER_PARAM_ADVANCED_CONTROL and related API
1.100 - bump API number to avoid confusion with mpv release versions
- actually apply the GL_MP_MPGetNativeDisplay change for the new render
API. This also means compatibility for anything but x11 and wayland
diff --git a/libmpv/client.h b/libmpv/client.h
index 77c149e405..488ccb332c 100644
--- a/libmpv/client.h
+++ b/libmpv/client.h
@@ -210,7 +210,7 @@ extern "C" {
* relational operators (<, >, <=, >=).
*/
#define MPV_MAKE_VERSION(major, minor) (((major) << 16) | (minor) | 0UL)
-#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 100)
+#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 101)
/**
* The API user is allowed to "#define MPV_ENABLE_DEPRECATED 0" before
@@ -614,6 +614,8 @@ void mpv_resume(mpv_handle *ctx);
*
* Unlike other libmpv APIs, this can be called at absolutely any time (even
* within wakeup callbacks), as long as the context is valid.
+ *
+ * Safe to be called from mpv render API threads.
*/
int64_t mpv_get_time_us(mpv_handle *ctx);
@@ -959,6 +961,8 @@ int mpv_command_string(mpv_handle *ctx, const char *args);
* be unified in the future. For now, calling this API means that the command
* will be synchronously executed on the core, without blocking the API user.
*
+ * * Safe to be called from mpv render API threads.
+ *
* @param reply_userdata the value mpv_event.reply_userdata of the reply will
* be set to (see section about asynchronous calls)
* @param args NULL-terminated list of strings (see mpv_command())
@@ -975,6 +979,8 @@ int mpv_command_async(mpv_handle *ctx, uint64_t reply_userdata,
* See mpv_command_async() for details. Retrieving the result is not
* supported yet.
*
+ * Safe to be called from mpv render API threads.
+ *
* @param reply_userdata the value mpv_event.reply_userdata of the reply will
* be set to (see section about asynchronous calls)
* @param args as in mpv_command_node()
@@ -1030,6 +1036,8 @@ int mpv_set_property_string(mpv_handle *ctx, const char *name, const char *data)
* the result status of the operation. Otherwise, this function is similar to
* mpv_set_property().
*
+ * Safe to be called from mpv render API threads.
+ *
* @param reply_userdata see section about asynchronous calls
* @param name The property name.
* @param format see enum mpv_format.
@@ -1090,6 +1098,8 @@ char *mpv_get_property_osd_string(mpv_handle *ctx, const char *name);
* as well as the property data with the MPV_EVENT_GET_PROPERTY_REPLY event.
* You should check the mpv_event.error field on the reply event.
*
+ * Safe to be called from mpv render API threads.
+ *
* @param reply_userdata see section about asynchronous calls
* @param name The property name.
* @param format see enum mpv_format.
@@ -1136,6 +1146,8 @@ int mpv_get_property_async(mpv_handle *ctx, uint64_t reply_userdata,
* Only the mpv_handle on which this was called will receive the property
* change events, or can unobserve them.
*
+ * Safe to be called from mpv render API threads.
+ *
* @param reply_userdata This will be used for the mpv_event.reply_userdata
* field for the received MPV_EVENT_PROPERTY_CHANGE
* events. (Also see section about asynchronous calls,
@@ -1155,6 +1167,8 @@ int mpv_observe_property(mpv_handle *mpv, uint64_t reply_userdata,
* Undo mpv_observe_property(). This will remove all observed properties for
* which the given number was passed as reply_userdata to mpv_observe_property.
*
+ * Safe to be called from mpv render API threads.
+ *
* @param registered_reply_userdata ID that was passed to mpv_observe_property
* @return negative value is an error code, >=0 is number of removed properties
* on success (includes the case when 0 were removed)
@@ -1586,6 +1600,8 @@ typedef struct mpv_event {
* (Informational note: currently, all events are enabled by default, except
* MPV_EVENT_TICK.)
*
+ * Safe to be called from mpv render API threads.
+ *
* @param event See enum mpv_event_id.
* @param enable 1 to enable receiving this event, 0 to disable it.
* @return error code
@@ -1625,6 +1641,9 @@ int mpv_request_log_messages(mpv_handle *ctx, const char *min_level);
* function internally calls mpv_wait_event(). Additionally, concurrent calls
* to different mpv_handles are always safe.
*
+ * As long as the timeout is 0, this is safe to be called from mpv render API
+ * threads.
+ *
* @param timeout Timeout in seconds, after which the function returns even if
* no event was received. A MPV_EVENT_NONE is returned on
* timeout. A value of 0 will disable waiting. Negative values
@@ -1648,6 +1667,8 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout);
* this call. But note that this dummy event might be skipped if there are
* already other events queued. All what counts is that the waiting thread
* is woken up at all.
+ *
+ * Safe to be called from mpv render API threads.
*/
void mpv_wakeup(mpv_handle *ctx);
diff --git a/libmpv/mpv.def b/libmpv/mpv.def
index 1d828f4b2b..efbf713622 100644
--- a/libmpv/mpv.def
+++ b/libmpv/mpv.def
@@ -38,6 +38,7 @@ mpv_render_context_render
mpv_render_context_report_swap
mpv_render_context_set_parameter
mpv_render_context_set_update_callback
+mpv_render_context_update
mpv_request_event
mpv_request_log_messages
mpv_resume
diff --git a/libmpv/render.h b/libmpv/render.h
index 34adb39646..af434b04e2 100644
--- a/libmpv/render.h
+++ b/libmpv/render.h
@@ -38,7 +38,7 @@ extern "C" {
* Preferably rendering should be done in a separate thread. If you call
* normal libmpv API functions on the renderer thread, deadlocks can result
* (these are made non-fatal with timeouts, but user experience will obviously
- * suffer).
+ * suffer). See "Threading" section below.
*
* You can output and embed video without this API by setting the mpv "wid"
* option to a native window handle (see "Embedding the video window" section
@@ -54,6 +54,9 @@ extern "C" {
* Threading
* ---------
*
+ * You are recommended to do rendering on a separate thread than normal libmpv
+ * use.
+ *
* The mpv_render_* functions can be called from any thread, under the
* following conditions:
* - only one of the mpv_render_* functions can be called at the same time
@@ -64,6 +67,32 @@ extern "C" {
* must be "current" in the calling thread, and it must be the same OpenGL
* context as the mpv_render_context was created with. Otherwise, undefined
* behavior will occur.
+ * - the thread does not call libmpv API functions other than the mpv_render_*
+ * functions, except APIs which are declared as safe (see below). Likewise,
+ * there must be no lock or wait dependency from the render thread to a
+ * thread using other libmpv functions. Basically, the situation that your
+ * render thread waits for a "not safe" libmpv API function to return must
+ * not happen. If you ignore this requirement, deadlocks can happen, which
+ * are made non-fatal with timeouts; then playback quality will be degraded,
+ * and the message
+ * mpv_render_context_render() not being called or stuck.
+ * is logged. If you set MPV_RENDER_PARAM_ADVANCED_CONTROL, you promise that
+ * this won't happen, and must absolutely guarantee it, or a real deadlock
+ * will freeze the mpv core thread forever.
+ *
+ * libmpv functions which are safe to call from a render thread are:
+ * - functions marked with "Safe to be called from mpv render API threads."
+ * - client.h functions which don't have an explicit or implicit mpv_handle
+ * parameter
+ * - mpv_render_* functions; but only for the same mpv_render_context pointer.
+ * If the pointer is different, mpv_render_context_free() is not safe. (The
+ * reason is that if MPV_RENDER_PARAM_ADVANCED_CONTROL is set, it may have
+ * to process still queued requests from the core, which it can do only for
+ * the current context, while requests for other contexts would deadlock.
+ * Also, it may have to wait and block for the core to terminate the video
+ * chain to make sure no resources are used after context destruction.)
+ * - if the mpv_handle parameter refers to a different mpv core than the one
+ * you're rendering for (very obscure, but allowed)
*
* Context and handle lifecycle
* ----------------------------
@@ -72,6 +101,8 @@ extern "C" {
* (with mpv_render_context_create()), or it will revert to a VO that creates
* its own window.
*
+ * Currently, there can be only 1 mpv_render_context at a time per mpv core.
+ *
* Calling mpv_render_context_free() while a VO is using the render context is
* active will disable video.
*
@@ -166,6 +197,39 @@ typedef enum mpv_render_param_type {
* Type: struct wl_display*
*/
MPV_RENDER_PARAM_WL_DISPLAY = 9,
+ /**
+ * Better control about rendering and enabling some advanced features. Valid
+ * for mpv_render_context_create().
+ *
+ * This conflates multiple requirements the API user promises to abide if
+ * this option is enabled:
+ *
+ * - The API user's render thread, which is calling the mpv_render_*()
+ * functions, never waits for the core. Otherwise deadlocks can happen.
+ * See "Threading" section.
+ * - The callback set with mpv_render_context_set_update_callback() can now
+ * be called even if there is no new frame. The API user should call the
+ * mpv_render_context_update() function, and interpret the return value
+ * for whether a new frame should be rendered.
+ * - Correct functionality is impossible if the update callback is not set,
+ * or not set soon enough after mpv_render_context_create() (the core can
+ * block while waiting for you to call mpv_render_context_update(), and
+ * if the update callback is not correctly set, it will deadlock, or
+ * block for too long).
+ *
+ * In general, setting this option will enable the following features (and
+ * possibly more):
+ *
+ * - "Direct rendering", which means the player decodes directly to a
+ * texture, which saves a copy per video frame ("vd-lavc-dr" option
+ * needs to be enabled, and the rendering backend as well as the
+ * underlying GPU API/driver needs to have support for it).
+ * - Rendering screenshots with the GPU API if supported by the backend
+ * (instead of using a suboptimal software fallback via libswscale).
+ *
+ * Type: int*: 0 for disable (default), 1 for enable
+ */
+ MPV_RENDER_PARAM_ADVANCED_CONTROL = 10,
} mpv_render_param_type;
/**
@@ -258,6 +322,8 @@ typedef void (*mpv_render_update_fn)(void *cb_ctx);
* This can be called from any thread, except from an update callback. In case
* of the OpenGL backend, no OpenGL state or API is accessed.
*
+ * Calling this will raise an update callback immediately.
+ *
* @param callback callback(callback_ctx) is called if the frame should be
* redrawn
* @param callback_ctx opaque argument to the callback
@@ -267,6 +333,43 @@ void mpv_render_context_set_update_callback(mpv_render_context *ctx,
void *callback_ctx);
/**
+ * The API user is supposed to call this when the update callback was invoked
+ * (like all mpv_render_* functions, this has to happen on the render thread,
+ * and _not_ from the update callback itself).
+ *
+ * This is optional if MPV_RENDER_PARAM_ADVANCED_CONTROL was not set (default).
+ * Otherwise, it's a hard requirement that this is called after each update
+ * callback. If multiple update callback happened, and the function could not
+ * be called sooner, it's OK to call it once after the last callback.
+ *
+ * If an update callback happens during or after this function, the function
+ * must be called again at the soonest possible time.
+ *
+ * If MPV_RENDER_PARAM_ADVANCED_CONTROL was set, this will do additional work
+ * such as allocating textures for the video decoder.
+ *
+ * @return a bitset of mpv_render_update_flag values (i.e. multiple flags are
+ * combined with bitwise or). Typically, this will tell the API user
+ * what should happen next. E.g. if the MPV_RENDER_UPDATE_FRAME flag is
+ * set, mpv_render_context_render() should be called. If flags unknown
+ * to the API user are set, or if the return value is 0, nothing needs
+ * to be done.
+ */
+uint64_t mpv_render_context_update(mpv_render_context *ctx);
+
+/**
+ * Flags returned by mpv_render_context_update(). Each value represents a bit
+ * in the function's return value.
+ */
+typedef enum mpv_render_update_flag {
+ /**
+ * A new video frame must be rendered. mpv_render_context_render() must be
+ * called.
+ */
+ MPV_RENDER_UPDATE_FRAME = 1 << 0,
+} mpv_render_context_flag;
+
+/**
* Render video.
*
* Typically renders the video to a target surface provided via mpv_render_param
diff --git a/player/client.c b/player/client.c
index 247f580498..0e50d735fc 100644
--- a/player/client.c
+++ b/player/client.c
@@ -1749,18 +1749,39 @@ int64_t mpv_get_time_us(mpv_handle *ctx)
#include "video/out/libmpv.h"
-// Used by vo_libmpv to synchronously uninitialize video.
-void kill_video(struct mp_client_api *client_api)
+struct kill_ctx {
+ struct MPContext *mpctx;
+ void (*fin)(void *ctx);
+ void *fin_ctx;
+};
+
+static void do_kill(void *ptr)
{
- struct MPContext *mpctx = client_api->mpctx;
- mp_dispatch_lock(mpctx->dispatch);
+ struct kill_ctx *k = ptr;
+ struct MPContext *mpctx = k->mpctx;
+
struct track *track = mpctx->vo_chain ? mpctx->vo_chain->track : NULL;
uninit_video_out(mpctx);
if (track) {
mpctx->error_playing = MPV_ERROR_VO_INIT_FAILED;
error_on_track(mpctx, track);
}
- mp_dispatch_unlock(mpctx->dispatch);
+
+ k->fin(k->fin_ctx);
+}
+
+// Used by vo_libmpv to (a)synchronously uninitialize video.
+void kill_video_async(struct mp_client_api *client_api, void (*fin)(void *ctx),
+ void *fin_ctx)
+{
+ struct MPContext *mpctx = client_api->mpctx;
+ struct kill_ctx *k = talloc_ptrtype(NULL, k);
+ *k = (struct kill_ctx){
+ .mpctx = mpctx,
+ .fin = fin,
+ .fin_ctx = fin_ctx,
+ };
+ mp_dispatch_enqueue_autofree(mpctx->dispatch, do_kill, k);
}
// Used by vo_libmpv to set the current render context.
diff --git a/player/client.h b/player/client.h
index b1c2ffc500..2512315d42 100644
--- a/player/client.h
+++ b/player/client.h
@@ -48,7 +48,8 @@ bool mp_set_main_render_context(struct mp_client_api *client_api,
struct mpv_render_context *ctx, bool active);
struct mpv_render_context *
mp_client_api_acquire_render_context(struct mp_client_api *ca);
-void kill_video(struct mp_client_api *client_api);
+void kill_video_async(struct mp_client_api *client_api, void (*fin)(void *ctx),
+ void *fin_ctx);
bool mp_streamcb_lookup(struct mpv_global *g, const char *protocol,
void **out_user_data, mpv_stream_cb_open_ro_fn *out_fn);
diff --git a/video/out/libmpv.h b/video/out/libmpv.h
index 8b99587984..ae154bbd24 100644
--- a/video/out/libmpv.h
+++ b/video/out/libmpv.h
@@ -12,6 +12,9 @@
void *get_mpv_render_param(mpv_render_param *params, mpv_render_param_type type,
void *def);
+#define GET_MPV_RENDER_PARAM(params, type, ctype, def) \
+ (*(ctype *)get_mpv_render_param(params, type, &(ctype){(def)}))
+
typedef int (*mp_render_cb_control_fn)(void *cb_ctx, int *events,
uint32_t request, void *data);
void mp_render_context_set_control_callback(mpv_render_context *ctx,
diff --git a/video/out/vo_libmpv.c b/video/out/vo_libmpv.c
index 61f73aee07..75ac514595 100644
--- a/video/out/vo_libmpv.c
+++ b/video/out/vo_libmpv.c
@@ -12,6 +12,7 @@
#include "mpv_talloc.h"
#include "common/common.h"
#include "misc/bstr.h"
+#include "misc/dispatch.h"
#include "common/msg.h"
#include "options/m_config.h"
#include "options/options.h"
@@ -39,13 +40,14 @@
* render API user anyway, and the (unlikely) deadlock is avoided with
* a timeout
*
- * So: mpv core > VO > mpv_render_context > mp_client_api.lock (locking)
+ * Locking: mpv core > VO > mpv_render_context.lock > mp_client_api.lock
+ * > mpv_render_context.update_lock
* And: render thread > VO (wait for present)
* VO > render thread (wait for present done, via timeout)
*/
struct vo_priv {
- struct mpv_render_context *ctx;
+ struct mpv_render_context *ctx; // immutable after init
};
struct mpv_render_context {
@@ -55,16 +57,27 @@ struct mpv_render_context {
atomic_bool in_use;
+ // --- Immutable after init
+ bool advanced_control;
+ struct mp_dispatch_queue *dispatch; // NULL if advanced_control disabled
+
pthread_mutex_t control_lock;
+ // --- Protected by control_lock
mp_render_cb_control_fn control_cb;
void *control_cb_ctx;
- pthread_mutex_t lock;
- pthread_cond_t wakeup;
+ pthread_mutex_t update_lock;
+ pthread_cond_t update_cond; // paired with update_lock
- // --- Protected by lock
+ // --- Protected by update_lock
mpv_render_update_fn update_cb;
void *update_cb_ctx;
+ bool had_kill_update; // update during termination
+
+ pthread_mutex_t lock;
+ pthread_cond_t video_wait; // paired with lock
+
+ // --- Protected by lock
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
@@ -110,20 +123,29 @@ void *get_mpv_render_param(mpv_render_param *params, mpv_render_param_type type,
static void forget_frames(struct mpv_render_context *ctx, bool all)
{
- pthread_cond_broadcast(&ctx->wakeup);
+ pthread_cond_broadcast(&ctx->video_wait);
if (all) {
talloc_free(ctx->cur_frame);
ctx->cur_frame = NULL;
}
}
+static void dispatch_wakeup(void *ptr)
+{
+ struct mpv_render_context *ctx = ptr;
+
+ update(ctx);
+}
+
int mpv_render_context_create(mpv_render_context **res, mpv_handle *mpv,
mpv_render_param *params)
{
mpv_render_context *ctx = talloc_zero(NULL, mpv_render_context);
pthread_mutex_init(&ctx->control_lock, NULL);
pthread_mutex_init(&ctx->lock, NULL);
- pthread_cond_init(&ctx->wakeup, NULL);
+ pthread_mutex_init(&ctx->update_lock, NULL);
+ pthread_cond_init(&ctx->update_cond, NULL);
+ pthread_cond_init(&ctx->video_wait, NULL);
ctx->global = mp_client_get_global(mpv);
ctx->client_api = ctx->global->client_api;
@@ -132,6 +154,12 @@ int mpv_render_context_create(mpv_render_context **res, mpv_handle *mpv,
ctx->vo_opts_cache = m_config_cache_alloc(ctx, ctx->global, &vo_sub_opts);
ctx->vo_opts = ctx->vo_opts_cache->opts;
+ if (GET_MPV_RENDER_PARAM(params, MPV_RENDER_PARAM_ADVANCED_CONTROL, int, 0)) {
+ ctx->advanced_control = true;
+ ctx->dispatch = mp_dispatch_create(ctx);
+ mp_dispatch_set_wakeup_fn(ctx->dispatch, dispatch_wakeup, ctx);
+ }
+
int err = MPV_ERROR_NOT_IMPLEMENTED;
for (int n = 0; render_backends[n]; n++) {
ctx->renderer = talloc_zero(NULL, struct render_backend);
@@ -176,10 +204,12 @@ void mpv_render_context_set_update_callback(mpv_render_context *ctx,
mpv_render_update_fn callback,
void *callback_ctx)
{
- pthread_mutex_lock(&ctx->lock);
+ pthread_mutex_lock(&ctx->update_lock);
ctx->update_cb = callback;
ctx->update_cb_ctx = callback_ctx;
- pthread_mutex_unlock(&ctx->lock);
+ if (ctx->update_cb)
+ ctx->update_cb(ctx->update_cb_ctx);
+ pthread_mutex_unlock(&ctx->update_lock);
}
void mp_render_context_set_control_callback(mpv_render_context *ctx,
@@ -192,6 +222,16 @@ void mp_render_context_set_control_callback(mpv_render_context *ctx,
pthread_mutex_unlock(&ctx->control_lock);
}
+static void kill_cb(void *ptr)
+{
+ struct mpv_render_context *ctx = ptr;
+
+ pthread_mutex_lock(&ctx->update_lock);
+ ctx->had_kill_update = true;
+ pthread_cond_broadcast(&ctx->update_cond);
+ pthread_mutex_unlock(&ctx->update_lock);
+}
+
void mpv_render_context_free(mpv_render_context *ctx)
{
if (!ctx)
@@ -205,19 +245,43 @@ void mpv_render_context_free(mpv_render_context *ctx)
// also bring down the decoder etc., which still might be using the hwdec
// context. The above removal guarantees it can't come back (so ctx->vo
// can't change to non-NULL).
- if (atomic_load(&ctx->in_use))
- kill_video(ctx->client_api);
+ if (atomic_load(&ctx->in_use)) {
+ kill_video_async(ctx->client_api, kill_cb, ctx);
+
+ while (atomic_load(&ctx->in_use)) {
+ // As long as the video decoders are not destroyed, they can still
+ // try to allocate new DR images and so on. This is a grotesque
+ // corner case, but possible. Also, more likely, DR images need to
+ // be released while the video chain is destroyed.
+ if (ctx->dispatch)
+ mp_dispatch_queue_process(ctx->dispatch, 0);
+
+ // Wait for kill_cb() or update() calls.
+ pthread_mutex_lock(&ctx->update_lock);
+ if (!ctx->had_kill_update)
+ pthread_cond_wait(&ctx->update_cond, &ctx->update_lock);
+ ctx->had_kill_update = false;
+ pthread_mutex_unlock(&ctx->update_lock);
+ }
+ }
assert(!atomic_load(&ctx->in_use));
assert(!ctx->vo);
+ // Possibly remaining outstanding work.
+ if (ctx->dispatch)
+ mp_dispatch_queue_process(ctx->dispatch, 0);
+
forget_frames(ctx, true);
ctx->renderer->fns->destroy(ctx->renderer);
talloc_free(ctx->renderer->priv);
talloc_free(ctx->renderer);
+ talloc_free(ctx->dispatch);
- pthread_cond_destroy(&ctx->wakeup);
+ pthread_cond_destroy(&ctx->update_cond);
+ pthread_cond_destroy(&ctx->video_wait);
+ pthread_mutex_destroy(&ctx->update_lock);
pthread_mutex_destroy(&ctx->lock);
pthread_mutex_destroy(&ctx->control_lock);
@@ -282,7 +346,7 @@ int mpv_render_context_render(mpv_render_context *ctx, mpv_render_param *params)
ctx->next_frame = NULL;
if (!(frame->redraw || !frame->current))
wait_present_count += 1;
- pthread_cond_signal(&ctx->wakeup);
+ pthread_cond_broadcast(&ctx->video_wait);
talloc_free(ctx->cur_frame);
ctx->cur_frame = vo_frame_ref(frame);
} else {
@@ -306,7 +370,7 @@ int mpv_render_context_render(mpv_render_context *ctx, mpv_render_param *params)
pthread_mutex_lock(&ctx->lock);
while (wait_present_count > ctx->present_count)
- pthread_cond_wait(&ctx->wakeup, &ctx->lock);
+ pthread_cond_wait(&ctx->video_wait, &ctx->lock);
pthread_mutex_unlock(&ctx->lock);
return err;
@@ -318,8 +382,22 @@ void mpv_render_context_report_swap(mpv_render_context *ctx)
pthread_mutex_lock(&ctx->lock);
ctx->flip_count += 1;
- pthread_cond_signal(&ctx->wakeup);
+ pthread_cond_broadcast(&ctx->video_wait);
+ pthread_mutex_unlock(&ctx->lock);
+}
+
+uint64_t mpv_render_context_update(mpv_render_context *ctx)
+{
+ uint64_t res = 0;
+
+ if (ctx->dispatch)
+ mp_dispatch_queue_process(ctx->dispatch, 0);
+
+ pthread_mutex_lock(&ctx->lock);
+ if (ctx->next_frame)
+ res |= MPV_RENDER_UPDATE_FRAME;
pthread_mutex_unlock(&ctx->lock);
+ return res;
}
int mpv_render_context_set_parameter(mpv_render_context *ctx,
@@ -336,11 +414,16 @@ int mpv_render_context_set_parameter(mpv_render_context *ctx,
return err;
}
-// Called locked.
static void update(struct mpv_render_context *ctx)
{
+ pthread_mutex_lock(&ctx->update_lock);
if (ctx->update_cb)
ctx->update_cb(ctx->update_cb_ctx);
+
+ // For the termination code.
+ ctx->had_kill_update = true;
+ pthread_cond_broadcast(&ctx->update_cond);
+ pthread_mutex_unlock(&ctx->update_lock);
}
static void draw_frame(struct vo *vo, struct vo_frame *frame)
@@ -352,8 +435,9 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
p->ctx->next_frame = vo_frame_ref(frame);
p->ctx->expected_flip_count = p->ctx->flip_count + 1;
p->ctx->redrawing = frame->redraw || !frame->current;
- update(p->ctx);
pthread_mutex_unlock(&p->ctx->lock);
+
+ update(p->ctx);
}
static void flip_page(struct vo *vo)
@@ -365,7 +449,7 @@ static void flip_page(struct vo *vo)
// Wait until frame was rendered
while (p->ctx->next_frame) {
- if (pthread_cond_timedwait(&p->ctx->wakeup, &p->ctx->lock, &ts)) {
+ if (pthread_cond_timedwait(&p->ctx->video_wait, &p->ctx->lock, &ts)) {
if (p->ctx->next_frame) {
MP_VERBOSE(vo, "mpv_render_context_render() not being called "
"or stuck.\n");
@@ -376,7 +460,7 @@ static void flip_page(struct vo *vo)
// Unblock mpv_render_context_render().
p->ctx->present_count += 1;
- pthread_cond_signal(&p->ctx->wakeup);
+ pthread_cond_broadcast(&p->ctx->video_wait);
if (p->ctx->redrawing)
goto done; // do not block for redrawing
@@ -387,7 +471,7 @@ static void flip_page(struct vo *vo)
// Assume the user calls it consistently _if_ it's called at all.
if (!p->ctx->flip_count)
break;
- if (pthread_cond_timedwait(&p->ctx->wakeup, &p->ctx->lock, &ts)) {
+ if (pthread_cond_timedwait(&p->ctx->video_wait, &p->ctx->lock, &ts)) {
MP_VERBOSE(vo, "mpv_render_report_swap() not being called.\n");
goto done;
}
@@ -401,7 +485,7 @@ done:
p->ctx->cur_frame = p->ctx->next_frame;
p->ctx->next_frame = NULL;
p->ctx->present_count += 2;
- pthread_cond_signal(&p->ctx->wakeup);
+ pthread_cond_signal(&p->ctx->video_wait);
vo_increment_drop_count(vo, 1);
}
@@ -474,6 +558,7 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
p->ctx->need_reconfig = true;
p->ctx->need_resize = true;
pthread_mutex_unlock(&p->ctx->lock);
+
control(vo, VOCTRL_RECONFIG, NULL);
return 0;
@@ -484,7 +569,9 @@ static void uninit(struct vo *vo)
struct vo_priv *p = vo->priv;
control(vo, VOCTRL_UNINIT, NULL);
+
pthread_mutex_lock(&p->ctx->lock);
+
forget_frames(p->ctx, true);
p->ctx->img_params = (struct mp_image_params){0};
p->ctx->need_reconfig = true;
@@ -492,11 +579,12 @@ static void uninit(struct vo *vo)
p->ctx->need_update_external = true;
p->ctx->need_reset = true;
p->ctx->vo = NULL;
- update(p->ctx);
pthread_mutex_unlock(&p->ctx->lock);
bool state = atomic_exchange(&p->ctx->in_use, false);
assert(state); // obviously must have been set
+
+ update(p->ctx);
}
static int preinit(struct vo *vo)