[vlc-commits] [Git][videolan/vlc][master] 7 commits: access: dvdread: fix DVD-Audio chapter seekpoints for all titles
Jean-Baptiste Kempf (@jbk)
gitlab at videolan.org
Mon Jul 13 21:37:39 UTC 2026
Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC
Commits:
c40e693f by Saifelden Mohamed Ismail at 2026-07-13T23:25:30+02:00
access: dvdread: fix DVD-Audio chapter seekpoints for all titles
- - - - -
881c88b6 by Saifelden Mohamed Ismail at 2026-07-13T23:25:30+02:00
access: dvdread: reset cell timestamp on DVD-Audio chapter seek
- - - - -
ee7f23ac by Saifelden Mohamed Ismail at 2026-07-13T23:25:30+02:00
input: update position after chapter seek
- - - - -
908f3581 by Saifelden Mohamed Ismail at 2026-07-13T23:25:30+02:00
player: update timers when selecting a chapter
- - - - -
1d9d8be0 by Saifelden Mohamed Ismail at 2026-07-13T23:25:30+02:00
access: dvdread: fix DVD-Video chapter seekpoints for all titles
- - - - -
7dd6094c by Saifelden Mohamed Ismail at 2026-07-13T23:25:30+02:00
access: dvdread: guard against invalid ATS title in DVD-Audio set_area
- - - - -
ff157804 by Saifelden Mohamed Ismail at 2026-07-13T23:25:30+02:00
access: dvdread: bounds-check program number in DVD-VR set_area
- - - - -
5 changed files:
- modules/access/dvdread_audio.c
- modules/access/dvdread_video.c
- modules/access/dvdread_vr.c
- src/input/input.c
- src/player/player.c
Changes:
=====================================
modules/access/dvdread_audio.c
=====================================
@@ -74,6 +74,13 @@ static int DvdAudioReadSetArea( demux_t *p_demux, int i_title, int i_track,
/* Title position inside the selected ats, i_title is the overall title number */
p_sys->i_ttn = p_vmg->info_table_second_sector->tracks_info[i_title].title_property;
+ if( p_vts->atsi_title_table == NULL || p_sys->i_ttn == 0
+ || p_sys->i_ttn > p_vts->atsi_title_table->nr_titles )
+ {
+ msg_Err( p_demux, "invalid ats title %d", i_title );
+ return VLC_EGENERIC;
+ }
+
const atsi_title_record_t *atsi_title_table=
p_sys->p_title_table = &p_vts->atsi_title_table->atsi_title_row_tables[p_sys->i_ttn-1];
@@ -96,40 +103,7 @@ static int DvdAudioReadSetArea( demux_t *p_demux, int i_title, int i_track,
p_sys->i_title_start_block, p_sys->i_title_end_block,
p_sys->i_title_blocks );
- if( p_sys->i_chapters > 0 )
- {
- input_title_t *p_title = p_sys->titles[i_title];
- if( p_title->seekpoint && p_sys->i_title_blocks > 0 )
- {
- const vlc_tick_t title_length = FROM_SCALE_NZ(
- p_sys->p_title_table->length_pts );
- const uint32_t first_sector =
- p_sys->p_title_table->atsi_track_pointer_rows[0].start_sector;
-
- p_title->i_length = title_length;
-
- const int max_sp = __MIN( p_title->i_seekpoint,
- p_sys->i_chapters );
- /* offset relative to title start */
- for( int sj = 0; sj < max_sp; sj++ )
- {
- const uint32_t sj_sector =
- p_sys->p_title_table->atsi_track_pointer_rows[sj].start_sector;
- const uint32_t offset = sj_sector > first_sector
- ? sj_sector - first_sector : 0;
- if( title_length > 0 &&
- offset > UINT64_MAX / (uint64_t)title_length )
- {
- msg_Err( p_demux, "chapter offset multiply overflow" );
- return VLC_EGENERIC;
- }
- /* widen for the multiply, product fits uint64_t */
- p_title->seekpoint[sj]->i_time_offset =
- (vlc_tick_t)( (uint64_t)offset * title_length /
- p_sys->i_title_blocks );
- }
- }
- }
+ /* seekpoints are set in DemuxTitles not here */
/* The structure of DVD-A discs seems to be the following
* each ATS IFO-> is a GROUP -> Contains multiple titles, Span across one or more AOBs -> each title contains multiple tracks or "Trackpoints",
@@ -200,6 +174,8 @@ static int DvdAudioReadSetArea( demux_t *p_demux, int i_title, int i_track,
/* current block relative to start of title*/
p_sys->i_cur_block=p_sys->p_title_table->atsi_track_pointer_rows[i_track].start_sector;
+ DvdReadResetCellTs( p_sys );
+
if( p_sys->cur_chapter != i_track)
{
p_sys->updates |= INPUT_UPDATE_SEEKPOINT;
@@ -321,7 +297,7 @@ static void DvdAudioReadDemuxTitles( demux_t *p_demux, int *pi_angle )
for( int i = 0; i < i_titles; i++ )
{
- const int32_t i_chapters =
+ uint8_t i_chapters =
p_sys->p_vmg_file->info_table_second_sector->tracks_info[i].nr_chapters_in_title;
msg_Dbg( p_demux, "title %d has %d chapters", i, i_chapters );
@@ -329,8 +305,7 @@ static void DvdAudioReadDemuxTitles( demux_t *p_demux, int *pi_angle )
if( unlikely( !t ) )
return;
- const atsi_track_timestamp_t *p_track_ts = NULL;
- uint8_t i_track_ts = 0;
+ const atsi_title_record_t *p_title_rec = NULL;
const track_info_t * const p_track_info =
&p_sys->p_vmg_file->info_table_second_sector->tracks_info[i];
t->i_length = FROM_SCALE_NZ( p_track_info->len_audio_zone_pts );
@@ -341,10 +316,22 @@ static void DvdAudioReadDemuxTitles( demux_t *p_demux, int *pi_angle )
&& p_track_info->title_property > 0
&& p_track_info->title_property <= p_ats_ifo->atsi_title_table->nr_titles )
{
- const atsi_title_record_t * const p_title_rec =
+ p_title_rec =
&p_ats_ifo->atsi_title_table->atsi_title_row_tables[p_track_info->title_property - 1];
- p_track_ts = p_title_rec->atsi_track_timestamp_rows;
- i_track_ts = p_title_rec->nr_tracks;
+ i_chapters = p_title_rec->nr_pointer_records;
+ t->i_length = FROM_SCALE_NZ( p_title_rec->length_pts );
+ }
+
+ const atsi_track_pointer_t *rows =
+ p_title_rec ? p_title_rec->atsi_track_pointer_rows : NULL;
+ uint32_t first_sector = 0, blocks = 0;
+ if( rows != NULL && p_title_rec->nr_pointer_records > 0 )
+ {
+ first_sector = rows[0].start_sector;
+ const uint32_t end_sector =
+ rows[p_title_rec->nr_pointer_records - 1].end_sector;
+ if( end_sector >= first_sector )
+ blocks = end_sector - first_sector + 1;
}
for( int j = 0; j < __MAX( i_chapters, 1 ); j++ )
@@ -352,8 +339,11 @@ static void DvdAudioReadDemuxTitles( demux_t *p_demux, int *pi_angle )
s = vlc_seekpoint_New();
if( unlikely( !s ) )
goto fail;
- if( p_track_ts != NULL && j < i_track_ts )
- s->i_time_offset = FROM_SCALE_NZ( (uint64_t)p_track_ts[j].first_pts_of_track );
+ if( blocks > 0 && j < p_title_rec->nr_pointer_records )
+ {
+ const uint32_t offset = rows[j].start_sector - first_sector;
+ s->i_time_offset = offset * t->i_length / blocks;
+ }
TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
}
=====================================
modules/access/dvdread_video.c
=====================================
@@ -584,47 +584,7 @@ static int DvdReadSetArea( demux_t *p_demux, int i_title, int i_chapter,
p_sys->i_cur_cell = p_pgc->program_map[pgn - 1] - 1;
DvdReadFindCell( p_demux );
- /* walk ptts accumulating per-cell durations to set each seekpoint's i_time_offset */
- if( p_sys->i_chapters > 0 )
- {
- input_title_t *p_title = p_sys->titles[i_title];
- if( p_title->seekpoint )
- {
- const int max_sp = __MIN( p_title->i_seekpoint,
- p_sys->i_chapters );
- const int max_ptt = p_vts->vts_ptt_srpt->title[p_sys->i_ttn - 1].nr_of_ptts;
- const pgc_t *p_pgc_seek = p_sys->p_cur_pgc;
- int cell = p_sys->i_title_start_cell;
- vlc_tick_t t = 0;
- for( int sj = 0; sj < max_sp && sj < max_ptt; sj++ )
- {
- const uint16_t sj_pgc_id =
- p_vts->vts_ptt_srpt->title[p_sys->i_ttn - 1]
- .ptt[sj].pgcn;
- const uint16_t sj_pgn =
- p_vts->vts_ptt_srpt->title[p_sys->i_ttn - 1]
- .ptt[sj].pgn;
-
- /* cells are pgc-local, stop on pgc mismatch */
- if( sj_pgc_id != pgc_id || sj_pgn == 0 )
- break;
- if( sj_pgn > p_pgc_seek->nr_of_programs )
- break;
-
- const int chapter_cell =
- p_pgc_seek->program_map[sj_pgn - 1] - 1;
- while( cell < chapter_cell &&
- cell <= p_sys->i_title_end_cell )
- {
- t += dvdtime_to_time( &p_pgc_seek->cell_playback[cell]
- .playback_time );
- cell++;
- }
- p_title->seekpoint[sj]->i_time_offset = t;
- }
- p_title->i_length = DvdReadTitleLength( p_sys );
- }
- }
+ /* seekpoints are set in DemuxTitles not here */
DvdReadResetCellTs( p_sys );
p_sys->i_next_vobu = p_sys->i_cur_block =
@@ -860,6 +820,61 @@ static int DvdReadSetArea( demux_t *p_demux, int i_title, int i_chapter,
return VLC_SUCCESS;
}
+/* the player copies seekpoints only once at open */
+static void DvdReadFillTitleTimes( const ifo_handle_t *p_vts, int i_ttn,
+ input_title_t *t )
+{
+ if( p_vts->vts_ptt_srpt == NULL || p_vts->vts_pgcit == NULL
+ || i_ttn <= 0 || i_ttn > p_vts->vts_ptt_srpt->nr_of_srpts )
+ return;
+
+ const ttu_t *ptt = &p_vts->vts_ptt_srpt->title[i_ttn - 1];
+ if( ptt->ptt == NULL || ptt->nr_of_ptts == 0 )
+ return;
+
+ const uint16_t pgc_id = ptt->ptt[0].pgcn;
+ if( pgc_id == 0 || pgc_id > p_vts->vts_pgcit->nr_of_pgci_srp )
+ return;
+
+ const pgc_t *p_pgc = p_vts->vts_pgcit->pgci_srp[pgc_id - 1].pgc;
+ if( p_pgc == NULL || p_pgc->cell_playback == NULL
+ || p_pgc->program_map == NULL )
+ return;
+
+ const uint16_t pgn = ptt->ptt[0].pgn;
+ if( pgn == 0 || pgn > p_pgc->nr_of_programs )
+ return;
+
+ const int start_cell = p_pgc->program_map[pgn - 1] - 1;
+ const int end_cell = p_pgc->nr_of_cells - 1;
+ if( start_cell < 0 || start_cell > end_cell )
+ return;
+
+ vlc_tick_t length = 0;
+ for( int ci = start_cell; ci <= end_cell; ci++ )
+ length += dvdtime_to_time( &p_pgc->cell_playback[ci].playback_time );
+ t->i_length = length;
+
+ const int max_sp = __MIN( t->i_seekpoint, ptt->nr_of_ptts );
+ int cell = start_cell;
+ vlc_tick_t offset = 0;
+ for( int sj = 0; sj < max_sp; sj++ )
+ {
+ const uint16_t sj_pgn = ptt->ptt[sj].pgn;
+ /* stop if the chapter is in another pgc */
+ if( ptt->ptt[sj].pgcn != pgc_id || sj_pgn == 0
+ || sj_pgn > p_pgc->nr_of_programs )
+ break;
+ const int chapter_cell = p_pgc->program_map[sj_pgn - 1] - 1;
+ while( cell < chapter_cell && cell <= end_cell )
+ {
+ offset += dvdtime_to_time( &p_pgc->cell_playback[cell].playback_time );
+ cell++;
+ }
+ t->seekpoint[sj]->i_time_offset = offset;
+ }
+}
+
/*****************************************************************************
* DemuxTitles: get the titles/chapters or group/tracks structure
*****************************************************************************/
@@ -894,6 +909,15 @@ static void DvdReadDemuxTitles( demux_t *p_demux, int *pi_angle )
}
TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
}
+
+ const title_info_t *p_tinfo = &p_sys->p_vmg_file->tt_srpt->title[i];
+ ifo_handle_t *p_vts = ifoOpen( p_sys->p_dvdread, p_tinfo->title_set_nr );
+ if( p_vts != NULL )
+ {
+ DvdReadFillTitleTimes( p_vts, p_tinfo->vts_ttn, t );
+ ifoClose( p_vts );
+ }
+
TAB_APPEND( p_sys->i_titles, p_sys->titles, t );
}
}
=====================================
modules/access/dvdread_vr.c
=====================================
@@ -353,6 +353,8 @@ static int DvdVRReadSetArea( demux_t *p_demux, int i_title, int i_chapter,
ci < p_sys->i_cur_cell; ci++ )
{
uint16_t s = p_sys->ud_pgcit->m_c_gi[ci].m_vobi_srpn;
+ if( s == 0 || s > p_sys->pgc_gi->nr_of_programs )
+ return VLC_EGENERIC;
p_sys->i_title_offset += p_sys->pgc_gi->pgi[s - 1].map.nr_of_vobu_info;
}
/* add within-program vobu offset for position accuracy */
=====================================
src/input/input.c
=====================================
@@ -2397,9 +2397,23 @@ static bool Control( input_thread_t *p_input,
es_out_Control(&priv->p_es_out->out, ES_OUT_RESET_PCR);
ResetFramePrevious( p_input );
priv->next_frame_need_data = false;
- demux_Control( priv->master->p_demux,
- DEMUX_SET_SEEKPOINT, i_seekpoint );
+ if( demux_Control( priv->master->p_demux,
+ DEMUX_SET_SEEKPOINT, i_seekpoint ) )
+ break;
+
input_SendEventSeekpoint( p_input, i_title, i_seekpoint );
+
+ input_title_t *p_title = priv->master->title[i_title];
+ vlc_tick_t i_length = p_title->i_length;
+ vlc_tick_t i_time = p_title->seekpoint[i_seekpoint]->i_time_offset;
+ if( i_time >= 0 && i_length > 0 )
+ {
+ input_SendEventTimes( p_input, (double)i_time / i_length,
+ i_time, priv->master->i_normal_time,
+ i_length, false );
+ b_force_update = true;
+ }
+
if( priv->i_slave > 0 )
SlaveSeek( p_input );
break;
=====================================
src/player/player.c
=====================================
@@ -942,12 +942,37 @@ vlc_player_GetSelectedChapterIdx(vlc_player_t *player)
return input->chapter_selected;
}
+static vlc_tick_t
+vlc_player_ChapterTime(struct vlc_player_input *input, size_t index)
+{
+ if (!input->titles)
+ return -1;
+ const struct vlc_player_title *title =
+ &input->titles->array[input->title_selected];
+ if (index >= title->chapter_count)
+ return -1;
+ return title->chapters[index].time;
+}
+
+/* move the timer right away so the seek bar does not wait for new data */
+static void
+vlc_player_UpdateChapterSeekState(struct vlc_player_input *input, size_t index)
+{
+ vlc_tick_t time = vlc_player_ChapterTime(input, index);
+ if (time < 0)
+ return;
+ if (time < VLC_TICK_0)
+ time = VLC_TICK_0;
+ vlc_player_UpdateTimerSeekState(input->player, time, -1);
+}
+
void
vlc_player_SelectChapterIdx(vlc_player_t *player, size_t index)
{
struct vlc_player_input *input = vlc_player_get_input_locked(player);
if (!input)
return;
+ vlc_player_UpdateChapterSeekState(input, index);
int ret = input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_SEEKPOINT,
&(vlc_value_t){ .i_int = index });
if (ret == VLC_SUCCESS)
@@ -960,6 +985,7 @@ vlc_player_SelectNextChapter(vlc_player_t *player)
struct vlc_player_input *input = vlc_player_get_input_locked(player);
if (!input)
return;
+ vlc_player_UpdateChapterSeekState(input, input->chapter_selected + 1);
int ret = input_ControlPush(input->thread, INPUT_CONTROL_SET_SEEKPOINT_NEXT,
NULL);
if (ret == VLC_SUCCESS)
@@ -972,6 +998,14 @@ vlc_player_SelectPrevChapter(vlc_player_t *player)
struct vlc_player_input *input = vlc_player_get_input_locked(player);
if (!input)
return;
+ size_t index = input->chapter_selected;
+ vlc_tick_t time = vlc_player_ChapterTime(input, index);
+ /* same rule as the input thread */
+ if (time >= 0 && vlc_player_input_GetTime(input, true, vlc_tick_now())
+ >= time + VLC_TICK_FROM_SEC(3))
+ vlc_player_UpdateChapterSeekState(input, index);
+ else if (index > 0)
+ vlc_player_UpdateChapterSeekState(input, index - 1);
int ret = input_ControlPush(input->thread, INPUT_CONTROL_SET_SEEKPOINT_PREV,
NULL);
if (ret == VLC_SUCCESS)
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/83b90a52c017f82b04193ac40ef0eb254a7d143e...ff157804f3d23eea3fe52b84e92ed831fe6c5393
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/83b90a52c017f82b04193ac40ef0eb254a7d143e...ff157804f3d23eea3fe52b84e92ed831fe6c5393
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help
More information about the vlc-commits
mailing list