[vlc-commits] [Git][videolan/vlc][master] 9 commits: demux: nuv: restore position on failed index

François Cartegnie (@fcartegnie) gitlab at videolan.org
Mon Sep 28 04:00:34 UTC 2026



François Cartegnie pushed to branch master at VideoLAN / VLC


Commits:
17f65c0c by François Cartegnie at 2026-09-28T05:49:26+02:00
demux: nuv: restore position on failed index

refs samples/nuv/mythtvexample.nuv

- - - - -
7a0cb8c4 by François Cartegnie at 2026-09-28T05:49:26+02:00
demux: nuv: avoid negative pts on start

- - - - -
dc11ad77 by François Cartegnie at 2026-09-28T05:49:26+02:00
demux: nuv: move var to local scope

and fixes off by one in value

- - - - -
c95158d8 by François Cartegnie at 2026-09-28T05:49:26+02:00
demux: nuv: fix data offsets and signedness

fix #29664

- - - - -
6c40003e by François Cartegnie at 2026-09-28T05:49:26+02:00
demux: nuv: fix format strings

- - - - -
f4a1bcb4 by François Cartegnie at 2026-09-28T05:49:26+02:00
demux: nuv: check index values

- - - - -
1d712ec6 by François Cartegnie at 2026-09-28T05:49:26+02:00
demux: nuv: check seek table length

- - - - -
b6aa1fb5 by François Cartegnie at 2026-09-28T05:49:26+02:00
demux: nuv: check all header values

- - - - -
319b4211 by François Cartegnie at 2026-09-28T05:49:26+02:00
demux: nuv: check float arithmetic

- - - - -


1 changed file:

- modules/demux/nuv.c


Changes:

=====================================
modules/demux/nuv.c
=====================================
@@ -34,6 +34,7 @@
 #include <vlc_plugin.h>
 #include <vlc_demux.h>
 #include <limits.h>
+#include <stdckdint.h>
 
 /* TODO:
  *  - test
@@ -110,6 +111,7 @@ typedef struct
 } header_t;
 
 #define NUV_FH_SIZE 12
+#define NUV_TIMECODE_BASE CLOCK_FREQ
 typedef struct
 {
     char i_type;        /* A: audio, V: video, S: sync; T: test
@@ -187,7 +189,6 @@ typedef struct
     bool b_seekable;
     /* frameheader buffer */
     uint8_t fh_buffer[NUV_FH_SIZE];
-    int64_t i_total_frames;
     vlc_tick_t i_total_length;
     /* first frame position (used for calculating size without seektable) */
     uint64_t i_first_frame_offset;
@@ -224,7 +225,6 @@ static int Open( vlc_object_t * p_this )
     p_sys->p_extra_f = NULL;
     p_sys->i_pcr = -1;
     p_sys->b_index = false;
-    p_sys->i_total_frames = -1;
     p_sys->i_total_length = -1;
     demux_IndexInit( &p_sys->idx );
 
@@ -387,14 +387,13 @@ static int Demux( demux_t *p_demux )
     /* */
     if( ( p_data = vlc_stream_Block( p_demux->s, fh.i_length ) ) == NULL )
         return VLC_DEMUXER_EOF;
-
-    p_data->i_dts = VLC_TICK_0 + (int64_t)fh.i_timecode * 1000;
+    p_data->i_dts = VLC_TICK_0 + NUV_TIMECODE_BASE + (int64_t)fh.i_timecode * 1000;
     p_data->i_pts = (fh.i_type == 'V') ? VLC_TICK_INVALID : p_data->i_dts;
 
     /* only add keyframes to index */
     if( !fh.i_keyframe && !p_sys->b_index )
         demux_IndexAppend( &p_sys->idx,
-                           p_data->i_dts - VLC_TICK_0,
+                           (int64_t)fh.i_timecode * 1000,
                            vlc_stream_Tell(p_demux->s) - NUV_FH_SIZE );
 
     /* */
@@ -461,7 +460,8 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
 
             if( p_sys->i_total_length > 0 && p_sys->i_pcr >= 0 )
             {
-                *pf = (double)p_sys->i_pcr / (double)p_sys->i_total_length;
+                vlc_tick_t time = __MAX(p_sys->i_pcr - NUV_TIMECODE_BASE, 0);
+                *pf = __MIN((double)time / p_sys->i_total_length, 1.0);
             }
             else
             {
@@ -506,8 +506,12 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
             return ControlSetPosition(p_demux, offset, true);
         }
 
