From 862d7d8a1a852fc6bb6dd13ca5db90f83b676579 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 29 Jul 2014 17:55:28 +0200 Subject: [PATCH] player: fix desync when seeking and switching external tracks If you for example use --audio-file, disable the external track, seek, and enable the external track again, the playback position of the external file was off, and you would get major A/V desync. This was actually supposed to work, but broke at some time ago (probably commit 2b87415f). It didn't work, because it attempted to seek the stream if it was already selected, which was always true due to reselect_demux_streams() being called before that. Fix by putting the initial selection and the seek together. --- player/audio.c | 2 +- player/core.h | 2 -- player/loadfile.c | 46 ++++++++++++++++++++++++---------------------- player/misc.c | 5 +++-- player/sub.c | 5 +---- player/video.c | 2 +- 6 files changed, 30 insertions(+), 32 deletions(-) diff --git a/player/audio.c b/player/audio.c index 3a7d26daa7..ab42573fad 100644 --- a/player/audio.c +++ b/player/audio.c @@ -96,7 +96,7 @@ void reinit_audio_chain(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; struct track *track = mpctx->current_track[0][STREAM_AUDIO]; - struct sh_stream *sh = init_demux_stream(mpctx, track); + struct sh_stream *sh = track ? track->stream : NULL; if (!sh) { uninit_player(mpctx, INITIALIZED_AO); goto no_audio; diff --git a/player/core.h b/player/core.h index 49ca1e042c..0c4f50e1d2 100644 --- a/player/core.h +++ b/player/core.h @@ -414,8 +414,6 @@ struct track *mp_track_by_tid(struct MPContext *mpctx, enum stream_type type, int tid); bool timeline_set_part(struct MPContext *mpctx, int i, bool force); double timeline_set_from_time(struct MPContext *mpctx, double pts, bool *need_reset); -struct sh_stream *init_demux_stream(struct MPContext *mpctx, struct track *track); -void reselect_demux_streams(struct MPContext *mpctx); void add_demuxer_tracks(struct MPContext *mpctx, struct demuxer *demuxer); bool mp_remove_track(struct MPContext *mpctx, struct track *track); struct playlist_entry *mp_next_file(struct MPContext *mpctx, int direction, diff --git a/player/loadfile.c b/player/loadfile.c index 657e50f6da..6c89057432 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -60,6 +60,8 @@ #include "command.h" #include "libmpv/client.h" +static void reselect_demux_streams(struct MPContext *mpctx); + static void uninit_sub(struct MPContext *mpctx, int order) { if (mpctx->d_sub[order]) @@ -264,41 +266,41 @@ void update_demuxer_properties(struct MPContext *mpctx) demuxer->events = 0; } +static bool need_init_seek(struct demuxer *demux) +{ + for (int n = 0; n < demux->num_streams; n++) { + struct sh_stream *stream = demux->streams[n]; + // Subtitle streams are not properly interleaved -> force init. seek. + if (stream->type != STREAM_SUB && demux_stream_is_selected(stream)) + return false; + } + return true; +} + // Enable needed streams, disable others. // Note that switching all tracks at once (instead when initializing something) // can be important, because reading from a demuxer stream (e.g. during init) // will implicitly discard interleaved packets from unselected streams. -void reselect_demux_streams(struct MPContext *mpctx) +// Also initializes position for external streams. +static void reselect_demux_streams(struct MPContext *mpctx) { // Note: we assume that all demuxer streams are covered by the track list. for (int t = 0; t < mpctx->num_tracks; t++) { struct track *track = mpctx->tracks[t]; - if (track->demuxer && track->stream) + if (track->demuxer && track->stream) { + bool need_init = track->selected && + mpctx->demuxer != track->demuxer && + need_init_seek(track->demuxer); demuxer_select_track(track->demuxer, track->stream, track->selected); - } -} - -// External demuxers might need a seek to the current playback position. -static void external_track_seek(struct MPContext *mpctx, struct track *track) -{ - if (track && track->demuxer && track->selected && track->is_external) { - for (int t = 0; t < mpctx->num_tracks; t++) { - struct track *other = mpctx->tracks[t]; - if (other->demuxer == track->demuxer && - demux_stream_is_selected(other->stream)) - return; + if (need_init) { + double pts = get_main_demux_pts(mpctx); + if (pts != MP_NOPTS_VALUE) + demux_seek(track->demuxer, pts, SEEK_ABSOLUTE); + } } - double pts = get_main_demux_pts(mpctx); - demux_seek(track->demuxer, pts, SEEK_ABSOLUTE); } } -struct sh_stream *init_demux_stream(struct MPContext *mpctx, struct track *track) -{ - external_track_seek(mpctx, track); - return track ? track->stream : NULL; -} - static struct sh_stream *select_fallback_stream(struct demuxer *d, enum stream_type type, int index) diff --git a/player/misc.c b/player/misc.c index e50ea6b143..3ae4a8ab9a 100644 --- a/player/misc.c +++ b/player/misc.c @@ -100,8 +100,9 @@ double get_main_demux_pts(struct MPContext *mpctx) double main_new_pos = MP_NOPTS_VALUE; if (mpctx->demuxer) { for (int n = 0; n < mpctx->demuxer->num_streams; n++) { - if (main_new_pos == MP_NOPTS_VALUE) - main_new_pos = demux_get_next_pts(mpctx->demuxer->streams[n]); + struct sh_stream *stream = mpctx->demuxer->streams[n]; + if (main_new_pos == MP_NOPTS_VALUE && stream->type != STREAM_SUB) + main_new_pos = demux_get_next_pts(stream); } } return main_new_pos; diff --git a/player/sub.c b/player/sub.c index 2018e6ed7b..2ee928794c 100644 --- a/player/sub.c +++ b/player/sub.c @@ -194,10 +194,7 @@ void reinit_subs(struct MPContext *mpctx, int order) assert(!(mpctx->initialized_flags & init_flag)); - struct sh_stream *sh = init_demux_stream(mpctx, track); - - // No track selected, or lazily added DVD track (will actually be created - // on first sub packet) + struct sh_stream *sh = track ? track->stream : NULL; if (!sh) return; diff --git a/player/video.c b/player/video.c index 40e8e956d3..09fc61fcf0 100644 --- a/player/video.c +++ b/player/video.c @@ -174,7 +174,7 @@ int reinit_video_chain(struct MPContext *mpctx) assert(!(mpctx->initialized_flags & INITIALIZED_VCODEC)); assert(!mpctx->d_video); struct track *track = mpctx->current_track[0][STREAM_VIDEO]; - struct sh_stream *sh = init_demux_stream(mpctx, track); + struct sh_stream *sh = track ? track->stream : NULL; if (!sh) goto no_video;