You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-08-10 06:10:52 +02:00
avformat/dvdvideodec: fix seeking on multi-angle discs
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 <kasper93@gmail.com> Signed-off-by: Marth64 <marth64@proxyid.net>
This commit is contained in:
@@ -110,6 +110,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 */
|
||||
@@ -164,6 +165,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 */
|
||||
@@ -297,6 +299,8 @@ static int dvdvideo_ifo_open(AVFormatContext *s)
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
|
||||
c->nb_angles = title_info.nr_of_angles;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -758,6 +762,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);
|
||||
@@ -1734,6 +1745,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");
|
||||
@@ -1753,9 +1765,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;
|
||||
@@ -1779,6 +1812,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;
|
||||
|
Reference in New Issue
Block a user