[vlc-commits] demux: webvtt: rewrite to handle overlaps

Francois Cartegnie git at videolan.org
Wed Dec 13 19:29:27 CET 2017


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Wed Dec 13 13:44:03 2017 +0100| [23d1f0087acab965f9d7fdac9ad1c5df20131ed7] | committer: Francois Cartegnie

demux: webvtt: rewrite to handle overlaps

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=23d1f0087acab965f9d7fdac9ad1c5df20131ed7
---

 modules/demux/webvtt.c | 244 +++++++++++++++++++++++++++++++++++--------------
 1 file changed, 174 insertions(+), 70 deletions(-)

diff --git a/modules/demux/webvtt.c b/modules/demux/webvtt.c
index 6ad9226f42..40eda6b294 100644
--- a/modules/demux/webvtt.c
+++ b/modules/demux/webvtt.c
@@ -36,6 +36,12 @@
  * Prototypes:
  *****************************************************************************/
 
+struct index_entry_s
+{
+    int64_t time;
+    unsigned active;
+};
+
 struct demux_sys_t
 {
     es_out_id_t *es;
@@ -55,54 +61,36 @@ struct demux_sys_t
         webvtt_cue_t *p_array;
         size_t  i_alloc;
         size_t  i_count;
-        size_t  i_current;
     } cues;
 
+    struct
+    {
+        struct index_entry_s *p_array;
+        size_t   i_alloc;
+        size_t   i_count;
+        size_t   i_current;
+    } index;
+
     webvtt_text_parser_t *p_streamparser;
 };
 
+#define WEBVTT_PREALLOC 64
+
 /*****************************************************************************
  *
  *****************************************************************************/
-static int cue_Compare( const void *a, const void *b )
-{
-    const mtime_t diff = ((webvtt_cue_t *)a)->i_start - ((webvtt_cue_t *)b)->i_start;
-    return (diff) ? diff / (( diff > 0 ) ? diff : -diff) : 0;
-}
-
-struct cue_searchkey
+static int cue_Compare( const void *a_, const void *b_ )
 {
-    webvtt_cue_t cue;
-    webvtt_cue_t *p_last;
-};
-
-static int cue_Bsearch_Compare( const void *key, const void *other )
-{
-    struct cue_searchkey *p_key = (struct cue_searchkey *) key;
-    webvtt_cue_t cue = *((webvtt_cue_t *) other);
-    p_key->p_last = (webvtt_cue_t *) other;
-    return cue_Compare( &p_key->cue, &cue );
-}
-
-static size_t cue_GetIndexByTime( demux_sys_t *p_sys, mtime_t i_time )
-{
-    size_t i_index = 0;
-    if( p_sys->cues.p_array )
+    webvtt_cue_t *a = (webvtt_cue_t *)a_;
+    webvtt_cue_t *b = (webvtt_cue_t *)b_;
+    if( a->i_start == b->i_start )
     {
-        struct cue_searchkey key;
-        key.cue.i_start = i_time;
-        key.p_last = NULL;
-
-        webvtt_cue_t *p_cue = bsearch( &key, p_sys->cues.p_array, p_sys->cues.i_count,
-                                      sizeof(webvtt_cue_t), cue_Bsearch_Compare );
-        if( p_cue )
-            key.p_last = p_cue;
-
-        i_index = (key.p_last - p_sys->cues.p_array);
-        if( cue_Compare( key.p_last, &key ) < 0 )
-            i_index++;
+        if( a->i_stop > b->i_stop )
+            return -1;
+        else
+            return ( a->i_stop < b->i_stop ) ? 1 : 0;
     }
-    return i_index;
+    else return a->i_start < b->i_start ? -1 : 1;
 }
 
 static block_t *ConvertWEBVTT( const webvtt_cue_t *p_cue, bool b_continued )
