From 95c27f0232bf3dd707c4b03a5be4319cfb205d01 Mon Sep 17 00:00:00 2001 From: Torsten Jager Date: Wed, 18 Dec 2013 11:39:35 +0100 Subject: Fix metronom video drift compensation. Old code was broken in 2 ways: * It repeatedly multiplied drift by 29/30, thus never reaching 0. * If decoder does not always set vo_frame.duration, strange jumps appeared. --- src/xine-engine/metronom.c | 53 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/xine-engine/metronom.c b/src/xine-engine/metronom.c index cdadf11f4..1baa73f84 100644 --- a/src/xine-engine/metronom.c +++ b/src/xine-engine/metronom.c @@ -481,41 +481,62 @@ static void metronom_got_video_frame (metronom_t *this, vo_frame_t *img) { this->force_video_jump = 0; this->video_vpts = vpts; this->video_drift = 0; + this->video_drift_step = 0; - xprintf(this->xine, XINE_VERBOSITY_DEBUG, "video jump\n"); + xprintf(this->xine, XINE_VERBOSITY_DEBUG, "metronom: video jump by %"PRId64" pts\n", diff); } else { - - this->video_drift = diff; - this->video_drift_step = diff / 30; - /* this will fix video drift with a constant compensation each - frame for about 1 second of video. */ - - if (diff) lprintf("video drift, drift is %" PRId64 "\n", this->video_drift); + /* TJ. Drift into new value over the next 30 frames. + Dont fall into the asymptote trap of bringing down step with remaining drift. */ + int64_t step; + if (diff < 0) { + step = (diff - 29) / 30; + if (this->video_drift_step < step) + step = this->video_drift_step < diff ? diff : this->video_drift_step; + } else { + step = (diff + 29) / 30; + if (this->video_drift_step > step) + step = this->video_drift_step > diff ? diff : this->video_drift_step; + } + this->video_drift = diff; + this->video_drift_step = step; } } else { /* VIDEO_PTS_MODE: do not use the predicted value */ - this->video_vpts = vpts; + this->video_drift = 0; + this->video_drift_step = 0; + this->video_vpts = vpts; } } img->vpts = this->video_vpts + this->av_offset; + /* We need to update this->video_vpts is both modes. + * this->video_vpts is used as the next frame vpts if next frame pts=0 + */ + this->video_vpts += this->img_duration - this->video_drift_step; + if (this->video_mode == VIDEO_PREDICTION_MODE) { lprintf("video vpts for %10"PRId64" : %10"PRId64" (duration:%d drift:%" PRId64 " step:%" PRId64 ")\n", pts, this->video_vpts, img->duration, this->video_drift, this->video_drift_step ); - if (this->video_drift * this->video_drift_step > 0) { - img->duration -= this->video_drift_step; - this->img_duration = img->duration; + /* reset drift compensation if work is done after this frame */ + if (this->video_drift_step) { this->video_drift -= this->video_drift_step; + if (this->video_drift_step < 0) { + if (this->video_drift >= 0) { + this->video_drift = 0; + this->video_drift_step = 0; + } + } else { + if (this->video_drift <= 0) { + this->video_drift = 0; + this->video_drift_step = 0; + } + } } } - /* We need to update this->video_vpts is both modes. - * this->video_vpts is used as the next frame vpts if next frame pts=0 - */ - this->video_vpts += this->img_duration; if (this->master) { this->master->set_option(this->master, METRONOM_LOCK, 0); -- cgit v1.2.3