[vlc-devel] [PATCH 15/19] ttml demux: add timing on span support

Stanislas Plessia stplessia at gmail.com
Mon Aug 29 16:01:13 CEST 2016


---
 modules/demux/ttml.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 214 insertions(+)

diff --git a/modules/demux/ttml.c b/modules/demux/ttml.c
index 4f57f67..79f93bb 100644
--- a/modules/demux/ttml.c
+++ b/modules/demux/ttml.c
@@ -346,6 +346,218 @@ static int ReadAttrNode( xml_reader_t* reader, node_t* p_node, const char* psz_n
     return VLC_SUCCESS;
 }
 
+static inline bool isVisibleSpan( subtitle_t* p_sub, mtime_t time_ref )
+{
+    return ( ( p_sub->i_start <= time_ref ) && ( p_sub->i_stop > time_ref ) );
+}
+
+static bool isInArray( vlc_array_t* p_array, mtime_t* p_elem )
+{
+    for( int i = 0; i < p_array->i_count ; i++ )
+    {
+        if( *(mtime_t*)p_array->pp_elems[i] == *p_elem )
+            return true;
+    }
+    return false;
+}
+
+static int addToArrayIfNotInside( vlc_array_t* p_array, mtime_t* p_elem )
+{
+    if( !isInArray( p_array, p_elem ) )
+    {
+        vlc_array_append( p_array, (void*)p_elem );
+        if( unlikely( p_array->pp_elems[p_array->i_count - 1] == NULL ) )
+            return VLC_ENOMEM;
+    }
+    return VLC_SUCCESS;
+}
+
+static int timeCmp( const void* p_time1, const void* p_time2 )
+{
+    return ( *(int*)p_time1 - *(int*)p_time2 );
+}
+
+/*
+* To set the span opacity to zero, we add the opacity
+* attribute after all the existing ones for him to be parsed last
+* in order to always be effective.
+*/
+static char* setOpacitiyToZero( char* psz_text )
+{
+    const char* psz_begin = strstr( psz_text, "<span " );
+    if( unlikely( psz_begin == NULL ) )
+        return NULL;
+
+    const char* psz_end = strstr( psz_text, ">" );
+    char* psz_cpy = malloc( strlen( psz_text ) );
+    if( unlikely( psz_cpy == NULL ) )
+        return NULL;
+
+    strncpy( psz_cpy, psz_text, psz_end - psz_begin );
+    psz_cpy = Append( psz_cpy, " tts:opacity=\"0\">%s", psz_end + 1);
+    return psz_cpy;
+}
+
+static void CleanSubs( subtitle_t** tab )
+{
+    for( int i = 0; tab[i] != NULL; i++ )
+    {
+        free( tab[i]->psz_text );
+        free( tab[i] );
+    }
+    free( tab );
+}
+
+/*
+* If the timing are set in the span tags, we will
+* create a new p tag for each time space in the
+* subtitle timeline in the function below.
+*/
+static int ParseTimeOnSpan( demux_sys_t* p_sys, char* psz_text )
+{
+    xml_reader_t* p_reader = p_sys->p_reader;
+    subtitle_t** pp_subtitles = calloc( 1, sizeof( *pp_subtitles ) );
+    if( unlikely( pp_subtitles == NULL ) )
+        return VLC_ENOMEM;
+
+    vlc_array_t* p_times = vlc_array_new();
+    if( unlikely( p_times == NULL ) )
+        goto error;
+
+    const char* psz_node_name;
+    int i_max_sub = 0;
+    int i_nb_span = 0;
+    int ret = VLC_ENOMEM;
+
+    int i_type = xml_ReaderNextNode( p_reader, &psz_node_name );
+    /*
+    * This loop will parse the current p tag and the
+    * spans inside, and will store every span text and times
+    * inside a subtitle_t structure.
+    */
+    do
+    {
+        if( i_type == XML_READER_STARTELEM && !strcasecmp( psz_node_name, "metadata" ) )
+        {
+            do
+            {
+                i_type = xml_ReaderNextNode( p_sys->p_reader, &psz_node_name );
+            }while ( i_type != XML_READER_ENDELEM || strcasecmp( psz_node_name, "metadata" ) );
+            i_type = xml_ReaderNextNode( p_sys->p_reader, &psz_node_name );
+        }
+        else if( i_type == XML_READER_STARTELEM )
+        {
+            node_t* p_node = calloc( 1, sizeof( *p_node ) );
+            if( unlikely( p_node == NULL ) )
+                goto error;
+
+            pp_subtitles[i_nb_span] = malloc( sizeof( *pp_subtitles[i_nb_span] ) );
+            if( unlikely( pp_subtitles[i_nb_span] == NULL ) )
+            {
+                ClearNode( p_node );
+                goto error;
+            }
+
+            if( ReadAttrNode( p_sys->p_reader, p_node, psz_node_name ) != VLC_SUCCESS )
+            {
+                ClearNode( p_node );
+                goto error;
+            }
+
+            if ( asprintf( &pp_subtitles[i_nb_span]->psz_text, "%s", NodeToStr( p_node ) ) < 0 )
+            {
+                ClearNode( p_node );
+                goto error;
+            }
+
+            Convert_time( &pp_subtitles[i_nb_span]->i_start, p_node->psz_begin );
+            Convert_time( &pp_subtitles[i_nb_span]->i_stop, p_node->psz_end );
+            ClearNode( p_node );
+        }
+        else if( i_type == XML_READER_TEXT )
+        {
+            pp_subtitles[i_nb_span]->psz_text = Append( pp_subtitles[i_nb_span]->psz_text, "%s", psz_node_name );
+        }
+        else if( i_type == XML_READER_ENDELEM )
+        {
+            pp_subtitles[i_nb_span]->psz_text = Append( pp_subtitles[i_nb_span]->psz_text, "</%s>", psz_node_name );
+            i_nb_span++;
+            pp_subtitles = realloc_or_free( pp_subtitles, ( i_nb_span + 1 ) * sizeof( *pp_subtitles ) );
+            if( unlikely( pp_subtitles == NULL ) )
+                goto error;
+        }
+        i_type = xml_ReaderNextNode( p_reader, &psz_node_name );
+
+    } while( i_type != XML_READER_ENDELEM || ( strcasecmp( psz_node_name, "p" ) && strcasecmp( psz_node_name, "tt:p" ) ) );
+
+    /*
+    * To split the timeline of the current subtitle, we take every
+    * time of the structure array once and sort them from
+    * earliest to last.
+    */
+    pp_subtitles[i_nb_span] = NULL;
+    for( int j = 0; j < i_nb_span; j++ )
+    {
+        if( addToArrayIfNotInside( p_times, &pp_subtitles[j]->i_start ) != VLC_SUCCESS )
+            goto error;
+
+        if( addToArrayIfNotInside( p_times, &pp_subtitles[j]->i_stop ) != VLC_SUCCESS )
+            goto error;
+    }
+
+    qsort( p_times->pp_elems, p_times->i_count, sizeof( mtime_t ), timeCmp );
+
+    if( p_sys->i_subtitles >= i_max_sub )
+    {
+        i_max_sub += 500;
+        p_sys->subtitle = realloc_or_free( p_sys->subtitle, sizeof( *p_sys->subtitle ) * i_max_sub );
+        if( unlikely( p_sys->subtitle == NULL ) )
+            goto error;
+    }
+    /*
+    * For each time space represented by the times inside the p_times array
+    * we create a p tag with all the spans inside.
+    *
+    * Then accroding to each span begin and end attributes,
+    * we check if it should be displayed or not, and if not,
+    * we set its opacity to zero and add the texts to the final
+    * subtitle structure.
+    */
+    for( int j = 0; j < ( p_times->i_count - 1 ); j++)
+    {
+        char* psz_sub = strdup( psz_text );
+        if( unlikely( psz_sub == NULL ) )
+            goto error;
+
+        for( int k = 0; k < i_nb_span; k++ )
+        {
+            if( isVisibleSpan( pp_subtitles[k], *(mtime_t*)p_times->pp_elems[j] ) )
+                psz_sub = Append( psz_sub, "%s", pp_subtitles[k]->psz_text );
+            else
+            {
+                char* psz_transparent= setOpacitiyToZero( pp_subtitles[k]->psz_text );
+                if( unlikely( psz_transparent == NULL ) )
+                    goto error;
+
+                psz_sub = Append( psz_sub, "%s",  psz_transparent );
+                free( psz_transparent );
+            }
+        }
+        psz_sub = Append( psz_sub, "</p>");
+        subtitle_t *p_subtitle = &p_sys->subtitle[p_sys->i_subtitles];
+        p_subtitle->i_start = *(mtime_t*)p_times->pp_elems[j];
+        p_subtitle->i_stop = *(mtime_t*)p_times->pp_elems[j+1];
+        p_subtitle->psz_text = psz_sub;
+        p_sys->i_subtitles++;
+    }
+    ret = VLC_SUCCESS;
+
+error:
+    CleanSubs( pp_subtitles );
+    vlc_array_destroy( p_times );
+    return ret;
+}
+
 static int ReadTTML( demux_t* p_demux )
 {
     demux_sys_t* p_sys = p_demux->p_sys;
@@ -475,6 +687,8 @@ static int ReadTTML( demux_t* p_demux )
                     p_subtitle->psz_text = psz_text;
                     p_sys->i_subtitles++;
                 }
+                else if( ParseTimeOnSpan( p_sys , psz_text ) == VLC_SUCCESS )
+                    continue;
                 else
                     goto error;
             }
-- 
2.7.4



More information about the vlc-devel mailing list