summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-11-25 22:07:56 +0100
committerwm4 <wm4@nowhere>2015-11-25 22:07:56 +0100
commit772961f0ceb091f099eec03e2e8a3a2b354aa18f (patch)
tree7a387da504ec55dd88339ba7685ec51d54c2a706
parent5bc9b273b33b7ed4f85684e956a2e8ecb5645277 (diff)
downloadmpv-772961f0ceb091f099eec03e2e8a3a2b354aa18f.tar.bz2
mpv-772961f0ceb091f099eec03e2e8a3a2b354aa18f.tar.xz
command, vo: add estimated-display-fps property
This is simply the average refresh rate. Including "bad" samples is actually an advantage, because the property exists only for informational purposes, and will reflect problems such as the driver skipping a vsync. Also export the standard deviation of the vsync frame duration (normalized to the range 0-1) as vsync-jitter property.
-rw-r--r--DOCS/man/input.rst8
-rw-r--r--player/command.c31
-rw-r--r--video/out/vo.c77
-rw-r--r--video/out/vo.h2
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);