[vlc-commits] [Git][videolan/vlc][master] 9 commits: demux: ts: change stime_t for ts_90khz_t

Steve Lhomme (@robUx4) gitlab at videolan.org
Tue Apr 29 08:44:07 UTC 2025



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
0a5bd92c by François Cartegnie at 2025-04-29T08:29:05+00:00
demux: ts: change stime_t for ts_90khz_t

- - - - -
cf2f062f by François Cartegnie at 2025-04-29T08:29:05+00:00
demux: ts: add TS_90KHZ_INVALID

- - - - -
2f5f0422 by François Cartegnie at 2025-04-29T08:29:05+00:00
demux: ts: remove SETANDVALID macro

- - - - -
fe9457b2 by François Cartegnie at 2025-04-29T08:29:05+00:00
demux: ts: remove usage of TS_UNKNOWN, simplifying comparisons

- - - - -
01aa9fee by François Cartegnie at 2025-04-29T08:29:05+00:00
codec: textst: use ts_90khz_t

- - - - -
e9e99b04 by François Cartegnie at 2025-04-29T08:29:05+00:00
demux: pva: use ts_90khz_t

- - - - -
af38707d by François Cartegnie at 2025-04-29T08:29:05+00:00
demux: ts: revert native scale changes to fix wrapping

Can never work with timestamp value 0

- - - - -
56b19564 by François Cartegnie at 2025-04-29T08:29:05+00:00
tests: add timestamps wrapping check

- - - - -
caf3ea13 by François Cartegnie at 2025-04-29T08:29:05+00:00
demux: ts: fix multiple wrapping

refs streams/ts/1fps93hours.ts

- - - - -


19 changed files:

- modules/codec/textst.c
- modules/demux/mpeg/pes.h
- modules/demux/mpeg/ps.h
- modules/demux/mpeg/timestamps.h
- modules/demux/mpeg/ts.c
- modules/demux/mpeg/ts.h
- modules/demux/mpeg/ts_hotfixes.c
- modules/demux/mpeg/ts_pes.c
- modules/demux/mpeg/ts_pes.h
- modules/demux/mpeg/ts_psi.c
- modules/demux/mpeg/ts_scte.c
- modules/demux/mpeg/ts_streams.c
- modules/demux/mpeg/ts_streams_private.h
- modules/demux/pva.c
- modules/demux/ty.c
- test/Makefile.am
- + test/modules/demux/timestamps.c
- test/modules/demux/ts_pes.c
- test/modules/meson.build


Changes:

