When seeking on multi-angle titles, libdvdnav does not lock on to the correct sectors initially as it seeks to find the right NAV packet.
This manifests itself as two bugs: (1) When seeking on the first angle in a multi-angle segment, frames from another angle will appear (for example in intro or credits scenes). This issue is present in VLC also. (2) When seeking during a segment on angle n+1, the demuxer cannot deduce the right position from dvdnav and does not allow seeking within the segment (due to it maintaining a strict state). Correct the issue by switching to angle 1 before doing the seek operation, and skipping 3 VOBUs (NAV packet led segments) ahead where dvdnav will have positioned itself correctly. Reported-by: Kacper Michajlow <kaspe...@gmail.com> Signed-off-by: Marth64 <mart...@proxyid.net> --- libavformat/dvdvideodec.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/libavformat/dvdvideodec.c b/libavformat/dvdvideodec.c index 5b9abebbf2..9596468ce2 100644 --- a/libavformat/dvdvideodec.c +++ b/libavformat/dvdvideodec.c @@ -111,6 +111,7 @@ typedef struct DVDVideoPlaybackState { int in_vts; /* if our navigator is in the VTS */ int is_seeking; /* relax navigation path while seeking */ int64_t nav_pts; /* PTS according to IFO, not frame-accurate */ + int nb_vobu_skip; /* number of VOBUs we should skip */ uint64_t pgc_duration_est; /* estimated duration as reported by IFO */ uint64_t pgc_elapsed; /* the elapsed time of the PGC, cell-relative */ int pgc_nb_pg_est; /* number of PGs as reported by IFOs */ @@ -165,6 +166,7 @@ typedef struct DVDVideoDemuxContext { /* playback control */ int64_t first_pts; /* the PTS of the first video keyframe */ + int nb_angles; /* number of angles in the current title */ int play_started; /* signal that playback has started */ DVDVideoPlaybackState play_state; /* the active playback state */ int64_t *prev_pts; /* track the previous PTS emitted per stream */ @@ -298,6 +300,8 @@ static int dvdvideo_ifo_open(AVFormatContext *s) return AVERROR_INVALIDDATA; } + c->nb_angles = title_info.nr_of_angles; + return 0; } @@ -759,6 +763,13 @@ static int dvdvideo_play_next_ps_block(AVFormatContext *s, DVDVideoPlaybackState return AVERROR_INVALIDDATA; } + if (state->nb_vobu_skip > 0) { + av_log(s, AV_LOG_VERBOSE, "Skipping VOBU at SCR %d\n", + e_dsi->dsi_gi.nv_pck_scr); + state->nb_vobu_skip -= 1; + continue; + } + state->vobu_duration = e_pci->pci_gi.vobu_e_ptm - e_pci->pci_gi.vobu_s_ptm; state->pgc_elapsed += state->vobu_duration; state->nav_pts = dvdnav_get_current_time(state->dvdnav); @@ -1736,6 +1747,7 @@ static int dvdvideo_read_seek(AVFormatContext *s, int stream_index, int64_t time int64_t new_nav_pts; pci_t* new_nav_pci; dsi_t* new_nav_dsi; + int seek_failed = 0; if (c->opt_menu || c->opt_chapter_start > 1) { av_log(s, AV_LOG_ERROR, "Seeking is not compatible with menus or chapter extraction\n"); @@ -1755,9 +1767,30 @@ static int dvdvideo_read_seek(AVFormatContext *s, int stream_index, int64_t time c->seek_warned = 1; } + /* dvdnav loses NAV packets when seeking on multi-angle discs, so enforce angle 1 then revert */ + if (c->nb_angles > 1) { + if (dvdnav_angle_change(c->play_state.dvdnav, 1) != DVDNAV_STATUS_OK) { + av_log(s, AV_LOG_ERROR, "Unable to open angle 1 for seeking\n"); + + return AVERROR_EXTERNAL; + } + } + /* XXX(PATCHWELCOME): use dvdnav_jump_to_sector_by_time(c->play_state.dvdnav, timestamp, 0) * when it is available in a released version of libdvdnav; it is more accurate */ if (dvdnav_time_search(c->play_state.dvdnav, timestamp) != DVDNAV_STATUS_OK) { + seek_failed = 1; + } + + if (c->nb_angles > 1) { + if (dvdnav_angle_change(c->play_state.dvdnav, c->opt_angle) != DVDNAV_STATUS_OK) { + av_log(s, AV_LOG_ERROR, "Unable to revert to angle %d after seeking\n", c->opt_angle); + + return AVERROR_EXTERNAL; + } + } + + if (seek_failed) { av_log(s, AV_LOG_ERROR, "libdvdnav: seeking to %" PRId64 " failed\n", timestamp); return AVERROR_EXTERNAL; @@ -1781,6 +1814,9 @@ static int dvdvideo_read_seek(AVFormatContext *s, int stream_index, int64_t time c->play_state.ptm_discont = 0; c->play_state.vobu_e_ptm = new_nav_pci->pci_gi.vobu_s_ptm; + /* if there are multiple angles, skip the next 3 VOBUs as dvdnav will be at the wrong angle */ + c->play_state.nb_vobu_skip = c->nb_angles > 1 ? 3 : 0; + c->first_pts = 0; c->play_started = 0; c->pts_offset = timestamp; -- 2.34.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".