[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