[vlc-commits] [Git][videolan/vlc][master] 5 commits: access: dvdread: clamp rebased vr pes timestamps in demux

Felix Paul Kühne (@fkuehne) gitlab at videolan.org
Sun May 10 07:00:28 UTC 2026



Felix Paul Kühne pushed to branch master at VideoLAN / VLC


Commits:
7d3e9df4 by Saifelden Mohamed Ismail at 2026-05-10T08:34:38+02:00
access: dvdread: clamp rebased vr pes timestamps in demux

- - - - -
814969ff by Saifelden Mohamed Ismail at 2026-05-10T08:34:38+02:00
access: dvdread: pass dvd-vr control through timeline base

- - - - -
4fef3177 by Saifelden Mohamed Ismail at 2026-05-10T08:34:38+02:00
access: dvdread: advance dvd-vr programs after sector completion

- - - - -
e493db73 by Saifelden Mohamed Ismail at 2026-05-10T08:34:38+02:00
access: dvdread: validate and map dvd-vr setarea/seek operations

- - - - -
55437c1f by Saifelden Mohamed Ismail at 2026-05-10T08:34:38+02:00
access: dvdread: normalize dvd-vr seekpoint offsets to title timeline

- - - - -


1 changed file:

- modules/access/dvdread.c


Changes:

=====================================
modules/access/dvdread.c
=====================================
@@ -559,6 +559,7 @@ static vlc_tick_t DvdReadGetTitleDuration( const demux_sys_t *p_sys )
 
 #ifdef DVDREAD_HAS_DVDVIDEORECORDING
 #define DVDVR_FALLBACK_SECTORS_PER_VOBU 512