@@ -189,14 +177,15 @@ static webvtt_cue_t * ParserGetCueHandler( void *priv )
         return &p_sys->cues.p_array[p_sys->cues.i_count - 1];
     }
 
-    if( p_sys->cues.i_alloc <= p_sys->cues.i_count )
+    if( p_sys->cues.i_alloc <= p_sys->cues.i_count &&
+       (SIZE_MAX / sizeof(webvtt_cue_t)) - WEBVTT_PREALLOC > p_sys->cues.i_alloc )
     {
         webvtt_cue_t *p_realloc = realloc( p_sys->cues.p_array,
-                sizeof( webvtt_cue_t ) * ( p_sys->cues.i_alloc + 64 ) );
+                sizeof(webvtt_cue_t) * ( p_sys->cues.i_alloc + WEBVTT_PREALLOC ) );
         if( p_realloc )
         {
             p_sys->cues.p_array = p_realloc;
-            p_sys->cues.i_alloc += 64;
+            p_sys->cues.i_alloc += WEBVTT_PREALLOC;
         }
     }
 
@@ -221,6 +210,27 @@ static void ParserCueDoneHandler( void *priv, webvtt_cue_t *p_cue )
     if( p_sys->cues.i_count > 0 &&
         p_sys->cues.p_array[p_sys->cues.i_count - 1].i_start != p_cue->i_start )
         ctx->b_ordered = false;
+
+    /* Store timings */
+    if( p_sys->index.i_alloc <= p_sys->index.i_count &&
+       (SIZE_MAX / sizeof(struct index_entry_s)) - WEBVTT_PREALLOC * 2 > p_sys->index.i_alloc )
+    {
+        void *p_realloc = realloc( p_sys->index.p_array,
+                                   sizeof(struct index_entry_s) *
+                                   ( p_sys->index.i_alloc + WEBVTT_PREALLOC * 2 ) );
+        if( p_realloc )
+        {
+            p_sys->index.p_array = p_realloc;
+            p_sys->index.i_alloc += WEBVTT_PREALLOC * 2;
+        }
+    }
+    if( p_sys->index.i_alloc > p_sys->index.i_count )
+    {
+        p_sys->index.p_array[p_sys->index.i_count].active = 1; /* tmp start tag */
+        p_sys->index.p_array[p_sys->index.i_count++].time = p_cue->i_start;
+        p_sys->index.p_array[p_sys->index.i_count].active = 0;
+        p_sys->index.p_array[p_sys->index.i_count++].time = p_cue->i_stop;
+    }
 }
 
 static void ParserHeaderHandler( void *priv, enum webvtt_header_line_e s,
@@ -272,6 +282,83 @@ static void StreamParserCueDoneHandler( void *priv, webvtt_cue_t *p_cue )
     free( p_cue );
 }
 
