[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