summaryrefslogtreecommitdiffstats
path: root/libmpv
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 /libmpv
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.
Diffstat (limited to 'libmpv')
-rw-r--r--libmpv/client.h23
-rw-r--r--libmpv/mpv.def1
-rw-r--r--libmpv/render.h105
3 files changed, 127 insertions, 2 deletions
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