player: detect audio PTS jumps, make video PTS heuristic less aggressive

This is another attempt at making files with sparse video frames work
better.

The problem is that you generally can't know whether a jump in video
timestamps is just a (very) long video frame, or a timestamp reset. Due
to the existence of files with sparse video frames (new frame only every
few seconds or longer), every heuristic will be arbitrary (in general,
at least).

But we can use the fact that if video is continuous, audio should also
be continuous. Audio discontinuities can be easily detected, and if that
happens, reset some of the playback state.

The way the playback state is reset is rather radical (resets decoders
as well), but it's just better not to cause too much obscure stuff to
happen here. If the A/V sync code were to be rewritten, it should
probably strictly use PTS values (not this strange time_frame/delay
stuff), which would make it much easier to detect such situations and
to react to them.
This commit is contained in:
wm4 2016-01-09 20:27:03 +01:00
parent 2a80680d95
commit bd5a02d080
4 changed files with 23 additions and 14 deletions

View File

@ -177,11 +177,21 @@ static int decode_new_frame(struct dec_audio *da)
da->pts += da->pts_offset / (double)da->waiting->rate;
da->pts_offset = 0;
}
double newpts = da->waiting->pts;
// Keep the interpolated timestamp if it doesn't deviate more
// than 1 ms from the real one. (MKV rounded timestamps.)
if (da->pts == MP_NOPTS_VALUE || da->pts_offset != 0 ||
fabs(da->pts - da->waiting->pts) > 0.001)
fabs(da->pts - newpts) > 0.001)
{
// Attempt to detect jumps in PTS. Even for the lowest
// sample rates and with worst container rounded timestamp,
// this should be a margin more than enough.
if (da->pts != MP_NOPTS_VALUE && fabs(newpts - da->pts) > 0.1)
{
MP_WARN(da, "Invalid audio PTS: %f -> %f\n",
da->pts, newpts);
da->pts_reset = true;
}
da->pts = da->waiting->pts;
da->pts_offset = 0;
}
@ -274,6 +284,7 @@ void audio_reset_decoding(struct dec_audio *d_audio)
af_seek_reset(d_audio->afilter);
d_audio->pts = MP_NOPTS_VALUE;
d_audio->pts_offset = 0;
d_audio->pts_reset = false;
if (d_audio->waiting) {
talloc_free(d_audio->waiting);
d_audio->waiting = NULL;

View File

@ -45,6 +45,8 @@ struct dec_audio {
double pts;
// number of samples output by decoder after last known pts
int pts_offset;
// set every time a jump in timestamps is encountered
bool pts_reset;
// For free use by the ad_driver
void *priv;
};

View File

@ -487,11 +487,12 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip)
double ptsdiff = written_pts - sync_pts;
// Missing timestamp, or PTS reset, or just broken.
if (written_pts == MP_NOPTS_VALUE || fabs(ptsdiff) > 3600) {
if (written_pts == MP_NOPTS_VALUE) {
MP_WARN(mpctx, "Failed audio resync.\n");
mpctx->audio_status = STATUS_FILLING;
return true;
}
ptsdiff = MPCLAMP(ptsdiff, -3600, 3600);
int align = af_format_sample_alignment(out_format.format);
*skip = (int)(-ptsdiff * play_samplerate) / align * align;
@ -544,6 +545,13 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
return; // try again next iteration
}
if (mpctx->d_video && d_audio->pts_reset) {
MP_VERBOSE(mpctx, "Reset playback due to audio timestamp reset.\n");
reset_playback_state(mpctx);
mpctx->sleeptime = 0;
return;
}
struct mp_audio out_format = {0};
ao_get_format(mpctx->ao, &out_format);
double play_samplerate = out_format.rate / mpctx->audio_speed;

View File

@ -563,22 +563,10 @@ static void handle_new_frame(struct MPContext *mpctx)
if (mpctx->video_pts != MP_NOPTS_VALUE) {
frame_time = pts - mpctx->video_pts;
double tolerance = 15;
if (mpctx->demuxer->ts_resets_possible) {
// Fortunately no real framerate is likely to go below this. It
// still could be that the file is VFR, but the demuxer reports a
// higher rate, so account for the case of e.g. 60hz demuxer fps
// but 23hz actual fps.
double fps = 23.976;
if (mpctx->d_video->fps > 0 && mpctx->d_video->fps < fps)
fps = mpctx->d_video->fps;
tolerance = 3 * 1.0 / fps;
}
if (frame_time <= 0 || frame_time >= tolerance) {
// Assume a discontinuity.
MP_WARN(mpctx, "Invalid video timestamp: %f -> %f\n",
mpctx->video_pts, pts);
if (mpctx->d_audio && fabs(frame_time) > 1.0)
mpctx->audio_status = STATUS_SYNCING;
frame_time = 0;
}
}