summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2018-04-20 19:26:04 +0200
committerJan Ekström <jeebjp@gmail.com>2018-04-29 02:21:32 +0300
commit67689ff6b42173b72bffecf23de3507e3ab605b0 (patch)
treea64a9ec7b382465e9e5dadb0bbd5b192a3fa5cd9
parent76844c9c519f4366463a70c8c2366a3d5dc9046c (diff)
downloadmpv-67689ff6b42173b72bffecf23de3507e3ab605b0.tar.bz2
mpv-67689ff6b42173b72bffecf23de3507e3ab605b0.tar.xz
client API: preparations for allowing render API to use DR etc.
DR (letting the decoder allocate texture memory) requires running the allocation on the render thread. This is rather hard with the render API, because the user controls this thread and when it's entered. It was not possible until now. This commit adds a bunch of infrastructure to make this possible. We add a new optional mode (MPV_RENDER_PARAM_ADVANCED_CONTROL) which basically lets the user's render thread and libmpv agree how this should be done. Misuse would lead to deadlocks. To make this less likely, strictly document thread safety/locking issues. In particular, document which libmpv functions can be called without issues. (The rest has to be assumed unsafe.) The worst issue is destruction of the render context while video is still active. To avoid certain unintended recursive locks (i.e. deadlocks, unless we'd make the locks recursive), make the update callback lock separate. Make "killing" the video chain asynchronous, so we can do extra work while video is being destroyed. Because losing wakeups is a big deal, setting the update callback now triggers a wakeup. (It would have been better if the wakeup callback were a parameter to mpv_render_context_create(), but too late.) This commit does not add DR yet; the following commit does this.
-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)