summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2018-04-21 00:26:58 +0200
committerJan Ekström <jeebjp@gmail.com>2018-04-29 02:21:32 +0300
commitce4d227986cf394a80fc687290bc732bbd8dbf22 (patch)
treec2f11a05e8961d613a244db5c2dfc7d67b1d0ccf
parent0be3a94e0b81d553849f9520f7ee9f2b6e34c6b4 (diff)
downloadmpv-ce4d227986cf394a80fc687290bc732bbd8dbf22.tar.bz2
mpv-ce4d227986cf394a80fc687290bc732bbd8dbf22.tar.xz
client API: add some render API extensions for timing
Attempts to enable the following things: - let a render API user do "proper" audio-sync video timing itself - make it possible to not re-render repeated frames if the API user has better mechanisms available (e.g. waiting for a DisplayLink cycle instead) - allow the user to delay or skip redraws if it makes sense Basically this information will be needed by API users who want to be "clever" about optimizing timing and rendering.
-rw-r--r--DOCS/client-api-changes.rst4
-rw-r--r--libmpv/mpv.def1
-rw-r--r--libmpv/render.h151
-rw-r--r--video/out/vo_libmpv.c92
4 files changed, 219 insertions, 29 deletions
diff --git a/DOCS/client-api-changes.rst b/DOCS/client-api-changes.rst
index 32244a6e37..cb80d0a698 100644
--- a/DOCS/client-api-changes.rst
+++ b/DOCS/client-api-changes.rst
@@ -34,6 +34,10 @@ API changes
--- mpv 0.29.0 ---
1.101 - add MPV_RENDER_PARAM_ADVANCED_CONTROL and related API
+ - add MPV_RENDER_PARAM_NEXT_FRAME_INFO and related symbols
+ - add MPV_RENDER_PARAM_BLOCK_FOR_TARGET_TIME
+ - add MPV_RENDER_PARAM_SKIP_RENDERING
+ - add mpv_render_context_get_info()
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/mpv.def b/libmpv/mpv.def
index efbf713622..b74378c4ae 100644
--- a/libmpv/mpv.def
+++ b/libmpv/mpv.def
@@ -34,6 +34,7 @@ mpv_opengl_cb_set_update_callback
mpv_opengl_cb_uninit_gl
mpv_render_context_create
mpv_render_context_free
+mpv_render_context_get_info
mpv_render_context_render
mpv_render_context_report_swap
mpv_render_context_set_parameter
diff --git a/libmpv/render.h b/libmpv/render.h
index af434b04e2..acd50cd03e 100644
--- a/libmpv/render.h
+++ b/libmpv/render.h
@@ -230,14 +230,55 @@ typedef enum mpv_render_param_type {
* Type: int*: 0 for disable (default), 1 for enable
*/
MPV_RENDER_PARAM_ADVANCED_CONTROL = 10,
+ /**
+ * Return information about the next frame to render. Valid for
+ * mpv_render_context_get_info().
+ *
+ * Type: mpv_render_frame_info*
+ *
+ * It strictly returns information about the _next_ frame. The implication
+ * is that e.g. mpv_render_context_update()'s return value will have
+ * MPV_RENDER_UPDATE_FRAME set, and the user is supposed to call
+ * mpv_render_context_render(). If there is no next frame, then the
+ * return value will have is_valid set to 0.
+ */
+ MPV_RENDER_PARAM_NEXT_FRAME_INFO = 11,
+ /**
+ * Enable or disable video timing. Valid for mpv_render_context_render().
+ *
+ * Type: int*: 0 for disable, 1 for enable (default)
+ *
+ * When video is timed to audio, the player attempts to render video a bit
+ * ahead, and then do a blocking wait until the target display time is
+ * reached. This blocks mpv_render_context_render() for up to the amount
+ * specified with the "video-timing-offset" global option. You can set
+ * this parameter to 0 to disable this kind of waiting. If you do, it's
+ * recommended to use the target time value in mpv_render_frame_info to
+ * wait yourself, or to set the "video-timing-offset" to 0 instead.
+ *
+ * Disabling this without doing anything in addition will result in A/V sync
+ * being slightly off.
+ */
+ MPV_RENDER_PARAM_BLOCK_FOR_TARGET_TIME = 12,
+ /**
+ * Use to skip rendering in mpv_render_context_render().
+ *
+ * Type: int*: 0 for rendering (default), 1 for skipping
+ *
+ * If this is set, you don't need to pass a target surface to the render
+ * function (and if you do, it's completely ignored). This can still call
+ * into the lower level APIs (i.e. if you use OpenGL, the OpenGL context
+ * must be set).
+ *
+ * Be aware that the render API will consider this frame as having been
+ * rendered. All other normal rules also apply, for example about whether
+ * you have to call mpv_render_context_report_swap(). It also does timing
+ * in the same way.
+ */
+ MPV_RENDER_PARAM_SKIP_RENDERING = 13,
} mpv_render_param_type;
/**
- * Predefined values for MPV_RENDER_PARAM_API_TYPE.
- */
-#define MPV_RENDER_API_TYPE_OPENGL "opengl"
-
-/**
* Used to pass arbitrary parameters to some mpv_render_* functions. The
* meaning of the data parameter is determined by the type, and each
* MPV_RENDER_PARAM_* documents what type the value must point to.
@@ -267,6 +308,84 @@ typedef struct mpv_render_param {
void *data;
} mpv_render_param;
+
+/**
+ * Predefined values for MPV_RENDER_PARAM_API_TYPE.
+ */
+#define MPV_RENDER_API_TYPE_OPENGL "opengl"
+
+/**
+ * Flags used in mpv_render_frame_info.flags. Each value represents a bit in it.
+ */
+typedef enum mpv_render_frame_info_flag {
+ /**
+ * Set if there is actually a next frame. If unset, there is no next frame
+ * yet, and other flags and fields that require a frame to be queued will
+ * be unset.
+ *
+ * This is set for _any_ kind of frame, even for redraw requests.
+ *
+ * Note that when this is unset, it simply means no new frame was
+ * decoded/queued yet, not necessarily that the end of the video was
+ * reached. A new frame can be queued after some time.
+ *
+ * If the return value of mpv_render_context_render() had the
+ * MPV_RENDER_UPDATE_FRAME flag set, this flag will usually be set as well,
+ * unless the frame is rendered, or discarded by other asynchronous events.
+ */
+ MPV_RENDER_FRAME_INFO_PRESENT = 1 << 0,
+ /**
+ * If set, the frame is not an actual new video frame, but a redraw request.
+ * For example if the video is paused, and an option that affects video
+ * rendering was changed (or any other reason), an update request can be
+ * issued and this flag will be set.
+ *
+ * Typically, redraw frames will not be subject to video timing.
+ *
+ * Implies MPV_RENDER_FRAME_INFO_PRESENT.
+ */
+ MPV_RENDER_FRAME_INFO_REDRAW = 1 << 1,
+ /**
+ * If set, this is supposed to reproduce the previous frame perfectly. This
+ * is usually used for certain "video-sync" options ("display-..." modes).
+ * Typically the renderer will blit the video from a FBO. Unset otherwise.
+ *
+ * Implies MPV_RENDER_FRAME_INFO_PRESENT.
+ */
+ MPV_RENDER_FRAME_INFO_REPEAT = 1 << 2,
+ /**
+ * If set, the player timing code expects that the user thread blocks on
+ * vsync (by either delaying the render call, or by making a call to
+ * mpv_render_context_report_swap() at vsync time).
+ *
+ * Implies MPV_RENDER_FRAME_INFO_PRESENT.
+ */
+ MPV_RENDER_FRAME_INFO_BLOCK_VSYNC = 1 << 3,
+} mpv_render_frame_info_flag;
+
+/**
+ * Information about the next video frame that will be rendered. Can be
+ * retrieved with MPV_RENDER_PARAM_NEXT_FRAME_INFO.
+ */
+typedef struct mpv_render_frame_info {
+ /**
+ * A bitset of mpv_render_frame_info_flag values (i.e. multiple flags are
+ * combined with bitwise or).
+ */
+ uint64_t flags;
+ /**
+ * Absolute time at which the frame is supposed to be displayed. This is in
+ * the same unit and base as the time returned by mpv_get_time_us(). For
+ * frames that are redrawn, or if vsync locked video timing is used (see
+ * "video-sync" option), then this can be 0. The "video-timing-offset"
+ * option determines how much "headroom" the render thread gets (but a high
+ * enough frame rate can reduce it anyway). mpv_render_context_render() will
+ * normally block until the time is elapsed, unless you pass it
+ * MPV_RENDER_PARAM_BLOCK_FOR_TARGET_TIME = 0.
+ */
+ int64_t target_time;
+} mpv_render_frame_info;
+
/**
* Initialize the renderer state. Depending on the backend used, this will
* access the underlying GPU API and initialize its own objects.
@@ -310,6 +429,28 @@ int mpv_render_context_create(mpv_render_context **res, mpv_handle *mpv,
int mpv_render_context_set_parameter(mpv_render_context *ctx,
mpv_render_param param);
+/**
+ * Retrieve information from the render context. This is NOT a counterpart to
+ * mpv_render_context_set_parameter(), because you generally can't read
+ * parameters set with it, and this function is not meant for this purpose.
+ * Instead, this is for communicating information from the renderer back to the
+ * user. See mpv_render_param_type; entries which support this function
+ * explicitly mention it, and for other entries you can assume it will fail.
+ *
+ * You pass param with param.type set and param.data pointing to a variable
+ * of the required data type. The function will then overwrite that variable
+ * with the returned value (at least on success).
+ *
+ * @param ctx a valid render context
+ * @param param the parameter type and data that should be retrieved
+ * @return error code. If a parameter could actually be retrieved, this returns
+ * success, otherwise an error code depending on the parameter type
+ * and situation. MPV_ERROR_NOT_IMPLEMENTED is used for unknown
+ * param.type, or if retrieving it is not supported.
+ */
+int mpv_render_context_get_info(mpv_render_context *ctx,
+ mpv_render_param param);
+
typedef void (*mpv_render_update_fn)(void *cb_ctx);
/**
diff --git a/video/out/vo_libmpv.c b/video/out/vo_libmpv.c
index ef5dc9809e..59d3b6883c 100644
--- a/video/out/vo_libmpv.c
+++ b/video/out/vo_libmpv.c
@@ -323,38 +323,45 @@ bool mp_render_context_acquire(mpv_render_context *ctx)
int mpv_render_context_render(mpv_render_context *ctx, mpv_render_param *params)
{
- int vp_w, vp_h;
- int err = ctx->renderer->fns->get_target_size(ctx->renderer, params,
- &vp_w, &vp_h);
- if (err < 0)
- return err;
-
pthread_mutex_lock(&ctx->lock);
- struct vo *vo = ctx->vo;
+ int do_render =
+ !GET_MPV_RENDER_PARAM(params, MPV_RENDER_PARAM_SKIP_RENDERING, int, 0);
- if (vo && (ctx->vp_w != vp_w || ctx->vp_h != vp_h || ctx->need_resize)) {
- ctx->vp_w = vp_w;
- ctx->vp_h = vp_h;
+ if (do_render) {
+ int vp_w, vp_h;
+ int err = ctx->renderer->fns->get_target_size(ctx->renderer, params,
+ &vp_w, &vp_h);
+ if (err < 0) {
+ pthread_mutex_unlock(&ctx->lock);
+ return err;
+ }
+
+ if (ctx->vo && (ctx->vp_w != vp_w || ctx->vp_h != vp_h ||
+ ctx->need_resize))
+ {
+ ctx->vp_w = vp_w;
+ ctx->vp_h = vp_h;
- m_config_cache_update(ctx->vo_opts_cache);
+ m_config_cache_update(ctx->vo_opts_cache);
- struct mp_rect src, dst;
- struct mp_osd_res osd;
- mp_get_src_dst_rects(ctx->log, ctx->vo_opts, vo->driver->caps,
- &ctx->img_params, vp_w, abs(vp_h),
- 1.0, &src, &dst, &osd);
+ struct mp_rect src, dst;
+ struct mp_osd_res osd;
+ mp_get_src_dst_rects(ctx->log, ctx->vo_opts, ctx->vo->driver->caps,
+ &ctx->img_params, vp_w, abs(vp_h),
+ 1.0, &src, &dst, &osd);
- ctx->renderer->fns->resize(ctx->renderer, &src, &dst, &osd);
+ ctx->renderer->fns->resize(ctx->renderer, &src, &dst, &osd);
+ }
+ ctx->need_resize = false;
}
- ctx->need_resize = false;
if (ctx->need_reconfig)
ctx->renderer->fns->reconfig(ctx->renderer, &ctx->img_params);
ctx->need_reconfig = false;
if (ctx->need_update_external)
- ctx->renderer->fns->update_external(ctx->renderer, vo);
+ ctx->renderer->fns->update_external(ctx->renderer, ctx->vo);
ctx->need_update_external = false;
if (ctx->need_reset) {
@@ -387,15 +394,22 @@ int mpv_render_context_render(mpv_render_context *ctx, mpv_render_param *params)
MP_STATS(ctx, "glcb-render");
- err = ctx->renderer->fns->render(ctx->renderer, params, frame);
+ int err = 0;
+
+ if (do_render)
+ err = ctx->renderer->fns->render(ctx->renderer, params, frame);
if (frame != &dummy)
talloc_free(frame);
- pthread_mutex_lock(&ctx->lock);
- while (wait_present_count > ctx->present_count)
- pthread_cond_wait(&ctx->video_wait, &ctx->lock);
- pthread_mutex_unlock(&ctx->lock);
+ if (GET_MPV_RENDER_PARAM(params, MPV_RENDER_PARAM_BLOCK_FOR_TARGET_TIME,
+ int, 1))
+ {
+ pthread_mutex_lock(&ctx->lock);
+ while (wait_present_count > ctx->present_count)
+ pthread_cond_wait(&ctx->video_wait, &ctx->lock);
+ pthread_mutex_unlock(&ctx->lock);
+ }
return err;
}
@@ -430,6 +444,36 @@ int mpv_render_context_set_parameter(mpv_render_context *ctx,
return ctx->renderer->fns->set_parameter(ctx->renderer, param);
}
+int mpv_render_context_get_info(mpv_render_context *ctx,
+ mpv_render_param param)
+{
+ int res = MPV_ERROR_NOT_IMPLEMENTED;
+ pthread_mutex_lock(&ctx->lock);
+
+ switch (param.type) {
+ case MPV_RENDER_PARAM_NEXT_FRAME_INFO: {
+ mpv_render_frame_info *info = param.data;
+ *info = (mpv_render_frame_info){0};
+ struct vo_frame *frame = ctx->next_frame;
+ if (frame) {
+ info->flags =
+ MPV_RENDER_FRAME_INFO_PRESENT |
+ (frame->redraw ? MPV_RENDER_FRAME_INFO_REDRAW : 0) |
+ (frame->repeat ? MPV_RENDER_FRAME_INFO_REPEAT : 0) |
+ (frame->display_synced && !frame->redraw ?
+ MPV_RENDER_FRAME_INFO_BLOCK_VSYNC : 0);
+ info->target_time = frame->pts;
+ }
+ res = 0;
+ break;
+ }
+ default:;
+ }
+
+ pthread_mutex_unlock(&ctx->lock);
+ return res;
+}
+
static void draw_frame(struct vo *vo, struct vo_frame *frame)
{
struct vo_priv *p = vo->priv;