+#define DVDVR_EARLY_SNAP_VOBU_WINDOW 4
 
 /* estimated sector span of a vr program's vobu map
  * dvd-vr has no flat vobu address map like VOBU_ADMAP in dvd-video
@@ -604,6 +605,25 @@ static vlc_tick_t DvdVRProgramDuration( const pgi_t *pgi )
     return FROM_SCALE_NZ( delta_ptm );
 }
 
+/* vobu_adr walked in order by callers, must be monotone */
+static bool DvdVRTimeInfosSane( const vobu_map_t *map, uint32_t total_sectors )
+{
+    if( map->nr_of_time_info == 0 || total_sectors == 0 )
+        return false;
+
+    uint32_t prev = 0;
+    for( int i = 0; i < map->nr_of_time_info; i++ )
+    {
+        const uint32_t adr = map->time_infos[i].vobu_adr;
+        if( adr >= total_sectors )
+            return false;
+        if( adr < prev )
+            return false;
+        prev = adr;
+    }
+    return true;
+}
+
 static uint32_t DvdVRReadTimeToVobuOffset( const demux_sys_t *p_sys, vlc_tick_t t )
 {
     if( p_sys->type != DVD_VR || !p_sys->pgc_gi || !p_sys->ud_pgcit ||
@@ -763,10 +783,8 @@ static bool DvdReadGetTimelineBase( const demux_sys_t *p_sys, vlc_tick_t *base )
 
 static vlc_tick_t DvdReadGetControlLength( const demux_sys_t *p_sys )
 {
-#ifdef DVDREAD_HAS_DVDVIDEORECORDING
     if( p_sys->type == DVD_VR && p_sys->cur_title >= 0 && p_sys->cur_title < p_sys->i_titles )
         return p_sys->titles[p_sys->cur_title]->i_length;
-#endif
 #ifdef DVDREAD_HAS_DVDAUDIO
     if( p_sys->type == DVD_A )
         return FROM_SCALE_NZ( p_sys->p_title_table->length_pts );
@@ -796,6 +814,20 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
         {
             pf = va_arg( args, double * );
 
+            /* vr i_title_offset is vobu count, block ratio wrong */
+            if( p_sys->type == DVD_VR && p_sys->cur_title >= 0 &&
+                p_sys->cur_title < p_sys->i_titles )
+            {
+                const vlc_tick_t length = p_sys->titles[p_sys->cur_title]->i_length;
+                vlc_tick_t time = 0;
+                if( length > 0 && DvdReadGetTimelineBase( p_sys, &time ) )
+                {
+                    if( time < 0 ) time = 0;
+                    if( time > length ) time = length;
+                    *pf = (double)time / length;
+                    return VLC_SUCCESS;
+                }
+            }
             if( p_sys->i_title_blocks )
                 *pf = (double)p_sys->i_title_offset / p_sys->i_title_blocks;
             else
@@ -826,6 +858,18 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
                     return VLC_EGENERIC;
                 }
                 const vlc_tick_t length = DvdReadGetControlLength( p_sys );
+
+                if( p_sys->type == DVD_VR )
+                {
+                    vlc_tick_t time = 0;
+                    if( length > 0 && DvdReadGetTimelineBase( p_sys, &time ) )
+                    {
+                        if( time < 0 ) time = 0;
+                        if( time > length ) time = length;
+                        *va_arg( args, vlc_tick_t * ) = time;
+                        return VLC_SUCCESS;
+                    }
+                }
                 *va_arg( args, vlc_tick_t * ) = p_sys->i_title_offset * length
                     / p_sys->i_title_blocks;
                 return VLC_SUCCESS;
@@ -979,14 +1023,13 @@ static int Demux( demux_t *p_demux )
     uint8_t p_buffer[DVD_VIDEO_LB_LEN * DVD_BLOCK_READ_ONCE];
     int i_blocks_once, i_read;
 
+    /* type-gated branches rely on runtime dvd_type_t value
+     * type can only be DVD_VR / DVD_A when libdvdread provides them
+     * fields used here are unconditional in demux_sys_t */
     bool at_end_of_title =
-#if defined(DVDREAD_HAS_DVDVIDEORECORDING)
         (type == DVD_VR && p_sys->i_cur_cell >= p_sys->i_title_end_cell
             && !p_sys->i_pack_len) ||
-#endif
-#if defined(DVDREAD_HAS_DVDAUDIO)
         (type == DVD_A && p_sys->i_cur_block >= p_sys->i_title_end_block) ||
-#endif
         (type == DVD_V && p_sys->i_cur_cell >= p_sys->p_cur_pgc->nr_of_cells);
 
     /*
@@ -1003,6 +1046,7 @@ static int Demux( demux_t *p_demux )
     if( !p_sys->i_pack_len && type == DVD_VR
         && p_sys->i_cur_cell < p_sys->i_title_end_cell )
     {
+        bool advanced = false;
         uint16_t srpn = p_sys->ud_pgcit->m_c_gi[p_sys->i_cur_cell].m_vobi_srpn;
         if( srpn == 0 || srpn > p_sys->pgc_gi->nr_of_programs )
         {
@@ -1011,44 +1055,53 @@ static int Demux( demux_t *p_demux )
         }
 
         vobu_map_t *map = &p_sys->pgc_gi->pgi[srpn - 1].map;
+        const uint32_t total_sectors = DvdVRGetProgramSectorSpan( p_sys, map );
+        const uint32_t block_end = map->vob_offset + total_sectors;
 
-        p_sys->i_cur_block = map->vob_offset;
-
-        if( map->nr_of_time_info >= 2 )
-        {
-            uint32_t last_adr = map->time_infos[map->nr_of_time_info - 1].vobu_adr;
-            uint32_t last_entn = map->time_infos[map->nr_of_time_info - 1].vobu_entn;
-            if( last_entn > 1 )
-                p_sys->i_pack_len = (uint32_t)( (uint64_t)last_adr
-                    * map->nr_of_vobu_info / ( last_entn - 1 ) );
-            else
-                p_sys->i_pack_len = map->nr_of_vobu_info * (DVD_VIDEO_LB_LEN / 4);
-        }
-        else
+        /* only advance to the next vr program once the current one is consumed
+         * vr has no dsi, block_end overshoot triggers advance */
+        if( total_sectors > 0 && p_sys->i_cur_block >= 0 &&
+            (uint32_t)p_sys->i_cur_block >= block_end )
         {
-            uint32_t sects_per_vobu = 0;
-            for( int p = 0; p < p_sys->pgc_gi->nr_of_programs; p++ )
+            /* recount from title start, valid after seeks */
+            uint32_t cell_offset = 0;
+            for( int c = p_sys->i_title_start_cell; c < p_sys->i_cur_cell; c++ )
             {
-                vobu_map_t *m = &p_sys->pgc_gi->pgi[p].map;
-                if( m->nr_of_time_info >= 2
-                    && m->time_infos[m->nr_of_time_info - 1].vobu_entn > 1 )
+                uint16_t cell_srpn = p_sys->ud_pgcit->m_c_gi[c].m_vobi_srpn;
+                if( cell_srpn == 0 || cell_srpn > p_sys->pgc_gi->nr_of_programs )
                 {
-                    sects_per_vobu = m->time_infos[m->nr_of_time_info - 1].vobu_adr
-                                   / ( m->time_infos[m->nr_of_time_info - 1].vobu_entn - 1 );
-                    break;
+                    msg_Err( p_demux, "invalid m_vobi_srpn %" PRIu16, cell_srpn );
+                    return VLC_EGENERIC;
                 }
+                cell_offset += p_sys->pgc_gi->pgi[cell_srpn - 1].map.nr_of_vobu_info;
             }
-            if( !sects_per_vobu )
-                sects_per_vobu = DVD_VIDEO_LB_LEN / 4; /* safe overestimate */
-            p_sys->i_pack_len = map->nr_of_vobu_info * sects_per_vobu;
-        }
+            p_sys->i_title_offset = cell_offset + map->nr_of_vobu_info;
+            p_sys->i_cur_cell++;
+            DvdReadResetCellTs( p_sys );
+            advanced = true;
 
-        p_sys->i_title_offset += map->nr_of_vobu_info;
+            if( p_sys->i_cur_cell < p_sys->i_title_end_cell )
+            {
+                srpn = p_sys->ud_pgcit->m_c_gi[p_sys->i_cur_cell].m_vobi_srpn;
+                if( unlikely( srpn == 0 || srpn > p_sys->pgc_gi->nr_of_programs ) )
+                {
+                    msg_Err( p_demux, "invalid m_vobi_srpn %" PRIu16, srpn );
+                    return VLC_EGENERIC;
+                }
+                map = &p_sys->pgc_gi->pgi[srpn - 1].map;
+            }
+        }
 
-        p_sys->i_cur_cell++;
-        DvdVRFindCell( p_demux );
+        if( advanced && p_sys->i_cur_cell < p_sys->i_title_end_cell )
+        {
+            p_sys->i_cur_block = map->vob_offset;
+            p_sys->i_pack_len = DvdVRGetProgramSectorSpan( p_sys, map );
+            DvdVRFindCell( p_demux );
+        }
+        at_end_of_title |=
+            (p_sys->i_cur_cell >= p_sys->i_title_end_cell
+                && !p_sys->i_pack_len);
     }
-    else
 #endif
 #if defined(DVDREAD_HAS_DVDAUDIO)
     if( !p_sys->i_pack_len && type == DVD_A )
@@ -1205,7 +1258,13 @@ static int DemuxBlock( demux_t *p_demux, const uint8_t *p, int len )
 
                 vlc_tick_t shift;
                 if( DvdReadCellTsShift( p_sys, &shift ) )
-                    es_out_SetPCR( p_demux->out, VLC_TICK_0 + i_scr + shift );
+                {
+                    vlc_tick_t pcr = VLC_TICK_0 + i_scr + shift;
+                    /* negative shift when cell time precedes first scr */
+                    if( pcr < VLC_TICK_0 )
+                        pcr = VLC_TICK_0;
+                    es_out_SetPCR( p_demux->out, pcr );
+                }
                 else
                 {
                     if( p_sys->cell_ts.dvd != VLC_TICK_INVALID )
@@ -1231,21 +1290,40 @@ static int DemuxBlock( demux_t *p_demux, const uint8_t *p, int len )
                 if( tk->es &&
                     !ps_pkt_parse_pes( VLC_OBJECT(p_demux), p_pkt, tk->i_skip ) )
                 {
-                    if( p_sys->cell_ts.dvd != VLC_TICK_INVALID &&
-                        p_sys->cell_ts.ps == VLC_TICK_INVALID )
+                    /* for vr anchor to earliest pts/dts seen,
+                     * reduces underflow risk in rebased timestamps */
+                    if( p_sys->type == DVD_VR &&
+                        p_sys->cell_ts.dvd != VLC_TICK_INVALID &&
+                        p_sys->cell_ts.ps != VLC_TICK_INVALID )
                     {
-                        if( p_pkt->i_dts != VLC_TICK_INVALID )
-                            p_sys->cell_ts.ps = p_pkt->i_dts;
-                        else if( p_pkt->i_pts != VLC_TICK_INVALID )
-                            p_sys->cell_ts.ps = p_pkt->i_pts;
+                        vlc_tick_t anchor = p_sys->cell_ts.ps;
+                        if( p_pkt->i_dts != VLC_TICK_INVALID && p_pkt->i_dts < anchor )
+                            anchor = p_pkt->i_dts;
+                        if( p_pkt->i_pts != VLC_TICK_INVALID && p_pkt->i_pts < anchor )
+                            anchor = p_pkt->i_pts;
+                        if( anchor < p_sys->cell_ts.ps )
+                            p_sys->cell_ts.ps = anchor;
                     }
                     vlc_tick_t shift;
                     if( DvdReadCellTsShift( p_sys, &shift ) )
                     {
+                        /* shared lift keeps dts and pts both >= VLC_TICK_0
+                         * while preserving the original dts/pts delta */
+                        vlc_tick_t lift = 0;
+                        if( p_pkt->i_dts != VLC_TICK_INVALID )
+                        {
+                            vlc_tick_t need = VLC_TICK_0 - ( p_pkt->i_dts + shift );
+                            if( need > lift ) lift = need;
+                        }
+                        if( p_pkt->i_pts != VLC_TICK_INVALID )
+                        {
+                            vlc_tick_t need = VLC_TICK_0 - ( p_pkt->i_pts + shift );
+                            if( need > lift ) lift = need;
+                        }
                         if( p_pkt->i_dts != VLC_TICK_INVALID )
-                            p_pkt->i_dts += shift;
+                            p_pkt->i_dts += shift + lift;
                         if( p_pkt->i_pts != VLC_TICK_INVALID )
-                            p_pkt->i_pts += shift;
+                            p_pkt->i_pts += shift + lift;
                     }
                     es_out_Send( p_demux->out, tk->es, p_pkt );
                 }
@@ -1909,6 +1987,9 @@ static int DvdVRReadSetArea( demux_t *p_demux, int i_title, int i_chapter,
     VLC_UNUSED( i_angle );
     demux_sys_t *p_sys = p_demux->p_sys;
 
+    if( i_title >= 0 && (unsigned)i_title >= p_sys->ud_pgcit->nr_of_pgci )
+        return VLC_EGENERIC;
+
     /* user made title selection */
     if( i_title >= 0 && i_title < p_sys->i_titles &&
         i_title != p_sys->i_title )
@@ -1937,6 +2018,13 @@ static int DvdVRReadSetArea( demux_t *p_demux, int i_title, int i_chapter,
         i_end_cell = p_sys->i_title_end_cell = p_sys->ud_pgcit->ud_pgci_items[i_title].first_prog_id - 1 +
            p_sys->ud_pgcit->ud_pgci_items[i_title].nr_of_programs;
 
+        if( i_end_cell > p_sys->pgc_gi->nr_of_programs )
+        {
+            msg_Warn( p_demux, "invalid end cell %d for title %d",
+                      i_end_cell, i_title );
+            return VLC_EGENERIC;
+        }
+
         p_sys->i_chapters = 0;
         for (int c = i_start_cell; c < i_end_cell; c++)
         {
@@ -1965,11 +2053,16 @@ static int DvdVRReadSetArea( demux_t *p_demux, int i_title, int i_chapter,
         {
             uint16_t srpn = p_sys->ud_pgcit->m_c_gi[c].m_vobi_srpn;
             if( srpn == 0 || srpn > p_sys->pgc_gi->nr_of_programs )
-                continue;
+            {
+                msg_Warn( p_demux, "invalid m_vobi_srpn %" PRIu16 " in title %d",
+                          srpn, i_title );
+                return VLC_EGENERIC;
+            }
             p_sys->i_title_blocks += p_sys->pgc_gi->pgi[srpn - 1].map.nr_of_vobu_info;
         }
         p_sys->i_title_offset = 0;
         p_sys->i_pack_len = 0;
+        DvdReadResetCellTs( p_sys );
 
         p_sys->i_title = i_title;
 
@@ -2048,32 +2141,49 @@ static int DvdVRReadSetArea( demux_t *p_demux, int i_title, int i_chapter,
         if (!found)
             return VLC_EGENERIC;
 
-        /* search map for the address */
-        vobu_map_t map = p_sys->pgc_gi->pgi[chapter_program - 1].map;
+        if( chapter_program == 0 || chapter_program > p_sys->pgc_gi->nr_of_programs )
+            return VLC_EGENERIC;
 
-        /* find a vobu index to search for */
-        uint32_t relative_ptm = chapter_ptm
-            - p_sys->pgc_gi->pgi[chapter_program - 1].header.vob_v_s_ptm.ptm;
+        const pgi_t *chapter_pgi = &p_sys->pgc_gi->pgi[chapter_program - 1];
+        const vobu_map_t *map = &chapter_pgi->map;
 
-        uint32_t total_pts = p_sys->pgc_gi->pgi[chapter_program - 1].header.vob_v_e_ptm.ptm
-            - p_sys->pgc_gi->pgi[chapter_program - 1].header.vob_v_s_ptm.ptm;
+        const int64_t relative_ptm = (int64_t)chapter_ptm -
+            (int64_t)chapter_pgi->header.vob_v_s_ptm.ptm;
+        const int64_t total_pts = (int64_t)chapter_pgi->header.vob_v_e_ptm.ptm -
+            (int64_t)chapter_pgi->header.vob_v_s_ptm.ptm;
+        const uint32_t total_sectors = DvdVRGetProgramSectorSpan( p_sys, map );
 
-        uint32_t estimated_vobu = 0;
-        if( total_pts > 0 && map.nr_of_vobu_info > 0 )
-            estimated_vobu = (uint32_t)( (uint64_t)relative_ptm
-                * map.nr_of_vobu_info / total_pts );
+        uint32_t within_vobu = 0;
+        if( total_pts > 0 && relative_ptm >= 0 && total_sectors > 0 )
+        {
+            if( map->nr_of_vobu_info > 0 )
+                within_vobu = (uint32_t)( relative_ptm *
+                              map->nr_of_vobu_info / total_pts );
+        }
+        if( map->nr_of_vobu_info > 0 && within_vobu >= map->nr_of_vobu_info )
+            within_vobu = map->nr_of_vobu_info - 1;
 
-        int time_offset_i = 0;
-        while ( time_offset_i < map.nr_of_time_info - 1
-            && map.time_infos[time_offset_i + 1].vobu_entn <= estimated_vobu )
-            time_offset_i++;
+        /* time_infos sparse, snap to entry only near program start */
+        uint32_t snap_sector = 0;
+        if( within_vobu < DVDVR_EARLY_SNAP_VOBU_WINDOW &&
+            DvdVRTimeInfosSane( map, total_sectors ) )
+        {
+            int idx = 0;
+            while( idx + 1 < map->nr_of_time_info &&
+                   map->time_infos[idx + 1].vobu_entn <= within_vobu )
+                idx++;
+            snap_sector = map->time_infos[idx].vobu_adr;
+        }
 
-        uint32_t chapter_offset = map.time_infos[time_offset_i].vobu_adr + map.vob_offset;
+        const uint32_t chapter_offset = map->vob_offset + snap_sector;
         p_sys->i_cur_block = chapter_offset;
-        p_sys->i_pack_len = 0;
+        p_sys->i_pack_len = total_sectors > snap_sector ? total_sectors - snap_sector : 1;
         p_sys->i_chapter = i_chapter;
         p_sys->cur_chapter = i_chapter;
+        DvdReadResetCellTs( p_sys );
+        es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
 
+        p_sys->i_cur_cell = -1;
         for( int ci = p_sys->i_title_start_cell;
              ci < p_sys->i_title_end_cell; ci++ )
         {
@@ -2083,6 +2193,8 @@ static int DvdVRReadSetArea( demux_t *p_demux, int i_title, int i_chapter,
                 break;
             }
         }
+        if( p_sys->i_cur_cell < p_sys->i_title_start_cell )
+            return VLC_EGENERIC;
 
         p_sys->i_title_offset = 0;
         for( int ci = p_sys->i_title_start_cell;
@@ -2091,6 +2203,8 @@ static int DvdVRReadSetArea( demux_t *p_demux, int i_title, int i_chapter,
             uint16_t s = p_sys->ud_pgcit->m_c_gi[ci].m_vobi_srpn;
             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 */
+        p_sys->i_title_offset += within_vobu;
 
         return VLC_SUCCESS;
     }
@@ -2148,10 +2262,18 @@ static void DvdVRFindCell( demux_t *p_demux )
 
 static vlc_tick_t DVDVRGetTitleLength( pgc_gi_t *pgc_gi, ud_pgcit_t *ud_pgcit, int program)
 {
+    if( program < 0 || (unsigned)program >= ud_pgcit->nr_of_pgci )
+        return 0;
+
     uint64_t length_ptm = 0;
     uint16_t first_prog_id = ud_pgcit->ud_pgci_items[program].first_prog_id;
     uint16_t nr_of_programs = ud_pgcit->ud_pgci_items[program].nr_of_programs;
 
+    if( first_prog_id == 0 || first_prog_id > pgc_gi->nr_of_programs ||
+        nr_of_programs == 0 ||
+        first_prog_id - 1 + nr_of_programs > pgc_gi->nr_of_programs )
+        return 0;
+
     for ( int i = 0; i < nr_of_programs; i++ )
         length_ptm += pgc_gi->pgi[first_prog_id -1 + i].header.vob_v_e_ptm.ptm
             - pgc_gi->pgi[first_prog_id -1 + i].header.vob_v_s_ptm.ptm;
@@ -2334,23 +2456,23 @@ static int DvdVRReadSeek( demux_t *p_demux, uint32_t i_block_offset )
     demux_sys_t *p_sys = p_demux->p_sys;
     int i_chapter = 0;
     uint32_t vobu_accum = 0;
+    bool found_cell = false;
 
     int cell_base = p_sys->ud_pgcit->ud_pgci_items[p_sys->i_title].first_prog_id - 1;
     int nr = p_sys->ud_pgcit->ud_pgci_items[p_sys->i_title].nr_of_programs;
 
-    int found = 0;
     for( int c = 0; c < nr; c++ )
     {
         int cell_idx = cell_base + c;
         uint16_t srpn = p_sys->ud_pgcit->m_c_gi[cell_idx].m_vobi_srpn;
         if( srpn == 0 || srpn > p_sys->pgc_gi->nr_of_programs )
-            continue;
+            return VLC_EGENERIC;
         uint16_t cell_vobus = p_sys->pgc_gi->pgi[srpn - 1].map.nr_of_vobu_info;
 
         if( i_block_offset < vobu_accum + cell_vobus )
         {
             p_sys->i_cur_cell = cell_idx;
-            found = 1;
+            found_cell = true;
             break;
         }
         vobu_accum += cell_vobus;
@@ -2359,10 +2481,20 @@ static int DvdVRReadSeek( demux_t *p_demux, uint32_t i_block_offset )
                    ? p_sys->ud_pgcit->m_c_gi[cell_idx].c_epi_n : 1;
     }
 
-    if( !found )
+    /* seek past title end: snap to last cell */
+    if( !found_cell && nr > 0 )
     {
-        msg_Err( p_demux, "couldn't find cell for block offset %u", i_block_offset );
-        return VLC_EGENERIC;
+        const int last_cell_idx = cell_base + nr - 1;
+        uint16_t srpn = p_sys->ud_pgcit->m_c_gi[last_cell_idx].m_vobi_srpn;
+        if( unlikely( srpn == 0 || srpn > p_sys->pgc_gi->nr_of_programs ) )
+            return VLC_EGENERIC;
+
+        const uint16_t last_vobus = p_sys->pgc_gi->pgi[srpn - 1].map.nr_of_vobu_info;
+        p_sys->i_cur_cell = last_cell_idx;
+        if( last_vobus > 0 && vobu_accum >= last_vobus )
+            vobu_accum -= last_vobus;
+        else
+            vobu_accum = 0;
     }
 
     if( i_chapter < p_sys->i_chapters &&
@@ -2372,9 +2504,49 @@ static int DvdVRReadSeek( demux_t *p_demux, uint32_t i_block_offset )
         p_sys->cur_chapter = i_chapter;
     }
 
-    p_sys->i_pack_len = 0;
-    p_sys->i_title_offset = vobu_accum;
+    if( p_sys->i_cur_cell < cell_base || p_sys->i_cur_cell >= cell_base + nr )
+        return VLC_EGENERIC;
+
+    uint16_t cur_srpn = p_sys->ud_pgcit->m_c_gi[p_sys->i_cur_cell].m_vobi_srpn;
+    if( unlikely( cur_srpn == 0 || cur_srpn > p_sys->pgc_gi->nr_of_programs ) )
+        return VLC_EGENERIC;
+
+    const vobu_map_t *map = &p_sys->pgc_gi->pgi[cur_srpn - 1].map;
+    uint32_t within_program = i_block_offset - vobu_accum;
+    const uint32_t total_sectors = DvdVRGetProgramSectorSpan( p_sys, map );
+    if( map->nr_of_vobu_info > 0 && within_program >= map->nr_of_vobu_info )
+        within_program = map->nr_of_vobu_info - 1;
+
+    uint32_t raw_sector = 0;
+    if( total_sectors > 0 && map->nr_of_vobu_info > 0 )
+    {
+        uint64_t scaled_sector = 0;
+        if( !ckd_mul( &scaled_sector, (uint64_t)within_program,
+                      (uint64_t)total_sectors ) )
+            raw_sector = (uint32_t)( scaled_sector / map->nr_of_vobu_info );
+    }
+
+    /* time_infos sparse, snap to entry only near program start */
+    uint32_t sector = raw_sector;
+    if( within_program < DVDVR_EARLY_SNAP_VOBU_WINDOW &&
+        DvdVRTimeInfosSane( map, total_sectors ) )
+    {
+        int idx = 0;
+        while( idx + 1 < map->nr_of_time_info &&
+               map->time_infos[idx + 1].vobu_adr <= raw_sector )
+            idx++;
+        sector = map->time_infos[idx].vobu_adr;
+    }
+
+    if( sector >= total_sectors )
+        sector = total_sectors > 0 ? total_sectors - 1 : 0;
+
+    p_sys->i_cur_block = map->vob_offset + sector;
+    p_sys->i_pack_len = total_sectors > sector ? total_sectors - sector : 1;
+    p_sys->i_title_offset = i_block_offset;
     p_sys->i_chapter = i_chapter;
+    DvdReadResetCellTs( p_sys );
+    es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
 
     return VLC_SUCCESS;
 }
@@ -2641,6 +2813,14 @@ static void DemuxTitles( demux_t *p_demux, int *pi_angle )
             return;
 #ifdef DVDREAD_HAS_DVDVIDEORECORDING
         if ( p_sys->type == DVD_VR ) {
+            /* ep_ptm disc-global, subtract title start for relative offset */
+            vlc_tick_t title_base = 0;
+            int cell_base = p_sys->ud_pgcit->ud_pgci_items[i].first_prog_id - 1;
+            int nr = p_sys->ud_pgcit->ud_pgci_items[i].nr_of_programs;
+            if( nr > 0 )
+                title_base = FROM_SCALE_NZ(
+                    p_sys->ud_pgcit->m_c_gi[cell_base].c_v_s_ptm.ptm );
+
             char* converted_title = FromCharset(
                 disc_charset,
                 p_sys->ud_pgcit->ud_pgci_items[i].title,
@@ -2657,8 +2837,6 @@ static void DemuxTitles( demux_t *p_demux, int *pi_angle )
             t->i_length = DVDVRGetTitleLength( p_sys->pgc_gi, p_sys->ud_pgcit, i );
 
             /* create one seekpoint per entry point */
-            int cell_base = p_sys->ud_pgcit->ud_pgci_items[i].first_prog_id - 1;
-            int nr = p_sys->ud_pgcit->ud_pgci_items[i].nr_of_programs;
             for( int c = 0; c < nr; c++ )
             {
                 m_c_gi_t *cell = &p_sys->ud_pgcit->m_c_gi[cell_base + c];
@@ -2670,7 +2848,9 @@ static void DemuxTitles( demux_t *p_demux, int *pi_angle )
                         if ( unlikely( !s ) )
                             goto fail;
 
-                        s->i_time_offset = FROM_SCALE_NZ( (uint64_t)cell->m_c_epi[ep].ep_ptm.ptm );
+                        vlc_tick_t ep_time =
+                            FROM_SCALE_NZ( cell->m_c_epi[ep].ep_ptm.ptm ) - title_base;
+                        s->i_time_offset = ep_time > 0 ? ep_time : 0;
                         TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
                     }
                 }
@@ -2680,7 +2860,9 @@ static void DemuxTitles( demux_t *p_demux, int *pi_angle )
                     if ( unlikely( !s ) )
                         goto fail;
 
-                    s->i_time_offset = FROM_SCALE_NZ( (uint64_t)cell->c_v_s_ptm.ptm );
+                    vlc_tick_t cell_time =
+                        FROM_SCALE_NZ( cell->c_v_s_ptm.ptm ) - title_base;
+                    s->i_time_offset = cell_time > 0 ? cell_time : 0;
                     TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
                 }
             }



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/fc961c9941054d751760dbd60ab68d2251f558b4...55437c1fbafc96bd1550e9ebca16f4cd63f2b78c

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/fc961c9941054d751760dbd60ab68d2251f558b4...55437c1fbafc96bd1550e9ebca16f4cd63f2b78c
You're receiving this email because of your account on code.videolan.org.




More information about the vlc-commits mailing list