diff options
-rw-r--r-- | DOCS/man/input.rst | 8 | ||||
-rw-r--r-- | player/command.c | 31 | ||||
-rw-r--r-- | video/out/vo.c | 77 | ||||
-rw-r--r-- | video/out/vo.h | 2 |
4 files changed, 117 insertions, 1 deletions
diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst index 367d9f185b..1a59f283fe 100644 --- a/DOCS/man/input.rst +++ b/DOCS/man/input.rst @@ -1487,6 +1487,14 @@ Property list available on all platforms. Note that any of the listed facts may change any time without a warning. +``estimated-display-fps`` + Only available if display-sync mode (as selected by ``--video-sync``) is + active. Returns the actual rate at which display refreshes seem to occur, + measured by system time. + +``vsync-jitter`` + Estimated deviation factor of the vsync duration. + ``video-aspect`` (RW) Video aspect, see ``--video-aspect``. diff --git a/player/command.c b/player/command.c index c6fb5d6fdd..cc30d2b21a 100644 --- a/player/command.c +++ b/player/command.c @@ -2629,6 +2629,32 @@ static int mp_property_display_fps(void *ctx, struct m_property *prop, return m_property_double_ro(action, arg, fps); } +static int mp_property_estimated_display_fps(void *ctx, struct m_property *prop, + int action, void *arg) +{ + MPContext *mpctx = ctx; + struct vo *vo = mpctx->video_out; + if (!vo) + return M_PROPERTY_UNAVAILABLE; + double interval = vo_get_estimated_vsync_interval(vo); + if (interval <= 0) + return M_PROPERTY_UNAVAILABLE; + return m_property_double_ro(action, arg, 1.0 / interval); +} + +static int mp_property_vsync_jitter(void *ctx, struct m_property *prop, + int action, void *arg) +{ + MPContext *mpctx = ctx; + struct vo *vo = mpctx->video_out; + if (!vo) + return M_PROPERTY_UNAVAILABLE; + double stddev = vo_get_estimated_vsync_jitter(vo); + if (stddev < 0) + return M_PROPERTY_UNAVAILABLE; + return m_property_double_ro(action, arg, stddev); +} + static int mp_property_display_names(void *ctx, struct m_property *prop, int action, void *arg) { @@ -3612,6 +3638,8 @@ static const struct m_property mp_properties[] = { {"window-minimized", mp_property_win_minimized}, {"display-names", mp_property_display_names}, {"display-fps", mp_property_display_fps}, + {"estimated-display-fps", mp_property_estimated_display_fps}, + {"vsync-jitter", mp_property_vsync_jitter}, {"working-directory", mp_property_cwd}, @@ -3656,7 +3684,8 @@ static const char *const *const mp_event_property_change[] = { "percent-pos", "time-remaining", "playtime-remaining", "playback-time", "estimated-vf-fps", "drop-frame-count", "vo-drop-frame-count", "total-avsync-change", "audio-speed-correction", "video-speed-correction", - "vo-delayed-frame-count", "mistimed-frame-count", "vsync-ratio"), + "vo-delayed-frame-count", "mistimed-frame-count", "vsync-ratio", + "estimated-display-fps", "vsync-jitter"), E(MPV_EVENT_VIDEO_RECONFIG, "video-out-params", "video-params", "video-format", "video-codec", "video-bitrate", "dwidth", "dheight", "width", "height", "fps", "aspect", "vo-configured", "current-vo", diff --git a/video/out/vo.c b/video/out/vo.c index 6606cf82f7..883d0e9818 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -142,6 +142,13 @@ struct vo_internal { int64_t vsync_interval; + int64_t *vsync_samples; + int num_vsync_samples; + int64_t prev_vsync; + double estimated_vsync_interval; + double estimated_vsync_jitter; + bool expecting_vsync; + int64_t flip_queue_offset; // queue flip events at most this much in advance int64_t delayed_count; @@ -312,6 +319,48 @@ void vo_destroy(struct vo *vo) dealloc_vo(vo); } +// Drop timing information on discontinuities like seeking. +// Always called locked. +static void reset_vsync_timings(struct vo *vo) +{ + struct vo_internal *in = vo->in; + in->num_vsync_samples = 0; + in->prev_vsync = 0; + in->estimated_vsync_interval = 0; + in->estimated_vsync_jitter = -1; + in->expecting_vsync = false; +} + +// Always called locked. +static void update_vsync_timing_after_swap(struct vo *vo) +{ + struct vo_internal *in = vo->in; + + if (!in->expecting_vsync || !in->prev_vsync) { + reset_vsync_timings(vo); + return; + } + + int64_t now = mp_time_us(); + if (in->num_vsync_samples >= 200) + in->num_vsync_samples -= 1; + MP_TARRAY_INSERT_AT(in, in->vsync_samples, in->num_vsync_samples, 0, + now - in->prev_vsync); + in->prev_vsync = now; + + double avg = 0, jitter = 0; + for (int n = 0; n < in->num_vsync_samples; n++) { + avg += in->vsync_samples[n] / 1e6; + double diff = in->vsync_samples[n] / (double)in->vsync_interval - 1.0; + jitter += diff * diff; + } + avg /= in->num_vsync_samples; + in->estimated_vsync_interval = avg; + in->estimated_vsync_jitter = sqrt(jitter / in->num_vsync_samples); + + MP_STATS(vo, "value %f jitter", in->estimated_vsync_jitter); +} + // to be called from VO thread only static void update_display_fps(struct vo *vo) { @@ -383,6 +432,7 @@ static void run_reconfig(void *p) talloc_free(in->current_frame); in->current_frame = NULL; forget_frames(vo); + reset_vsync_timings(vo); pthread_mutex_unlock(&in->lock); update_display_fps(vo); @@ -705,6 +755,10 @@ static bool render_frame(struct vo *vo) if (in->current_frame->num_vsyncs > 0) in->current_frame->num_vsyncs -= 1; + in->expecting_vsync = in->current_frame->display_synced && !in->paused; + if (in->expecting_vsync && !in->prev_vsync) + in->prev_vsync = mp_time_us(); + if (in->dropped_frame) { in->drop_count += 1; } else { @@ -740,6 +794,8 @@ static bool render_frame(struct vo *vo) double diff = (in->vsync_interval - in->vsync_interval_approx) / 1e6; if (fabs(diff) < 0.150) MP_STATS(vo, "value %f vsync-diff", diff); + + update_vsync_timing_after_swap(vo); } if (!in->dropped_frame) { @@ -864,6 +920,7 @@ void vo_set_paused(struct vo *vo, bool paused) if (in->paused && in->dropped_frame) in->request_redraw = true; in->last_flip = 0; + reset_vsync_timings(vo); } pthread_mutex_unlock(&in->lock); vo_control(vo, paused ? VOCTRL_PAUSE : VOCTRL_RESUME, NULL); @@ -911,6 +968,7 @@ void vo_seek_reset(struct vo *vo) pthread_mutex_lock(&in->lock); forget_frames(vo); in->last_flip = 0; + reset_vsync_timings(vo); in->send_reset = true; wakeup_locked(vo); pthread_mutex_unlock(&in->lock); @@ -1022,6 +1080,25 @@ int64_t vo_get_vsync_interval(struct vo *vo) return res; } +// Returns duration of a display refresh in seconds. +double vo_get_estimated_vsync_interval(struct vo *vo) +{ + struct vo_internal *in = vo->in; + pthread_mutex_lock(&in->lock); + double res = in->estimated_vsync_interval; + pthread_mutex_unlock(&in->lock); + return res; +} + +double vo_get_estimated_vsync_jitter(struct vo *vo) +{ + struct vo_internal *in = vo->in; + pthread_mutex_lock(&in->lock); + double res = in->estimated_vsync_jitter; + pthread_mutex_unlock(&in->lock); + return res; +} + // Get the time in seconds at after which the currently rendering frame will // end. Returns positive values if the frame is yet to be finished, negative // values if it already finished. diff --git a/video/out/vo.h b/video/out/vo.h index bd68858b43..0f934ebdc4 100644 --- a/video/out/vo.h +++ b/video/out/vo.h @@ -348,6 +348,8 @@ void vo_set_queue_params(struct vo *vo, int64_t offset_us, bool vsync_timed, int num_req_frames); int vo_get_num_req_frames(struct vo *vo); int64_t vo_get_vsync_interval(struct vo *vo); +double vo_get_estimated_vsync_interval(struct vo *vo); +double vo_get_estimated_vsync_jitter(struct vo *vo); double vo_get_display_fps(struct vo *vo); double vo_get_delay(struct vo *vo); |