[vlc-commits] [Git][videolan/vlc][master] 6 commits: access: dvdread: module selection routing fix
Steve Lhomme (@robUx4)
gitlab at videolan.org
Sun Apr 26 05:02:03 UTC 2026
Steve Lhomme pushed to branch master at VideoLAN / VLC
Commits:
2bf0e513 by Saifelden Mohamed Ismail at 2026-04-26T06:20:59+02:00
access: dvdread: module selection routing fix
- - - - -
15cddb22 by Saifelden Mohamed Ismail at 2026-04-26T06:20:59+02:00
access: dvdread: make dvdtime_to_time input const
- - - - -
0f66966e by Saifelden Mohamed Ismail at 2026-04-26T06:20:59+02:00
access: dvdread: add timing helpers
- - - - -
52bf2b53 by Saifelden Mohamed Ismail at 2026-04-26T06:20:59+02:00
access: dvdread: wire PCR/PTS rebasing
- - - - -
37e54814 by Saifelden Mohamed Ismail at 2026-04-26T06:20:59+02:00
access: dvdread: factor length helper and handle DEMUX_SET_TIME
use one helper for length in get time, get length, and set time.
check empty title blocks earlier and keep seek math simple.
- - - - -
734cb671 by Saifelden Mohamed Ismail at 2026-04-26T06:20:59+02:00
access: dvdread: account for inclusive chapter sector range
- - - - -
1 changed file:
- modules/access/dvdread.c
Changes:
=====================================
modules/access/dvdread.c
=====================================
@@ -62,6 +62,7 @@
#include <dvdread/nav_print.h>
#include <assert.h>
+#include <stdckdint.h>
#include <limits.h>
#include "disc_helper.h"
@@ -128,11 +129,11 @@ vlc_module_begin ()
set_callbacks( Open, Close )
add_submodule()
set_capability( "access", 1 )
- add_shortcut( "dvda", "dvd" )
+ add_shortcut( "dvda" )
set_callbacks( OpenAudio, Close )
add_submodule()
set_capability( "access", 1 )
- add_shortcut( "dvdvr", "dvd" )
+ add_shortcut( "dvdvr" )
set_callbacks( OpenVideoRecording, Close )
vlc_module_end ()
@@ -213,6 +214,16 @@ typedef struct
int i_cur_cell;
int i_next_cell;
+ /* ps/dvd anchor pair for rebasing ps scr/pts onto the disc timeline
+ * dvd is accumulated cell playback time from title start
+ * ps is the matching scr from the first ps pack of that cell
+ * both are VLC_TICK_INVALID until the first pack of a cell is seen */
+ struct
+ {
+ vlc_tick_t dvd;
+ vlc_tick_t ps;
+ } cell_ts;
+
/* Track */
ps_track_t tk[PS_TK_COUNT];
@@ -231,6 +242,12 @@ static int Control ( demux_t *, int, va_list );
static int Demux ( demux_t * );
static int DemuxBlock( demux_t *, const uint8_t *, int );
+static inline void DvdReadResetCellTs( demux_sys_t *p_sys )
+{
+ p_sys->cell_ts.dvd = VLC_TICK_INVALID;
+ p_sys->cell_ts.ps = VLC_TICK_INVALID;
+}
+
static void DemuxTitles( demux_t *, int * );
static void ESNew( demux_t *, int, int );
@@ -392,6 +409,9 @@ static int OpenCommon( vlc_object_t *p_this , dvd_type_t type )
p_sys->cur_title = p_sys->cur_chapter = 0;
p_sys->i_mux_rate = 0;
+ /* reset anchors so the first cell seen after open captures fresh timestamps */
+ DvdReadResetCellTs( p_sys );
+
p_sys->i_angle = var_CreateGetInteger( p_demux, "dvdread-angle" );
if( p_sys->i_angle <= 0 ) p_sys->i_angle = 1;
@@ -487,7 +507,7 @@ static void Close( vlc_object_t *p_this )
free( p_sys );
}
-static vlc_tick_t dvdtime_to_time( dvd_time_t *dtime )
+static vlc_tick_t dvdtime_to_time( const dvd_time_t *dtime )
{
/* Macro to convert Binary Coded Decimal to Decimal */
#define BCD2D(__x__) (((__x__ & 0xf0) >> 4) * 10 + (__x__ & 0x0f))
@@ -514,6 +534,246 @@ static vlc_tick_t dvdtime_to_time( dvd_time_t *dtime )
return vlc_tick_from_sec(sec) + VLC_TICK_FROM_MS(f_ms);
}
+/* total playback duration of the current title in vlc_tick_t
+ * for dvd-video sums per-cell playback_time across the active cell range
+ * so angle-restricted titles report the correct length
+ * falls back to pgc playback_time for other disc types */
+static vlc_tick_t DvdReadGetTitleDuration( const demux_sys_t *p_sys )
+{
+ if( p_sys->type == DVD_V && p_sys->p_cur_pgc && p_sys->p_cur_pgc->cell_playback &&
+ p_sys->i_title_start_cell >= 0 &&
+ p_sys->i_title_start_cell < p_sys->i_title_end_cell )
+ {
+ vlc_tick_t dur = 0;
+ for( int ci = p_sys->i_title_start_cell; ci <= p_sys->i_title_end_cell; ci++ )
+ {
+ dur += dvdtime_to_time( &p_sys->p_cur_pgc->cell_playback[ci].playback_time );
+ }
+ return dur;
+ }
+
+ if( p_sys->p_cur_pgc )
+ return dvdtime_to_time( &p_sys->p_cur_pgc->playback_time );
+ return 0;
+}
+
+#ifdef DVDREAD_HAS_DVDVIDEORECORDING
+#define DVDVR_FALLBACK_SECTORS_PER_VOBU 512
+
+/* estimated sector span of a vr program's vobu map
+ * dvd-vr has no flat vobu address map like VOBU_ADMAP in dvd-video
+ * instead time_info entries store vobu_adr as sector offset and vobu_entn
+ * as vobu index so the last entry gives a sectors-per-vobu ratio
+ * falls back to a per-program or constant ratio when no usable entries exist
+ * returns 0 when the map has no VOBUs */
+static uint32_t DvdVRGetProgramSectorSpan( const demux_sys_t *p_sys,
+ const vobu_map_t *map )
+{
+ if( map->nr_of_time_info > 0 )
+ {
+ const uint32_t last_adr = map->time_infos[map->nr_of_time_info - 1].vobu_adr;
+ const uint32_t last_entn = map->time_infos[map->nr_of_time_info - 1].vobu_entn;
+ if( last_entn > 1 )
+ return (uint32_t)( (uint64_t)last_adr * map->nr_of_vobu_info /
+ ( last_entn - 1 ) );
+ }
+
+ uint32_t sects_per_vobu = DVDVR_FALLBACK_SECTORS_PER_VOBU;
+ for( int p = 0; p < p_sys->pgc_gi->nr_of_programs; p++ )
+ {
+ const vobu_map_t *m = &p_sys->pgc_gi->pgi[p].map;
+ if( m->nr_of_time_info > 0 &&
+ m->time_infos[m->nr_of_time_info - 1].vobu_entn > 1 )
+ {
+ 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;
+ }
+ }
+
+ return map->nr_of_vobu_info * sects_per_vobu;
+}
+
+static vlc_tick_t DvdVRProgramDuration( const pgi_t *pgi )
+{
+ if( pgi->header.vob_v_e_ptm.ptm <= pgi->header.vob_v_s_ptm.ptm )
+ return 0;
+
+ const uint32_t delta_ptm =
+ pgi->header.vob_v_e_ptm.ptm - pgi->header.vob_v_s_ptm.ptm;
+ return FROM_SCALE_NZ( delta_ptm );
+}
+
+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 ||
+ p_sys->i_title_start_cell < 0 || p_sys->i_title_end_cell <= p_sys->i_title_start_cell )
+ return 0;
+
+ vlc_tick_t acc = 0;
+ uint32_t vobu_acc = 0;
+
+ for( int ci = p_sys->i_title_start_cell; ci < p_sys->i_title_end_cell; ci++ )
+ {
+ uint16_t srpn = p_sys->ud_pgcit->m_c_gi[ci].m_vobi_srpn;
+ if( srpn == 0 || srpn > p_sys->pgc_gi->nr_of_programs )
+ return vobu_acc;
+
+ const pgi_t *pgi = &p_sys->pgc_gi->pgi[srpn - 1];
+ const uint32_t vobus = pgi->map.nr_of_vobu_info;
+ if( vobus == 0 )
+ continue;
+
+ const vlc_tick_t dur = DvdVRProgramDuration( pgi );
+ if( dur == 0 )
+ continue;
+ if( acc + dur > t )
+ {
+ const vlc_tick_t within = t - acc;
+ const uint32_t within_vobu = (uint32_t)(within * vobus / dur);
+ return vobu_acc + within_vobu;
+ }
+
+ acc += dur;
+ vobu_acc += vobus;
+ }
+
+ return p_sys->i_title_blocks > 0 ? p_sys->i_title_blocks - 1 : 0;
+}
+
+#endif
+
+static bool DvdReadCellTsShift( const demux_sys_t *p_sys, vlc_tick_t *shift )
+{
+ if( p_sys->cell_ts.dvd == VLC_TICK_INVALID ||
+ p_sys->cell_ts.ps == VLC_TICK_INVALID )
+ return false;
+ *shift = p_sys->cell_ts.dvd - p_sys->cell_ts.ps;
+ return true;
+}
+
+static bool DvdReadGetTimelineBase( const demux_sys_t *p_sys, vlc_tick_t *base )
+{
+ switch( p_sys->type )
+ {
+ case DVD_V:
+ {
+ const pgc_t *p_pgc = p_sys->p_cur_pgc;
+ if( !p_pgc || !p_pgc->cell_playback ||
+ p_sys->i_title_start_cell < 0 || p_sys->i_cur_cell < 0 ||
+ p_sys->i_title_start_cell >= p_pgc->nr_of_cells ||
+ p_sys->i_cur_cell >= p_pgc->nr_of_cells ||
+ p_sys->i_title_start_cell > p_sys->i_cur_cell )
+ return false;
+
+ vlc_tick_t cell_dvd_time = 0;
+ for( int i = p_sys->i_title_start_cell; i < p_sys->i_cur_cell; i++ )
+ cell_dvd_time += dvdtime_to_time( &p_pgc->cell_playback[i].playback_time );
+
+ const cell_playback_t *p_cell = &p_pgc->cell_playback[p_sys->i_cur_cell];
+ vlc_tick_t cell_duration = dvdtime_to_time( &p_cell->playback_time );
+ const uint32_t cell_sectors = p_cell->last_sector - p_cell->first_sector + 1;
+
+ if( cell_duration > 0 && cell_sectors > 0 )
+ {
+ int64_t within_sectors = p_sys->i_cur_block - (int64_t)p_cell->first_sector;
+
+ if( within_sectors < 0 )
+ within_sectors = 0;
+ else if( within_sectors >= (int64_t)cell_sectors )
+ within_sectors = (int64_t)cell_sectors - 1;
+
+ vlc_tick_t scaled;
+ if( !ckd_mul( &scaled, within_sectors, cell_duration ) )
+ cell_dvd_time += scaled / cell_sectors;
+ }
+
+ *base = cell_dvd_time;
+ return true;
+ }
+#ifdef DVDREAD_HAS_DVDAUDIO
+ case DVD_A:
+ {
+ if( !p_sys->p_title_table || p_sys->i_title_blocks == 0 )
+ return false;
+
+ const uint32_t first_sector =
+ p_sys->p_title_table->atsi_track_pointer_rows[0].start_sector;
+ const uint32_t current_sector = p_sys->i_cur_block > 0 ? (uint32_t)p_sys->i_cur_block : 0;
+ const uint32_t current_offset = current_sector > first_sector ? current_sector - first_sector : 0;
+ const vlc_tick_t title_length =
+ FROM_SCALE_NZ( p_sys->p_title_table->length_pts );
+ *base = (vlc_tick_t)( current_offset * title_length
+ / p_sys->i_title_blocks );
+ return true;
+ }
+#endif
+#ifdef DVDREAD_HAS_DVDVIDEORECORDING
+ case DVD_VR:
+ {
+ if( p_sys->i_title_start_cell < 0 || p_sys->i_cur_cell < 0 ||
+ p_sys->i_title_start_cell > p_sys->i_cur_cell ||
+ p_sys->i_cur_cell >= p_sys->i_title_end_cell )
+ return false;
+
+ vlc_tick_t cell_dvd_time = 0;
+ for( int ci = p_sys->i_title_start_cell;
+ ci < p_sys->i_cur_cell && ci < p_sys->i_title_end_cell; ci++ )
+ {
+ uint16_t srpn = p_sys->ud_pgcit->m_c_gi[ci].m_vobi_srpn;
+ if( srpn == 0 || srpn > p_sys->pgc_gi->nr_of_programs )
+ return false;
+ const pgi_t *scan_pgi = &p_sys->pgc_gi->pgi[srpn - 1];
+ const vlc_tick_t scan_dur = DvdVRProgramDuration( scan_pgi );
+ if( scan_dur <= 0 )
+ return false;
+ cell_dvd_time += scan_dur;
+ }
+
+ 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 )
+ return false;
+
+ const pgi_t *pgi = &p_sys->pgc_gi->pgi[srpn - 1];
+ const vlc_tick_t program_duration = DvdVRProgramDuration( pgi );
+ if( program_duration <= 0 )
+ return false;
+ const uint32_t total_sectors = DvdVRGetProgramSectorSpan( p_sys, &pgi->map );
+ if( total_sectors > 0 )
+ {
+ int64_t within_sectors = (int64_t)p_sys->i_cur_block - pgi->map.vob_offset;
+ if( within_sectors < 0 )
+ within_sectors = 0;
+ else if( within_sectors >= (int64_t)total_sectors )
+ within_sectors = (int64_t)total_sectors - 1;
+
+ vlc_tick_t scaled;
+ if( !ckd_mul( &scaled, within_sectors, program_duration ) )
+ cell_dvd_time += scaled / total_sectors;
+ }
+
+ *base = cell_dvd_time;
+ return true;
+ }
+#endif
+ default:
+ return false;
+ }
+}
+
+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 );
+#endif
+ return DvdReadGetTitleDuration( p_sys );
+}
+
/*****************************************************************************
* Control:
*****************************************************************************/
@@ -560,19 +820,12 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
case DEMUX_GET_TIME:
if( p_sys->cur_title >= 0 && p_sys->cur_title < p_sys->i_titles )
{
- vlc_tick_t length;
-#if defined(DVDREAD_HAS_DVDVIDEORECORDING)
- if (p_sys->type == DVD_VR )
- length = p_sys->titles[p_sys->cur_title]->i_length;
- else
-#endif
-#if defined(DVDREAD_HAS_DVDAUDIO)
- if (p_sys->type == DVD_A )
- length = FROM_SCALE_NZ( ( uint64_t ) p_sys->p_title_table->length_pts );
- else
-#endif
- length = dvdtime_to_time( &p_sys->p_cur_pgc->playback_time );
-
+ if( unlikely( p_sys->i_title_blocks == 0 ) )
+ {
+ *va_arg( args, vlc_tick_t * ) = 0;
+ return VLC_EGENERIC;
+ }
+ const vlc_tick_t length = DvdReadGetControlLength( p_sys );
*va_arg( args, vlc_tick_t * ) = p_sys->i_title_offset * length
/ p_sys->i_title_blocks;
return VLC_SUCCESS;
@@ -580,21 +833,44 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
*va_arg( args, vlc_tick_t * ) = 0;
return VLC_EGENERIC;
- case DEMUX_GET_LENGTH:
- if( p_sys->cur_title >= 0 && p_sys->cur_title < p_sys->i_titles )
- {
- vlc_tick_t length;
+ case DEMUX_SET_TIME:
+ {
+ const vlc_tick_t i_time = va_arg( args, vlc_tick_t );
+ if( p_sys->cur_title < 0 || p_sys->cur_title >= p_sys->i_titles ||
+ p_sys->i_title_blocks == 0 )
+ return VLC_EGENERIC;
+
+ const vlc_tick_t length = DvdReadGetControlLength( p_sys );
+
+ if( length <= 0 )
+ return VLC_EGENERIC;
+
+ vlc_tick_t t = i_time;
+ if( t < 0 ) t = 0;
+ if( t > length ) t = length;
+
+ const uint32_t i_block_offset =
+ (uint32_t)( t * p_sys->i_title_blocks / length );
+
#if defined(DVDREAD_HAS_DVDVIDEORECORDING)
- if (p_sys->type == DVD_VR )
- length = p_sys->titles[p_sys->cur_title]->i_length;
- else
+ if ( p_sys->type == DVD_VR )
+ {
+ const uint32_t vr_vobu =
+ DvdVRReadTimeToVobuOffset( p_sys, t );
+ return DvdVRReadSeek( p_demux, vr_vobu );
+ }
#endif
#if defined(DVDREAD_HAS_DVDAUDIO)
- if ( p_sys->type == DVD_A )
- length = FROM_SCALE_NZ( ( uint64_t ) p_sys->p_title_table->length_pts );
- else
+ if ( p_sys->type == DVD_A )
+ return DvdAudioReadSeek( p_demux, i_block_offset );
#endif
- length = dvdtime_to_time( &p_sys->p_cur_pgc->playback_time );
+ return DvdReadSeek( p_demux, i_block_offset );
+ }
+
+ case DEMUX_GET_LENGTH:
+ if( p_sys->cur_title >= 0 && p_sys->cur_title < p_sys->i_titles )
+ {
+ const vlc_tick_t length = DvdReadGetControlLength( p_sys );
*va_arg( args, vlc_tick_t * ) = length;
return VLC_SUCCESS;
@@ -848,21 +1124,26 @@ static int Demux( demux_t *p_demux )
return -1;
}
- p_sys->i_cur_block += i_read;
- if( type != DVD_VR )
- p_sys->i_title_offset += i_read;
+ const uint32_t i_block_start = p_sys->i_cur_block;
+ const int i_title_offset_start = p_sys->i_title_offset;
#if 0
msg_Dbg( p_demux, "i_blocks: %d len: %d current: 0x%02x",
- i_read, p_sys->i_pack_len, p_sys->i_cur_block );
+ i_read, p_sys->i_pack_len, i_block_start );
#endif
for( int i = 0; i < i_read; i++ )
{
+ p_sys->i_cur_block = i_block_start + i;
+
DemuxBlock( p_demux, p_buffer + i * DVD_VIDEO_LB_LEN,
DVD_VIDEO_LB_LEN );
}
+ p_sys->i_cur_block = i_block_start + i_read;
+ if( type != DVD_VR )
+ p_sys->i_title_offset = i_title_offset_start + i_read;
+
return 1;
}
@@ -912,7 +1193,26 @@ static int DemuxBlock( demux_t *p_demux, const uint8_t *p, int len )
if( !ps_pkt_parse_pack( p_pkt->p_buffer, p_pkt->i_buffer,
&i_scr, &i_mux_rate ) )
{
- es_out_SetPCR( p_demux->out, VLC_TICK_0 + i_scr );
+ if( p_sys->cell_ts.dvd == VLC_TICK_INVALID ||
+ p_sys->cell_ts.ps == VLC_TICK_INVALID )
+ {
+ vlc_tick_t base;
+ if( DvdReadGetTimelineBase( p_sys, &base ) )
+ {
+ p_sys->cell_ts.dvd = base;
+ p_sys->cell_ts.ps = i_scr;
+ }
+ }
+
+ vlc_tick_t shift;
+ if( DvdReadCellTsShift( p_sys, &shift ) )
+ es_out_SetPCR( p_demux->out, VLC_TICK_0 + i_scr + shift );
+ else
+ {
+ if( p_sys->cell_ts.dvd != VLC_TICK_INVALID )
+ msg_Dbg( p_demux, "segment timestamp anchor not initialized yet" );
+ es_out_SetPCR( p_demux->out, VLC_TICK_0 + i_scr );
+ }
if( i_mux_rate > 0 ) p_sys->i_mux_rate = i_mux_rate;
}
block_Release( p_pkt );
@@ -932,6 +1232,22 @@ 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 )
+ {
+ 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 shift;
+ if( DvdReadCellTsShift( p_sys, &shift ) )
+ {
+ if( p_pkt->i_dts != VLC_TICK_INVALID )
+ p_pkt->i_dts += shift;
+ if( p_pkt->i_pts != VLC_TICK_INVALID )
+ p_pkt->i_pts += shift;
+ }
es_out_Send( p_demux->out, tk->es, p_pkt );
}
else
@@ -1882,7 +2198,7 @@ static int DvdAudioReadSeek( demux_t *p_demux, uint32_t i_block_offset )
for ( i_chapter = 0 ; i_chapter < p_sys->i_chapters; i_chapter++ ) {
uint32_t start = p_sys->p_title_table->atsi_track_pointer_rows[i_chapter].start_sector;
uint32_t end = p_sys->p_title_table->atsi_track_pointer_rows[i_chapter].end_sector;
- uint32_t chapter_len= end-start;
+ uint32_t chapter_len = end - start + 1;
if ( i_block_offset < i_seek_blocks + chapter_len )
break;
@@ -2050,6 +2366,8 @@ static void DvdReadHandleDSI( demux_t *p_demux, uint8_t *p_data )
{
p_sys->i_cur_cell = p_sys->i_next_cell;
+ DvdReadResetCellTs( p_sys );
+
/* End of title */
if( p_sys->i_cur_cell >= p_sys->p_cur_pgc->nr_of_cells ) return;
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/8d61853d5b79c0753db6c8cc7dc551c70112440b...734cb67180e5fe97fabff8c99e3f10831e73c016
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/8d61853d5b79c0753db6c8cc7dc551c70112440b...734cb67180e5fe97fabff8c99e3f10831e73c016
You're receiving this email because of your account on code.videolan.org.
More information about the vlc-commits
mailing list