=====================================
modules/codec/textst.c
=====================================
@@ -237,8 +237,8 @@ static int Decode(decoder_t *p_dec, block_t *p_block)
         (p_block->i_flags & BLOCK_FLAG_CORRUPTED) == 0 &&
         (p_sub = decoder_NewSubpictureText(p_dec)))
     {
-        p_sub->i_start = FROM_SCALE(((int64_t)(p_block->p_buffer[3] & 0x01) << 32) | GetDWBE(&p_block->p_buffer[4]));
-        p_sub->i_stop = FROM_SCALE(((int64_t)(p_block->p_buffer[8] & 0x01) << 32) | GetDWBE(&p_block->p_buffer[9]));
+        p_sub->i_start = FROM_SCALE(((ts_90khz_t)(p_block->p_buffer[3] & 0x01) << 32) | GetDWBE(&p_block->p_buffer[4]));
+        p_sub->i_stop = FROM_SCALE(((ts_90khz_t)(p_block->p_buffer[8] & 0x01) << 32) | GetDWBE(&p_block->p_buffer[9]));
         if (p_sub->i_start < p_block->i_dts)
         {
             p_sub->i_stop += p_block->i_dts - p_sub->i_start;


=====================================
modules/demux/mpeg/pes.h
=====================================
@@ -22,16 +22,16 @@
 
 #include "timestamps.h"
 
-static inline stime_t GetPESTimestamp( const uint8_t *p_data )
+static inline ts_90khz_t GetPESTimestamp( const uint8_t *p_data )
 {
-    return  ((int64_t)(p_data[ 0]&0x0e ) << 29)|
-             (int64_t)(p_data[1] << 22)|
-            ((int64_t)(p_data[2]&0xfe) << 14)|
-             (int64_t)(p_data[3] << 7)|
-             (int64_t)(p_data[4] >> 1);
+    return  ((ts_90khz_t)(p_data[ 0]&0x0e ) << 29)|
+             (ts_90khz_t)(p_data[1] << 22)|
+            ((ts_90khz_t)(p_data[2]&0xfe) << 14)|
+             (ts_90khz_t)(p_data[3] << 7)|
+             (ts_90khz_t)(p_data[4] >> 1);
 }
 
-static inline bool ExtractPESTimestamp( const uint8_t *p_data, uint8_t i_flags, stime_t *ret )
+static inline bool ExtractPESTimestamp( const uint8_t *p_data, uint8_t i_flags, ts_90khz_t *ret )
 {
     /* !warn broken muxers set incorrect flags. see #17773 and #19140 */
     /* check marker bits, and i_flags = b 0010, 0011 or 0001 */
@@ -48,20 +48,20 @@ static inline bool ExtractPESTimestamp( const uint8_t *p_data, uint8_t i_flags,
 }
 
 /* PS SCR timestamp as defined in H222 2.5.3.2 */
-static inline stime_t ExtractPackHeaderTimestamp( const uint8_t *p_data )
+static inline ts_90khz_t ExtractPackHeaderTimestamp( const uint8_t *p_data )
 {
-    return ((int64_t)(p_data[ 0]&0x38 ) << 27)|
-            ((int64_t)(p_data[0]&0x03 ) << 28)|
-             (int64_t)(p_data[1] << 20)|
-            ((int64_t)(p_data[2]&0xf8 ) << 12)|
-            ((int64_t)(p_data[2]&0x03 ) << 13)|
-             (int64_t)(p_data[3] << 5) |
-             (int64_t)(p_data[4] >> 3);
+    return  ((ts_90khz_t)(p_data[0]&0x38 ) << 27)|
+            ((ts_90khz_t)(p_data[0]&0x03 ) << 28)|
+             (ts_90khz_t)(p_data[1] << 20)|
+            ((ts_90khz_t)(p_data[2]&0xf8 ) << 12)|
+            ((ts_90khz_t)(p_data[2]&0x03 ) << 13)|
+             (ts_90khz_t)(p_data[3] << 5) |
+             (ts_90khz_t)(p_data[4] >> 3);
 }
 
 inline
 static int ParsePESHeader( vlc_object_t *p_object, const uint8_t *p_header, size_t i_header,
-                           unsigned *pi_skip, stime_t *pi_dts, stime_t *pi_pts,
+                           unsigned *pi_skip, ts_90khz_t *pi_dts, ts_90khz_t *pi_pts,
                            uint8_t *pi_stream_id, bool *pb_pes_scambling )
 {
     unsigned i_skip;


=====================================
modules/demux/mpeg/ps.h
=====================================
@@ -456,7 +456,7 @@ static inline int ps_pkt_parse_pack( const uint8_t *p_pkt, size_t i_pkt,
     }
     else if( i_pkt >= 12 && (p[4] >> 4) == 0x02 ) /* MPEG-1 Pack SCR, same bits as PES/PTS */
     {
-        stime_t i_scr;
+        ts_90khz_t i_scr;
         if(!ExtractPESTimestamp( &p[4], 0x02, &i_scr ))
             return VLC_EGENERIC;
         *pi_scr = FROM_SCALE( i_scr );
@@ -511,8 +511,8 @@ static inline int ps_pkt_parse_system( const uint8_t *p_pkt, size_t i_pkt,
 static inline int ps_pkt_parse_pes( vlc_object_t *p_object, block_t *p_pes, int i_skip_extra )
 {
     unsigned int i_skip  = 0;
-    stime_t i_pts = -1;
-    stime_t i_dts = -1;
+    ts_90khz_t i_pts = TS_90KHZ_INVALID;
+    ts_90khz_t i_dts = TS_90KHZ_INVALID;
     uint8_t i_stream_id = 0;
     bool b_pes_scrambling = false;
 
@@ -539,12 +539,12 @@ static inline int ps_pkt_parse_pes( vlc_object_t *p_object, block_t *p_pes, int
     p_pes->i_buffer -= i_skip;
 
     /* ISO/IEC 13818-1 2.7.5: if no pts and no dts, then dts == pts */
-    if( i_pts >= 0 && i_dts < 0 )
+    if( i_pts != TS_90KHZ_INVALID && i_dts == TS_90KHZ_INVALID )
         i_dts = i_pts;
 
-    if( i_dts >= 0 )
+    if( i_dts != TS_90KHZ_INVALID )
         p_pes->i_dts = FROM_SCALE( i_dts );
-    if( i_pts >= 0 )
+    if( i_pts != TS_90KHZ_INVALID )
         p_pes->i_pts = FROM_SCALE( i_pts );
 
     return VLC_SUCCESS;


=====================================
modules/demux/mpeg/timestamps.h
=====================================
@@ -25,15 +25,25 @@
 #define FROM_SCALE(x) (VLC_TICK_0 + FROM_SCALE_NZ(x))
 #define TO_SCALE(x)   TO_SCALE_NZ((x) - VLC_TICK_0)
 
-typedef int64_t stime_t;
+typedef int64_t ts_90khz_t;
+#define TS_90KHZ_INVALID -1
 
-static inline stime_t TimeStampWrapAround( stime_t i_first_pcr, stime_t i_time )
+#define TS_33BITS_ROLL_NZ      FROM_SCALE_NZ(0x1FFFFFFFF)
+#define TS_33BITS_HALF_ROLL_NZ FROM_SCALE_NZ(0x0FFFFFFFF)
+
+static inline vlc_tick_t TimeStampWrapAround( vlc_tick_t i_past_pcr, vlc_tick_t i_time )
 {
-    stime_t i_adjust = 0;
-    if( i_first_pcr > 0x0FFFFFFFF && i_time < 0x0FFFFFFFF )
-        i_adjust = 0x1FFFFFFFF;
+    if( i_past_pcr == VLC_TICK_INVALID || i_time >= i_past_pcr )
+        return i_time;
+
+    vlc_tick_t delta = i_past_pcr - i_time;
+    if( delta >= TS_33BITS_HALF_ROLL_NZ )
+    {
+        vlc_tick_t rolls = (delta + TS_33BITS_ROLL_NZ - 1) / TS_33BITS_ROLL_NZ;
+        i_time += rolls * TS_33BITS_ROLL_NZ;
+    }
 
-    return i_time + i_adjust;
+    return i_time;
 }
 
 #endif


=====================================
modules/demux/mpeg/ts.c
=====================================
@@ -186,17 +186,17 @@ static inline int PIDGet( block_t *p )
 {
     return ( (p->p_buffer[1]&0x1f)<<8 )|p->p_buffer[2];
 }
-static stime_t GetPCR( const block_t * );
+static ts_90khz_t GetPCR( const block_t * );
 
 static block_t * ProcessTSPacket( demux_t *p_demux, ts_pid_t *pid, block_t *p_pkt, int * );
 static bool GatherSectionsData( demux_t *p_demux, ts_pid_t *, block_t *, size_t );
 static bool GatherPESData( demux_t *p_demux, ts_pid_t *, block_t *, size_t );
-static void ProgramSetPCR( demux_t *p_demux, ts_pmt_t *p_prg, stime_t i_pcr );
+static void ProgramSetPCR( demux_t *p_demux, ts_pmt_t *p_prg, vlc_tick_t i_pcr );
 
 static block_t* ReadTSPacket( demux_t *p_demux );
-static int SeekToTime( demux_t *p_demux, const ts_pmt_t *, stime_t time );
+static int SeekToTime( demux_t *p_demux, const ts_pmt_t *, vlc_tick_t time );
 static void ReadyQueuesPostSeek( demux_t *p_demux );
-static void PCRHandle( demux_t *p_demux, ts_pid_t *, stime_t );
+static void PCRHandle( demux_t *p_demux, ts_pid_t *, ts_90khz_t );
 static void PCRFixHandle( demux_t *, ts_pmt_t *, block_t * );
 
 #define TS_PACKET_SIZE_188 188
@@ -411,7 +411,7 @@ static int Open( vlc_object_t *p_this )
 
     vlc_dictionary_init( &p_sys->attachments, 0 );
 
-    p_sys->patfix.i_first_dts = -1;
+    p_sys->patfix.i_first_dts = VLC_TICK_INVALID;
     p_sys->patfix.i_timesourcepid = 0;
     p_sys->patfix.b_pcrhasnopcrfield = false;
     p_sys->patfix.status = var_CreateGetBool( p_demux, "ts-patfix" ) ? PAT_WAITING : PAT_FIXTRIED;
@@ -698,8 +698,8 @@ static int Demux( demux_t *p_demux )
         }
 
         /* Adaptation field cannot be scrambled */
-        stime_t i_pcr = GetPCR( p_pkt );
-        if( i_pcr >= 0 )
+        ts_90khz_t i_pcr = GetPCR( p_pkt );
+        if( i_pcr != TS_90KHZ_INVALID )
             PCRHandle( p_demux, p_pid, i_pcr );
 
         /* Probe streams to build PAT/PMT after MIN_PAT_INTERVAL in case we don't see any PAT */
@@ -949,14 +949,13 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
 
         if( !p_sys->b_ignore_time_for_positions &&
              p_pmt &&
-             p_pmt->pcr.i_first > -1 && SETANDVALID(p_pmt->i_last_dts) &&
-             p_pmt->pcr.i_current > -1 )
+             p_pmt->pcr.i_first != VLC_TICK_INVALID &&
+             p_pmt->i_last_dts != VLC_TICK_INVALID &&
+             p_pmt->pcr.i_current != VLC_TICK_INVALID )
         {
-            double i_length = TimeStampWrapAround( p_pmt->pcr.i_first,
-                                                   p_pmt->i_last_dts ) - p_pmt->pcr.i_first;
+            double i_length = p_pmt->i_last_dts - p_pmt->pcr.i_first;
             i_length += p_pmt->pcr.i_pcroffset;
-            double i_pos = TimeStampWrapAround( p_pmt->pcr.i_first,
-                                                p_pmt->pcr.i_current ) - p_pmt->pcr.i_first;
+            double i_pos = p_pmt->pcr.i_current - p_pmt->pcr.i_first;
             if( i_length > 0 )
             {
                 *pf = i_pos / i_length;
@@ -982,30 +981,30 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
         if( p_sys->b_access_control &&
            !p_sys->b_ignore_time_for_positions && b_bool && p_pmt )
         {
-            time_t i_time, i_length;
+            time_t i_time, i_length = 0;
+            vlc_tick_t i_seektime = VLC_TICK_0 + vlc_tick_from_sec( i_length * f );
             if( !EITCurrentEventTime( p_pmt, p_sys, &i_time, &i_length ) &&
-                 i_length > 0 && !SeekToTime( p_demux, p_pmt, (int64_t)(TO_SCALE( vlc_tick_from_sec( i_length * f ))) ) )
+                 i_length > 0 && !SeekToTime( p_demux, p_pmt, i_seektime ) )
             {
                 ReadyQueuesPostSeek( p_demux );
-                es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
-                                vlc_tick_from_sec( i_length * f ) );
+                es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, i_seektime );
                 return VLC_SUCCESS;
             }
         }
 
         if( !p_sys->b_ignore_time_for_positions && b_bool && p_pmt &&
-             p_pmt->pcr.i_first > -1 && SETANDVALID(p_pmt->i_last_dts) &&
-             p_pmt->pcr.i_current > -1 )
+             p_pmt->pcr.i_first != VLC_TICK_INVALID &&
+             p_pmt->i_last_dts != VLC_TICK_INVALID &&
+             p_pmt->pcr.i_current != VLC_TICK_INVALID )
         {
-            stime_t i_length = TimeStampWrapAround( p_pmt->pcr.i_first,
-                                                    p_pmt->i_last_dts ) - p_pmt->pcr.i_first;
-            i64 = p_pmt->pcr.i_first + (int64_t)(i_length * f);
+            vlc_tick_t i_length = p_pmt->i_last_dts - p_pmt->pcr.i_first;
+            i64 = p_pmt->pcr.i_first + i_length * f;
             if( i64 <= p_pmt->i_last_dts )
             {
                 if( !SeekToTime( p_demux, p_pmt, i64 ) )
                 {
                     ReadyQueuesPostSeek( p_demux );
-                    es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, FROM_SCALE(i64) );
+                    es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, i64 );
                     return VLC_SUCCESS;
                 }
             }
@@ -1024,12 +1023,12 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
     {
         vlc_tick_t i_time = va_arg( args, vlc_tick_t );
 
-        if( p_sys->b_canseek && p_pmt && p_pmt->pcr.i_first > -1 &&
-           !SeekToTime( p_demux, p_pmt, p_pmt->pcr.i_first + TO_SCALE(i_time) ) )
+        if( p_sys->b_canseek && p_pmt && p_pmt->pcr.i_first != VLC_TICK_INVALID &&
+           !SeekToTime( p_demux, p_pmt, p_pmt->pcr.i_first + i_time ) )
         {
             ReadyQueuesPostSeek( p_demux );
             es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
-                            FROM_SCALE(p_pmt->pcr.i_first) + i_time - VLC_TICK_0 );
+                            p_pmt->pcr.i_first + i_time - VLC_TICK_0 );
             return VLC_SUCCESS;
         }
         break;
@@ -1046,20 +1045,19 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
             }
         }
 
-        if( p_pmt && p_pmt->pcr.i_current > -1 && p_pmt->pcr.i_first > -1 )
+        if( p_pmt && p_pmt->pcr.i_current != VLC_TICK_INVALID && p_pmt->pcr.i_first != VLC_TICK_INVALID )
         {
-            stime_t i_pcr = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->pcr.i_current );
-            *va_arg( args, vlc_tick_t * ) = FROM_SCALE(i_pcr - p_pmt->pcr.i_first);
+            *va_arg( args, vlc_tick_t * ) = p_pmt->pcr.i_current - p_pmt->pcr.i_first;
             return VLC_SUCCESS;
         }
         break;
     case DEMUX_GET_NORMAL_TIME:
         if ((p_sys->b_access_control && !EITCurrentEventTime( p_pmt, p_sys, NULL, NULL))
-         || (!p_pmt || p_pmt->pcr.i_current == -1 || p_pmt->pcr.i_first == -1))
+         || (!p_pmt || p_pmt->pcr.i_current == VLC_TICK_INVALID || p_pmt->pcr.i_first == VLC_TICK_INVALID))
             return VLC_EGENERIC; /* use VLC_TICK_0 as Normal Play Time*/
 
         /* Use the first pcr of the current program as Normal Play Time */