+/*****************************************************************************
+ * Demux Index
+ *****************************************************************************/
+
+static int index_Compare( const void *a_, const void *b_ )
+{
+    struct index_entry_s *a = (struct index_entry_s *) a_;
+    struct index_entry_s *b = (struct index_entry_s *) b_;
+    if( a->time == b->time )
+    {
+        if( a->active > b->active )
+            return -1;
+        else
+            return b->active - a->active;
+    }
+    else return a->time < b->time ? -1 : 1;
+}
+
+static size_t getIndexByTime( demux_sys_t *p_sys, mtime_t i_time )
+{
+    for( size_t i=0; i<p_sys->index.i_count; i++ )
+    {
+        if( p_sys->index.p_array[i].time >= i_time )
+            return i;
+    }
+    return 0;
+}
+
+static void BuildIndex( demux_t *p_demux )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    /* Order time entries ascending, start time before end time */
+    qsort( p_sys->index.p_array, p_sys->index.i_count,
+           sizeof(struct index_entry_s), index_Compare );
+
+    /* Build actives count
+    TIME 3000 count 1
+    TIME 14500 count 2 (1 overlap)
+    TIME 16100 count 3 (2 overlaps)
+    TIME 16100 count 2 (1 overlap.. because there next start == end)
+    TIME 18000 count 3
+    TIME 18000 count 2 */
+    unsigned i_overlaps = 0;
+    for( size_t i=0; i<p_sys->index.i_count; i++ )
+    {
+        if( p_sys->index.p_array[i].active )
+            p_sys->index.p_array[i].active = ++i_overlaps;
+        else
+            p_sys->index.p_array[i].active = --i_overlaps;
+    }
+}
+
+static block_t *demux_Range( demux_t *p_demux, mtime_t i_start, mtime_t i_end )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    block_t *p_list = NULL;
+    block_t **pp_append = &p_list;
+    for( size_t i=0; i<p_sys->cues.i_count; i++ )
+    {
+        const webvtt_cue_t *p_cue = &p_sys->cues.p_array[i];
+        if( p_cue->i_start > i_start )
+        {
+            break;
+        }
+        else if( p_cue->i_start <= i_start && p_cue->i_stop > i_start )
+        {
+            *pp_append = ConvertWEBVTT( p_cue, p_sys->index.i_current > 0 );
+            if( *pp_append )
+                pp_append = &((*pp_append)->p_next);
+        }
+    }
+
+    return ( p_list ) ? block_ChainGather( p_list ) : NULL;
+}
+
 static int ReadWEBVTT( demux_t *p_demux )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
@@ -298,6 +385,8 @@ static int ReadWEBVTT( demux_t *p_demux )
     if( !ctx.b_ordered )
         qsort( p_sys->cues.p_array, p_sys->cues.i_count, sizeof(webvtt_cue_t), cue_Compare );
 
