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.
This commit is contained in:
wm4 2015-11-25 22:07:56 +01:00
parent 5bc9b273b3
commit 772961f0ce
4 changed files with 117 additions and 1 deletions

View File

@ -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``.

View File

@ -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",

View File

@ -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.

View File

@ -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);