-        *va_arg( args, vlc_tick_t * ) = FROM_SCALE( p_pmt->pcr.i_first );
+        *va_arg( args, vlc_tick_t * ) = p_pmt->pcr.i_first;
         return VLC_SUCCESS;
 
     case DEMUX_GET_LENGTH:
@@ -1075,19 +1073,19 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
 
         if( !p_sys->b_ignore_time_for_positions &&
             p_pmt &&
-           ( p_pmt->pcr.i_first > -1 || p_pmt->pcr.i_first_dts != -1 ) &&
-             p_pmt->i_last_dts > 0 )
+           ( p_pmt->pcr.i_first != VLC_TICK_INVALID || p_pmt->pcr.i_first_dts != VLC_TICK_INVALID ) &&
+             p_pmt->i_last_dts != VLC_TICK_INVALID )
         {
-            stime_t i_start = (p_pmt->pcr.i_first > -1) ? p_pmt->pcr.i_first :
-                              p_pmt->pcr.i_first_dts;
-            stime_t i_last = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->i_last_dts );
+            vlc_tick_t i_start = (p_pmt->pcr.i_first != VLC_TICK_INVALID) ? p_pmt->pcr.i_first :
+                                  p_pmt->pcr.i_first_dts;
+            vlc_tick_t i_last = p_pmt->i_last_dts;
             i_last += p_pmt->pcr.i_pcroffset;
             if( i_start > i_last )
             {
                 msg_Warn( p_demux, "Can't get stream duration. Edited ?" );
                 return VLC_EGENERIC;
             }
-            *va_arg( args, vlc_tick_t * ) = FROM_SCALE(i_last - i_start);
+            *va_arg( args, vlc_tick_t * ) = i_last - i_start;
             return VLC_SUCCESS;
         }
         break;
