[vlc-commits] [Git][videolan/vlc][master] 8 commits: demux: avi: exclude SPU from invalid timescale

Steve Lhomme (@robUx4) gitlab at videolan.org
Thu Apr 6 18:40:03 UTC 2023



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
60f17ae9 by Francois Cartegnie at 2023-04-06T18:25:00+00:00
demux: avi: exclude SPU from invalid timescale

refs #17475

- - - - -
4b8b108f by Francois Cartegnie at 2023-04-06T18:25:00+00:00
demux: avi: set XSUB source size

- - - - -
c56dcec0 by Francois Cartegnie at 2023-04-06T18:25:00+00:00
demux: avi: add defines

- - - - -
72955a1d by Francois Cartegnie at 2023-04-06T18:25:00+00:00
demux: avi: remove unused index member

- - - - -
b82343fe by Francois Cartegnie at 2023-04-06T18:25:00+00:00
demux: avi: reorder members

- - - - -
50dac82b by Francois Cartegnie at 2023-04-06T18:25:00+00:00
demux: avi: reduce loops variables scope

- - - - -
a2310c67 by Francois Cartegnie at 2023-04-06T18:25:00+00:00
demux: avi: remove stack array

- - - - -
bc5da140 by Francois Cartegnie at 2023-04-06T18:25:00+00:00
demux: avi: fix and improve Xsub

* Properly set timings on samples.
* Demux accordingly to timings
* Seek approximatively instead of reloading all samples.

refs #17475

- - - - -


2 changed files:

- modules/demux/avi/avi.c
- modules/demux/avi/libavi.h


Changes:

=====================================
modules/demux/avi/avi.c
=====================================
@@ -121,9 +121,8 @@ typedef struct
 
 typedef struct
 {
-    vlc_fourcc_t i_id;
-    uint32_t     i_flags;
     uint64_t     i_pos;
+    uint32_t     i_flags;
     uint32_t     i_length;
     uint64_t     i_lengthtotal;
 
@@ -145,8 +144,8 @@ typedef struct
     bool            b_activated;
     bool            b_eof;
 
-    unsigned int    i_rate;
-    unsigned int    i_scale;
+    unsigned int    i_rate; /* for audio & video */
+    unsigned int    i_scale;/* for audio & video */
     unsigned int    i_samplesize;
 
     struct bitmapinfoheader_properties bihprops;
@@ -158,6 +157,9 @@ typedef struct
 
     int             i_dv_audio_rate;
     es_out_id_t     *p_es_dv_audio;
+    /* SPU specific */
+    vlc_tick_t      i_last_embedded_endtime;
+    vlc_tick_t      i_next_embedded_time;
 
     /* Avi Index */
     avi_index_t     idx;
@@ -169,6 +171,17 @@ typedef struct
     unsigned int    i_blockno;
     unsigned int    i_blocksize;
 
+    struct
+    {
+        bool b_ok;
+
+        int64_t i_toread;
+
+        int64_t i_posf; /* where we will read :
+                       if i_idxposb == 0 : beginning of chunk (+8 to access data)
+                       else : point on data directly */
+    } demuxctx;
+
 } avi_track_t;
 
 typedef struct
@@ -224,6 +237,8 @@ static void AVI_IndexLoad    ( demux_t * );
 static void AVI_IndexCreate  ( demux_t * );
 
 static void AVI_ExtractSubtitle( demux_t *, unsigned int i_stream, avi_chunk_list_t *, avi_chunk_STRING_t * );
+static avi_track_t * AVI_GetVideoTrackForXsub( demux_sys_t * );
+static int AVI_SeekSubtitleTrack( demux_sys_t *, avi_track_t * );
 
 static void AVI_DvHandleAudio( demux_t *, avi_track_t *, block_t * );
 
@@ -267,6 +282,45 @@ static bool IsQNAPCodec(uint32_t biCompression)
     }
 }
 
