[vlc-commits] [Git][videolan/vlc][master] 5 commits: codec: ttml: tag nodes with namespaces

Felix Paul Kühne (@fkuehne) gitlab at videolan.org
Fri Mar 22 20:37:38 UTC 2024



Felix Paul Kühne pushed to branch master at VideoLAN / VLC


Commits:
18800af2 by François Cartegnie at 2024-03-22T20:15:27+00:00
codec: ttml: tag nodes with namespaces

- - - - -
d9a8494f by François Cartegnie at 2024-03-22T20:15:27+00:00
codec: ttml: add tt_node_match for namespaces

- - - - -
29759b71 by François Cartegnie at 2024-03-22T20:15:27+00:00
codec: ttml: add namespaces registry

- - - - -
a55c9e9a by François Cartegnie at 2024-03-22T20:15:27+00:00
codec: ttml add more namespaces defines

- - - - -
44879313 by François Cartegnie at 2024-03-22T20:15:27+00:00
codec/demux: ttml: handle non-default namespaces

- - - - -


5 changed files:

- modules/codec/ttml/encttml.c
- modules/codec/ttml/substtml.c
- modules/codec/ttml/ttml.c
- modules/codec/ttml/ttml.h
- modules/demux/ttml.c


Changes:

=====================================
modules/codec/ttml/encttml.c
=====================================
@@ -48,7 +48,7 @@ static void AddTextNode( tt_node_t *p_parent, const char *psz_text )
         if( nl )
         {
             tt_subtextnode_New( p_parent, psz, nl - psz );
-            tt_node_New( p_parent, "br" );
+            tt_node_New( p_parent, "br", NULL );
             psz += nl - psz + 1;
             if( *psz == '\0' )
                 break;
@@ -67,21 +67,21 @@ static block_t *Encode( encoder_t *p_enc, subpicture_t *p_spu )
     if( p_spu == NULL )
         return NULL;
 
-    tt_node_t *p_root = tt_node_New( NULL, "tt" );
+    tt_node_t *p_root = tt_node_New( NULL, "tt", TT_NS );
     if( !p_root )
         return NULL;
 
     tt_node_AddAttribute( p_root, "xmlns", TT_NS );
     tt_node_AddAttribute( p_root, "xmlns:tts", TT_NS_STYLING );
 
-    tt_node_t *p_body = tt_node_New( p_root, "body" );
+    tt_node_t *p_body = tt_node_New( p_root, "body", NULL );
     if( !p_body )
     {
         tt_node_RecursiveDelete( p_root );
         return NULL;
     }
 
-    tt_node_t *p_div = tt_node_New( p_body, "div" );
+    tt_node_t *p_div = tt_node_New( p_body, "div", NULL );
     if( !p_div )
     {
         tt_node_RecursiveDelete( p_root );
@@ -96,7 +96,7 @@ static block_t *Encode( encoder_t *p_enc, subpicture_t *p_spu )
             p_region->p_text->psz_text == NULL )
             continue;
 
-        tt_node_t *p_par = tt_node_New( p_div, "p" );
+        tt_node_t *p_par = tt_node_New( p_div, "p", NULL );
         if( !p_par )
             continue;
 
@@ -117,7 +117,7 @@ static block_t *Encode( encoder_t *p_enc, subpicture_t *p_spu )
             const text_style_t *style = p_segment->style;
             if( style && style->i_features )
             {
-                tt_node_t *p_span = tt_node_New( p_par, "span" );
+                tt_node_t *p_span = tt_node_New( p_par, "span", NULL );
                 if( !p_span )
                     continue;
 


=====================================
modules/codec/ttml/substtml.c
=====================================
@@ -124,7 +124,8 @@ enum
  * Then we convert attributes, merging with style by id or region
  * style, and sets from parent node.
  */
-static tt_node_t *ParseTTML( decoder_t *, const uint8_t *, size_t );
+static tt_node_t *ParseTTML( decoder_t *, tt_namespaces_t *,
+                             const uint8_t *, size_t );
 
 static void ttml_style_Delete( ttml_style_t* p_ttml_style )
 {
@@ -280,16 +281,17 @@ static bool ttml_read_coords( const char *value, ttml_length_t *h, ttml_length_t
     return false;
 }
 
-static tt_node_t * FindNode( tt_node_t *p_node, const char *psz_nodename,
+static tt_node_t * FindNode( tt_namespaces_t *p_nss, tt_node_t *p_node,
+                             const char *psz_nodename, const char *psz_namespace,
                              size_t i_maxdepth, const char *psz_id )
 {
-    if( !tt_node_NameCompare( p_node->psz_node_name, psz_nodename ) )
+    if( tt_node_Match( p_node, psz_nodename, psz_namespace ) )
     {
         if( psz_id != NULL )
         {
-            char *psz = vlc_dictionary_value_for_key( &p_node->attr_dict, "xml:id" );
+            const char *psz = tt_node_GetAttribute( p_nss, p_node, "id", TT_NS_XML );
             if( !psz ) /* People can't do xml properly */
-                psz = vlc_dictionary_value_for_key( &p_node->attr_dict, "id" );
+                psz = tt_node_GetAttribute( p_nss, p_node, "id", NULL );
             if( psz && !strcmp( psz, psz_id ) )
                 return p_node;
         }
@@ -305,7 +307,9 @@ static tt_node_t * FindNode( tt_node_t *p_node, const char *psz_nodename,
         if( p_child->i_type == TT_NODE_TYPE_TEXT )
             continue;
 
-        p_node = FindNode( (tt_node_t *) p_child, psz_nodename, i_maxdepth - 1, psz_id );
+        p_node = FindNode( p_nss, (tt_node_t *) p_child,
+                           psz_nodename, psz_namespace,
+                           i_maxdepth - 1, psz_id );
         if( p_node )
             return p_node;
     }
@@ -316,25 +320,25 @@ static tt_node_t * FindNode( tt_node_t *p_node, const char *psz_nodename,
 static void FillTextStyle( const char *psz_attr, const char *psz_val,
                            text_style_t *p_text_style )
 {
-    if( !strcasecmp ( "tts:fontFamily", psz_attr ) )
+    if( !strcasecmp ( "fontFamily", psz_attr ) )
     {
         free( p_text_style->psz_fontname );
         p_text_style->psz_fontname = strdup( psz_val );
     }
-    else if( !strcasecmp( "tts:opacity", psz_attr ) )
+    else if( !strcasecmp( "opacity", psz_attr ) )
     {
         p_text_style->i_background_alpha = atoi( psz_val );
         p_text_style->i_font_alpha = atoi( psz_val );
         p_text_style->i_features |= STYLE_HAS_BACKGROUND_ALPHA | STYLE_HAS_FONT_ALPHA;
     }
-    else if( !strcasecmp( "tts:color", psz_attr ) )
+    else if( !strcasecmp( "color", psz_attr ) )
     {
         unsigned int i_color = vlc_html_color( psz_val, NULL );
         p_text_style->i_font_color = (i_color & 0xffffff);
         p_text_style->i_font_alpha = (i_color & 0xFF000000) >> 24;
         p_text_style->i_features |= STYLE_HAS_FONT_COLOR | STYLE_HAS_FONT_ALPHA;
     }
-    else if( !strcasecmp( "tts:backgroundColor", psz_attr ) )
+    else if( !strcasecmp( "backgroundColor", psz_attr ) )
     {
         unsigned int i_color = vlc_html_color( psz_val, NULL );
         p_text_style->i_background_color = i_color & 0xFFFFFF;
@@ -343,7 +347,7 @@ static void FillTextStyle( const char *psz_attr, const char *psz_val,
                                                   | STYLE_HAS_BACKGROUND_ALPHA;
         p_text_style->i_style_flags |= STYLE_BACKGROUND;
     }
-    else if( !strcasecmp( "tts:fontStyle", psz_attr ) )
+    else if( !strcasecmp( "fontStyle", psz_attr ) )
     {
         if( !strcasecmp ( "italic", psz_val ) || !strcasecmp ( "oblique", psz_val ) )
             p_text_style->i_style_flags |= STYLE_ITALIC;
@@ -351,7 +355,7 @@ static void FillTextStyle( const char *psz_attr, const char *psz_val,
             p_text_style->i_style_flags &= ~STYLE_ITALIC;
         p_text_style->i_features |= STYLE_HAS_FLAGS;
     }
-    else if( !strcasecmp ( "tts:fontWeight", psz_attr ) )
+    else if( !strcasecmp ( "fontWeight", psz_attr ) )
     {
         if( !strcasecmp ( "bold", psz_val ) )
             p_text_style->i_style_flags |= STYLE_BOLD;
@@ -359,7 +363,7 @@ static void FillTextStyle( const char *psz_attr, const char *psz_val,
             p_text_style->i_style_flags &= ~STYLE_BOLD;
         p_text_style->i_features |= STYLE_HAS_FLAGS;
     }
-    else if( !strcasecmp ( "tts:textDecoration", psz_attr ) )
+    else if( !strcasecmp ( "textDecoration", psz_attr ) )
     {
         if( !strcasecmp ( "underline", psz_val ) )
             p_text_style->i_style_flags |= STYLE_UNDERLINE;
@@ -371,7 +375,7 @@ static void FillTextStyle( const char *psz_attr, const char *psz_val,
             p_text_style->i_style_flags &= ~STYLE_STRIKEOUT;
         p_text_style->i_features |= STYLE_HAS_FLAGS;
     }
-    else if( !strcasecmp( "tts:textOutline", psz_attr ) )
+    else if( !strcasecmp( "textOutline", psz_attr ) )
     {
         char *value = strdup( psz_val );
         char* psz_saveptr = NULL;
@@ -433,10 +437,13 @@ static void FillUpdaterCoords( ttml_context_t *p_ctx, ttml_length_t h, ttml_leng
 }
 
 static void FillRegionStyle( ttml_context_t *p_ctx,
-                             const char *psz_attr, const char *psz_val,
-                             ttml_region_t *p_region )
+                             const char *psz_attr, const char *psz_namespace,
+                             const char *psz_val, ttml_region_t *p_region )
 {
-    if( !strcasecmp( "tts:displayAlign", psz_attr ) )
+    if( strcmp( psz_namespace, TT_NS_STYLING ) )
+        return;
+
+    if( !strcasecmp( "displayAlign", psz_attr ) )
     {
         p_region->updt.inner_align &= ~(SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_BOTTOM);
         if( !strcasecmp( "after", psz_val ) )
@@ -445,12 +452,12 @@ static void FillRegionStyle( ttml_context_t *p_ctx,
             /* "before" */
             p_region->updt.inner_align |= SUBPICTURE_ALIGN_TOP;
     }
-    else if( !strcasecmp ( "tts:origin", psz_attr ) ||
-             !strcasecmp ( "tts:extent", psz_attr ) )
+    else if( !strcasecmp ( "origin", psz_attr ) ||
+             !strcasecmp ( "extent", psz_attr ) )
     {
         ttml_length_t x, y;
         if( ttml_read_coords( psz_val, &x, &y ) )
-            FillUpdaterCoords( p_ctx, x, y, (psz_attr[4] == 'o'), &p_region->updt );
+            FillUpdaterCoords( p_ctx, x, y, (psz_attr[0] == 'o'), &p_region->updt );
     }
 }
 
@@ -483,20 +490,30 @@ static void ComputeTTMLStyles( ttml_context_t *p_ctx, const vlc_dictionary_t *p_
         p_text_style->i_font_size = len.i_value;
 }
 
-static void FillTTMLStyle( const char *psz_attr, const char *psz_val,
-                           ttml_style_t *p_ttml_style )
+static void FillTTMLStyle( const char *psz_attr, const char *psz_namespace,
+                           const char *psz_val, ttml_style_t *p_ttml_style )
 {
-    if( !strcasecmp( "tts:extent", psz_attr ) )
+    if( !strcmp( psz_namespace, TT_NS_XML ) )
+    {
+        if( !strcasecmp( "space", psz_attr ) )
+            p_ttml_style->b_preserve_space = !strcmp( "preserve", psz_val );
+        return;
+    }
+
+    if( strcmp( psz_namespace, TT_NS_STYLING ) )
+        return;
+
+    if( !strcasecmp( "extent", psz_attr ) )
     {
         ttml_read_coords( psz_val, &p_ttml_style->extent_h,
                                    &p_ttml_style->extent_v );
     }
-    else if( !strcasecmp( "tts:origin", psz_attr ) )
+    else if( !strcasecmp( "origin", psz_attr ) )
     {
         ttml_read_coords( psz_val, &p_ttml_style->origin_h,
                                    &p_ttml_style->origin_v );
     }
-    else if( !strcasecmp( "tts:textAlign", psz_attr ) )
+    else if( !strcasecmp( "textAlign", psz_attr ) )
     {
         p_ttml_style->i_text_align &= ~(SUBPICTURE_ALIGN_LEFT|SUBPICTURE_ALIGN_RIGHT);
         if( !strcasecmp ( "left", psz_val ) )
@@ -513,13 +530,13 @@ static void FillTTMLStyle( const char *psz_attr, const char *psz_val,
         printf("**%s %x\n", psz_val, p_ttml_style->i_text_align);
 #endif
     }
-    else if( !strcasecmp( "tts:fontSize", psz_attr ) )
+    else if( !strcasecmp( "fontSize", psz_attr ) )
     {
         ttml_length_t len = ttml_read_length( psz_val );
         if( len.unit != TTML_UNIT_UNKNOWN && len.i_value > 0.0 )
             p_ttml_style->font_size = len;
     }
-    else if( !strcasecmp( "tts:direction", psz_attr ) )
+    else if( !strcasecmp( "direction", psz_attr ) )
     {
         if( !strcasecmp( "rtl", psz_val ) )
         {
@@ -532,14 +549,14 @@ static void FillTTMLStyle( const char *psz_attr, const char *psz_val,
             p_ttml_style->b_direction_set = true;
         }
     }
-    else if( !strcasecmp( "tts:unicodeBidi", psz_attr ) )
+    else if( !strcasecmp( "unicodeBidi", psz_attr ) )
     {
             if( !strcasecmp( "bidiOverride", psz_val ) )
                 p_ttml_style->i_direction |= UNICODE_BIDI_OVERRIDE & ~UNICODE_BIDI_EMBEDDED;
             else if( !strcasecmp( "embed", psz_val ) )
                 p_ttml_style->i_direction |= UNICODE_BIDI_EMBEDDED & ~UNICODE_BIDI_OVERRIDE;
     }
-    else if( !strcasecmp( "tts:writingMode", psz_attr ) )
+    else if( !strcasecmp( "writingMode", psz_attr ) )
     {
         if( !strcasecmp( "rl", psz_val ) || !strcasecmp( "rltb", psz_val ) )
         {
@@ -554,17 +571,13 @@ static void FillTTMLStyle( const char *psz_attr, const char *psz_val,
             p_ttml_style->b_direction_set = true;
         }
     }
-    else if( !strcmp( "tts:display", psz_attr ) )
+    else if( !strcmp( "display", psz_attr ) )
     {
         if( !strcmp( "none", psz_val ) )
             p_ttml_style->display = TTML_DISPLAY_NONE;
         else
             p_ttml_style->display = TTML_DISPLAY_AUTO;
     }
-    else if( !strcasecmp( "xml:space", psz_attr ) )
-    {
-        p_ttml_style->b_preserve_space = !strcmp( "preserve", psz_val );
-    }
     else FillTextStyle( psz_attr, psz_val, p_ttml_style->font_style );
 }
 
@@ -576,27 +589,22 @@ static void DictionaryMerge( const vlc_dictionary_t *p_src, vlc_dictionary_t *p_
         for ( const vlc_dictionary_entry_t* p_entry = p_src->p_entries[i];
                                             p_entry != NULL; p_entry = p_entry->p_next )
         {
-            if( !strncmp( "tts:", p_entry->psz_key, 4 ) ||
-                !strncmp( "ttp:", p_entry->psz_key, 4 ) ||
-                !strcmp( "xml:space", p_entry->psz_key ) )
+            if( vlc_dictionary_has_key( p_dst, p_entry->psz_key ) )
             {
-                if( vlc_dictionary_has_key( p_dst, p_entry->psz_key ) )
+                if( b_override )
                 {
-                    if( b_override )
-                    {
-                        vlc_dictionary_remove_value_for_key( p_dst, p_entry->psz_key, NULL, NULL );
-                        vlc_dictionary_insert( p_dst, p_entry->psz_key, p_entry->p_value );
-                    }
-                }
-                else
+                    vlc_dictionary_remove_value_for_key( p_dst, p_entry->psz_key, NULL, NULL );
                     vlc_dictionary_insert( p_dst, p_entry->psz_key, p_entry->p_value );
+                }
             }
+            else
+                vlc_dictionary_insert( p_dst, p_entry->psz_key, p_entry->p_value );
         }
     }
 }
 
-static void DictMergeWithStyleID( ttml_context_t *p_ctx, const char *psz_styles,
-                                  vlc_dictionary_t *p_dst )
+static void DictMergeWithStyleID( ttml_context_t *p_ctx, tt_namespaces_t *p_nss,
+                                  const char *psz_styles, vlc_dictionary_t *p_dst )
 {
     assert(p_ctx->p_rootnode);
     char *psz_dup;
@@ -612,8 +620,10 @@ static void DictMergeWithStyleID( ttml_context_t *p_ctx, const char *psz_styles,
         while( psz_id )
         {
             /* Lookup referenced style ID */
-            const tt_node_t *p_node = FindNode( p_ctx->p_rootnode,
-                                                "style", -1, psz_id );
+            const tt_node_t *p_node = FindNode( p_nss,
+                                                p_ctx->p_rootnode,
+                                                "style", TT_NS,
+                                                -1, psz_id );
             if( p_node )
                 DictionaryMerge( &p_node->attr_dict, &tempdict, true );
 
@@ -628,23 +638,25 @@ static void DictMergeWithStyleID( ttml_context_t *p_ctx, const char *psz_styles,
     }
 }
 
-static void DictMergeWithRegionID( ttml_context_t *p_ctx, const char *psz_id,
-                                   vlc_dictionary_t *p_dst )
+static void DictMergeWithRegionID( ttml_context_t *p_ctx, tt_namespaces_t *p_nss,
+                                   const char *psz_id, vlc_dictionary_t *p_dst )
 {
     assert(p_ctx->p_rootnode);
     if( psz_id && p_ctx->p_rootnode )
     {
-        const tt_node_t *p_regionnode = FindNode( p_ctx->p_rootnode,
-                                                 "region", -1, psz_id );
+        const tt_node_t *p_regionnode = FindNode( p_nss,
+                                                  p_ctx->p_rootnode,
+                                                 "region", TT_NS,
+                                                 -1, psz_id );
         if( !p_regionnode )
             return;
 
         DictionaryMerge( &p_regionnode->attr_dict, p_dst, false );
 
-        const char *psz_styleid = (const char *)
-                vlc_dictionary_value_for_key( &p_regionnode->attr_dict, "style" );
+        const char *psz_styleid =
+            tt_node_GetAttribute( p_nss, p_regionnode, "style", NULL );
         if( psz_styleid )
-            DictMergeWithStyleID( p_ctx, psz_styleid, p_dst );
+            DictMergeWithStyleID( p_ctx, p_nss, psz_styleid, p_dst );
 
         for( const tt_basenode_t *p_child = p_regionnode->p_child;
                                   p_child; p_child = p_child->p_next )
@@ -653,7 +665,7 @@ static void DictMergeWithRegionID( ttml_context_t *p_ctx, const char *psz_id,
                 continue;
 
             const tt_node_t *p_node = (const tt_node_t *) p_child;
-            if( !tt_node_NameCompare( p_node->psz_node_name, "style" ) )
+            if( tt_node_Match( p_node, "style", TT_NS ) )
             {
                 DictionaryMerge( &p_node->attr_dict, p_dst, false );
             }
@@ -661,7 +673,8 @@ static void DictMergeWithRegionID( ttml_context_t *p_ctx, const char *psz_id,
     }
 }
 
-static void DictToTTMLStyle( ttml_context_t *p_ctx, const vlc_dictionary_t *p_dict,
+static void DictToTTMLStyle( ttml_context_t *p_ctx, tt_namespaces_t *p_nss,
+                             const vlc_dictionary_t *p_dict,
                              ttml_style_t *p_ttml_style )
 {
     for( int i = 0; i < p_dict->i_size; ++i )
@@ -669,13 +682,18 @@ static void DictToTTMLStyle( ttml_context_t *p_ctx, const vlc_dictionary_t *p_di
         for ( vlc_dictionary_entry_t* p_entry = p_dict->p_entries[i];
               p_entry != NULL; p_entry = p_entry->p_next )
         {
-            FillTTMLStyle( p_entry->psz_key, p_entry->p_value, p_ttml_style );
+            const char *psz_namespace = tt_namespaces_GetURI( p_nss, p_entry->psz_key );
+            if( !psz_namespace )
+                continue;
+            const char *psz_name = tt_LocalName( p_entry->psz_key );
+            FillTTMLStyle( psz_name, psz_namespace, p_entry->p_value, p_ttml_style );
         }
     }
     ComputeTTMLStyles( p_ctx, p_dict, p_ttml_style );
 }
 
-static ttml_style_t * InheritTTMLStyles( ttml_context_t *p_ctx, tt_node_t *p_node )
+static ttml_style_t * InheritTTMLStyles( ttml_context_t *p_ctx, tt_namespaces_t *p_nss,
+                                         tt_node_t *p_node )
 {
     assert( p_node );
     ttml_style_t *p_ttml_style = NULL;
@@ -687,20 +705,18 @@ static ttml_style_t * InheritTTMLStyles( ttml_context_t *p_ctx, tt_node_t *p_nod
     {
         DictionaryMerge( &p_node->attr_dict, &merged, false );
 
-        const char *psz_styleid = (const char *)
-                vlc_dictionary_value_for_key( &p_node->attr_dict, "style" );
+        const char *psz_styleid = tt_node_GetAttribute( p_nss, p_node, "style", NULL );
         if( psz_styleid )
-            DictMergeWithStyleID( p_ctx, psz_styleid, &merged );
+            DictMergeWithStyleID( p_ctx, p_nss, psz_styleid, &merged );
 
-        const char *psz_regionid = (const char *)
-                vlc_dictionary_value_for_key( &p_node->attr_dict, "region" );
+        const char *psz_regionid = tt_node_GetAttribute( p_nss, p_node, "region", NULL );
         if( psz_regionid )
-            DictMergeWithRegionID( p_ctx, psz_regionid, &merged );
+            DictMergeWithRegionID( p_ctx, p_nss, psz_regionid, &merged );
     }
 
     if( !vlc_dictionary_is_empty( &merged ) && (p_ttml_style = ttml_style_New()) )
     {
-        DictToTTMLStyle( p_ctx, &merged, p_ttml_style );
+        DictToTTMLStyle( p_ctx, p_nss, &merged, p_ttml_style );
     }
 
     vlc_dictionary_clear( &merged, NULL, NULL );
@@ -708,13 +724,14 @@ static ttml_style_t * InheritTTMLStyles( ttml_context_t *p_ctx, tt_node_t *p_nod
     return p_ttml_style;
 }
 
-static int ParseTTMLChunk( xml_reader_t *p_reader, tt_node_t **pp_rootnode )
+static int ParseTTMLChunk( xml_reader_t *p_reader, tt_namespaces_t *p_nss,
+                           tt_node_t **pp_rootnode )
 {
-    const char* psz_node_name;
+    const char *psz_node_name, *psz_node_namespace;
 
     do
     {
-        int i_type = xml_ReaderNextNode( p_reader, &psz_node_name );
+        int i_type = xml_ReaderNextNodeNS( p_reader, &psz_node_name, &psz_node_namespace );
 
         if( i_type <= XML_READER_NONE )
             break;
@@ -725,19 +742,20 @@ static int ParseTTMLChunk( xml_reader_t *p_reader, tt_node_t **pp_rootnode )
                 break;
 
             case XML_READER_STARTELEM:
-                if( tt_node_NameCompare( psz_node_name, "tt" ) ||
+                if( strcmp( psz_node_namespace, TT_NS ) ||
+                    strcmp( tt_LocalName( psz_node_name ), "tt" ) ||
                     *pp_rootnode != NULL )
                     return VLC_EGENERIC;
-
-                *pp_rootnode = tt_node_NewRead( p_reader, NULL, psz_node_name );
+                *pp_rootnode = tt_node_NewRead( p_reader, p_nss, NULL,
+                                                psz_node_name, psz_node_namespace );
                 if( !*pp_rootnode ||
-                    tt_nodes_Read( p_reader, *pp_rootnode ) != VLC_SUCCESS )
+                    tt_nodes_Read( p_reader, p_nss, *pp_rootnode ) != VLC_SUCCESS )
                     return VLC_EGENERIC;
                 break;
 
             case XML_READER_ENDELEM:
                 if( !*pp_rootnode ||
-                    tt_node_NameCompare( psz_node_name, (*pp_rootnode)->psz_node_name ) )
+                    strcmp( psz_node_name, (*pp_rootnode)->psz_node_name ) )
                     return VLC_EGENERIC;
                 break;
         }
@@ -791,7 +809,8 @@ static void StripSpacing( text_segment_t *p_segment )
         *p = ' ';
 }
 
-static ttml_region_t *GetTTMLRegion( ttml_context_t *p_ctx, const char *psz_region_id )
+static ttml_region_t *GetTTMLRegion( ttml_context_t *p_ctx, tt_namespaces_t *p_nss,
+                                     const char *psz_region_id )
 {
     ttml_region_t *p_region = ( ttml_region_t * )
             vlc_dictionary_value_for_key( &p_ctx->regions, psz_region_id ? psz_region_id : "" );
@@ -804,7 +823,7 @@ static ttml_region_t *GetTTMLRegion( ttml_context_t *p_ctx, const char *psz_regi
             vlc_dictionary_t merged;
             vlc_dictionary_init( &merged, 0 );
             /* Get all attributes, including region > style */
-            DictMergeWithRegionID( p_ctx, psz_region_id, &merged );
+            DictMergeWithRegionID( p_ctx, p_nss, psz_region_id, &merged );
             if( (p_region = ttml_region_New( false )) )
             {
                 /* Fill from its own attributes */
@@ -813,8 +832,12 @@ static ttml_region_t *GetTTMLRegion( ttml_context_t *p_ctx, const char *psz_regi
                     for ( vlc_dictionary_entry_t* p_entry = merged.p_entries[i];
                           p_entry != NULL; p_entry = p_entry->p_next )
                     {
-                        FillRegionStyle( p_ctx, p_entry->psz_key, p_entry->p_value,
-                                         p_region );
+                        const char *psz_namespace = tt_namespaces_GetURI( p_nss, p_entry->psz_key );
+                        if( !psz_namespace )
+                            continue;
+                        const char *psz_name = tt_LocalName( p_entry->psz_key );
+                        FillRegionStyle( p_ctx, psz_name, psz_namespace,
+                                         p_entry->p_value, p_region );
                     }
                 }
             }
@@ -840,7 +863,8 @@ static void AppendLineBreakToRegion( ttml_region_t *p_region )
     }
 }
 
-static void AppendTextToRegion( ttml_context_t *p_ctx, const tt_textnode_t *p_ttnode,
+static void AppendTextToRegion( ttml_context_t *p_ctx, tt_namespaces_t *p_nss,
+                                const tt_textnode_t *p_ttnode,
                                 const ttml_style_t *p_set_styles, ttml_region_t *p_region )
 {
     text_segment_t *p_segment;
@@ -852,7 +876,7 @@ static void AppendTextToRegion( ttml_context_t *p_ctx, const tt_textnode_t *p_tt
     if( p_segment )
     {
         bool b_preserve_space = false;
-        ttml_style_t *s = InheritTTMLStyles( p_ctx, p_ttnode->p_parent );
+        ttml_style_t *s = InheritTTMLStyles( p_ctx, p_nss, p_ttnode->p_parent );
         if( s )
         {
             if( p_set_styles )
@@ -899,12 +923,13 @@ static void AppendTextToRegion( ttml_context_t *p_ctx, const tt_textnode_t *p_tt
     p_region->pp_last_segment = &p_segment->p_next;
 }
 
-static const char * GetSMPTEImage( ttml_context_t *p_ctx, const char *psz_id )
+static const char * GetSMPTEImage( ttml_context_t *p_ctx, tt_namespaces_t *p_nss,
+                                   const char *psz_id )
 {
     if( !p_ctx->p_rootnode )
         return NULL;
 
-    tt_node_t *p_head = FindNode( p_ctx->p_rootnode, "head", 1, NULL );
+    tt_node_t *p_head = FindNode( p_nss, p_ctx->p_rootnode, "head", TT_NS, 1, NULL );
     if( !p_head )
         return NULL;
 
@@ -915,10 +940,11 @@ static const char * GetSMPTEImage( ttml_context_t *p_ctx, const char *psz_id )
             continue;
 
         tt_node_t *p_node = (tt_node_t *) p_child;
-        if( tt_node_NameCompare( p_node->psz_node_name, "metadata" ) )
+        if( !tt_node_Match( p_node, "metadata", TT_NS ) )
             continue;
 
-        tt_node_t *p_imagenode = FindNode( p_node, "smpte:image", 1, psz_id );
+        tt_node_t *p_imagenode = FindNode( p_nss, p_node, "image", TT_NS_SMPTE_TT_EXT,
+                                          1, psz_id );
         if( !p_imagenode )
             continue;
 
@@ -935,7 +961,8 @@ static const char * GetSMPTEImage( ttml_context_t *p_ctx, const char *psz_id )
     return NULL;
 }
 
-static void ConvertNodesToRegionContent( ttml_context_t *p_ctx, const tt_node_t *p_node,
+static void ConvertNodesToRegionContent( ttml_context_t *p_ctx, tt_namespaces_t *p_nss,
+                                         const tt_node_t *p_node,
                                          ttml_region_t *p_region,
                                          const ttml_style_t *p_upper_set_styles,
                                          tt_time_t playbacktime )
@@ -944,34 +971,29 @@ static void ConvertNodesToRegionContent( ttml_context_t *p_ctx, const tt_node_t
        !tt_timings_Contains( &p_node->timings, &playbacktime ) )
         return;
 
-    const char *psz_regionid = (const char *)
-        vlc_dictionary_value_for_key( &p_node->attr_dict, "region" );
+    const char *psz_regionid = tt_node_GetAttribute( p_nss, p_node, "region", NULL );
 
     /* Region isn't set or is changing */
     if( psz_regionid || p_region == NULL )
-        p_region = GetTTMLRegion( p_ctx, psz_regionid );
+        p_region = GetTTMLRegion( p_ctx, p_nss, psz_regionid );
 
     /* Check for bitmap profile defined by ST2052 / SMPTE-TT */
-    if( !tt_node_NameCompare( p_node->psz_node_name, "div" ) &&
-         vlc_dictionary_has_key( &p_node->attr_dict, "smpte:backgroundImage" ) )
+    if( tt_node_Match( p_node, "div", TT_NS ) )
     {
-        if( !p_region->bgbitmap.p_bytes )
+        const char *psz_id = tt_node_GetAttribute( p_nss, p_node, "backgroundImage",
+                                                   TT_NS_SMPTE_TT_EXT );
+        if( !p_region->bgbitmap.p_bytes && psz_id && *psz_id == '#' )
         {
-            const char *psz_id = vlc_dictionary_value_for_key( &p_node->attr_dict,
-                                                               "smpte:backgroundImage" );
             /* Seems SMPTE can't make diff between html and xml.. */
-            if( psz_id && *psz_id == '#' )
-            {
-                const char *psz_base64 = GetSMPTEImage( p_ctx, &psz_id[1] );
-                if( psz_base64 )
-                    p_region->bgbitmap.i_bytes =
-                        vlc_b64_decode_binary( &p_region->bgbitmap.p_bytes, psz_base64 );
-            }
+            const char *psz_base64 = GetSMPTEImage( p_ctx, p_nss, &psz_id[1] );
+            if( psz_base64 )
+                p_region->bgbitmap.i_bytes =
+                    vlc_b64_decode_binary( &p_region->bgbitmap.p_bytes, psz_base64 );
         }
     }
 
     /* awkward paragraph handling */
-    if( !tt_node_NameCompare( p_node->psz_node_name, "p" ) &&
+    if( tt_node_Match( p_node, "p", TT_NS ) &&
         p_region->updt.p_segments )
     {
         AppendLineBreakToRegion( p_region );
@@ -987,10 +1009,10 @@ static void ConvertNodesToRegionContent( ttml_context_t *p_ctx, const tt_node_t
     {
         if( p_child->i_type == TT_NODE_TYPE_TEXT )
         {
-            AppendTextToRegion( p_ctx, (const tt_textnode_t *) p_child,
+            AppendTextToRegion( p_ctx, p_nss, (const tt_textnode_t *) p_child,
                                 p_set_styles, p_region );
         }
-        else if( !tt_node_NameCompare( ((const tt_node_t *)p_child)->psz_node_name, "set" ) )
+        else if( tt_node_Match( (const tt_node_t *)p_child, "set", TT_NS ) )
         {
             const tt_node_t *p_set = (const tt_node_t *)p_child;
             if( !tt_time_Valid( &playbacktime ) ||
@@ -999,17 +1021,17 @@ static void ConvertNodesToRegionContent( ttml_context_t *p_ctx, const tt_node_t
                 if( p_set_styles != NULL || (p_set_styles = ttml_style_New()) )
                 {
                     /* Merge with or create a local set of styles to apply to following childs */
-                    DictToTTMLStyle( p_ctx, &p_set->attr_dict, p_set_styles );
+                    DictToTTMLStyle( p_ctx, p_nss, &p_set->attr_dict, p_set_styles );
                 }
             }
         }
-        else if( !tt_node_NameCompare( ((const tt_node_t *)p_child)->psz_node_name, "br" ) )
+        else if( tt_node_Match( (const tt_node_t *)p_child, "br", TT_NS ) )
         {
             AppendLineBreakToRegion( p_region );
         }
         else
         {
-            ConvertNodesToRegionContent( p_ctx, (const tt_node_t *) p_child,
+            ConvertNodesToRegionContent( p_ctx, p_nss, (const tt_node_t *) p_child,
                                          p_region, p_set_styles, playbacktime );
         }
     }
@@ -1018,7 +1040,8 @@ static void ConvertNodesToRegionContent( ttml_context_t *p_ctx, const tt_node_t
         ttml_style_Delete( p_set_styles );
 }
 
-static tt_node_t *ParseTTML( decoder_t *p_dec, const uint8_t *p_buffer, size_t i_buffer )
+static tt_node_t *ParseTTML( decoder_t *p_dec, tt_namespaces_t *p_nss,
+                             const uint8_t *p_buffer, size_t i_buffer )
 {
     stream_t*       p_sub;
     xml_reader_t*   p_xml_reader;
@@ -1035,7 +1058,7 @@ static tt_node_t *ParseTTML( decoder_t *p_dec, const uint8_t *p_buffer, size_t i
     }
 
     tt_node_t *p_rootnode = NULL;
-    if( ParseTTMLChunk( p_xml_reader, &p_rootnode ) != VLC_SUCCESS )
+    if( ParseTTMLChunk( p_xml_reader, p_nss, &p_rootnode ) != VLC_SUCCESS )
     {
         if( p_rootnode )
             tt_node_RecursiveDelete( p_rootnode );
@@ -1048,7 +1071,8 @@ static tt_node_t *ParseTTML( decoder_t *p_dec, const uint8_t *p_buffer, size_t i
     return p_rootnode;
 }
 
-static void InitTTMLContext( tt_node_t *p_rootnode, ttml_context_t *p_ctx )
+static void InitTTMLContext( tt_namespaces_t *p_nss, tt_node_t *p_rootnode,
+                             ttml_context_t *p_ctx )
 {
     p_ctx->p_rootnode = p_rootnode;
     /* set defaults required for size/cells computation */
@@ -1059,16 +1083,14 @@ static void InitTTMLContext( tt_node_t *p_rootnode, ttml_context_t *p_ctx )
     p_ctx->i_cell_resolution_v = TTML_DEFAULT_CELL_RESOLUTION_V;
     p_ctx->i_cell_resolution_h = TTML_DEFAULT_CELL_RESOLUTION_H;
     /* and override them */
-    const char *value = vlc_dictionary_value_for_key( &p_rootnode->attr_dict,
-                                                      "tts:extent" );
-    if( value != kVLCDictionaryNotFound )
+    const char *value = tt_node_GetAttribute( p_nss, p_rootnode, "extent", TT_NS_STYLING );
+    if( value )
     {
         ttml_read_coords( value, &p_ctx->root_extent_h,
                                &p_ctx->root_extent_v );
     }
-    value = vlc_dictionary_value_for_key( &p_rootnode->attr_dict,
-                                          "ttp:cellResolution" );
-    if( value != kVLCDictionaryNotFound )
+    value = tt_node_GetAttribute( p_nss, p_rootnode, "cellResolution", TT_NS_PARAMETER );
+    if( value )
     {
         unsigned w, h;
         if( sscanf( value, "%u %u", &w, &h) == 2 && w && h )
@@ -1079,22 +1101,23 @@ static void InitTTMLContext( tt_node_t *p_rootnode, ttml_context_t *p_ctx )
     }
 }
 
-static ttml_region_t *GenerateRegions( tt_node_t *p_rootnode, tt_time_t playbacktime )
+static ttml_region_t *GenerateRegions( tt_namespaces_t *p_nss, tt_node_t *p_rootnode,
+                                       tt_time_t playbacktime )
 {
     ttml_region_t*  p_regions = NULL;
     ttml_region_t** pp_region_last = &p_regions;
 
-    if( !tt_node_NameCompare( p_rootnode->psz_node_name, "tt" ) )
+    if( tt_node_Match( p_rootnode, "tt", TT_NS ) )
     {
-        const tt_node_t *p_bodynode = FindNode( p_rootnode, "body", 1, NULL );
+        const tt_node_t *p_bodynode = FindNode( p_nss, p_rootnode, "body", TT_NS, 1, NULL );
         if( p_bodynode )
         {
             ttml_context_t context;
-            InitTTMLContext( p_rootnode, &context );
+            InitTTMLContext( p_nss, p_rootnode, &context );
             context.p_rootnode = p_rootnode;
 
             vlc_dictionary_init( &context.regions, 1 );
-            ConvertNodesToRegionContent( &context, p_bodynode, NULL, NULL, playbacktime );
+            ConvertNodesToRegionContent( &context, p_nss, p_bodynode, NULL, NULL, playbacktime );
 
             for( int i = 0; i < context.regions.i_size; ++i )
             {
@@ -1109,8 +1132,8 @@ static ttml_region_t *GenerateRegions( tt_node_t *p_rootnode, tt_time_t playback
             vlc_dictionary_clear( &context.regions, NULL, NULL );
         }
     }
-    else if ( !tt_node_NameCompare( p_rootnode->psz_node_name, "div" ) ||
-              !tt_node_NameCompare( p_rootnode->psz_node_name, "p" ) )
+    else if ( tt_node_Match( p_rootnode, "div", TT_NS ) ||
+              tt_node_Match( p_rootnode, "p", TT_NS ) )
     {
         /* TODO */
     }
@@ -1314,9 +1337,14 @@ static int ParseBlock( decoder_t *p_dec, const block_t *p_block )
         return VLCDEC_SUCCESS;
     }
 
-    tt_node_t *p_rootnode = ParseTTML( p_dec, p_block->p_buffer, p_block->i_buffer );
+    tt_namespaces_t namespaces;
+    tt_namespaces_Init( &namespaces );
+    tt_node_t *p_rootnode = ParseTTML( p_dec, &namespaces, p_block->p_buffer, p_block->i_buffer );
     if( !p_rootnode )
+    {
+        tt_namespaces_Clean( &namespaces );
         return VLCDEC_SUCCESS;
+    }
 
     tt_timings_Resolve( (tt_basenode_t *) p_rootnode, &temporal_extent,
                         &p_timings_array, &i_timings_count );
@@ -1346,7 +1374,7 @@ static int ParseBlock( decoder_t *p_dec, const block_t *p_block )
 
         bool b_bitmap_regions = false;
         subpicture_t *p_spu = NULL;
-        ttml_region_t *p_regions = GenerateRegions( p_rootnode, p_timings_array[i] );
+        ttml_region_t *p_regions = GenerateRegions( &namespaces, p_rootnode, p_timings_array[i] );
         if( p_regions )
         {
             if( p_regions->bgbitmap.i_bytes > 0 && p_regions->updt.p_segments == NULL )
@@ -1388,6 +1416,7 @@ static int ParseBlock( decoder_t *p_dec, const block_t *p_block )
     }
 
     tt_node_RecursiveDelete( p_rootnode );
+    tt_namespaces_Clean( &namespaces );
 
     free( p_timings_array );
 


=====================================
modules/codec/ttml/ttml.c
=====================================
@@ -65,12 +65,134 @@ vlc_module_begin ()
 #endif
 vlc_module_end ()
 
+struct tt_namespace_s
+{
+    char *psz_prefix;
+    char *psz_uri;
+    struct vlc_list links;
+};
 
-int tt_node_NameCompare( const char* psz_tagname, const char* psz_pattern )
+void tt_namespaces_Clean( tt_namespaces_t *nss )
 {
-    if( !strncasecmp( "tt:", psz_tagname, 3 ) )
-        psz_tagname += 3;
-    return strcasecmp( psz_tagname, psz_pattern );
+    struct tt_namespace_s *ns;
+    vlc_list_foreach( ns, &nss->nodes, links )
+    {
+        free( ns->psz_prefix );
+        free( ns->psz_uri );
+        free( ns );
+    }
+}
+
+void tt_namespaces_Init( tt_namespaces_t *nss )
+{
+    vlc_list_init( &nss->nodes );
+}
+
+const char * tt_namespaces_GetURI( const tt_namespaces_t *nss,
+                                   const char *psz_qn )
+{
+    const struct tt_namespace_s *ns;
+    vlc_list_foreach_const( ns, &nss->nodes, links )
+    {
+        /* compares prefixed name against raw prefix */
+        for( size_t i=0; ; i++ )
+        {
+            if( ns->psz_prefix[i] == psz_qn[i] )
+            {
+                if( psz_qn[i] == '\0' )
+                    return ns->psz_uri;
+            }
+            else
+            {
+                if( ns->psz_prefix[i] == '\0' && psz_qn[i] == ':' )
+                    return ns->psz_uri;
+                else
+                    break;
+            }
+        }
+    }
+    return NULL;
+}
+
+const char * tt_namespaces_GetPrefix( const tt_namespaces_t *nss,
+                                      const char *psz_uri )
+{
+    const struct tt_namespace_s *ns;
+    vlc_list_foreach_const( ns, &nss->nodes, links )
+    {
+        if( !strcmp( ns->psz_uri, psz_uri ) )
+            return ns->psz_prefix;
+    }
+    return NULL;
+}
+
+void tt_namespaces_Register( tt_namespaces_t *nss, const char *psz_prefix,
+                             const char *psz_uri )
+{
+    if( tt_namespaces_GetPrefix( nss, psz_uri ) )
+        return;
+    struct tt_namespace_s *ns = malloc(sizeof(*ns));
+    if( ns )
+    {
+        const char *sep = strchr( psz_prefix, ':' );
+        if( sep )
+            ns->psz_prefix = strndup( psz_prefix, sep - psz_prefix );
+        else
+            ns->psz_prefix = strdup("");
+        ns->psz_uri = strdup( psz_uri );
+        if( !ns->psz_prefix || !ns->psz_uri )
+        {
+            free( ns->psz_prefix );
+            free( ns->psz_uri );
+            free( ns );
+            return;
+        }
+        vlc_list_append( &ns->links, &nss->nodes );
+    }
+}
+
+static const char * tt_node_InheritNS( const tt_node_t *p_node )
+{
+    for( ; p_node ; p_node = p_node->p_parent )
+    {
+        if( p_node->psz_namespace )
+            return p_node->psz_namespace;
+    }
+    return NULL;
+}
+
+bool tt_node_Match( const tt_node_t *p_node, const char *psz_name, const char *psz_namespace )
+{
+    /* compare local part first (should have less chars) */
+    const char *psz_nodelocal = tt_LocalName( p_node->psz_node_name );
+    const char *psz_namelocal = tt_LocalName( psz_name );
+    if( strcmp( psz_namelocal, psz_nodelocal ) )
+        return false;
+
+    const char *psz_nodens = p_node->psz_namespace;
+    if( !psz_nodens )
+        psz_nodens = tt_node_InheritNS( p_node->p_parent );
+    if( psz_namespace && psz_nodens )
+        return !strcmp( psz_namespace, psz_nodens );
+    return !!psz_namespace == !!psz_nodens;
+}
+
+const char * tt_node_GetAttribute( tt_namespaces_t *p_nss, const tt_node_t *p_node,
+                                   const char *psz_name, const char *psz_namespace )
+{
+    const void *value;
+    char *alloc = NULL;
+    if( psz_namespace )
+    {
+        const char *psz_prefix = tt_namespaces_GetPrefix( p_nss, psz_namespace );
+        if( psz_prefix == NULL ||
+            asprintf( &alloc, "%s:%s", psz_prefix, psz_name ) < 1 )
+            return NULL;
+        psz_name = alloc;
+    }
+    value = vlc_dictionary_value_for_key( &p_node->attr_dict, psz_name );
+    free( alloc );
+    return value != kVLCDictionaryNotFound ? (const char *)value : NULL;
 }
 
 bool tt_node_HasChild( const tt_node_t *p_node )
@@ -171,6 +293,7 @@ static void tt_node_FreeDictValue( void* p_value, void* p_obj )
 static void tt_node_Delete( tt_node_t *p_node )
 {
     free( p_node->psz_node_name );
+    free( p_node->psz_namespace );
     vlc_dictionary_clear( &p_node->attr_dict, tt_node_FreeDictValue, NULL );
     free( p_node );
 }
@@ -240,7 +363,9 @@ tt_textnode_t *tt_subtextnode_New( tt_node_t *p_parent, const char *psz_text, si
     return tt_textnode_NewImpl( p_parent, strndup( psz_text, len ) );
 }
 
-tt_node_t * tt_node_New( tt_node_t* p_parent, const char* psz_node_name )
+tt_node_t * tt_node_New( tt_node_t* p_parent,
+                         const char* psz_node_name,
+                         const char *psz_namespace )
 {
     tt_node_t *p_node = calloc( 1, sizeof( *p_node ) );
     if( !p_node )
@@ -248,6 +373,13 @@ tt_node_t * tt_node_New( tt_node_t* p_parent, const char* psz_node_name )
 
     p_node->i_type = TT_NODE_TYPE_ELEMENT;
     p_node->psz_node_name = strdup( psz_node_name );
+    const char *psz_parent_ns = tt_node_InheritNS( p_parent );
+    /* set new namespace if not same as parent */
+    if( psz_namespace &&
+        (!psz_parent_ns || strcmp( psz_namespace, psz_parent_ns )) )
+        p_node->psz_namespace = strdup( psz_namespace );
+    else
+        p_node->psz_namespace = NULL;
     if( unlikely( p_node->psz_node_name == NULL ) )
     {
         free( p_node );
@@ -264,17 +396,21 @@ tt_node_t * tt_node_New( tt_node_t* p_parent, const char* psz_node_name )
     return p_node;
 }
 
-tt_node_t * tt_node_NewRead( xml_reader_t* reader, tt_node_t* p_parent, const char* psz_node_name )
+tt_node_t * tt_node_NewRead( xml_reader_t* reader,
+                             tt_namespaces_t *p_nss, tt_node_t* p_parent,
+                             const char* psz_node_name, const char *psz_namespace )
 {
-    tt_node_t *p_node = tt_node_New( p_parent, psz_node_name );
+    tt_node_t *p_node = tt_node_New( p_parent, psz_node_name, psz_namespace );
     if( !p_node )
         return NULL;
 
-    const char* psz_value = NULL;
-    for( const char* psz_key = xml_ReaderNextAttr( reader, &psz_value );
+    const char* psz_value = NULL, *psz_ns = NULL;
+    for( const char* psz_key = xml_ReaderNextAttrNS( reader, &psz_value, &psz_ns );
          psz_key != NULL;
-         psz_key = xml_ReaderNextAttr( reader, &psz_value ) )
+         psz_key = xml_ReaderNextAttrNS( reader, &psz_value, &psz_ns ) )
     {
+        if( psz_ns && psz_key )
+        tt_namespaces_Register( p_nss, psz_key, psz_ns );
         char *psz_val = strdup( psz_value );
         if( psz_val )
         {
@@ -351,15 +487,15 @@ static int tt_node_Skip( xml_reader_t *p_reader, const char *psz_skipped )
     return VLC_EGENERIC;
 }
 #endif
-int tt_nodes_Read( xml_reader_t *p_reader, tt_node_t *p_root_node )
+int tt_nodes_Read( xml_reader_t *p_reader, tt_namespaces_t *p_nss, tt_node_t *p_root_node )
 {
     size_t i_depth = 0;
     tt_node_t *p_node = p_root_node;
 
     do
     {
-        const char* psz_node_name;
-        int i_type = xml_ReaderNextNode( p_reader, &psz_node_name );
+        const char *psz_node_name, *psz_node_namespace;
+        int i_type = xml_ReaderNextNodeNS( p_reader, &psz_node_name, &psz_node_namespace );
         /* !warn read empty state now as attributes reading will **** it up */
         bool b_empty = xml_ReaderIsEmptyElement( p_reader );
 
@@ -373,7 +509,10 @@ int tt_nodes_Read( xml_reader_t *p_reader, tt_node_t *p_root_node )
 
             case XML_READER_STARTELEM:
             {
-                tt_node_t *p_newnode = tt_node_NewRead( p_reader, p_node, psz_node_name );
+                tt_namespaces_Register( p_nss, psz_node_name, psz_node_namespace );
+                tt_node_t *p_newnode = tt_node_NewRead( p_reader, p_nss, p_node,
+                                                        psz_node_name,
+                                                        psz_node_namespace );
                 if( !p_newnode )
                     return VLC_EGENERIC;
                 if( !b_empty )
@@ -393,7 +532,7 @@ int tt_nodes_Read( xml_reader_t *p_reader, tt_node_t *p_root_node )
 
             case XML_READER_ENDELEM:
             {
-                if( strcmp( psz_node_name, p_node->psz_node_name ) )
+                if( !tt_node_Match( p_node, psz_node_name, psz_node_namespace ) )
                     return VLC_EGENERIC;
 
                 if( i_depth == 0 )


=====================================
modules/codec/ttml/ttml.h
=====================================
@@ -21,6 +21,7 @@
 #include <vlc_tick.h>
 #include <vlc_arrays.h>
 #include <vlc_memstream.h>
+#include <vlc_list.h>
 
 int tt_OpenDemux( vlc_object_t* p_this );
 void tt_CloseDemux( vlc_object_t* p_demux );
@@ -67,6 +68,22 @@ struct tt_searchkey
 #define TT_NS_PROFILE     TT_NS "/profile/"
 #define TT_NS_FEATURE     TT_NS "/feature/"
 #define TT_NS_EXTENSION   TT_NS "/extension/"
+#define TT_NS_XML             "http://www.w3.org/XML/1998/namespace"
+#define TT_NS_SMPTE_TT_EXT    "http://www.smpte-ra.org/schemas/2052-1/2010/smpte-tt"
+
+typedef struct
+{
+    struct vlc_list nodes;
+} tt_namespaces_t;
+
+void tt_namespaces_Init( tt_namespaces_t *nss );
+void tt_namespaces_Clean( tt_namespaces_t *nss );
+void tt_namespaces_Register( tt_namespaces_t *nss, const char *psz_prefix,
+                             const char *psz_uri );
+const char * tt_namespaces_GetURI( const tt_namespaces_t *nss,
+                                   const char *psz_qn ); /* qn or prefix */
+const char * tt_namespaces_GetPrefix( const tt_namespaces_t *nss,
+                                      const char *psz_uri );
 
 enum
 {
@@ -94,6 +111,7 @@ struct tt_node_t
     char *psz_node_name;
     tt_timings_t timings;
     vlc_dictionary_t attr_dict;
+    char *psz_namespace;
 };
 
 typedef struct
@@ -102,17 +120,26 @@ typedef struct
     char *psz_text;
 } tt_textnode_t;
 
+static inline const char *tt_LocalName( const char *psz_qname )
+{
+    const char *psz_local = strchr( psz_qname, ':' );
+    return psz_local ? psz_local + 1 : psz_qname;
+}
+
 tt_textnode_t *tt_textnode_New( tt_node_t *p_parent, const char *psz_text );
 tt_textnode_t *tt_subtextnode_New( tt_node_t *p_parent, const char *psz_text, size_t );
-tt_node_t * tt_node_New( tt_node_t* p_parent, const char* psz_node_name );
-tt_node_t * tt_node_NewRead( xml_reader_t* reader, tt_node_t* p_parent, const char* psz_node_name );
+tt_node_t * tt_node_New( tt_node_t* p_parent, const char* psz_node_name, const char *psz_namespace );
+tt_node_t * tt_node_NewRead( xml_reader_t* reader, tt_namespaces_t *, tt_node_t* p_parent,
+                             const char* psz_node_name, const char *psz_namespace );
 void tt_node_RecursiveDelete( tt_node_t *p_node );
-int  tt_node_NameCompare( const char* psz_tagname, const char* psz_pattern );
+bool tt_node_Match( const tt_node_t *p_node, const char* psz_name, const char* psz_namespace );
+const char * tt_node_GetAttribute( tt_namespaces_t *, const tt_node_t *p_node,
+                                   const char *psz_name, const char *psz_namespace );
 bool tt_node_HasChild( const tt_node_t *p_node );
 int  tt_node_AddAttribute( tt_node_t *p_node, const char *key, const char *value );
 void tt_node_RemoveAttribute( tt_node_t *p_node, const char *key );
 
-int tt_nodes_Read( xml_reader_t *p_reader, tt_node_t *p_root_node );
+int tt_nodes_Read( xml_reader_t *p_reader, tt_namespaces_t *, tt_node_t *p_root_node );
 
 void tt_timings_Resolve( tt_basenode_t *p_child, const tt_timings_t *p_container_timings,
                          tt_time_t **pp_array, size_t *pi_count );


=====================================
modules/demux/ttml.c
=====================================
@@ -51,6 +51,7 @@ typedef struct
     bool            b_first_time;
 
     tt_node_t         *p_rootnode;
+    tt_namespaces_t   namespaces;
 
     tt_timings_t    temporal_extent;
 
@@ -157,11 +158,11 @@ static int Control( demux_t* p_demux, int i_query, va_list args )
 static int ReadTTML( demux_t* p_demux )
 {
     demux_sys_t* p_sys = p_demux->p_sys;
-    const char* psz_node_name;
+    const char* psz_node_name, *psz_node_namespace;
 
     do
     {
-        int i_type = xml_ReaderNextNode( p_sys->p_reader, &psz_node_name );
+        int i_type = xml_ReaderNextNodeNS( p_sys->p_reader, &psz_node_name, &psz_node_namespace );
         bool b_empty = xml_ReaderIsEmptyElement( p_sys->p_reader );
 
         if( i_type <= XML_READER_NONE )
@@ -173,21 +174,25 @@ static int ReadTTML( demux_t* p_demux )
                 break;
 
             case XML_READER_STARTELEM:
-                if( tt_node_NameCompare( psz_node_name, "tt" ) ||
+                if( strcmp( psz_node_namespace, TT_NS ) ||
+                    strcmp( tt_LocalName( psz_node_name ), "tt" ) ||
                     p_sys->p_rootnode != NULL )
                     return VLC_EGENERIC;
 
-                p_sys->p_rootnode = tt_node_NewRead( p_sys->p_reader, NULL, psz_node_name );
+                p_sys->p_rootnode = tt_node_NewRead( p_sys->p_reader, &p_sys->namespaces, NULL,
+                                                     psz_node_name,
+                                                     psz_node_namespace );
                 if( b_empty )
                     break;
                 if( !p_sys->p_rootnode ||
-                    tt_nodes_Read( p_sys->p_reader, p_sys->p_rootnode ) != VLC_SUCCESS )
+                    tt_nodes_Read( p_sys->p_reader,
+                                  &p_sys->namespaces, p_sys->p_rootnode ) != VLC_SUCCESS )
                     return VLC_EGENERIC;
                 break;
 
             case XML_READER_ENDELEM:
                 if( !p_sys->p_rootnode ||
-                    tt_node_NameCompare( psz_node_name, p_sys->p_rootnode->psz_node_name ) )
+                    strcmp( psz_node_name, p_sys->p_rootnode->psz_node_name ) )
                     return VLC_EGENERIC;
                 break;
         }
@@ -341,6 +346,7 @@ int tt_OpenDemux( vlc_object_t* p_this )
     tt_time_Init( &p_sys->temporal_extent.end );
     tt_time_Init( &p_sys->temporal_extent.dur );
     p_sys->temporal_extent.begin.base = 0;
+    tt_namespaces_Init( &p_sys->namespaces );
 
     p_sys->p_xml = xml_Create( p_demux );
     if( !p_sys->p_xml )
@@ -418,6 +424,8 @@ void tt_CloseDemux( vlc_object_t* p_this )
     if( p_sys->p_xml )
         xml_Delete( p_sys->p_xml );
 
+    tt_namespaces_Clean( &p_sys->namespaces );
+
     free( p_sys->times.p_array );
 
     free( p_sys );



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/54c93e010b96eb0ba046b39c4c61a3ecc95bee06...44879313bab5b7208b12cb4968cf03a6f521e531

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/54c93e010b96eb0ba046b39c4c61a3ecc95bee06...44879313bab5b7208b12cb4968cf03a6f521e531
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