@@ -1386,7 +1384,7 @@ static vlc_tick_t GetTimeForUntimed( const ts_pmt_t *p_pmt )
         if( (p_pid->i_flags & FLAG_FILTERED) && SEEN(p_pid) &&
              p_pid->type == TYPE_STREAM &&
              p_pid->u.p_stream->p_es &&
-             SETANDVALID(p_pid->u.p_stream->i_last_dts) )
+             p_pid->u.p_stream->i_last_dts != VLC_TICK_INVALID )
         {
             const ts_es_t *p_es = p_pid->u.p_stream->p_es;
             if( p_es->fmt.i_cat == VIDEO_ES || p_es->fmt.i_cat == AUDIO_ES )
@@ -1425,24 +1423,19 @@ static block_t * ConvertPESBlock( demux_t *p_demux, ts_es_t *p_es,
     {
         const ts_pmt_t *p_pmt = p_es->p_program;
         if( p_block->i_pts != VLC_TICK_INVALID &&
-            p_pmt->pcr.i_current > -1 )
+            p_pmt->pcr.i_current != VLC_TICK_INVALID )
         {
             /* Teletext can have totally offset timestamps... RAI1, German */
-            vlc_tick_t i_pcr = FROM_SCALE(TimeStampWrapAround( p_pmt->pcr.i_first,
-                                                               p_pmt->pcr.i_current ));
-            if( i_pcr < p_block->i_pts || i_pcr - p_block->i_pts > CLOCK_FREQ )
+            if( p_pmt->pcr.i_current < p_block->i_pts || p_pmt->pcr.i_current - p_block->i_pts > CLOCK_FREQ )
                 p_block->i_dts = p_block->i_pts = VLC_TICK_INVALID;
         }
         if( p_block->i_pts == VLC_TICK_INVALID )
         {
             /* Teletext may have missing PTS (ETSI EN 300 472 Annexe A)
              * In this case use the last PCR + 40ms */
-            stime_t i_ts = GetTimeForUntimed( p_es->p_program );
-            if( SETANDVALID(i_ts) )
-            {
-                i_ts = TimeStampWrapAround( p_pmt->pcr.i_first, i_ts );
-                p_block->i_dts = p_block->i_pts = FROM_SCALE(i_ts) + VLC_TICK_FROM_MS(40);
-            }
+            vlc_tick_t i_ts = GetTimeForUntimed( p_es->p_program );
+            if( i_ts != VLC_TICK_INVALID )
+                p_block->i_dts = p_block->i_pts = i_ts + VLC_TICK_FROM_MS(40);
         }
     }
     else if( p_es->fmt.i_codec == VLC_CODEC_ARIB_A ||
@@ -1553,14 +1546,16 @@ static void SendDataChain( demux_t *p_demux, ts_es_t *p_es, block_t *p_chain )
  * gathering stuff
  ****************************************************************************/
 static void ParsePESDataChain( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes,
-                               uint32_t i_flags, stime_t i_append_pcr )
+                               uint32_t i_flags, ts_90khz_t i_append_pcr )
 {
     uint8_t header[34];
     unsigned i_pes_size = 0;
     unsigned i_skip = 0;
-    stime_t i_dts = -1;
-    stime_t i_pts = -1;
-    vlc_tick_t i_length = 0;
+    ts_90khz_t i_pktdts = TS_90KHZ_INVALID;
+    ts_90khz_t i_pktpts = TS_90KHZ_INVALID;
+    ts_90khz_t i_length = 0;
+    vlc_tick_t i_dts = VLC_TICK_INVALID;
+    vlc_tick_t i_pts = VLC_TICK_INVALID;
     uint8_t i_stream_id;
     bool b_pes_scrambling = false;
     const es_mpeg4_descriptor_t *p_mpeg4desc = NULL;
@@ -1592,17 +1587,17 @@ static void ParsePESDataChain( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes,
     ts_es_t *p_es = pid->u.p_stream->p_es;
 
     if( ParsePESHeader( VLC_OBJECT(p_demux), (uint8_t*)&header, i_max, &i_skip,
-                        &i_dts, &i_pts, &i_stream_id, &b_pes_scrambling ) == VLC_EGENERIC )
+                        &i_pktdts, &i_pktpts, &i_stream_id, &b_pes_scrambling ) == VLC_EGENERIC )
     {
         block_ChainRelease( p_pes );
         return;
     }
     else
     {
-        if( i_pts != -1 && p_es->p_program )
-            i_pts = TimeStampWrapAround( p_es->p_program->pcr.i_first, i_pts );
-        if( i_dts != -1 && p_es->p_program )
-            i_dts = TimeStampWrapAround( p_es->p_program->pcr.i_first, i_dts );
+        if( i_pktpts != TS_90KHZ_INVALID && p_es->p_program )
+            i_pts = TimeStampWrapAround( p_es->p_program->pcr.i_first, FROM_SCALE(i_pktpts) );
+        if( i_pktdts != TS_90KHZ_INVALID && p_es->p_program )
+            i_dts = TimeStampWrapAround( p_es->p_program->pcr.i_first, FROM_SCALE(i_pktdts) );
         if( b_pes_scrambling )
             p_pes->i_flags |= BLOCK_FLAG_SCRAMBLED;
     }
@@ -1661,10 +1656,10 @@ static void ParsePESDataChain( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes,
     }
 
     /* ISO/IEC 13818-1 2.7.5: if no pts and no dts, then dts == pts */
-    if( i_pts >= 0 && i_dts < 0 )
+    if( i_pts != VLC_TICK_INVALID && i_dts == VLC_TICK_INVALID )
         i_dts = i_pts;
 
-    if( i_dts >= 0 )
+    if( i_dts != TS_90KHZ_INVALID )
         pid->u.p_stream->i_last_dts = i_dts;
 
     if( p_pes )
@@ -1676,11 +1671,11 @@ static void ParsePESDataChain( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes,
             return;
         }
 
-        if( i_dts >= 0 )
-            p_pes->i_dts = FROM_SCALE(i_dts);
+        if( i_dts != VLC_TICK_INVALID )
+            p_pes->i_dts = i_dts;
 
-        if( i_pts >= 0 )
-            p_pes->i_pts = FROM_SCALE(i_pts);
+        if( i_pts != VLC_TICK_INVALID )
+            p_pes->i_pts = i_pts;
 
         p_pes->i_length = FROM_SCALE_NZ(i_length);
 
@@ -1700,7 +1695,7 @@ static void ParsePESDataChain( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes,
             if( !p_pmt->pcr.b_fix_done ) /* Not seen yet */
                 PCRFixHandle( p_demux, p_pmt, p_block );
 
-            if( p_es->id && (p_pmt->pcr.i_current > -1 || p_pmt->pcr.b_disable) )
+            if( p_es->id && (p_pmt->pcr.i_current != VLC_TICK_INVALID || p_pmt->pcr.b_disable) )
             {
                 if( pid->u.p_stream->prepcr.p_head )
                 {
@@ -1718,27 +1713,24 @@ static void ParsePESDataChain( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes,
                 if ( p_pmt->pcr.b_disable && p_block->i_dts != VLC_TICK_INVALID &&
                      ( p_pmt->i_pid_pcr == pid->i_pid || p_pmt->i_pid_pcr == 0x1FFF ) )
                 {
-                    stime_t i_pcr = ( p_block->i_dts > p_sys->i_generated_pcr_dpb_offset )
-                                  ? TO_SCALE(p_block->i_dts - p_sys->i_generated_pcr_dpb_offset)
-                                  : TO_SCALE(p_block->i_dts);
+                    vlc_tick_t i_pcr = p_block->i_dts;
+                    if( i_pcr > VLC_TICK_0 + p_sys->i_generated_pcr_dpb_offset )
+                        i_pcr -= p_sys->i_generated_pcr_dpb_offset;
                     ProgramSetPCR( p_demux, p_pmt, i_pcr );
                 }
 
                 /* Compute PCR/DTS offset if any */
-                stime_t i_pcrref = SETANDVALID(i_append_pcr) ? i_append_pcr : p_pmt->pcr.i_first;
+                vlc_tick_t i_pcrref = (i_append_pcr != TS_90KHZ_INVALID) ? FROM_SCALE(i_append_pcr) : p_pmt->pcr.i_first;
                 if( p_pmt->pcr.i_pcroffset == -1 && p_block->i_dts != VLC_TICK_INVALID &&
-                    SETANDVALID(i_pcrref) &&
-                   (p_es->fmt.i_cat == VIDEO_ES || p_es->fmt.i_cat == AUDIO_ES) )
+                    i_pcrref != VLC_TICK_INVALID &&
+                    (p_es->fmt.i_cat == VIDEO_ES || p_es->fmt.i_cat == AUDIO_ES) )
                 {
-                    stime_t i_dts27 = TO_SCALE(p_block->i_dts);
-                    i_dts27 = TimeStampWrapAround( i_pcrref, i_dts27 );
-                    i_pcrref = TimeStampWrapAround( p_pmt->pcr.i_first, i_pcrref );
-                    if( i_dts27 + (CLOCK_FREQ/90000) < i_pcrref )
+                    if( p_block->i_dts + FROM_SCALE_NZ(CLOCK_FREQ/90000) < i_pcrref )
                     {
-                        p_pmt->pcr.i_pcroffset = i_pcrref - i_dts27 + TO_SCALE_NZ(VLC_TICK_FROM_MS(80));
+                        p_pmt->pcr.i_pcroffset = i_pcrref - p_block->i_dts + VLC_TICK_FROM_MS(80);
                         msg_Warn( p_demux, "Broken stream: pid %d sends packets with dts %"PRId64
                                            "us later than pcr, applying delay",
-                                  pid->i_pid, FROM_SCALE_NZ(i_pcrref - i_dts27) );
+                                  pid->i_pid, i_pcrref - p_block->i_dts );
                     }
                     else p_pmt->pcr.i_pcroffset = 0;
                 }
@@ -1746,9 +1738,9 @@ static void ParsePESDataChain( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes,
                 if( p_pmt->pcr.i_pcroffset != -1 )
                 {
                     if( p_block->i_dts != VLC_TICK_INVALID )
-                        p_block->i_dts += FROM_SCALE_NZ(p_pmt->pcr.i_pcroffset);
+                        p_block->i_dts += p_pmt->pcr.i_pcroffset;
                     if( p_block->i_pts != VLC_TICK_INVALID )
-                        p_block->i_pts += FROM_SCALE_NZ(p_pmt->pcr.i_pcroffset);
+                        p_block->i_pts += p_pmt->pcr.i_pcroffset;
                 }
 
                 /*** From here, block can become a chain again though conversion below ***/
@@ -1783,7 +1775,7 @@ static void ParsePESDataChain( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes,
                     block_ChainLastAppend( &pid->u.p_stream->prepcr.pp_last, p_block );
 
                 /* PCR Seen and no es->id, cleanup current and prepcr blocks */
-                if( p_pmt->pcr.i_current > -1)
+                if( p_pmt->pcr.i_current != VLC_TICK_INVALID )
                 {
                     block_ChainRelease( pid->u.p_stream->prepcr.p_head );
                     pid->u.p_stream->prepcr.p_head = NULL;
@@ -1799,7 +1791,7 @@ static void ParsePESDataChain( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes,
 }
 
 static void PESDataChainHandle( vlc_object_t *p_obj, void *priv, block_t *p_data,
-                                uint32_t i_flags, stime_t i_appendpcr )
+                                uint32_t i_flags, ts_90khz_t i_appendpcr )
 {
     ParsePESDataChain( (demux_t *)p_obj, (ts_pid_t *) priv, p_data, i_flags, i_appendpcr );
 }
@@ -1882,11 +1874,11 @@ static block_t* ReadTSPacket( demux_t *p_demux )
     return p_pkt;
 }
 
-static stime_t GetPCR( const block_t *p_pkt )
+static ts_90khz_t GetPCR( const block_t *p_pkt )
 {
     const uint8_t *p = p_pkt->p_buffer;
 
-    stime_t i_pcr = -1;
+    ts_90khz_t i_pcr = TS_90KHZ_INVALID;
 
     if(unlikely(p_pkt->i_buffer < 12))
         return i_pcr;
@@ -1899,11 +1891,11 @@ static stime_t GetPCR( const block_t *p_pkt )
         ( p[5] & 0x10 ) ) /* PCR carry flag */
     {
         /* PCR is 33 bits */
-        i_pcr = ( (stime_t)p[6] << 25 ) |
-                ( (stime_t)p[7] << 17 ) |
-                ( (stime_t)p[8] << 9 ) |
-                ( (stime_t)p[9] << 1 ) |
-                ( (stime_t)p[10] >> 7 );
+        i_pcr = ( (ts_90khz_t)p[6] << 25 ) |
+                ( (ts_90khz_t)p[7] << 17 ) |
+                ( (ts_90khz_t)p[8] << 9 ) |
+                ( (ts_90khz_t)p[9] << 1 ) |
+                ( (ts_90khz_t)p[10] >> 7 );
     }
     return i_pcr;
 }
@@ -1986,7 +1978,7 @@ static void ReadyQueuesPostSeek( demux_t *p_demux )
             for( ts_es_t *p_es = p_pes->p_es; p_es; p_es = p_es->p_next )
                 p_es->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
 
-            pid->u.p_stream->i_last_dts = -1;
+            pid->u.p_stream->i_last_dts = VLC_TICK_INVALID;
 
             if( pid->u.p_stream->prepcr.p_head )
             {
@@ -2000,16 +1992,16 @@ static void ReadyQueuesPostSeek( demux_t *p_demux )
 
             FlushESBuffer( pid->u.p_stream );
         }
-        p_pmt->pcr.i_current = -1;
+        p_pmt->pcr.i_current = VLC_TICK_INVALID;
     }
 }
 
-static int SeekToTime( demux_t *p_demux, const ts_pmt_t *p_pmt, stime_t i_scaledtime )
+static int SeekToTime( demux_t *p_demux, const ts_pmt_t *p_pmt, vlc_tick_t i_seektime )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
 
     /* Deal with common but worst binary search case */
-    if( p_pmt->pcr.i_first == i_scaledtime && p_sys->b_canseek )
+    if( p_pmt->pcr.i_first == i_seektime && p_sys->b_canseek )
         return vlc_stream_Seek( p_sys->stream, 0 );
 
     const int64_t i_stream_size = stream_Size( p_sys->stream );
@@ -2038,7 +2030,7 @@ static int SeekToTime( demux_t *p_demux, const ts_pmt_t *p_pmt, stime_t i_scaled
         uint64_t i_pos = i_splitpos;
         while( i_pos < i_tail_pos )
         {
-            stime_t i_pcr = -1;
+            ts_90khz_t i_pktpcr = TS_90KHZ_INVALID;
             block_t *p_pkt = ReadTSPacket( p_demux );
             if( !p_pkt )
             {
@@ -2058,37 +2050,37 @@ static int SeekToTime( demux_t *p_demux, const ts_pmt_t *p_pmt, stime_t i_scaled
                     if( p_pkt->i_buffer >= 4 + 2 + 5 )
                     {
                         if( p_pmt->i_pid_pcr == i_pid )
-                            i_pcr = GetPCR( p_pkt );
+                            i_pktpcr = GetPCR( p_pkt );
                         i_skip += 1 + __MIN(p_pkt->p_buffer[4], 182);
                     }
                 }
 
-                if( i_pcr == -1 && p_pid->type == TYPE_STREAM &&
+                if( i_pktpcr == TS_90KHZ_INVALID && p_pid->type == TYPE_STREAM &&
                     ts_stream_Find_es( p_pid->u.p_stream, p_pmt ) &&
                    (p_pkt->p_buffer[1] & 0xC0) == 0x40 && /* Payload start but not corrupt */
                    (p_pkt->p_buffer[3] & 0xD0) == 0x10    /* Has payload but is not encrypted */
                 )
                 {
-                    stime_t i_dts = -1;
-                    stime_t i_pts = -1;
+                    ts_90khz_t i_pktdts = TS_90KHZ_INVALID;
+                    ts_90khz_t i_pktpts = TS_90KHZ_INVALID;
                     uint8_t i_stream_id;
                     if ( VLC_SUCCESS == ParsePESHeader( VLC_OBJECT(p_demux), &p_pkt->p_buffer[i_skip],
                                                         p_pkt->i_buffer - i_skip, &i_skip,
-                                                        &i_dts, &i_pts, &i_stream_id, NULL ) )
+                                                        &i_pktdts, &i_pktpts, &i_stream_id, NULL ) )
                     {
-                        if( i_dts > -1 )
-                            i_pcr = i_dts;
+                        if( i_pktdts != TS_90KHZ_INVALID )
+                            i_pktpcr = i_pktdts;
                     }
                 }
             }
             block_Release( p_pkt );
 
-            if( i_pcr != -1 )
+            if( i_pktpcr != TS_90KHZ_INVALID )
             {
-                stime_t i_diff = i_scaledtime - TimeStampWrapAround( p_pmt->pcr.i_first, i_pcr );
+                vlc_tick_t i_diff = i_seektime - TimeStampWrapAround( p_pmt->pcr.i_first, FROM_SCALE(i_pktpcr) );
                 if ( i_diff < 0 )
                     i_tail_pos = (i_splitpos >= p_sys->i_packet_size) ? i_splitpos - p_sys->i_packet_size : 0;
-                else if( i_diff < TO_SCALE(VLC_TICK_0 + VLC_TICK_FROM_MS(500)) )
+                else if( i_diff < VLC_TICK_FROM_MS(500) )
                     b_found = true;
                 else
                     i_head_pos = i_pos;
@@ -2118,7 +2110,7 @@ static int ProbeChunk( demux_t *p_demux, int i_program, bool b_end, bool *pb_fou
 
     for( ;; )
     {
-        stime_t i_pcr = -1;
+        ts_90khz_t i_pcr = TS_90KHZ_INVALID;
 
         if( i_count++ > PROBE_CHUNK_COUNT || !( p_pkt = ReadTSPacket( p_demux ) ) )
         {
@@ -2146,10 +2138,10 @@ static int ProbeChunk( demux_t *p_demux, int i_program, bool b_end, bool *pb_fou
                 i_pcr = GetPCR( p_pkt );
 
             /* Designated PCR pid will be valid, don't repick (on the fly probing) */
-            if( i_pcr != -1 && !p_pid->probed.i_pcr_count )
+            if( i_pcr != TS_90KHZ_INVALID && !p_pid->probed.i_pcr_count )
                 p_pid->probed.i_pcr_count++;
 
-            if( i_pcr == -1 &&
+            if( i_pcr == TS_90KHZ_INVALID &&
                 (p_pkt->p_buffer[1] & 0xC0) == 0x40 && /* payload start */
                 (p_pkt->p_buffer[3] & 0xD0) == 0x10 && /* Has payload but is not encrypted */
                 p_pid->type == TYPE_STREAM &&
@@ -2157,8 +2149,8 @@ static int ProbeChunk( demux_t *p_demux, int i_program, bool b_end, bool *pb_fou
               )
             {
                 b_pcrresult = false;
-                stime_t i_dts = -1;
-                stime_t i_pts = -1;
+                ts_90khz_t i_dts = TS_90KHZ_INVALID;
+                ts_90khz_t i_pts = TS_90KHZ_INVALID;
                 uint8_t i_stream_id;
                 unsigned i_skip = 4;
                 if ( b_adaptfield ) // adaptation field
@@ -2168,14 +2160,14 @@ static int ProbeChunk( demux_t *p_demux, int i_program, bool b_end, bool *pb_fou
                                                     p_pkt->i_buffer - i_skip, &i_skip,
                                                     &i_dts, &i_pts, &i_stream_id, NULL ) )
                 {
-                    if( i_dts != -1 )
+                    if( i_dts != TS_90KHZ_INVALID )
                         i_pcr = i_dts;
-                    else if( i_pts != -1 )
+                    else if( i_pts != TS_90KHZ_INVALID )
                         i_pcr = i_pts;
                 }
             }
 
-            if( i_pcr != -1 )
+            if( i_pcr != TS_90KHZ_INVALID )
             {
                 ts_pat_t *p_pat = GetPID(p_sys, 0)->u.p_pat;
                 for( int i=0; i<p_pat->programs.i_size; i++ )
@@ -2188,17 +2180,17 @@ static int ProbeChunk( demux_t *p_demux, int i_program, bool b_end, bool *pb_fou
                     {
                         if( b_end )
                         {
-                            p_pmt->i_last_dts = i_pcr;
+                            p_pmt->i_last_dts = FROM_SCALE(i_pcr);
                             p_pmt->i_last_dts_byte = vlc_stream_Tell( p_sys->stream );
                         }
                         /* Start, only keep first */
-                        else if( b_pcrresult && p_pmt->pcr.i_first == -1 )
+                        else if( b_pcrresult && p_pmt->pcr.i_first == VLC_TICK_INVALID )
                         {
-                            p_pmt->pcr.i_first = i_pcr;
+                            p_pmt->pcr.i_first = FROM_SCALE(i_pcr);
                         }
-                        else if( p_pmt->pcr.i_first_dts == -1 )
+                        else if( p_pmt->pcr.i_first_dts == VLC_TICK_INVALID )
                         {
-                            p_pmt->pcr.i_first_dts = i_pcr;
+                            p_pmt->pcr.i_first_dts = FROM_SCALE(i_pcr);
                         }
 
                         if( i_program == 0 || i_program == p_pmt->i_number )
@@ -2280,13 +2272,13 @@ int ProbeEnd( demux_t *p_demux, int i_program )
     return (b_found) ? VLC_SUCCESS : VLC_EGENERIC;
 }
 
-static void ProgramSetPCR( demux_t *p_demux, ts_pmt_t *p_pmt, stime_t i_pcr )
+static void ProgramSetPCR( demux_t *p_demux, ts_pmt_t *p_pmt, vlc_tick_t i_pcr )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
 
     /* Check if we have enqueued blocks waiting the/before the
        PCR barrier, and then adapt pcr so they have valid PCR when dequeuing */
-    if( p_pmt->pcr.i_current == -1 && p_pmt->pcr.b_fix_done )
+    if( p_pmt->pcr.i_current == VLC_TICK_INVALID && p_pmt->pcr.b_fix_done )
     {
         vlc_tick_t i_mindts = VLC_TICK_INVALID;
 
@@ -2312,21 +2304,22 @@ static void ProgramSetPCR( demux_t *p_demux, ts_pmt_t *p_pmt, stime_t i_pcr )
             {
                 msg_Dbg( p_demux, "Program %d PCR prequeue fixup %"PRId64"->%"PRId64,
                          p_pmt->i_number, TO_SCALE(i_mindts), i_pcr );
-                i_pcr = TO_SCALE(i_mindts);
+                i_pcr = i_mindts;
             }
             else i_pcr = p_pmt->pcr.i_first;
         }
     }
 
     p_pmt->pcr.i_current = i_pcr;
-    if( p_pmt->pcr.i_first == -1 )
+
+    if( p_pmt->pcr.i_first == VLC_TICK_INVALID )
     {
         p_pmt->pcr.i_first = i_pcr; // now seen
     }
 
     if ( p_sys->i_pmt_es )
     {
-        es_out_Control( p_demux->out, ES_OUT_SET_GROUP_PCR, p_pmt->i_number, FROM_SCALE(i_pcr) );
+        es_out_Control( p_demux->out, ES_OUT_SET_GROUP_PCR, p_pmt->i_number, i_pcr );
         /* growing files/named fifo handling */
         if( p_sys->b_access_control == false &&
             vlc_stream_Tell( p_sys->stream ) > p_pmt->i_last_dts_byte )
@@ -2364,7 +2357,7 @@ static int IsVideoEnd( ts_pid_t *p_pid )
              ( tail[ i_tail - 1 ] == 0xb7 ||  tail[ i_tail - 1 ] == 0x0a ) );
 }
 
-static void PCRCheckDTS( demux_t *p_demux, ts_pmt_t *p_pmt, stime_t i_pcr)
+static void PCRCheckDTS( demux_t *p_demux, ts_pmt_t *p_pmt, vlc_tick_t i_pcr)
 {
     for( int i=0; i<p_pmt->e_streams.i_size; i++ )
     {
@@ -2394,33 +2387,35 @@ static void PCRCheckDTS( demux_t *p_demux, ts_pmt_t *p_pmt, stime_t i_pcr)
             continue;
 
         unsigned i_skip = 0;
-        stime_t i_dts = -1;
-        stime_t i_pts = -1;
+        ts_90khz_t i_pktdts = TS_90KHZ_INVALID;
+        ts_90khz_t i_pktpts = TS_90KHZ_INVALID;
+        vlc_tick_t i_dts = VLC_TICK_INVALID;
+        vlc_tick_t i_pts = VLC_TICK_INVALID;
         uint8_t i_stream_id;
 
         if( ParsePESHeader( VLC_OBJECT(p_demux), (uint8_t*)&header, i_max, &i_skip,
-                            &i_dts, &i_pts, &i_stream_id, NULL ) == VLC_EGENERIC )
+                            &i_pktdts, &i_pktpts, &i_stream_id, NULL ) == VLC_EGENERIC )
             continue;
 
+        if( i_pktdts != TS_90KHZ_INVALID )
+            i_dts = TimeStampWrapAround( i_pcr, FROM_SCALE(i_pktdts) );
+        if( i_pktpts > TS_90KHZ_INVALID )
+            i_pts = TimeStampWrapAround( i_pcr, FROM_SCALE(i_pktpts) );
+
         if (p_pmt->pcr.i_pcroffset > 0) {
-            if( i_dts != -1 )
+            if( i_dts != VLC_TICK_INVALID )
                 i_dts += p_pmt->pcr.i_pcroffset;
-            if( i_pts != -1 )
+            if( i_pts != VLC_TICK_INVALID )
                 i_pts += p_pmt->pcr.i_pcroffset;
         }
 
-        if( i_dts != -1 )
-            i_dts = TimeStampWrapAround( i_pcr, i_dts );
-        if( i_pts != -1 )
-            i_pts = TimeStampWrapAround( i_pcr, i_pts );
-
-        if(( i_dts != -1 && i_dts <= i_pcr ) ||
-           ( i_pts != -1 && i_pts <= i_pcr ))
+        if(( i_dts != VLC_TICK_INVALID && i_dts <= i_pcr ) ||
+           ( i_pts != VLC_TICK_INVALID && i_pts <= i_pcr ))
         {
             if( IsVideoEnd( p_pid ) )
             {
                 msg_Warn( p_demux, "send queued data for pid %d: TS %"PRId64" <= PCR %"PRId64"\n",
-                          p_pid->i_pid, i_dts != -1 ? i_dts : i_pts, i_pcr);
+                          p_pid->i_pid, i_dts != VLC_TICK_INVALID ? i_dts : i_pts, i_pcr);
                 ts_pes_parse_callback cb = { .p_obj = VLC_OBJECT(p_demux),
                                              .priv = p_pid,
                                              .pf_parse = PESDataChainHandle };
@@ -2430,7 +2425,7 @@ static void PCRCheckDTS( demux_t *p_demux, ts_pmt_t *p_pmt, stime_t i_pcr)
     }
 }
 
-static void PCRHandle( demux_t *p_demux, ts_pid_t *pid, stime_t i_pcr )
+static void PCRHandle( demux_t *p_demux, ts_pid_t *pid, ts_90khz_t i_pcr )
 {
     demux_sys_t   *p_sys = p_demux->p_sys;
 
@@ -2449,7 +2444,12 @@ static void PCRHandle( demux_t *p_demux, ts_pid_t *pid, stime_t i_pcr )
         ts_pmt_t *p_pmt = p_pat->programs.p_elems[i]->u.p_pmt;
         if( p_pmt->pcr.b_disable )
             continue;
-        stime_t i_program_pcr = TimeStampWrapAround( p_pmt->pcr.i_first, i_pcr );
+
+        vlc_tick_t i_past_pcr = p_pmt->pcr.i_current;
+        if( i_past_pcr == VLC_TICK_INVALID )
+            i_past_pcr = p_pmt->pcr.i_first;
+
+        vlc_tick_t i_program_pcr = TimeStampWrapAround( i_past_pcr, FROM_SCALE(i_pcr) );
 
         if( p_pmt->i_pid_pcr == 0x1FFF ) /* That program has no dedicated PCR pid ISO/IEC 13818-1 2.4.4.9 */
         {
@@ -2465,7 +2465,7 @@ static void PCRHandle( demux_t *p_demux, ts_pid_t *pid, stime_t i_pcr )
             if( p_pmt->i_pid_pcr == pid->i_pid ) /* If that program references current pid as PCR */
             {
                 /* We've found a target group for update */
-                PCRCheckDTS( p_demux, p_pmt, i_pcr );
+                PCRCheckDTS( p_demux, p_pmt, FROM_SCALE(i_pcr) );
                 ProgramSetPCR( p_demux, p_pmt, i_program_pcr );
             }
         }
@@ -2525,13 +2525,13 @@ static void PCRFixHandle( demux_t *p_demux, ts_pmt_t *p_pmt, block_t *p_block )
         return;
     }
     /* Record the first data packet timestamp in case there won't be any PCR */
-    else if( p_pmt->pcr.i_first_dts == TS_TICK_UNKNOWN )
+    else if( p_pmt->pcr.i_first_dts == VLC_TICK_INVALID )
     {
         p_pmt->pcr.i_first_dts = TO_SCALE(p_block->i_dts);
     }
     else if( p_block->i_dts - FROM_SCALE(p_pmt->pcr.i_first_dts) > VLC_TICK_FROM_MS(500) ) /* "PCR repeat rate shall not exceed 100ms" */
     {
-        if( p_pmt->pcr.i_current < 0 &&
+        if( p_pmt->pcr.i_current == VLC_TICK_INVALID &&
             GetPID( p_sys, p_pmt->i_pid_pcr )->probed.i_pcr_count == 0 )
         {
             int i_cand = FindPCRCandidate( p_pmt );
@@ -2697,12 +2697,14 @@ static bool GatherPESData( demux_t *p_demux, ts_pid_t *p_pid, block_t *p_pkt, si
                                  .priv = p_pid,
                                  .pf_parse = PESDataChainHandle };
     const bool b_unit_start = p_pkt->p_buffer[1]&0x40;
+
     p_pkt->p_buffer += i_skip; /* point to PES */
     p_pkt->i_buffer -= i_skip;
 
     const ts_es_t *p_es = p_pid->u.p_stream->p_es;
-    stime_t i_append_pcr = ( p_es && p_es->p_program )
-                         ? p_es->p_program->pcr.i_current : TS_TICK_UNKNOWN;
+    ts_90khz_t i_append_pcr = ( p_es && p_es->p_program && p_es->p_program->pcr.i_current != VLC_TICK_INVALID )
+                                  ? TO_SCALE(p_es->p_program->pcr.i_current)
+                                  : TS_90KHZ_INVALID;
 
     return ts_pes_Gather( &cb, p_pid->u.p_stream,
                           p_pkt, b_unit_start,


=====================================
modules/demux/mpeg/ts.h
=====================================
@@ -31,12 +31,6 @@ typedef struct csa_t csa_t;
 
 #define TS_PSI_PAT_PID 0x00
 
-_Static_assert (VLC_TICK_INVALID + 1 == VLC_TICK_0,
-                "can't define TS_UNKNOWN reference");
-#define TS_TICK_UNKNOWN (VLC_TICK_INVALID - 1)
-
-#define SETANDVALID(a) (a != TS_TICK_UNKNOWN && a != VLC_TICK_INVALID)
-
 typedef enum ts_standards_e
 {
     TS_STANDARD_AUTO = 0,
@@ -130,7 +124,7 @@ struct demux_sys_t
 
     struct
     {
-        stime_t i_first_dts;     /* first dts encountered for the stream */
+        vlc_tick_t i_first_dts;     /* first dts encountered for the stream */
         int     i_timesourcepid; /* which pid we saved the dts from */
         bool    b_pcrhasnopcrfield;
         enum { PAT_WAITING = 0, PAT_MISSING, PAT_FIXTRIED } status; /* set if we haven't seen PAT within MIN_PAT_INTERVAL */


=====================================
modules/demux/mpeg/ts_hotfixes.c
=====================================
@@ -82,7 +82,7 @@ void ProbePES( demux_t *p_demux, ts_pid_t *pid, const uint8_t *p_pesstart, size_
         return;
 
     size_t i_pesextoffset = 8;
-    stime_t i_dts = -1;
+    ts_90khz_t i_dts = TS_90KHZ_INVALID;
     if( p_pes[7] & 0x80 ) // PTS
     {
         i_pesextoffset += 5;
@@ -197,15 +197,15 @@ void ProbePES( demux_t *p_demux, ts_pid_t *pid, const uint8_t *p_pesstart, size_
 
 codecprobingend:
     /* Track timestamps and flag missing PAT */
-    if( !p_sys->patfix.i_timesourcepid && i_dts > -1 )
+    if( !p_sys->patfix.i_timesourcepid && i_dts != TS_90KHZ_INVALID )
     {
-        p_sys->patfix.i_first_dts = i_dts;
+        p_sys->patfix.i_first_dts = FROM_SCALE(i_dts);
         p_sys->patfix.i_timesourcepid = pid->i_pid;
     }
-    else if( p_sys->patfix.i_timesourcepid == pid->i_pid && i_dts > -1 &&
+    else if( p_sys->patfix.i_timesourcepid == pid->i_pid && i_dts != TS_90KHZ_INVALID &&
              p_sys->patfix.status == PAT_WAITING )
     {
-        if( i_dts - p_sys->patfix.i_first_dts > TO_SCALE(MIN_PAT_INTERVAL) )
+        if( i_dts - p_sys->patfix.i_first_dts > MIN_PAT_INTERVAL )
             p_sys->patfix.status = PAT_MISSING;
     }
 


=====================================
modules/demux/mpeg/ts_pes.c
=====================================
@@ -98,7 +98,7 @@ static uint8_t *FindNextPESHeader( uint8_t *p_buf, size_t i_buffer )
 
 static bool ts_pes_Push( ts_pes_parse_callback *cb,
                   ts_stream_t *p_pes, block_t *p_pkt,
-                  bool b_unit_start, stime_t i_append_pcr )
+                  bool b_unit_start, ts_90khz_t i_append_pcr )
 {
     bool b_ret = false;
 
@@ -167,7 +167,7 @@ bool ts_pes_Drain( ts_pes_parse_callback *cb, ts_stream_t *p_pes )
 bool ts_pes_Gather( ts_pes_parse_callback *cb,
                     ts_stream_t *p_pes, block_t *p_pkt,
                     bool b_unit_start, bool b_valid_scrambling,
-                    stime_t i_append_pcr )
+                    ts_90khz_t i_append_pcr )
 {
     bool b_ret = false;
     bool b_single_payload = b_unit_start; /* Single payload in case of unit start */


=====================================
modules/demux/mpeg/ts_pes.h
=====================================
@@ -20,6 +20,8 @@
 #ifndef VLC_TS_PES_H
 #define VLC_TS_PES_H
 
+#include "timestamps.h"
+
 #define BLOCK_FLAG_PRIVATE_SOURCE_RANDOM_ACCESS (1 << BLOCK_FLAG_PRIVATE_SHIFT)
 #define BLOCK_FLAG_PRIVATE_PACKET_LOSS          (2 << BLOCK_FLAG_PRIVATE_SHIFT)
 
@@ -29,7 +31,7 @@ typedef struct
 {
     vlc_object_t *p_obj;
     void *priv;
-    void(*pf_parse)(vlc_object_t *, void *, block_t *, uint32_t, stime_t );
+    void(*pf_parse)(vlc_object_t *, void *, block_t *, uint32_t, ts_90khz_t );
 } ts_pes_parse_callback;
 
 bool ts_pes_Drain( ts_pes_parse_callback *cb, ts_stream_t *p_pes );
@@ -37,7 +39,7 @@ bool ts_pes_Drain( ts_pes_parse_callback *cb, ts_stream_t *p_pes );
 bool ts_pes_Gather( ts_pes_parse_callback *cb,
                     ts_stream_t *p_pes, block_t *p_pkt,
                     bool b_unit_start, bool b_valid_scrambling,
-                    stime_t i_append_pcr );
+                    ts_90khz_t i_append_pcr );
 
 
 #endif


=====================================
modules/demux/mpeg/ts_psi.c
=====================================
@@ -2227,11 +2227,11 @@ static void PMTCallBack( void *data, dvbpsi_pmt_t *p_dvbpsipmt )
     UpdatePESFilters( p_demux, p_sys->seltype == PROGRAM_ALL );
 
     /* Probe Boundaries */
-    if( p_sys->b_canfastseek && p_pmt->i_last_dts == TS_TICK_UNKNOWN )
+    if( p_sys->b_canfastseek && !p_pmt->b_last_dts_probed )
     {
-        p_pmt->i_last_dts = 0;
         ProbeStart( p_demux, p_pmt->i_number );
         ProbeEnd( p_demux, p_pmt->i_number );
+        p_pmt->b_last_dts_probed = true;
     }
 
     dvbpsi_pmt_delete( p_dvbpsipmt );


=====================================
modules/demux/mpeg/ts_scte.c
=====================================
@@ -61,10 +61,9 @@ void SCTE18_Section_Callback( dvbpsi_t *p_handle, const dvbpsi_psi_section_t* p_
                 continue;
 
             const ts_pmt_t *p_pmt = p_es->p_program;
-            const stime_t i_date = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->pcr.i_current );
             block_t *p_block = block_Alloc( p_section->p_payload_end - p_section->p_payload_start );
             memcpy( p_block->p_buffer, p_section->p_payload_start, i_payload );
-            p_block->i_dts = p_block->i_pts = FROM_SCALE( i_date );
+            p_block->i_dts = p_block->i_pts = p_pmt->pcr.i_current;
 
             es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, p_es->id, true );
             es_out_Send( p_demux->out, p_es->id, p_block );
@@ -81,7 +80,7 @@ void SCTE27_Section_Callback( demux_t *p_demux,
     ts_stream_t *p_pes = (ts_stream_t *) p_pes_cb_data;
     assert( p_pes->p_es->fmt.i_codec == VLC_CODEC_SCTE_27 );
     ts_pmt_t *p_pmt = p_pes->p_es->p_program;
-    stime_t i_date = p_pmt->pcr.i_current;
+    vlc_tick_t i_date = p_pmt->pcr.i_current;
 
     block_t *p_content = block_Alloc( i_sectiondata );
     if( unlikely(!p_content) || unlikely(!p_pes->p_es->id) )
@@ -102,9 +101,9 @@ void SCTE27_Section_Callback( demux_t *p_demux,
         bool is_immediate = p_content->p_buffer[i_offset + 3] & 0x40;
         if( !is_immediate )
         {
-            stime_t i_display_in = GetDWBE( &p_content->p_buffer[i_offset + 4] );
+            vlc_tick_t i_display_in = FROM_SCALE(GetDWBE( &p_content->p_buffer[i_offset + 4] ));
             if( i_display_in < i_date )
-                i_date = i_display_in + (1ll << 32);
+                i_date = i_display_in + FROM_SCALE_NZ(1ll << 32);
             else
                 i_date = i_display_in;
         }


=====================================
modules/demux/mpeg/ts_streams.c
=====================================
@@ -104,16 +104,17 @@ ts_pmt_t *ts_pmt_New( demux_t *p_demux )
     pmt->od.i_version = -1;
     ARRAY_INIT( pmt->od.objects );
 
-    pmt->i_last_dts = TS_TICK_UNKNOWN;
+    pmt->i_last_dts = VLC_TICK_INVALID;
     pmt->i_last_dts_byte = 0;
+    pmt->b_last_dts_probed = false;
 
     pmt->p_atsc_si_basepid      = NULL;
     pmt->p_si_sdt_pid = NULL;
 
-    pmt->pcr.i_current = TS_TICK_UNKNOWN;
-    pmt->pcr.i_first  = TS_TICK_UNKNOWN;
+    pmt->pcr.i_current = VLC_TICK_INVALID;
+    pmt->pcr.i_first  = VLC_TICK_INVALID;
     pmt->pcr.b_disable = false;
-    pmt->pcr.i_first_dts = TS_TICK_UNKNOWN;
+    pmt->pcr.i_first_dts = VLC_TICK_INVALID;
     pmt->pcr.i_pcroffset = -1;
 
     pmt->pcr.b_fix_done = false;
@@ -270,14 +271,14 @@ ts_stream_t *ts_stream_New( demux_t *p_demux, ts_pmt_t *p_program )
     pes->gather.pp_last = &pes->gather.p_data;
     pes->gather.i_saved = 0;
     pes->gather.i_block_flags = 0;
-    pes->gather.i_append_pcr = VLC_TICK_INVALID;
+    pes->gather.i_append_pcr = TS_90KHZ_INVALID;
     pes->b_broken_PUSI_conformance = false;
     pes->b_always_receive = false;
     pes->p_sections_proc = NULL;
     pes->p_proc = NULL;
     pes->prepcr.p_head = NULL;
     pes->prepcr.pp_last = &pes->prepcr.p_head;
-    pes->i_last_dts = -1;
+    pes->i_last_dts = VLC_TICK_INVALID;
 
     return pes;
 }


=====================================
modules/demux/mpeg/ts_streams_private.h
=====================================
@@ -59,11 +59,11 @@ struct ts_pmt_t
 
     struct
     {
-        stime_t i_current;
-        stime_t i_first; // seen <> != TS_TICK_UNKNOWN
+        vlc_tick_t i_current;
+        vlc_tick_t i_first; // seen <> != TS_TICK_UNKNOWN
         /* broken PCR handling */
-        stime_t i_first_dts;
-        stime_t i_pcroffset;
+        vlc_tick_t i_first_dts;
+        vlc_tick_t i_pcroffset;
         bool    b_disable; /* ignore PCR field, use dts */
         bool    b_fix_done;
     } pcr;
@@ -74,8 +74,9 @@ struct ts_pmt_t
         time_t i_event_length;
     } eit;
 
-    stime_t i_last_dts;
+    vlc_tick_t i_last_dts;
     uint64_t i_last_dts_byte;
+    bool b_last_dts_probed;
 
     /* CA */
     //en50221_capmt_info_t *capmt;
@@ -131,7 +132,7 @@ struct ts_stream_t
         block_t     **pp_last;
         uint8_t     saved[5];
         size_t      i_saved;
-        stime_t     i_append_pcr;
+        ts_90khz_t  i_append_pcr;
         uint32_t    i_block_flags;
     } gather;
 
@@ -146,7 +147,7 @@ struct ts_stream_t
         block_t **pp_last;
     } prepcr;
 
-    stime_t i_last_dts;
+    vlc_tick_t i_last_dts;
 };
 
 typedef struct ts_si_context_t ts_si_context_t;


=====================================
modules/demux/pva.c
=====================================
@@ -148,7 +148,7 @@ static int Demux( demux_t *p_demux )
     const uint8_t *p_peek;
     int         i_size;
     block_t     *p_frame;
-    int64_t     i_pts;
+    ts_90khz_t  i_pts;
     int         i_skip;
 
     if( vlc_stream_Peek( p_demux->s, &p_peek, 8 ) < 8 )
@@ -190,7 +190,7 @@ static int Demux( demux_t *p_demux )
             p_sys->i_vc = p_peek[3];
 
             /* read the PTS and potential extra bytes TODO: make it a bit more optimised */
-            i_pts = -1;
+            i_pts = TS_90KHZ_INVALID;
             i_skip = 8;
             if( p_peek[5]&0x10 )
             {
@@ -233,7 +233,7 @@ static int Demux( demux_t *p_demux )
             {
                 p_frame->p_buffer += i_skip;
                 p_frame->i_buffer -= i_skip;
-                if( i_pts >= 0 )
+                if( i_pts != TS_90KHZ_INVALID )
                     p_frame->i_pts = FROM_SCALE(i_pts);
                 block_ChainAppend( &p_sys->p_es, p_frame );
             }
@@ -399,8 +399,8 @@ static void ParsePES( demux_t *p_demux )
     uint8_t     hdr[30];
 
     unsigned    i_skip;
-    stime_t     i_dts = -1;
-    stime_t     i_pts = -1;
+    ts_90khz_t  i_dts = TS_90KHZ_INVALID;
+    ts_90khz_t  i_pts = TS_90KHZ_INVALID;
 
     p_sys->p_pes = NULL;
 
@@ -442,9 +442,9 @@ static void ParsePES( demux_t *p_demux )
     p_pes->i_buffer -= i_skip;
     p_pes->p_buffer += i_skip;
 
-    if( i_dts >= 0 )
+    if( i_dts != TS_90KHZ_INVALID )
         p_pes->i_dts = FROM_SCALE(i_dts);
-    if( i_pts >= 0 )
+    if( i_pts != TS_90KHZ_INVALID )
         p_pes->i_pts = FROM_SCALE(i_pts);
 
     /* Set PCR */


=====================================
modules/demux/ty.c
=====================================
@@ -565,7 +565,7 @@ static void Close( vlc_object_t *p_this )
  * Assume buf points to beginning of PTS */
 static vlc_tick_t get_pts( const uint8_t *buf )
 {
-    stime_t i_pts = GetPESTimestamp( buf );
+    ts_90khz_t i_pts = GetPESTimestamp( buf );
     return FROM_SCALE_NZ(i_pts); /* convert PTS (90Khz clock) to microseconds */
 }
 


=====================================
test/Makefile.am
=====================================
@@ -54,6 +54,7 @@ check_PROGRAMS = \
 	test_modules_packetizer_mpegvideo \
 	test_modules_codec_hxxx_helper \
 	test_modules_keystore \
+	test_modules_demux_timestamps \
 	test_modules_demux_timestamps_filter \
 	test_modules_demux_ts_pes \
 	test_modules_playlist_m3u \
@@ -254,6 +255,7 @@ test_modules_keystore_SOURCES = modules/keystore/test.c
 test_modules_keystore_LDADD = $(LIBVLCCORE) $(LIBVLC)
 test_modules_tls_SOURCES = modules/misc/tls.c
 test_modules_tls_LDADD = $(LIBVLCCORE) $(LIBVLC)
+test_modules_demux_timestamps_SOURCES = modules/demux/timestamps.c
 test_modules_demux_timestamps_filter_LDADD = $(LIBVLCCORE) $(LIBVLC)
 test_modules_demux_timestamps_filter_SOURCES = modules/demux/timestamps_filter.c
 test_modules_demux_ts_pes_LDADD = $(LIBVLCCORE) $(LIBVLC)


=====================================
test/modules/demux/timestamps.c
=====================================
@@ -0,0 +1,97 @@
+/*****************************************************************************
+ * timestamps.c:
+ *****************************************************************************
+ * Copyright © 2025 VideoLabs, VideoLAN and VLC Authors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_tick.h>
+
+#include "../../../modules/demux/mpeg/timestamps.h"
+
+#define ASSERT(a) do {\
+if(!(a)) { \
+        fprintf(stderr, "failed line %d\n", __LINE__); \
+        return 1; } \
+} while(0)
+
+int main(void)
+{
+    /* Should not wrap without reference */
+    vlc_tick_t ts1 = 50;
+    vlc_tick_t ts2 = TimeStampWrapAround(VLC_TICK_INVALID, ts1);
+    ASSERT(ts2 == ts1);
+
+    ts1 = TS_33BITS_ROLL_NZ * 3/4;
+    ts2 = TimeStampWrapAround(VLC_TICK_INVALID, ts1);
+    ASSERT(ts2 == ts1);
+
+    /* Should not wrap */
+    ts1 = VLC_TICK_0 + TS_33BITS_HALF_ROLL_NZ;
+    ts2 = TimeStampWrapAround(VLC_TICK_0, ts1);
+    ASSERT(ts2 == ts1);
+
+    ts1 = VLC_TICK_0 + TS_33BITS_ROLL_NZ;
+    ts2 = TimeStampWrapAround(VLC_TICK_0, ts1);
+    ASSERT(ts2 == ts1);
+
+    ts1 = VLC_TICK_0 + TS_33BITS_ROLL_NZ;
+    ts2 = TimeStampWrapAround(ts1, ts1);
+    ASSERT(ts2 == ts1);
+
+    ts1 = VLC_TICK_0 + TS_33BITS_ROLL_NZ;
+    ts2 = TimeStampWrapAround(ts1 - 100, ts1);
+    ASSERT(ts2 == ts1);
+
+    ts1 = VLC_TICK_0 + TS_33BITS_ROLL_NZ;
+    ts2 = TimeStampWrapAround(VLC_TICK_0 + TS_33BITS_HALF_ROLL_NZ, ts1);
+    ASSERT(ts2 == ts1);
+
+    /* Should wrap */
+    ts1 = VLC_TICK_0;
+    ts2 = TimeStampWrapAround(VLC_TICK_0 + TS_33BITS_HALF_ROLL_NZ, ts1);
+    ASSERT(ts2 > ts1);
+    ASSERT(ts2 == ts1 + TS_33BITS_ROLL_NZ);
+
+    ts1 = VLC_TICK_0;
+    ts2 = TimeStampWrapAround(VLC_TICK_0 + TS_33BITS_ROLL_NZ * 3/4, ts1);
+    ASSERT(ts2 == ts1 + TS_33BITS_ROLL_NZ);
+
+    ts1 = VLC_TICK_0;
+    ts2 = TimeStampWrapAround(VLC_TICK_0 + TS_33BITS_ROLL_NZ, ts1);
+    ASSERT(ts2 == ts1 + TS_33BITS_ROLL_NZ);
+
+    ts1 = VLC_TICK_0 + TS_33BITS_HALF_ROLL_NZ;
+    ts2 = TimeStampWrapAround(VLC_TICK_0 + TS_33BITS_ROLL_NZ, ts1);
+    ASSERT(ts2 == ts1 + TS_33BITS_ROLL_NZ);
+
+    /* Should wrap multiple times */
+    ts1 = VLC_TICK_0;
+    ts2 = TimeStampWrapAround(VLC_TICK_0 + TS_33BITS_ROLL_NZ * 2, ts1);
+    ASSERT(ts2 > ts1);
+    ASSERT(ts2 == ts1 + TS_33BITS_ROLL_NZ * 2);
+
+    ts1 = VLC_TICK_0 + TS_33BITS_HALF_ROLL_NZ;
+    ts2 = TimeStampWrapAround(VLC_TICK_0 + TS_33BITS_ROLL_NZ * 5, ts1);
+    ASSERT(ts2 > ts1);
+    ASSERT(ts2 == ts1 + TS_33BITS_ROLL_NZ * 5);
+
+    return 0;
+}


=====================================
test/modules/demux/ts_pes.c
=====================================
@@ -32,7 +32,7 @@
 #include "../../libvlc/test.h"
 
 static void Parse(vlc_object_t *obj, void *priv, block_t *data,
-                  uint32_t i_flags, stime_t t)
+                  uint32_t i_flags, ts_90khz_t t)
 {
     VLC_UNUSED(obj);
     VLC_UNUSED(t);


=====================================
test/modules/meson.build
=====================================
@@ -75,6 +75,12 @@ vlc_tests += {
 }
 endif
 
+vlc_tests += {
+    'name' : 'test_modules_demux_timestamps',
+    'sources' : files('demux/timestamps.c'),
+    'suite' : ['modules', 'test_modules']
+}
+
 vlc_tests += {
     'name' : 'test_modules_demux_timestamps_filter',
     'sources' : files('demux/timestamps_filter.c'),



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/0626b7cdc0ddae673595ef6c1a72a53377023e29...caf3ea133abc93da7d640fbec5243e3478c3e1d8

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


VideoLAN code repository instance


More information about the vlc-commits mailing list