François Cartegnie pushed to branch 3.0.x at VideoLAN / VLC
Commits:
7d68e75e by Alexandre Janniaux at 2026-01-21T14:52:48+01:00
demux: subtitle: add VLC_TICK_0 to timings
Currently, 0 is mapped to VLC_TICK_INVALID. Since the timings will be
reported as-is to the core afterwards, they need to start at VLC_TICK_0.
It won't change the duration, given that i_start - i_stop removes the
constant VLC_TICK_0 offset.
(adapted from commit f97cfab41ccdddc7ef6209ff70a0678eb38b31e1)
- - - - -
aeb7f161 by Alexandre Janniaux at 2026-01-21T14:52:48+01:00
demux: subtitle: refactor timing parsing
Refactor the parsing of timing values so that it doesn't allocate
memory. Indeed, we just need to parse the input string to locate where
the delimiter is, and call the parsing of timing values on each side of
the arrow.
We also use a length validation defensively, despite the current
impossibility to parse characters like `-->` in the sscanf given the
current format, to ensure that future addition here would not try to
parse additional characters and to ensure that future usage of the
function on strings that were not splitted by such character before
(eg. 0:0:0, 0 limited on 6 characters) don't impact the parsing.
(adapted from commit c76b8a42d216382b12e2de5d9cfd4d34a04a7431)
- - - - -
0b492bbf by Alexandre Janniaux at 2026-01-21T14:52:48+01:00
demux: subtitle: improve parsing of sized string
As mentioned in previous commit, it's possible for the value parsing
function to be called on a shorter string of a longer valid format,
which should not be parsed in that case.
The problem didn't arise previously because the function could only be
called on null-terminated strings and those strings were only built from
the split of ` --> ` into two strings.
(cherry picked from commit c74180ad548326e2c7537c0e0d0df19aecdde3af)
- - - - -
d6e06d83 by Steve Lhomme at 2026-01-21T14:52:48+01:00
demux/subtitles: set i_stop to VLC_TICK_INVALID when it's not known
This is the expected value in that case.
(adapted from commit b93fe566a928113d806b6d64582214b9d634a540)
- - - - -
68eb7869 by François Cartegnie at 2026-01-21T14:52:48+01:00
demux: subtitle: add HMS helper to avoid int overflows
(adapted from commit 2ef92b2aefcab1b4aa14a8a54d7321b6e2ac60ac)
- - - - -
1 changed file:
- modules/demux/subtitle.c
Changes:
=====================================
modules/demux/subtitle.c
=====================================
@@ -2,11 +2,12 @@
* subtitle.c: Demux for subtitle text files.
*****************************************************************************
* Copyright (C) 1999-2007 VLC authors and VideoLAN
- * $Id$
+ * Copyright (C) 2023 Videolabs
*
* Authors: Laurent Aimar <[email protected]>
* Derk-Jan Hartman <hartman at videolan dot org>
* Jean-Baptiste Kempf <[email protected]>
+ * Alexandre Janniaux <[email protected]>
*
* 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
@@ -250,6 +251,11 @@ static int Control( demux_t *, int, va_list );
static void Fix( demux_t * );
static char * get_language_from_filename( const char * );
+static vlc_tick_t vlc_tick_from_HMS( int h, int m, int s )
+{
+ return vlc_tick_from_sec(h * INT64_C(3600) + m * INT64_C(60) + s);
+}
+
/*****************************************************************************
* Decoder format output function
*****************************************************************************/
@@ -880,7 +886,7 @@ static int Demux( demux_t *p_demux )
{
p_block->i_dts =
p_block->i_pts = VLC_TICK_0 + p_subtitle->i_start;
- if( p_subtitle->i_stop >= 0 && p_subtitle->i_stop >=
p_subtitle->i_start )
+ if( p_subtitle->i_stop != VLC_TICK_INVALID &&
p_subtitle->i_stop >= p_subtitle->i_start )
p_block->i_length = p_subtitle->i_stop -
p_subtitle->i_start;
es_out_Send( p_demux->out, p_sys->es, p_block );
@@ -1043,7 +1049,7 @@ static int ParseMicroDvd( vlc_object_t *p_obj,
subs_properties_t *p_props,
/* */
p_subtitle->i_start = VLC_TICK_0 + i_start * p_props->i_microsecperframe;
- p_subtitle->i_stop = i_stop >= 0 ? (VLC_TICK_0 + i_stop *
p_props->i_microsecperframe) : -1;
+ p_subtitle->i_stop = i_stop >= 0 ? (VLC_TICK_0 + i_stop *
p_props->i_microsecperframe) : VLC_TICK_INVALID;
p_subtitle->psz_text = psz_text;
return VLC_SUCCESS;
}
@@ -1130,24 +1136,33 @@ static int ParseSubRipSubViewer( vlc_object_t *p_obj,
subs_properties_t *p_props
* Parses SubRip timing value.
*/
static int subtitle_ParseSubRipTimingValue(int64_t *timing_value,
- const char *s)
+ const char *s, size_t length)
{
int h1, m1, s1, d1 = 0;
- if ( sscanf( s, "%d:%d:%d,%d",
- &h1, &m1, &s1, &d1 ) == 4 ||
- sscanf( s, "%d:%d:%d.%d",
- &h1, &m1, &s1, &d1 ) == 4 ||
- sscanf( s, "%d:%d:%d",
- &h1, &m1, &s1) == 3 )
- {
- (*timing_value) = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1) +
- VLC_TICK_FROM_MS( d1 );
+ int count;
+ if (sscanf(s, "%d:%d:%d,%d%n", &h1, &m1, &s1, &d1, &count) == 4
+ && (size_t)count <= length)
+ goto success;
- return VLC_SUCCESS;
- }
+ if (sscanf(s, "%d:%d:%d.%d%n", &h1, &m1, &s1, &d1, &count) == 4
+ && (size_t)count <= length)
+ goto success;
+
+ d1 = 0;
+ if (sscanf(s, "%d:%d:%d%n", &h1, &m1, &s1, &count) == 3
+ && (size_t)count <= length)
+ goto success;
return VLC_EGENERIC;
+
+success:
+ (*timing_value) = VLC_TICK_0
+ + vlc_tick_from_HMS(h1, m1, s1)
+ + VLC_TICK_FROM_MS(d1);
+
+ return VLC_SUCCESS;
+
}
/* subtitle_ParseSubRipTiming
@@ -1156,23 +1171,18 @@ static int subtitle_ParseSubRipTimingValue(int64_t
*timing_value,
static int subtitle_ParseSubRipTiming( subtitle_t *p_subtitle,
const char *s )
{
- int i_result = VLC_EGENERIC;
- char *psz_start, *psz_stop;
- psz_start = malloc( strlen(s) + 1 );
- psz_stop = malloc( strlen(s) + 1 );
-
- if( sscanf( s, "%s --> %s", psz_start, psz_stop) == 2 &&
- subtitle_ParseSubRipTimingValue( &p_subtitle->i_start, psz_start ) ==
VLC_SUCCESS &&
- subtitle_ParseSubRipTimingValue( &p_subtitle->i_stop, psz_stop ) ==
VLC_SUCCESS )
- {
- i_result = VLC_SUCCESS;
- }
+ const char *delimiter = strstr(s, " --> ");
+ if (delimiter == NULL || delimiter == s)
+ return VLC_EGENERIC;
- free(psz_start);
- free(psz_stop);
+ int ret = subtitle_ParseSubRipTimingValue(&p_subtitle->i_start, s,
(size_t)(delimiter - s));
+ if (ret != VLC_SUCCESS)
+ return ret;
- return i_result;
+ const char *right = delimiter + strlen(" --> ");
+ return subtitle_ParseSubRipTimingValue(&p_subtitle->i_stop, right,
strlen(right));
}
+
/* ParseSubRip
*/
static int ParseSubRip( vlc_object_t *p_obj, subs_properties_t *p_props,
@@ -1195,15 +1205,14 @@ static int subtitle_ParseSubViewerTiming( subtitle_t
*p_subtitle,
if( sscanf( s, "%d:%d:%d.%d,%d:%d:%d.%d",
&h1, &m1, &s1, &d1, &h2, &m2, &s2, &d2) == 8 )
- {
- p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1) +
- VLC_TICK_FROM_MS( d1 ) + VLC_TICK_0;
+ return VLC_EGENERIC;
- p_subtitle->i_stop = vlc_tick_from_sec( h2 * 3600 + m2 * 60 + s2 ) +
- VLC_TICK_FROM_MS( d2 ) + VLC_TICK_0;
- return VLC_SUCCESS;
- }
- return VLC_EGENERIC;
+ p_subtitle->i_start = vlc_tick_from_HMS( h1, m1, s1 ) +
+ VLC_TICK_FROM_MS( d1 ) + VLC_TICK_0;
+
+ p_subtitle->i_stop = vlc_tick_from_HMS( h2, m2, s2 ) +
+ VLC_TICK_FROM_MS( d2 ) + VLC_TICK_0;
+ return VLC_SUCCESS;
}
/* ParseSubViewer
@@ -1285,9 +1294,9 @@ static int ParseSSA( vlc_object_t *p_obj,
subs_properties_t *p_props,
psz_text = psz_temp;
}
- p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1
) +
+ p_subtitle->i_start = vlc_tick_from_HMS( h1, m1, s1 ) +
VLC_TICK_FROM_MS( c1 * 10 ) + VLC_TICK_0;
- p_subtitle->i_stop = vlc_tick_from_sec( h2 * 3600 + m2 * 60 + s2
) +
+ p_subtitle->i_stop = vlc_tick_from_HMS( h2, m2, s2 ) +
VLC_TICK_FROM_MS( c2 * 10 ) + VLC_TICK_0;
p_subtitle->psz_text = psz_text;
return VLC_SUCCESS;
@@ -1337,7 +1346,7 @@ static int ParseVplayer( vlc_object_t *p_obj,
subs_properties_t *p_props,
if( sscanf( s, "%d:%d:%d%*c%[^\r\n]",
&h1, &m1, &s1, psz_text ) == 4 )
{
- p_subtitle->i_start = VLC_TICK_0 + vlc_tick_from_sec( h1 * 3600 +
m1 * 60 + s1 );
+ p_subtitle->i_start = VLC_TICK_0 + vlc_tick_from_HMS( h1, m1, s1 );
p_subtitle->i_stop = -1;
break;
}
@@ -1498,7 +1507,7 @@ static int ParseDVDSubtitle(vlc_object_t *p_obj,
subs_properties_t *p_props,
"{T %d:%d:%d:%d",
&h1, &m1, &s1, &c1 ) == 4 )
{
- p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1
) +
+ p_subtitle->i_start = vlc_tick_from_HMS( h1, m1, s1 ) +
VLC_TICK_FROM_MS( c1 * 10 ) + VLC_TICK_0;
p_subtitle->i_stop = -1;
break;
@@ -1571,7 +1580,7 @@ static int ParseMPL2(vlc_object_t *p_obj,
subs_properties_t *p_props,
sscanf( s, "[%d][%d] %[^\r\n]", &i_start, &i_stop, psz_text ) == 3)
{
p_subtitle->i_start = VLC_TICK_0 + VLC_TICK_FROM_MS(i_start * 100);
- p_subtitle->i_stop = i_stop >= 0 ? VLC_TICK_0 +
VLC_TICK_FROM_MS(i_stop * 100) : -1;
+ p_subtitle->i_stop = i_stop >= 0 ? VLC_TICK_0 +
VLC_TICK_FROM_MS(i_stop * 100) : VLC_TICK_INVALID;
break;
}
free( psz_text );
@@ -1679,8 +1688,8 @@ static int ParsePJS(vlc_object_t *p_obj,
subs_properties_t *p_props,
if( sscanf (s, "%d,%d,\"%[^\n\r]", &t1, &t2, psz_text ) == 3 )
{
/* 1/10th of second ? Frame based ? FIXME */
- p_subtitle->i_start = VLC_TICK_0 + 10 * t1;
- p_subtitle->i_stop = VLC_TICK_0 + 10 * t2;
+ p_subtitle->i_start = VLC_TICK_0 + INT64_C(10) * t1;
+ p_subtitle->i_stop = VLC_TICK_0 + INT64_C(10) * t2;
/* Remove latest " */
psz_text[ strlen(psz_text) - 1 ] = '\0';
@@ -1825,10 +1834,10 @@ static int ParseJSS( vlc_object_t *p_obj,
subs_properties_t *p_props,
if( sscanf( s, "%d:%d:%d.%d %d:%d:%d.%d %[^\n\r]",
&h1, &m1, &s1, &f1, &h2, &m2, &s2, &f2, psz_text ) == 9 )
{
- p_subtitle->i_start = VLC_TICK_0 + vlc_tick_from_sec( ( h1 *3600 +
m1 * 60 + s1 ) +
- (int64_t)( ( f1 + p_props->jss.i_time_shift ) /
p_props->jss.i_time_resolution ) );
- p_subtitle->i_stop = VLC_TICK_0 + vlc_tick_from_sec( ( h2 *3600 +
m2 * 60 + s2 ) +
- (int64_t)( ( f2 + p_props->jss.i_time_shift ) /
p_props->jss.i_time_resolution ) );
+ p_subtitle->i_start = VLC_TICK_0 + vlc_tick_from_HMS( h1, m1, s1 )
+
+ vlc_tick_from_sec( ( f1 + p_props->jss.i_time_shift ) /
p_props->jss.i_time_resolution );
+ p_subtitle->i_stop = VLC_TICK_0 + vlc_tick_from_HMS( h2, m2, s2 ) +
+ vlc_tick_from_sec( ( f2 + p_props->jss.i_time_shift ) /
p_props->jss.i_time_resolution );
break;
}
/* Short time lines */
@@ -1886,7 +1895,7 @@ static int ParseJSS( vlc_object_t *p_obj,
subs_properties_t *p_props,
sscanf( &psz_text[shift], "%d.%d", &sec, &f);
sec *= inv;
}
- p_props->jss.i_time_shift = ( ( h * 3600 + m * 60 + sec )
+ p_props->jss.i_time_shift = ( ( h * INT64_C(3600) + m *
INT64_C(60) + sec )
* p_props->jss.i_time_resolution + f ) * inv;
}
break;
@@ -2062,8 +2071,8 @@ static int ParsePSB( vlc_object_t *p_obj,
subs_properties_t *p_props,
if( sscanf( s, "{%d:%d:%d}{%d:%d:%d}%[^\r\n]",
&h1, &m1, &s1, &h2, &m2, &s2, psz_text ) == 7 )
{
- p_subtitle->i_start = VLC_TICK_0 + vlc_tick_from_sec( h1 * 3600 +
m1 * 60 + s1 );
- p_subtitle->i_stop = VLC_TICK_0 + vlc_tick_from_sec( h2 * 3600 +
m2 * 60 + s2 );
+ p_subtitle->i_start = VLC_TICK_0 + vlc_tick_from_HMS( h1, m1, s1 );
+ p_subtitle->i_stop = VLC_TICK_0 + vlc_tick_from_HMS( h2, m2, s2 );
break;
}
free( psz_text );
@@ -2085,12 +2094,12 @@ static vlc_tick_t ParseRealTime( const char *psz )
int h, m, s, f;
if( sscanf( psz, "%d:%d:%d.%d", &h, &m, &s, &f ) == 4 )
{
- return vlc_tick_from_sec((( h * 60 + m ) * 60 ) + s )
+ return vlc_tick_from_HMS( h, m, s )
+ VLC_TICK_FROM_MS(f * 10) + VLC_TICK_0;
}
if( sscanf( psz, "%d:%d.%d", &m, &s, &f ) == 3 )
{
- return vlc_tick_from_sec(( m * 60 ) + s )
+ return vlc_tick_from_HMS( 0, m, s )
+ VLC_TICK_FROM_MS(f * 10) + VLC_TICK_0;
}
if( sscanf( psz, "%d.%d", &s, &f ) == 2 )
@@ -2100,7 +2109,7 @@ static vlc_tick_t ParseRealTime( const char *psz )
}
if( sscanf( psz, "%d:%d", &m, &s ) == 2 )
{
- return vlc_tick_from_sec(( m * 60 ) + s )
+ return vlc_tick_from_HMS( 0, m, s )
+ VLC_TICK_0;
}
if( sscanf( psz, "%d", &s ) == 1 )
@@ -2233,7 +2242,7 @@ static int ParseDKS( vlc_object_t *p_obj,
subs_properties_t *p_props,
if( sscanf( s, "[%d:%d:%d]%[^\r\n]",
&h1, &m1, &s1, psz_text ) == 4 )
{
- p_subtitle->i_start = VLC_TICK_0 + vlc_tick_from_sec( h1 * 3600 +
m1 * 60 + s1 );
+ p_subtitle->i_start = VLC_TICK_0 + vlc_tick_from_HMS( h1, m1, s1 );
s = TextGetLine( txt );
if( !s )
@@ -2243,7 +2252,7 @@ static int ParseDKS( vlc_object_t *p_obj,
subs_properties_t *p_props,
}
if( sscanf( s, "[%d:%d:%d]", &h2, &m2, &s2 ) == 3 )
- p_subtitle->i_stop = vlc_tick_from_sec(h2 * 3600 + m2 * 60 +
s2 );
+ p_subtitle->i_stop = vlc_tick_from_HMS( h2, m2, s2 );
else
p_subtitle->i_stop = -1;
break;
@@ -2282,7 +2291,7 @@ static int ParseSubViewer1( vlc_object_t *p_obj,
subs_properties_t *p_props,
if( sscanf( s, "[%d:%d:%d]", &h1, &m1, &s1 ) == 3 )
{
- p_subtitle->i_start = VLC_TICK_0 + vlc_tick_from_sec( h1 * 3600 +
m1 * 60 + s1 );
+ p_subtitle->i_start = VLC_TICK_0 + vlc_tick_from_HMS( h1, m1, s1 );
s = TextGetLine( txt );
if( !s )
@@ -2300,7 +2309,7 @@ static int ParseSubViewer1( vlc_object_t *p_obj,
subs_properties_t *p_props,
}
if( sscanf( s, "[%d:%d:%d]", &h2, &m2, &s2 ) == 3 )
- p_subtitle->i_stop = vlc_tick_from_sec( h2 * 3600 + m2 * 60 +
s2 );
+ p_subtitle->i_stop = vlc_tick_from_HMS( h2, m2, s2 );
else
p_subtitle->i_stop = -1;
@@ -2334,10 +2343,10 @@ static int ParseCommonSBV( vlc_object_t *p_obj,
subs_properties_t *p_props,
&h1, &m1, &s1, &d1,
&h2, &m2, &s2, &d2 ) == 8 )
{
- p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1
) +
+ p_subtitle->i_start = vlc_tick_from_HMS( h1, m1, s1 ) +
VLC_TICK_FROM_MS( d1 ) + VLC_TICK_0;
- p_subtitle->i_stop = vlc_tick_from_sec( h2 * 3600 + m2 * 60 + s2
) +
+ p_subtitle->i_stop = vlc_tick_from_HMS( h2, m2, s2 ) +
VLC_TICK_FROM_MS( d2 ) + VLC_TICK_0;
if( p_subtitle->i_start < p_subtitle->i_stop )
break;
@@ -2420,7 +2429,7 @@ static int ParseSCC( vlc_object_t *p_obj,
subs_properties_t *p_props,
continue;
/* convert everything to seconds */
- uint64_t i_frames = h * 3600 + m * 60 + s;
+ int64_t i_frames = h * INT64_C(3600) + m * INT64_C(60) + s;
if( c == ';' && p_rate->b_drop_allowed ) /* dropframe */
{
View it on GitLab:
https://code.videolan.org/videolan/vlc/-/compare/6d0deb89c44cfab819efbc2c5318b7cc39c8a50f...68eb7869748270ba72d58aafd37162d8f9ca994f
--
View it on GitLab:
https://code.videolan.org/videolan/vlc/-/compare/6d0deb89c44cfab819efbc2c5318b7cc39c8a50f...68eb7869748270ba72d58aafd37162d8f9ca994f
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance_______________________________________________
vlc-commits mailing list
[email protected]
https://mailman.videolan.org/listinfo/vlc-commits