+    BuildIndex( p_demux );
+
     memstream_Grab( &ctx.regions, &p_sys->regions_headers.p_data,
                                   &p_sys->regions_headers.i_data );
     memstream_Grab( &ctx.styles, &p_sys->styles_headers.p_data,
@@ -348,21 +437,21 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
         case DEMUX_SET_TIME:
             i64 = va_arg( args, int64_t );
             {
-                p_sys->cues.i_current = cue_GetIndexByTime( p_sys, i64 );
+                p_sys->index.i_current = getIndexByTime( p_sys, i64 );
                 p_sys->b_first_time = true;
                 p_sys->i_next_demux_time =
-                        p_sys->cues.p_array[p_sys->cues.i_current].i_start;
+                        p_sys->index.p_array[p_sys->index.i_current].time;
                 p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
                 return VLC_SUCCESS;
             }
 
         case DEMUX_GET_POSITION:
             pf = va_arg( args, double * );
-            if( p_sys->cues.i_current >= p_sys->cues.i_count )
+            if( p_sys->index.i_current >= p_sys->index.i_count )
             {
                 *pf = 1.0;
             }
-            else if( p_sys->cues.i_count > 0 )
+            else if( p_sys->index.i_count > 0 )
             {
                 *pf = (double) p_sys->i_next_demux_time /
                       (p_sys->i_length + 0.5);
@@ -378,10 +467,10 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
             if( p_sys->cues.i_count )
             {
                 i64 = f * p_sys->i_length;
-                p_sys->cues.i_current = cue_GetIndexByTime( p_sys, i64 );
+                p_sys->index.i_current = getIndexByTime( p_sys, i64 );
                 p_sys->b_first_time = true;
                 p_sys->i_next_demux_time =
-                        p_sys->cues.p_array[p_sys->cues.i_current].i_start;
+                        p_sys->index.p_array[p_sys->index.i_current].time;
                 p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
                 return VLC_SUCCESS;
             }
@@ -435,46 +524,59 @@ static int Demux( demux_t *p_demux )
     demux_sys_t *p_sys = p_demux->p_sys;
 
     int64_t i_barrier = p_sys->i_next_demux_time;
-    while( p_sys->cues.i_current < p_sys->cues.i_count &&
-           p_sys->cues.p_array[p_sys->cues.i_current].i_start <= i_barrier )
-    {
-        const webvtt_cue_t *p_cue = &p_sys->cues.p_array[p_sys->cues.i_current];
 
-        if ( !p_sys->b_slave && p_sys->b_first_time )
+    while( p_sys->index.i_current < p_sys->index.i_count &&
+           p_sys->index.p_array[p_sys->index.i_current].time <= i_barrier )
+    {
+        /* Find start and end of our interval */
+        mtime_t i_start_time = p_sys->index.p_array[p_sys->index.i_current].time;
+        mtime_t i_end_time = i_start_time;
+        /* use next interval time as end time */
+        while( ++p_sys->index.i_current < p_sys->index.i_count )
         {
-            es_out_SetPCR( p_demux->out, VLC_TS_0 + i_barrier );
-            p_sys->b_first_time = false;
+            if( i_start_time != p_sys->index.p_array[p_sys->index.i_current].time )
+            {
+                i_end_time = p_sys->index.p_array[p_sys->index.i_current].time;
+                break;
+            }
         }
 
-        if( p_cue->i_start >= 0 )
+        block_t *p_block = demux_Range( p_demux, i_start_time, i_end_time );
+        if( p_block )
         {
-            block_t *p_block = ConvertWEBVTT( p_cue, p_sys->cues.i_current > 0 );
-            if( p_block )
+            p_block->i_length = i_end_time - i_start_time;
+            p_block->i_dts = p_block->i_pts = VLC_TS_0 + i_start_time;
+
+            if( p_sys->i_next_block_flags )
             {
-                p_block->i_dts =
-                p_block->i_pts = VLC_TS_0 + p_cue->i_start;
-                if( p_cue->i_stop >= 0 && p_cue->i_stop >= p_cue->i_start )
-                    p_block->i_length = p_cue->i_stop - p_cue->i_start;
-
-                if( p_sys->i_next_block_flags )
-                {
-                    p_block->i_flags = p_sys->i_next_block_flags;
-                    p_sys->i_next_block_flags = 0;
-                }
-                es_out_Send( p_demux->out, p_sys->es, p_block );
+                p_block->i_flags = p_sys->i_next_block_flags;
+                p_sys->i_next_block_flags = 0;
             }
+
+            if ( !p_sys->b_slave && p_sys->b_first_time )
+            {
+                es_out_SetPCR( p_demux->out, p_block->i_dts );
+                p_sys->b_first_time = false;
+            }
+
+            es_out_Send( p_demux->out, p_sys->es, p_block );
         }
 
-        p_sys->cues.i_current++;
+        if( p_sys->index.i_current < p_sys->index.i_count &&
+            p_sys->index.p_array[p_sys->index.i_current].active > 1 )
+        {
+            /* we'll need to clear up overlaps */
+            p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
+        }
     }
 
     if ( !p_sys->b_slave )
     {
         es_out_SetPCR( p_demux->out, VLC_TS_0 + i_barrier );
-        p_sys->i_next_demux_time += CLOCK_FREQ / 8;
+        p_sys->i_next_demux_time += CLOCK_FREQ;
     }
 
-    if( p_sys->cues.i_current >= p_sys->cues.i_count )
+    if( p_sys->index.i_current >= p_sys->index.i_count )
         return VLC_DEMUXER_EOF;
 
     return VLC_DEMUXER_SUCCESS;
@@ -609,6 +711,8 @@ void CloseDemux( vlc_object_t *p_this )
         webvtt_cue_Clean( &p_sys->cues.p_array[i] );
     free( p_sys->cues.p_array );
 
+    free( p_sys->index.p_array );
+
     if( p_sys->p_streamparser )
     {
         webvtt_text_parser_Feed( p_sys->p_streamparser, NULL );



More information about the vlc-commits mailing list