+#define XSUB_HEADER_SIZE 0x1B
+static int ExtractXsubSampleInfo( const uint8_t *p_buf,
+                                  vlc_tick_t *pi_start, vlc_tick_t *pi_end )
+{
+    unsigned t[8];
+    char buffer[XSUB_HEADER_SIZE + 1];
+    memcpy( buffer, p_buf, XSUB_HEADER_SIZE );
+    buffer[XSUB_HEADER_SIZE] = '\0';
+    if( sscanf( buffer, "[%u:%2u:%2u.%3u-%u:%2u:%2u.%3u]",
+                &t[0], &t[1], &t[2], &t[3], &t[4], &t[5], &t[6], &t[7] ) != 8 )
+        return VLC_EGENERIC;
+    *pi_start = vlc_tick_from_sec( t[0] * 3600 + t[1] * 60 + t[2] ) +
+                                   VLC_TICK_FROM_MS(t[3]);
+    *pi_end = vlc_tick_from_sec( t[4] * 3600 + t[5] * 60 + t[6] ) +
+                                 VLC_TICK_FROM_MS(t[7]);
+    return VLC_SUCCESS;
+}
+
+static int AVI_PeekSample( stream_t *s, size_t i_skip,
+                           const uint8_t **pp_peek, size_t i_peek )
+{
+    ssize_t i_ret = vlc_stream_Peek( s, pp_peek, i_skip + i_peek );
+    *pp_peek += i_skip;
+    if ( i_ret < 0 || (size_t) i_ret != i_skip + i_peek )
+        return VLC_EGENERIC;
+    return VLC_SUCCESS;
+}
+
+static vlc_tick_t AVI_GetXsubSampleTimeAt( stream_t *s, uint64_t pos )
+{
+    const uint8_t *p_peek;
+    vlc_tick_t i_nzstart, i_nzend;
+    if( vlc_stream_Seek( s, pos ) != VLC_SUCCESS ||
+        AVI_PeekSample( s, 8, &p_peek, XSUB_HEADER_SIZE ) ||
+        ExtractXsubSampleInfo( p_peek, &i_nzstart, &i_nzend ) )
+        return VLC_TICK_INVALID;
+    return VLC_TICK_0 + i_nzstart;
+}
+
 /*****************************************************************************
  * Close: frees unused data
  *****************************************************************************/
@@ -484,11 +538,6 @@ static int Open( vlc_object_t * p_this )
         tk->i_samplesize = p_strh->i_samplesize;
         msg_Dbg( p_demux, "stream[%u] rate:%u scale:%u samplesize:%u",
                 i, tk->i_rate, tk->i_scale, tk->i_samplesize );
-        if( !tk->i_scale || !tk->i_rate || !(tk->i_rate * CLOCK_FREQ / tk->i_scale) )
-        {
-            free( tk );
-            continue;
-        }
 
         switch( p_strh->i_type )
         {
@@ -608,10 +657,12 @@ static int Open( vlc_object_t * p_this )
 
             case( AVIFOURCC_vids ):
             {
-                if( p_vids->p_bih->biCompression == VLC_FOURCC( 'D', 'X', 'S', 'B' ) )
+                if( p_vids->p_bih->biCompression == FOURCC_DXSB )
                 {
                    msg_Dbg( p_demux, "stream[%u] subtitles", i );
                    es_format_Init( &tk->fmt, SPU_ES, p_vids->p_bih->biCompression );
+                   tk->fmt.subs.spu.i_original_frame_width = p_vids->p_bih->biWidth;
+                   tk->fmt.subs.spu.i_original_frame_height = p_vids->p_bih->biHeight;
                    break;
                 }
 
@@ -629,7 +680,7 @@ static int Open( vlc_object_t * p_this )
                     !strncasecmp( (char*)&p_strh->i_handler, "XVID", 4 ) )
                 {
                     tk->fmt.i_codec           =
-                    tk->fmt.i_original_fourcc = VLC_FOURCC( 'X', 'V', 'I', 'D' );
+                    tk->fmt.i_original_fourcc = FOURCC_XVID;
                 }
 
                 if( IsQNAPCodec( p_vids->p_bih->biCompression ) )
@@ -693,6 +744,16 @@ static int Open( vlc_object_t * p_this )
                 free( tk );
                 continue;
         }
+
+        if( tk->fmt.i_cat != SPU_ES &&
+           (!tk->i_scale || !tk->i_rate || !(tk->i_rate * CLOCK_FREQ / tk->i_scale)) )
+        {
+            msg_Warn( p_demux, "stream[%u] has invalid timescale", i );
+            es_format_Clean(&tk->fmt);
+            free( tk );
+            continue;
+        }
+
         tk->fmt.i_id = i;
         if( p_strn && p_strn->p_str )
             tk->fmt.psz_description = FromACP( p_strn->p_str );
