diff options
-rw-r--r-- | DOCS/client-api-changes.rst | 1 | ||||
-rw-r--r-- | libmpv/client.h | 23 | ||||
-rw-r--r-- | libmpv/mpv.def | 1 | ||||
-rw-r--r-- | libmpv/render.h | 105 | ||||
-rw-r--r-- | player/client.c | 31 | ||||
-rw-r--r-- | player/client.h | 3 | ||||
-rw-r--r-- | video/out/libmpv.h | 3 | ||||
-rw-r--r-- | video/out/vo_libmpv.c | 132 |
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) |