[vlc-commits] codec: substtml: add multiple region handling

Francois Cartegnie git at videolan.org
Tue Jan 10 15:49:46 CET 2017


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Mon Jan  9 12:27:40 2017 +0100| [7be2dfca1a465cec4f0b6b1734256093045ff8ec] | committer: Francois Cartegnie

codec: substtml: add multiple region handling

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

 modules/codec/ttml/substtml.c | 332 +++++++++++++++++++++++++-----------------
 1 file changed, 200 insertions(+), 132 deletions(-)

diff --git a/modules/codec/ttml/substtml.c b/modules/codec/ttml/substtml.c
index 19680fb..681f8f9 100644
--- a/modules/codec/ttml/substtml.c
+++ b/modules/codec/ttml/substtml.c
@@ -43,13 +43,8 @@
 
 typedef struct
 {
-    char*           psz_styleid;
     text_style_t*   font_style;
-    int             i_align;
-    int             i_margin_h;
-    int             i_margin_v;
-    int             i_margin_percent_h;
-    int             i_margin_percent_v;
+    int             i_text_align;
     int             i_direction;
     bool            b_direction_set;
     bool            b_preserve_space;
@@ -57,9 +52,16 @@ typedef struct
 
 typedef struct
 {
-    tt_node_t *             p_rootnode; /* for now. FIXME: split header */
+    vlc_dictionary_t regions;
+    tt_node_t *      p_rootnode; /* for now. FIXME: split header */
 } ttml_context_t;
 
+typedef struct
+{
+    subpicture_updater_sys_region_t updt;
+    text_segment_t **pp_last_segment;
+} ttml_region_t;
+
 struct decoder_sys_t
 {
     int                     i_align;
@@ -73,13 +75,11 @@ enum
     UNICODE_BIDI_OVERRIDE = 4,
 };
 
-static text_segment_t *ParseTTML( decoder_t *, const uint8_t *, size_t,
-                                  ttml_style_t ** );
+static ttml_region_t *ParseTTML( decoder_t *, const uint8_t *, size_t );
 
 static void ttml_style_Delete( ttml_style_t* p_ttml_style )
 {
     text_style_Delete( p_ttml_style->font_style );
-    free( p_ttml_style->psz_styleid );
     free( p_ttml_style );
 }
 
@@ -98,6 +98,26 @@ static ttml_style_t * ttml_style_New( )
     return p_ttml_style;
 }
 
+static void ttml_region_Delete( ttml_region_t *p_region )
+{
+    SubpictureUpdaterSysRegionClean( &p_region->updt );
+    free( p_region );
+}
+
+static ttml_region_t *ttml_region_New( )
+{
+    ttml_region_t *p_ttml_region = calloc( 1, sizeof( ttml_region_t ) );
+    if( unlikely( !p_ttml_region ) )
+        return NULL;
+
+    SubpictureUpdaterSysRegionInit( &p_ttml_region->updt );
+    p_ttml_region->pp_last_segment = &p_ttml_region->updt.p_segments;
+    /* Align to bottom by default. !Warn: center align is obtained with NO flags */
+    p_ttml_region->updt.align = SUBPICTURE_ALIGN_BOTTOM;
+
+    return p_ttml_region;
+}
+
 static tt_node_t * FindNode( tt_node_t *p_node, const char *psz_nodename,
                              size_t i_maxdepth, const char *psz_id )
 {
@@ -221,21 +241,17 @@ static void FillTextStyle( const char *psz_attr, const char *psz_val,
     }
 }
 