+        case DEMUX_GET_NORMAL_TIME:
+            *va_arg( args, vlc_tick_t * ) = VLC_TICK_0 + NUV_TIMECODE_BASE;
+            return VLC_SUCCESS;
+
         case DEMUX_GET_TIME:
-            *va_arg( args, vlc_tick_t * ) = __MAX(p_sys->i_pcr, 0);
+            *va_arg( args, vlc_tick_t * ) = __MAX(p_sys->i_pcr - NUV_TIMECODE_BASE, 0);
             return VLC_SUCCESS;
 
         case DEMUX_SET_TIME:
@@ -531,10 +535,10 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
             else if( vlc_stream_Tell( p_demux->s ) > p_sys->i_first_frame_offset )
             {
                 /* This should give an approximation of the total duration */
-                if (p_sys->i_pcr <= 0)
+                if (p_sys->i_pcr <= NUV_TIMECODE_BASE)
                     *va_arg( args, vlc_tick_t * ) = 0;
                 else
-                    *va_arg( args, vlc_tick_t * ) = p_sys->i_pcr *
+                    *va_arg( args, vlc_tick_t * ) = (p_sys->i_pcr - NUV_TIMECODE_BASE) *
                             (double)( stream_Size( p_demux->s ) - p_sys->i_first_frame_offset ) /
                             (double)( vlc_stream_Tell( p_demux->s ) - p_sys->i_first_frame_offset );
 
@@ -631,6 +635,18 @@ static inline void GetDoubleLE( double *pd, void *src )
 #endif
 }
 
+static bool FrameToTime( int64_t frame, double fps, vlc_tick_t *time )
+{
+    double value = (double)frame * (double)CLOCK_FREQ / fps;
+
+    if( !isfinite(value) || value < 0.0 || value >= (double)VLC_TICK_MAX ||
+        ( frame > 0 && value < 1.0 ) )
+        return false;
+
+    *time = (vlc_tick_t)value;
+    return true;
+}
+
 /* HeaderLoad:
  */
 static int HeaderLoad( demux_t *p_demux, header_t *h )
@@ -661,7 +677,20 @@ static int HeaderLoad( demux_t *p_demux, header_t *h )
              h->i_keyframe_distance );
 #endif
 
-    return (h->d_fps) ? VLC_SUCCESS : VLC_EGENERIC;
+    if( !isfinite(h->d_fps) || h->d_fps <= 0.0 ||
+        h->i_video_blocks < -1 || h->i_audio_blocks < -1 || h->i_text_blocks < -1 ||
+        h->i_keyframe_distance < 0 )
+        return VLC_EGENERIC;
+
+    if( h->i_video_blocks != 0 )
+    {
+        if( h->i_width <= 0 || h->i_height <= 0 ||
+            !isfinite(h->d_aspect) || h->d_aspect <= 0.0 ||
+            h->d_aspect >= (double)UINT_MAX / h->i_height )
+            return VLC_EGENERIC;
+    }
+
+    return VLC_SUCCESS;
 }
 
 /* FrameHeaderLoad:
@@ -746,46 +775,54 @@ static int ExtendedHeaderLoad( demux_t *p_demux, extended_header_t *h )
 static int SeekTableLoad( demux_t *p_demux, demux_sys_t *p_sys )
 {
     frame_header_t fh;
-    int64_t i_original_pos;
-    int64_t i_time, i_offset;
-    int keyframe, last_keyframe = 0, frame = 0, kfa_entry_id = 0;
+    uint64_t i_original_pos;
+    int32_t last_keyframe = 0, frame = 0, kfa_entry_id = 0;
+    int ret = VLC_EGENERIC;
 
     if( p_sys->exh.i_seektable_offset <= 0 )
         return VLC_SUCCESS;
 
-    /* Save current position */
-    i_original_pos = vlc_stream_Tell( p_demux->s );
+    i_original_pos = vlc_stream_Tell( p_demux->s ); /* Save current position */
 #if 0
-    msg_Dbg( p_demux, "current offset %"PRIi64, i_original_pos );
+    msg_Dbg( p_demux, "current offset %"PRIu64, i_original_pos );
 
     msg_Dbg( p_demux, "seeking in stream to %"PRIi64, p_sys->exh.i_seektable_offset );
 #endif
     if( vlc_stream_Seek( p_demux->s, p_sys->exh.i_seektable_offset ) )
-        return VLC_EGENERIC;
+        goto restore;
 
     if( FrameHeaderLoad( p_demux, &fh ) )
-        return VLC_EGENERIC;
+        goto restore;
 