@@ -874,6 +935,17 @@ static block_t * ReadFrame( demux_t *p_demux, const avi_track_t *tk,
     if( i_osize == i_size - 1 )
         p_frame->i_buffer--;
 
+    if( tk->fmt.i_codec == FOURCC_DXSB && p_frame->i_buffer > XSUB_HEADER_SIZE )
+    {
+        vlc_tick_t i_start, i_end;
+        if( !ExtractXsubSampleInfo( p_frame->p_buffer, &i_start, &i_end ) )
+        {
+            p_frame->i_dts = p_frame->i_pts = VLC_TICK_0 + i_start;
+            if( i_end > i_start )
+                p_frame->i_length = i_start - i_end;
+        }
+    }
+
     if( tk->bihprops.i_stride > INT32_MAX - 3 )
     {
         p_frame->i_buffer = 0;
@@ -1002,31 +1074,16 @@ static void AVI_SendFrame( demux_t *p_demux, avi_track_t *tk, block_t *p_frame )
  *****************************************************************************
  * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
  *****************************************************************************/
-typedef struct
-{
-    bool b_ok;
-
-    int64_t i_toread;
-
-    int64_t i_posf; /* where we will read :
-                   if i_idxposb == 0 : beginning of chunk (+8 to access data)
-                   else : point on data directly */
-} avi_track_toread_t;
-
 static int Demux_Seekable( demux_t *p_demux )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
 
     unsigned int i_track_count = 0;
-    unsigned int i_track;
-    /* cannot be more than 100 stream (dcXX or wbXX) */
-    avi_track_toread_t toread[100];
-
 
     /* detect new selected/unselected streams */
-    for( i_track = 0; i_track < p_sys->i_track; i_track++ )
+    for( unsigned int i = 0; i < p_sys->i_track; i++ )
     {
-        avi_track_t *tk = p_sys->track[i_track];
+        avi_track_t *tk = p_sys->track[i];
         bool  b = false;
 
         es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, tk->p_es, &b );
@@ -1040,7 +1097,7 @@ static int Demux_Seekable( demux_t *p_demux )
         {
             if( p_sys->b_seekable)
             {
-                AVI_TrackSeek( p_demux, i_track, p_sys->i_time );
+                AVI_TrackSeek( p_demux, i, p_sys->i_time );
             }
             tk->b_activated = true;
         }
@@ -1072,29 +1129,46 @@ static int Demux_Seekable( demux_t *p_demux )
     p_sys->i_time += p_sys->i_read_increment;
 
     /* init toread */
-    for( i_track = 0; i_track < p_sys->i_track; i_track++ )
+    for( unsigned i = 0; i < p_sys->i_track; i++ )
     {
-        avi_track_t *tk = p_sys->track[i_track];
+        avi_track_t *tk = p_sys->track[i];
 
-        toread[i_track].b_ok = tk->b_activated && !tk->b_eof;
+        tk->demuxctx.b_ok = tk->b_activated && !tk->b_eof;
         if( tk->i_idxposc < tk->idx.i_size )
         {
-            toread[i_track].i_posf = tk->idx.p_entry[tk->i_idxposc].i_pos;
+            tk->demuxctx.i_posf = tk->idx.p_entry[tk->i_idxposc].i_pos;
            if( tk->i_idxposb > 0 )
            {
-                toread[i_track].i_posf += 8 + tk->i_idxposb;
+                tk->demuxctx.i_posf += 8 + tk->i_idxposb;
            }
         }
         else
         {
-            toread[i_track].i_posf = -1;
+            tk->demuxctx.i_posf = -1;
+        }
+
+        /* Xsub specific. There's no possible chunk/byte<->time */
+        if( tk->fmt.i_codec == FOURCC_DXSB )
+        {
+            /* load spu times */
+            if( tk->i_next_embedded_time == VLC_TICK_INVALID && tk->demuxctx.i_posf != -1 )
+                tk->i_next_embedded_time = AVI_GetXsubSampleTimeAt(
+                            p_demux->s, tk->idx.p_entry[ tk->i_idxposc].i_pos );
+            vlc_tick_t i_reftime = tk->i_next_embedded_time != VLC_TICK_INVALID
+                                 ? tk->i_next_embedded_time : tk->i_last_embedded_endtime;
+            if( i_reftime != VLC_TICK_INVALID &&
+                p_sys->i_time - i_reftime  >  VLC_TICK_FROM_SEC(-2) )
+                tk->demuxctx.i_toread = 1;
+            else
+                tk->demuxctx.i_toread = -1;
+            continue;
         }
 
         vlc_tick_t i_dpts = p_sys->i_time - AVI_GetPTS( tk );
 
         if( tk->i_samplesize )
         {
-            toread[i_track].i_toread = AVI_PTSToByte( tk, i_dpts );
+            tk->demuxctx.i_toread = AVI_PTSToByte( tk, i_dpts );
         }
         else if ( i_dpts > VLC_TICK_FROM_SEC(-2) ) /* don't send a too early dts (low fps video) */
         {
@@ -1105,40 +1179,40 @@ static int Demux_Seekable( demux_t *p_demux )
                  * That does not even work when reading amount < scale / rate */
                 i_chunks_count++;
             }
-            toread[i_track].i_toread = i_chunks_count;
+            tk->demuxctx.i_toread = i_chunks_count;
         }
         else
-            toread[i_track].i_toread = -1;
+            tk->demuxctx.i_toread = -1;
     }
 
     for( ;; )
     {
-        avi_track_t     *tk;
-        bool       b_done;
+        bool b_done = true;
         block_t         *p_frame;
-        int64_t i_pos;
-        unsigned int i;
+        int64_t i_pos = -1;
+        unsigned int i_track = 0;
 
         /* search for first chunk to be read */
-        for( i = 0, b_done = true, i_pos = -1; i < p_sys->i_track; i++ )
+        for( unsigned i = 0; i < p_sys->i_track; i++ )
         {
-            if( !toread[i].b_ok ||
+            avi_track_t *tk = p_sys->track[i];
+            if( !tk->demuxctx.b_ok ||
                 ( p_sys->b_fastseekable && p_sys->b_interleaved &&
-                  AVI_GetDPTS( p_sys->track[i], toread[i].i_toread ) <= -p_sys->i_read_increment ) )
+                  AVI_GetDPTS( tk, tk->demuxctx.i_toread ) <= -p_sys->i_read_increment ) )
             {
                 continue;
             }
 
-            if( toread[i].i_toread > 0 )
+            if( tk->demuxctx.i_toread > 0 )
             {
                 b_done = false; /* not yet finished */
 
-                if( toread[i].i_posf > 0 )
+                if( tk->demuxctx.i_posf > 0 )
                 {
-                    if( i_pos == -1 || i_pos > toread[i].i_posf )
+                    if( i_pos == -1 || i_pos > tk->demuxctx.i_posf )
                     {
                         i_track = i;
-                        i_pos = toread[i].i_posf;
+                        i_pos = tk->demuxctx.i_posf;
                     }
                 }
             }
@@ -1146,9 +1220,10 @@ static int Demux_Seekable( demux_t *p_demux )
 
         if( b_done )
         {
-            for( i = 0; i < p_sys->i_track; i++ )
+            for( unsigned i = 0; i < p_sys->i_track; i++ )
             {
-                if( toread[i].b_ok && toread[i].i_toread >= 0 )
+                const avi_track_t *tk = p_sys->track[i];
+                if( tk->demuxctx.b_ok && tk->demuxctx.i_toread >= 0 )
                     return VLC_DEMUXER_SUCCESS;
             }
             msg_Warn( p_demux, "all tracks have failed, exiting..." );
@@ -1204,11 +1279,10 @@ static int Demux_Seekable( demux_t *p_demux )
                 else
                 {
                     i_track = avi_pk.i_stream;
-                    tk = p_sys->track[i_track];
+                    avi_track_t *tk = p_sys->track[i_track];
 
                     /* add this chunk to the index */
                     avi_entry_t index;
-                    index.i_id     = avi_pk.i_fourcc;
                     index.i_flags  = AVI_GetKeyFlag(tk, avi_pk.i_peek);
                     index.i_pos    = avi_pk.i_pos;
                     index.i_length = avi_pk.i_size;
@@ -1217,7 +1291,7 @@ static int Demux_Seekable( demux_t *p_demux )
 
                     /* do we will read this data ? */
                     if( i_indexid >= 0 &&
-                        AVI_GetDPTS( tk, toread[i_track].i_toread ) > -p_sys->i_read_increment )
+                        AVI_GetDPTS( tk, tk->demuxctx.i_toread ) > -p_sys->i_read_increment )
                     {
                         tk->i_idxposc = (unsigned int) i_indexid;
                         tk->i_idxposb = 0;
@@ -1243,7 +1317,7 @@ static int Demux_Seekable( demux_t *p_demux )
         }
 
         /* Set the track to use */
-        tk = p_sys->track[i_track];
+        avi_track_t *tk = p_sys->track[i_track];
 
         size_t i_size;
         unsigned i_ck_remaining_bytes = tk->idx.p_entry[tk->i_idxposc].i_length -
@@ -1255,7 +1329,7 @@ static int Demux_Seekable( demux_t *p_demux )
             int64_t i_toread;
 
             /* remaining bytes to read inside the current read increment */
-            if( ( i_toread = toread[i_track].i_toread ) <= 0 )
+            if( ( i_toread = tk->demuxctx.i_toread ) <= 0 )
             {
                 if( tk->i_samplesize > 1 )
                 {
@@ -1283,11 +1357,15 @@ static int Demux_Seekable( demux_t *p_demux )
         {
             msg_Warn( p_demux, "failed reading data" );
             tk->b_eof = false;
-            toread[i_track].b_ok = false;
+            tk->demuxctx.b_ok = false;
             continue;
         }
 
-        p_frame->i_pts = VLC_TICK_0 + AVI_GetPTS( tk );
+        if( p_frame->i_pts == VLC_TICK_INVALID )
+            p_frame->i_pts = VLC_TICK_0 + AVI_GetPTS( tk );
+        else
+            tk->i_last_embedded_endtime = p_frame->i_pts + p_frame->i_length;
+
         if( tk->idx.p_entry[tk->i_idxposc].i_flags&AVIIF_KEYFRAME )
         {
             p_frame->i_flags = BLOCK_FLAG_TYPE_I;
@@ -1300,7 +1378,7 @@ static int Demux_Seekable( demux_t *p_demux )
         /* advance chunk/byte pointers */
         if( tk->i_samplesize )
         {
-            toread[i_track].i_toread -= i_size;
+            tk->demuxctx.i_toread -= i_size;
             tk->i_idxposb += i_size;
             if( tk->i_idxposb >=
                     tk->idx.p_entry[tk->i_idxposc].i_length )
@@ -1317,23 +1395,27 @@ static int Demux_Seekable( demux_t *p_demux )
             {
                 tk->i_blockno += tk->i_blocksize > 0 ? ( i_size + tk->i_blocksize - 1 ) / tk->i_blocksize : 1;
             }
-            toread[i_track].i_toread--;
+            else if( tk->fmt.i_cat == SPU_ES )
+            {
+                tk->i_next_embedded_time = VLC_TICK_INVALID;
+            }
+            tk->demuxctx.i_toread--;
         }
 
         /* check new chunk and set new read pos */
         if( tk->i_idxposc < tk->idx.i_size)
         {
-            toread[i_track].i_posf =
+            tk->demuxctx.i_posf =
                 tk->idx.p_entry[tk->i_idxposc].i_pos;
             if( tk->i_idxposb > 0 )
             {
-                toread[i_track].i_posf += 8 + tk->i_idxposb;
+                tk->demuxctx.i_posf += 8 + tk->i_idxposb;
             }
 
         }
         else /* all chunks read for this track */
         {
-            toread[i_track].i_posf = -1;
+            tk->demuxctx.i_posf = -1;
         }
 
         AVI_SendFrame( p_demux, tk, p_frame );
@@ -1452,7 +1534,11 @@ static int Demux_UnSeekable( demux_t *p_demux )
                 {
                     return VLC_DEMUXER_EGENERIC;
                 }
-                p_frame->i_pts = VLC_TICK_0 + AVI_GetPTS( p_stream );
+
+                if( p_frame->i_pts == VLC_TICK_INVALID )
+                    p_frame->i_pts = VLC_TICK_0 + AVI_GetPTS( p_stream );
+                else
+                    p_stream->i_last_embedded_endtime = p_frame->i_pts + p_frame->i_length;
 
                 AVI_SendFrame( p_demux, p_stream, p_frame );
             }
@@ -1476,6 +1562,7 @@ static int Demux_UnSeekable( demux_t *p_demux )
                     p_stream->i_blockno += p_stream->i_blocksize > 0 ? ( avi_pk.i_size + p_stream->i_blocksize - 1 ) / p_stream->i_blocksize : 1;
                 }
                 p_stream->i_idxposc++;
+                p_stream->i_next_embedded_time = VLC_TICK_INVALID;
             }
 
         }
@@ -1547,7 +1634,7 @@ static int Seek( demux_t *p_demux, vlc_tick_t i_date, double f_ratio, bool b_acc
             for( unsigned i = 0; i < p_sys->i_track; i++ )
             {
                 avi_track_t *p_track = p_sys->track[i];
-                if( !p_track->b_activated )
+                if( !p_track->b_activated || p_stream->fmt.i_cat == SPU_ES )
                     continue;
 
                 p_stream = p_track;
@@ -1905,7 +1992,6 @@ static int AVI_StreamChunkFind( demux_t *p_demux, avi_track_t *tk )
 
             /* add this chunk to the index */
             avi_entry_t index;
-            index.i_id     = avi_pk.i_fourcc;
             index.i_flags  = AVI_GetKeyFlag(tk_pk, avi_pk.i_peek);
             index.i_pos    = avi_pk.i_pos;
             index.i_length = avi_pk.i_size;
@@ -2018,6 +2104,9 @@ static int AVI_TrackSeek( demux_t *p_demux,
     avi_track_t  *tk = p_sys->track[i_stream];
     vlc_tick_t i_oldpts;
 
+    if( tk->fmt.i_cat == SPU_ES )
+        return AVI_SeekSubtitleTrack( p_sys, tk );
+
     i_oldpts = AVI_GetPTS( tk );
 
     if( !tk->i_samplesize )
@@ -2469,7 +2558,6 @@ static int AVI_IndexLoad_idx1( demux_t *p_demux,
             (i_cat == p_sys->track[i_stream]->fmt.i_cat || i_cat == UNKNOWN_ES ) )
         {
             avi_entry_t index;
-            index.i_id     = p_idx1->entry[i_index].i_fourcc;
             index.i_flags  = p_idx1->entry[i_index].i_flags&(~AVIIF_FIXKEYFRAME);
             index.i_pos    = p_idx1->entry[i_index].i_pos + i_offset;
             index.i_length = p_idx1->entry[i_index].i_length;
@@ -2515,7 +2603,6 @@ static void __Parse_indx( demux_t *p_demux, avi_index_t *p_index, uint64_t *pi_m
     {
         for( unsigned i = 0; i < p_indx->i_entriesinuse; i++ )
         {
-            index.i_id     = p_indx->i_id;
             index.i_flags  = p_indx->idx.std[i].i_size & 0x80000000 ? 0 : AVIIF_KEYFRAME;
             index.i_pos    = p_indx->i_baseoffset + p_indx->idx.std[i].i_offset - 8;
             index.i_length = p_indx->idx.std[i].i_size&0x7fffffff;
@@ -2528,7 +2615,6 @@ static void __Parse_indx( demux_t *p_demux, avi_index_t *p_index, uint64_t *pi_m
     {
         for( unsigned i = 0; i < p_indx->i_entriesinuse; i++ )
         {
-            index.i_id     = p_indx->i_id;
             index.i_flags  = p_indx->idx.field[i].i_size & 0x80000000 ? 0 : AVIIF_KEYFRAME;
             index.i_pos    = p_indx->i_baseoffset + p_indx->idx.field[i].i_offset - 8;
             index.i_length = p_indx->idx.field[i].i_size;
@@ -2730,7 +2816,6 @@ static void AVI_IndexCreate( demux_t *p_demux )
             avi_track_t *tk = p_sys->track[pk.i_stream];
 
             avi_entry_t index;
-            index.i_id      = pk.i_fourcc;
             index.i_flags   = AVI_GetKeyFlag(tk, pk.i_peek);
             index.i_pos     = pk.i_pos;
             index.i_length  = pk.i_size;
@@ -2909,6 +2994,22 @@ static void AVI_DvHandleAudio( demux_t *p_demux, avi_track_t *tk, block_t *p_fra
 /*****************************************************************************
  * Subtitles
  *****************************************************************************/
+/*
+    Subtitles only exists in a hackish way as AVI is samples rate based and
+    does not provide any way for variable rate or variable duration content.
+
+    Only 2 subtitles formats:
+    - Xsub from DivX
+        Samples are prefixed with a text formatted timestamp containing
+        start and stop times.
+        Track index provides sample location.
+        Chunk position is meaningless. All timings are within sample.
+    * - Txt
+        Samples are stored in a similar format as Xsub, but regrouped in a
+        single sample with metadata header. Location found using track index.
+        Those are extracted and exposed as attachment.
+        This track has to be ignored for playback.
+*/
 static void AVI_ExtractSubtitle( demux_t *p_demux,
                                  unsigned int i_stream,
                                  avi_chunk_list_t *p_strl,
@@ -3041,6 +3142,56 @@ exit:
     if( p_indx == &ck.indx )
         AVI_ChunkClean( p_demux->s, &ck );
 }
+
+static avi_track_t * AVI_GetVideoTrackForXsub( demux_sys_t *p_sys )
+{
+    for( unsigned i = 0; i < p_sys->i_track; i++ )
+    {
+        avi_track_t *p_stream = p_sys->track[i];
+        if( p_stream->b_activated &&
+            p_stream->fmt.i_cat == VIDEO_ES &&
+            p_stream->idx.i_size )
+            return p_stream;
+    }
+    return NULL;
+}
+
+static int AVI_SeekSubtitleTrack( demux_sys_t *p_sys, avi_track_t *tk )
+{
+    if( tk->fmt.i_codec != FOURCC_DXSB )
+        return VLC_EGENERIC;
+
+    const avi_track_t *p_videotk = AVI_GetVideoTrackForXsub( p_sys );
+    if( !p_videotk )
+        return VLC_EGENERIC;
+    /* Seek into SPU index using video track */
+
+    unsigned idx = p_videotk->i_idxposc < p_videotk->idx.i_size
+            ? p_videotk->i_idxposc : p_videotk->idx.i_size - 1;
+    uint64_t i_pos = p_videotk->idx.p_entry[idx].i_pos;
+    /* invalidate sample timestamp */
+    tk->i_next_embedded_time = VLC_TICK_INVALID;
+    tk->i_last_embedded_endtime = VLC_TICK_INVALID;
+    for( tk->i_idxposc = 0; tk->i_idxposc<tk->idx.i_size; tk->i_idxposc++ )
+    {
+        /* match next pos, or closest -256KB sample */
+        if( tk->idx.p_entry[tk->i_idxposc].i_pos > i_pos )
+        {
+            tk->b_eof = false;
+            break;
+        }
+        else if( tk->idx.p_entry[tk->i_idxposc].i_pos + (1 << 28) <= i_pos )
+        {
+            tk->b_eof = false;
+        }
+    }
+    tk->b_eof = tk->i_idxposc == tk->idx.i_size;
+    if(!tk->b_eof)
+        tk->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
+
+    return VLC_SUCCESS;
+}
+
 /*****************************************************************************
  * Stream management
  *****************************************************************************/


=====================================
modules/demux/avi/libavi.h
=====================================
@@ -364,6 +364,10 @@ int     AVI_ChunkFetchIndexes( stream_t *, avi_chunk_t *p_riff );
 
 /* *** codex stuff ***  */
 
+/* DivX */
+#define FOURCC_DXSB         VLC_FOURCC('D','X','S','B') /* xsub */
+#define FOURCC_XVID         VLC_FOURCC('X','V','I','D')
+
 /* DV */
 #define FOURCC_dvsd         VLC_FOURCC('d','v','s','d')
 #define FOURCC_dvhd         VLC_FOURCC('d','v','h','d')



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/d91e21aec08133479a38686a1ace84638731a4f4...bc5da1405ea79e7b29f531323e2a440fda24bb4f

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/d91e21aec08133479a38686a1ace84638731a4f4...bc5da1405ea79e7b29f531323e2a440fda24bb4f
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