-static void FillTTMLStyle( const char *psz_attr, const char *psz_val,
-                           ttml_style_t *p_ttml_style )
+static void FillRegionStyle( const char *psz_attr, const char *psz_val,
+                             ttml_region_t *p_region )
 {
-    if( !strcasecmp( "tts:textAlign", psz_attr ) )
+    if( !strcasecmp( "tts:displayAlign", psz_attr ) )
     {
-        if( !strcasecmp ( "left", psz_val ) )
-            p_ttml_style->i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT;
-        else if( !strcasecmp ( "right", psz_val ) )
-            p_ttml_style->i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_RIGHT;
+        if( !strcasecmp ( "top", psz_val ) )
+            p_region->updt.align = SUBPICTURE_ALIGN_TOP;
         else if( !strcasecmp ( "center", psz_val ) )
-            p_ttml_style->i_align = SUBPICTURE_ALIGN_BOTTOM;
-        else if( !strcasecmp ( "start", psz_val ) )
-            p_ttml_style->i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_LEFT;
-        else if( !strcasecmp ( "end", psz_val ) )
-            p_ttml_style->i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_RIGHT;
+            p_region->updt.align = 0;
+        else
+            p_region->updt.align = SUBPICTURE_ALIGN_BOTTOM;
     }
     else if( !strcasecmp ( "tts:origin", psz_attr ) )
     {
@@ -248,30 +264,36 @@ static void FillTTMLStyle( const char *psz_attr, const char *psz_val,
             return;
         const char *psz_percent_sign = strchr( psz_token, '%' );
 
+        p_region->updt.origin.x = atoi( psz_token );
         if( psz_percent_sign != NULL && psz_percent_sign < psz_separator )
-        {
-            p_ttml_style->i_margin_h = 0;
-            p_ttml_style->i_margin_percent_h = atoi( psz_token );
-        }
-        else
-        {
-            p_ttml_style->i_margin_h = atoi( psz_token );
-            p_ttml_style->i_margin_percent_h = 0;
-        }
+            p_region->updt.flags |= UPDT_REGION_EXTENT_X_IS_PERCENTILE;
+
         while( isspace( *psz_separator ) )
             psz_separator++;
         psz_token = psz_separator;
         psz_percent_sign = strchr( psz_token, '%' );
+
+        p_region->updt.origin.y = atoi( psz_token );
         if( psz_percent_sign != NULL )
-        {
-            p_ttml_style->i_margin_v = 0;
-            p_ttml_style->i_margin_percent_v = atoi( psz_val );
-        }
-        else
-        {
-            p_ttml_style->i_margin_v = atoi( psz_val );
-            p_ttml_style->i_margin_percent_v = 0;
-        }
+            p_region->updt.flags |= UPDT_REGION_EXTENT_Y_IS_PERCENTILE;
+    }
+}
+
+static void FillTTMLStyle( const char *psz_attr, const char *psz_val,
+                           ttml_style_t *p_ttml_style )
+{
+    if( !strcasecmp( "tts:textAlign", psz_attr ) )
+    {
+        if( !strcasecmp ( "left", psz_val ) )
+            p_ttml_style->i_text_align = SUBPICTURE_ALIGN_LEFT;
+        else if( !strcasecmp ( "right", psz_val ) )
+            p_ttml_style->i_text_align = SUBPICTURE_ALIGN_RIGHT;
+        else if( !strcasecmp ( "center", psz_val ) )
+            p_ttml_style->i_text_align = 0;
+        else if( !strcasecmp ( "start", psz_val ) ) /* FIXME: should be BIDI based */
+            p_ttml_style->i_text_align = SUBPICTURE_ALIGN_LEFT;
+        else if( !strcasecmp ( "end", psz_val ) )  /* FIXME: should be BIDI based */
+            p_ttml_style->i_text_align = SUBPICTURE_ALIGN_RIGHT;
     }
     else if( !strcasecmp( "tts:direction", psz_attr ) )
     {
@@ -298,13 +320,13 @@ static void FillTTMLStyle( const char *psz_attr, const char *psz_val,
         if( !strcasecmp( "rl", psz_val ) || !strcasecmp( "rltb", psz_val ) )
         {
             p_ttml_style->i_direction = UNICODE_BIDI_RTL | UNICODE_BIDI_OVERRIDE;
-            p_ttml_style->i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_RIGHT;
+            //p_ttml_style->i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_RIGHT;
             p_ttml_style->b_direction_set = true;
         }
         else if( !strcasecmp( "lr", psz_val ) || !strcasecmp( "lrtb", psz_val ) )
         {
             p_ttml_style->i_direction = UNICODE_BIDI_LTR | UNICODE_BIDI_OVERRIDE;
-            p_ttml_style->i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_LEFT;
+            //p_ttml_style->i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_LEFT;
             p_ttml_style->b_direction_set = true;
         }
     }
@@ -375,6 +397,7 @@ static void DictMergeWithRegionID( ttml_context_t *p_ctx, const char *psz_id,
 
 static ttml_style_t * InheritTTMLStyles( ttml_context_t *p_ctx, tt_node_t *p_node )
 {
+    assert( p_node );
     ttml_style_t *p_ttml_style = NULL;
     vlc_dictionary_t merged;
     vlc_dictionary_init( &merged, 0 );
@@ -495,18 +518,93 @@ static void StripSpacing( text_segment_t *p_segment )
         *p = ' ';
 }
 
-static text_segment_t * ConvertNodesToSegments( ttml_context_t *p_ctx, const tt_node_t *p_node,
-                                                bool b_has_prev_text, ttml_style_t **pp_ret_ttml_style )
+static ttml_region_t *GetTTMLRegion( ttml_context_t *p_ctx, const char *psz_region_id )
 {
-    text_segment_t *p_head = NULL;
-    text_segment_t **pp_last = &p_head;
+    ttml_region_t *p_region = ( ttml_region_t * )
+            vlc_dictionary_value_for_key( &p_ctx->regions, psz_region_id ? psz_region_id : "" );
+    if( p_region == NULL )
+    {
+        if( psz_region_id && strcmp( psz_region_id, "" ) ) /* not default region */
+        {
+            /* Create if missing and exists as node */
+            const tt_node_t *p_node = FindNode( p_ctx->p_rootnode, "region", -1, psz_region_id );
+            if( p_node && (p_region = ttml_region_New()) )
+            {
+                /* Fill from its own attributes */
+                for( int i = 0; i < p_node->attr_dict.i_size; ++i )
+                {
+                    for ( vlc_dictionary_entry_t* p_entry = p_node->attr_dict.p_entries[i];
+                          p_entry != NULL; p_entry = p_entry->p_next )
+                    {
+                        FillRegionStyle( p_entry->psz_key, p_entry->p_value, p_region );
+                    }
+                }
+            }
+            vlc_dictionary_insert( &p_ctx->regions, psz_region_id, p_region );
+        }
+        else if( (p_region = ttml_region_New()) ) /* create default */
+        {
+            vlc_dictionary_insert( &p_ctx->regions, "", p_region );
+        }
+    }
+    return p_region;
+}
+
+static void AppendLineBreakToRegion( ttml_region_t *p_region )
+{
+    text_segment_t *p_segment = text_segment_New( "\n" );
+    if( p_segment )
+    {
+        *p_region->pp_last_segment = p_segment;
+        p_region->pp_last_segment = &p_segment->p_next;
+    }
+}
+
+static void AppendTextToRegion( ttml_context_t *p_ctx, const tt_textnode_t *p_ttnode,
+                                ttml_region_t *p_region )
+{
+    text_segment_t *p_segment;
+
+    if( p_region == NULL )
+        return;
+
+    p_segment = text_segment_New( p_ttnode->psz_text );
+    if( p_segment )
+    {
+        ttml_style_t *s = InheritTTMLStyles( p_ctx, p_ttnode->p_parent );
+        if( s )
+        {
+            p_segment->style = s->font_style;
+            s->font_style = NULL;
+
+            if( !s->b_preserve_space )
+                StripSpacing( p_segment );
+            if( s->b_direction_set )
+                BIDIConvert( p_segment, s->i_direction );
+
+            ttml_style_Delete( s );
+        }
+    }
+
+    *p_region->pp_last_segment = p_segment;
+    p_region->pp_last_segment = &p_segment->p_next;
+}
+
+static void ConvertNodesToRegionContent( ttml_context_t *p_ctx, const tt_node_t *p_node,
+                                         ttml_region_t *p_region )
+{
+    const char *psz_regionid = (const char *)
+        vlc_dictionary_value_for_key( &p_node->attr_dict, "region" );
+
+    /* Region isn't set or is changing */
+    if( psz_regionid || p_region == NULL )
+        p_region = GetTTMLRegion( p_ctx, psz_regionid );
 
     /* awkward paragraph handling */
-    if( !tt_node_NameCompare( p_node->psz_node_name, "p" ) && b_has_prev_text )
+    if( !tt_node_NameCompare( p_node->psz_node_name, "p" ) &&
+        p_region->updt.p_segments )
     {
-        *pp_last = text_segment_New( "\n" );
-        if( (*pp_last) )
-            pp_last = &(*pp_last)->p_next;
+        AppendLineBreakToRegion( p_region );
     }
 
     for( const tt_basenode_t *p_child = p_node->p_child;
@@ -514,53 +612,25 @@ static text_segment_t * ConvertNodesToSegments( ttml_context_t *p_ctx, const tt_
     {
         if( p_child->i_type == TT_NODE_TYPE_TEXT )
         {
-            const tt_textnode_t *p_ttnode = (const tt_textnode_t *) p_child;
-            *pp_last = text_segment_New( p_ttnode->psz_text );
-            ttml_style_t *s = InheritTTMLStyles( p_ctx, p_child->p_parent );
-            if( s )
-            {
-                (*pp_last)->style = s->font_style;
-                s->font_style = NULL;
-                if( !s->b_preserve_space )
-                    StripSpacing( *pp_last );
-                if( s->b_direction_set )
-                    BIDIConvert( *pp_last, s->i_direction );
-                /* FIXME: This is carried from broken prev code feat
-                 * as there can be multiple regions. Return first ttml style
-                 * to apply to default SPU region for now */
-                if( *pp_ret_ttml_style == NULL )
-                    *pp_ret_ttml_style = s;
-                else
-                    ttml_style_Delete( s );
-            }
+            AppendTextToRegion( p_ctx, (const tt_textnode_t *) p_child, p_region );
+        }
+        else if( !tt_node_NameCompare( ((const tt_node_t *)p_child)->psz_node_name, "br" ) )
+        {
+            AppendLineBreakToRegion( p_region );
         }
         else
         {
-            const tt_node_t *p_childnode = (const tt_node_t *) p_child;
-            if( !tt_node_NameCompare( p_childnode->psz_node_name, "br" ) )
-            {
-                *pp_last = text_segment_New( "\n" );
-            }
-            else
-            {
-                *pp_last = ConvertNodesToSegments( p_ctx, p_childnode,
-                                                   p_head != NULL, pp_ret_ttml_style );
-            }
+            ConvertNodesToRegionContent( p_ctx, (const tt_node_t *) p_child, p_region );
         }
-
-        while( (*pp_last) )
-            pp_last = &(*pp_last)->p_next;
     }
-
-    return p_head;
 }
 
-static text_segment_t *ParseTTML( decoder_t *p_dec,  const uint8_t *p_buffer, size_t i_buffer,
-                                  ttml_style_t **p_ret_ttml_style )
+static ttml_region_t *ParseTTML( decoder_t *p_dec,  const uint8_t *p_buffer, size_t i_buffer )
 {
     stream_t*       p_sub = NULL;
     xml_reader_t*   p_xml_reader = NULL;
-    text_segment_t* p_segments = NULL;
+    ttml_region_t*  p_regions = NULL;
+    ttml_region_t** pp_region_last = &p_regions;
 
     p_sub = vlc_stream_MemoryNew( p_dec, (uint8_t*) p_buffer, i_buffer, true );
     if( unlikely( p_sub == NULL ) )
@@ -588,8 +658,20 @@ static text_segment_t *ParseTTML( decoder_t *p_dec,  const uint8_t *p_buffer, si
         {
             ttml_context_t context;
             context.p_rootnode = p_rootnode;
-            p_segments = ConvertNodesToSegments( &context, p_bodynode,
-                                                 false, p_ret_ttml_style );
+            vlc_dictionary_init( &context.regions, 1 );
+            ConvertNodesToRegionContent( &context, p_bodynode, NULL );
+
+            for( int i = 0; i < context.regions.i_size; ++i )
+            {
+                for ( const vlc_dictionary_entry_t* p_entry = context.regions.p_entries[i];
+                                                    p_entry != NULL; p_entry = p_entry->p_next )
+                {
+                    *pp_region_last = (ttml_region_t *) p_entry->p_value;
+                    pp_region_last = (ttml_region_t **) &(*pp_region_last)->updt.p_next;
+                }
+            }
+
+            vlc_dictionary_clear( &context.regions, NULL, NULL );
         }
     }
     else if ( !tt_node_NameCompare( p_rootnode->psz_node_name, "div" ) ||
@@ -604,12 +686,11 @@ end:
     xml_ReaderDelete( p_xml_reader );
     vlc_stream_Delete( p_sub );
 
-    return p_segments;
+    return p_regions;
 }
 
 static subpicture_t *ParseBlock( decoder_t *p_dec, const block_t *p_block )
 {
-    decoder_sys_t *p_sys = p_dec->p_sys;
     subpicture_t *p_spu = NULL;
 
     if( p_block->i_flags & BLOCK_FLAG_CORRUPTED )
@@ -622,18 +703,13 @@ static subpicture_t *ParseBlock( decoder_t *p_dec, const block_t *p_block )
         return NULL;
     }
 
-    ttml_style_t *p_ttml_style = NULL;
-    text_segment_t *p_segments = ParseTTML( p_dec, p_block->p_buffer, p_block->i_buffer,
-                                            &p_ttml_style );
-    if( p_segments )
+    ttml_region_t *p_regions = ParseTTML( p_dec, p_block->p_buffer, p_block->i_buffer );
+    if( p_regions )
     {
         /* Create the subpicture unit */
         p_spu = decoder_NewSubpictureText( p_dec );
         if( !p_spu )
         {
-            text_segment_ChainDelete( p_segments );
-            if( p_ttml_style )
-                ttml_style_Delete( p_ttml_style );
             return NULL;
         }
 
@@ -643,55 +719,47 @@ static subpicture_t *ParseBlock( decoder_t *p_dec, const block_t *p_block )
         p_spu->b_absolute = false;
 
         subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
+        subpicture_updater_sys_region_t *p_updtregion = NULL;
 
-        /* Broken stuff. See comments */
-        if( p_ttml_style )
+        /* Create region update info from each ttml region */
+        for( ttml_region_t *p_region = p_regions;
+                            p_region; p_region = (ttml_region_t *) p_region->updt.p_next )
         {
-            if( p_ttml_style->i_margin_percent_h )
+            if( p_updtregion == NULL )
             {
-                p_spu_sys->region.origin.x = p_ttml_style->i_margin_percent_h;
-                p_spu_sys->region.flags |= UPDT_REGION_ORIGIN_X_IS_PERCENTILE;
+                p_updtregion = &p_spu_sys->region;
             }
             else
             {
-                p_spu_sys->region.origin.x = p_ttml_style->i_margin_h;
+                p_updtregion = SubpictureUpdaterSysRegionNew();
+                if( p_updtregion == NULL )
+                    break;
+                SubpictureUpdaterSysRegionAdd( &p_spu_sys->region, p_updtregion );
             }
 
-            if( p_ttml_style->i_margin_percent_v )
-            {
-                p_spu_sys->region.origin.y = p_ttml_style->i_margin_percent_v;
-                p_spu_sys->region.flags |= UPDT_REGION_ORIGIN_Y_IS_PERCENTILE;
-            }
-            else
-            {
-                p_spu_sys->region.origin.y = p_ttml_style->i_margin_v;
-            }
-
-            if( p_ttml_style->i_align & SUBPICTURE_ALIGN_LEFT )
-                p_spu_sys->region.inner_align = SUBPICTURE_ALIGN_LEFT;
-            else if( p_ttml_style->i_align & SUBPICTURE_ALIGN_LEFT )
-                p_spu_sys->region.inner_align = SUBPICTURE_ALIGN_RIGHT;
-
             /* broken legacy align var (can't handle center...) */
-            if( (p_ttml_style->i_align & SUBPICTURE_ALIGN_MASK) == 0 &&
-                (p_sys->i_align & SUBPICTURE_ALIGN_MASK) != 0 )
+            if( p_dec->p_sys->i_align & SUBPICTURE_ALIGN_MASK )
             {
-                p_spu_sys->region.align = p_sys->i_align;
-            }
-            else
-            {
-                if( p_ttml_style->i_align & SUBPICTURE_ALIGN_TOP )
-                    p_spu_sys->region.align = SUBPICTURE_ALIGN_TOP;
-                else
-                    p_spu_sys->region.align = SUBPICTURE_ALIGN_BOTTOM;
+                p_spu_sys->region.align = p_dec->p_sys->i_align & (SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_TOP);
+                p_spu_sys->region.inner_align = p_dec->p_sys->i_align & (SUBPICTURE_ALIGN_LEFT|SUBPICTURE_ALIGN_RIGHT);
             }
+
+            /* copy and take ownership of pointeds */
+            *p_updtregion = p_region->updt;
+            p_updtregion->p_next = NULL;
+            p_region->updt.p_region_style = NULL;
+            p_region->updt.p_segments = NULL;
         }
 
-        p_spu_sys->region.p_segments = p_segments;
     }
 
-    if( p_ttml_style )
-        ttml_style_Delete( p_ttml_style );
+    /* cleanup */
+    while( p_regions )
+    {
+        ttml_region_t *p_nextregion = (ttml_region_t *) p_regions->updt.p_next;
+        ttml_region_Delete( p_regions );
+        p_regions = p_nextregion;
+    }
 
     return p_spu;
 }



More information about the vlc-commits mailing list