-    if( fh.i_type != 'Q' )
+    if( fh.i_type != 'Q' || fh.i_length < 12 )
     {
         msg_Warn( p_demux, "invalid seektable, frame type=%c", fh.i_type );
-        return VLC_EGENERIC;
+        goto restore;
     }
 
     /* */
     uint8_t *p_seek_table = malloc( fh.i_length );
     if( p_seek_table == NULL )
-        return VLC_ENOMEM;
+    {
+        ret = VLC_ENOMEM;
+        goto restore;
+    }
 
     if( vlc_stream_Read( p_demux->s, p_seek_table,
                          fh.i_length ) != fh.i_length )
     {
         free( p_seek_table );
-        return VLC_EGENERIC;
+        goto restore;
     }
     const int32_t i_seek_elements = fh.i_length / 12;
 
     /* Get keyframe adjust offsets */
+    /*  typedef struct kfatable_entry
+        {
+            int adjust;
+            int keyframe_number;
+        }
+       https://github.com/MythTV/mythtv/blob/fixes/0.22/mythtv/libs/libmythtv/format.h */
     int32_t i_kfa_elements = 0;
     uint8_t *p_kfa_table = NULL;
 
@@ -795,13 +832,13 @@ static int SeekTableLoad( demux_t *p_demux, demux_sys_t *p_sys )
         if( vlc_stream_Seek( p_demux->s, p_sys->exh.i_keyframe_adjust_offset ) )
         {
             free( p_seek_table );
-            return VLC_EGENERIC;
+            goto restore;
         }
 
         if( FrameHeaderLoad( p_demux, &fh ) )
         {
             free( p_seek_table );
-            return VLC_EGENERIC;
+            goto restore;
         }
 
         if( fh.i_type == 'K' && fh.i_length >= 8 )
@@ -811,15 +848,15 @@ static int SeekTableLoad( demux_t *p_demux, demux_sys_t *p_sys )
             if( p_kfa_table == NULL )
             {
                 free( p_seek_table );
-                return VLC_ENOMEM;
+                ret = VLC_ENOMEM;
+                goto restore;
             }
 
             if( vlc_stream_Read( p_demux->s, p_kfa_table,
                                  fh.i_length ) != fh.i_length )
             {
-                free( p_seek_table );
-                free( p_kfa_table );
-                return VLC_EGENERIC;
+                ret = VLC_EGENERIC;
+                goto cleanup;
             }
 
             i_kfa_elements = fh.i_length / 8;
@@ -831,58 +868,103 @@ static int SeekTableLoad( demux_t *p_demux, demux_sys_t *p_sys )
 
     for( int32_t j = 0; j < i_seek_elements; j++)
     {
+        const uint8_t *p_seek_entry = p_seek_table + j * 12;
 #if 0
-        uint8_t* p = p_seek_table + j * 12;
+        const uint8_t* p = p_seek_entry;
         msg_Dbg( p_demux, "%x %x %x %x %x %x %x %x %x %x %x %x",
         p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11]);
 #endif
-        keyframe = GetDWLE( p_seek_table + j * 12 + 8 );
+        int32_t keyframe = GetDWLE( p_seek_entry + 8 );
+        int32_t keyframe_delta;
+        int32_t frame_delta;
+        int32_t next_frame;
+
+        if( keyframe < 0 || keyframe < last_keyframe ||
+            ckd_sub( &keyframe_delta, keyframe, last_keyframe ) ||
+            ckd_mul( &frame_delta, keyframe_delta,
+                     p_sys->hdr.i_keyframe_distance ) ||
+            ckd_add( &next_frame, frame, frame_delta ) )
+        {
+            msg_Warn( p_demux, "broken index with keyframe value %"PRIi32, keyframe );
+            ret = VLC_EGENERIC;
+            goto cleanup;
+        }
 
-        frame += (keyframe - last_keyframe) * p_sys->hdr.i_keyframe_distance;
+        frame = next_frame;
 
-        if( kfa_entry_id < i_kfa_elements && *(int32_t*)(p_kfa_table + kfa_entry_id * 12 + 4) == j )
+        if( kfa_entry_id < i_kfa_elements )
         {
-            frame -= *(int32_t*)(p_kfa_table + kfa_entry_id * 12);
-            msg_Dbg( p_demux, "corrected keyframe %d with current frame number %d (corrected with %d)",
-                        keyframe, frame, *(int32_t*)(p_kfa_table + kfa_entry_id * 12) );
-            kfa_entry_id++;
+            const uint8_t *p_kfa_entry = &p_kfa_table[kfa_entry_id * 8];
+            int32_t i_kfa_adjust = GetDWLE(p_kfa_entry + 0);
+            int32_t i_kfa_keyframe = GetDWLE(p_kfa_entry + 4);
+            if( i_kfa_keyframe == j )
+            {
+                if( i_kfa_adjust < frame )
+                {
+                    int32_t corrected_frame;
+
+                    if( ckd_sub( &corrected_frame, frame, i_kfa_adjust ) )
+                    {
+                        msg_Warn( p_demux, "broken keyframe adjustment" );
+                        ret = VLC_EGENERIC;
+                        goto cleanup;
+                    }
+
+                    frame = corrected_frame;
+                }
+                else
+                    frame = 0;
+                msg_Dbg( p_demux, "corrected keyframe %"PRIi32" with current frame number %"PRIi32" (corrected with %"PRIi32")",
+                         keyframe, frame, i_kfa_adjust );
+                kfa_entry_id++;
+            }
         }
 
-        i_offset = GetQWLE( p_seek_table + j * 12 );
+        int64_t i_offset = GetQWLE( p_seek_entry );
 
-        if( i_offset == 0 && frame != 0 )
-            msg_Dbg( p_demux, "invalid file offset %d %"PRIi64, keyframe, i_offset );
+        if( ( i_offset == 0 && frame != 0 ) || i_offset < 0 )
+            msg_Dbg( p_demux, "invalid file offset %"PRIi32" %"PRIi64, keyframe, i_offset );
         else
         {
-            i_time = (double)( (vlc_tick_t)frame * CLOCK_FREQ ) / p_sys->hdr.d_fps;
+            vlc_tick_t i_time;
+            if( !FrameToTime( frame, p_sys->hdr.d_fps, &i_time ) )
+            {
+                msg_Warn( p_demux, "index timestamp is out of range" );
+                ret = VLC_EGENERIC;
+                goto cleanup;
+            }
             demux_IndexAppend( &p_sys->idx, i_time , i_offset );
 #if 0
-            msg_Dbg( p_demux, "adding entry position %d %"PRIi64 " file offset %"PRIi64, keyframe, i_time, i_offset );
+            msg_Dbg( p_demux, "adding entry position %"PRIi32" %"PRIi64 " file offset %"PRIi64, keyframe, i_time, i_offset );
 #endif
         }
 
         last_keyframe = keyframe;
     }
 
-    p_sys->i_total_frames = (int64_t)frame;
+    p_sys->b_index = p_sys->idx.i_idx > 0;
 
-    p_sys->b_index = true;
-
-    p_sys->i_total_length = p_sys->i_total_frames * CLOCK_FREQ / p_sys->hdr.d_fps;
+    if( !FrameToTime( frame + INT64_C(1), p_sys->hdr.d_fps, &p_sys->i_total_length ) )
+    {
+        p_sys->i_total_length = -1;
+        msg_Warn( p_demux, "total time is out of range" );
+        goto cleanup;
+    }
 
-    msg_Dbg( p_demux, "index table loaded (%d elements)", i_seek_elements );
+    msg_Dbg( p_demux, "index table loaded (%"PRIi32" elements)", i_seek_elements );
 
-    if( i_kfa_elements )
-        free ( p_kfa_table );
+    ret = VLC_SUCCESS;
 
+cleanup:
+    free ( p_kfa_table );
     free ( p_seek_table );
 
+restore:
     /* Restore stream position */
     if( vlc_stream_Seek( p_demux->s, i_original_pos ) )
         return VLC_EGENERIC;
 
-    return VLC_SUCCESS;
-
+    return ret;
 }
 
 /*****************************************************************************/
@@ -899,7 +981,7 @@ static void demux_IndexClean( demux_index_t *p_idx )
     p_idx->idx = NULL;
 }
 static void demux_IndexAppend( demux_index_t *p_idx,
-                               int64_t i_time, int64_t i_offset )
+                               vlc_tick_t i_time, int64_t i_offset )
 {
     /* Be sure to append new entry (we don't insert point) */
     if( p_idx->i_idx > 0 && p_idx->idx[p_idx->i_idx-1].i_time >= i_time )



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/d92ec8c941d5af28e69b8f726d0116510145a462...319b4211854ee47b527cb914c00bd44423fa5656

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/d92ec8c941d5af28e69b8f726d0116510145a462...319b4211854ee47b527cb914c00bd44423fa5656
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help




More information about the vlc-commits mailing list