codec/demux: ttml: handle non-default namespaces

This commit is contained in:
François Cartegnie 2024-02-29 17:56:06 +01:00 committed by Felix Paul Kühne
parent a55c9e9a6b
commit 44879313ba
4 changed files with 196 additions and 145 deletions

View File

@ -124,7 +124,8 @@ enum
* Then we convert attributes, merging with style by id or region * Then we convert attributes, merging with style by id or region
* style, and sets from parent node. * 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 ) 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; 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 ) 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 ) 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 */ 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 ) ) if( psz && !strcmp( psz, psz_id ) )
return p_node; 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 ) if( p_child->i_type == TT_NODE_TYPE_TEXT )
continue; 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 ) if( p_node )
return 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, static void FillTextStyle( const char *psz_attr, const char *psz_val,
text_style_t *p_text_style ) text_style_t *p_text_style )
{ {
if( !strcasecmp ( "tts:fontFamily", psz_attr ) ) if( !strcasecmp ( "fontFamily", psz_attr ) )
{ {
free( p_text_style->psz_fontname ); free( p_text_style->psz_fontname );
p_text_style->psz_fontname = strdup( psz_val ); 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_background_alpha = atoi( psz_val );
p_text_style->i_font_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; 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 ); unsigned int i_color = vlc_html_color( psz_val, NULL );
p_text_style->i_font_color = (i_color & 0xffffff); p_text_style->i_font_color = (i_color & 0xffffff);
p_text_style->i_font_alpha = (i_color & 0xFF000000) >> 24; p_text_style->i_font_alpha = (i_color & 0xFF000000) >> 24;
p_text_style->i_features |= STYLE_HAS_FONT_COLOR | STYLE_HAS_FONT_ALPHA; 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 ); unsigned int i_color = vlc_html_color( psz_val, NULL );
p_text_style->i_background_color = i_color & 0xFFFFFF; 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; | STYLE_HAS_BACKGROUND_ALPHA;
p_text_style->i_style_flags |= STYLE_BACKGROUND; 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 ) ) if( !strcasecmp ( "italic", psz_val ) || !strcasecmp ( "oblique", psz_val ) )
p_text_style->i_style_flags |= STYLE_ITALIC; 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_style_flags &= ~STYLE_ITALIC;
p_text_style->i_features |= STYLE_HAS_FLAGS; 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 ) ) if( !strcasecmp ( "bold", psz_val ) )
p_text_style->i_style_flags |= STYLE_BOLD; 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_style_flags &= ~STYLE_BOLD;
p_text_style->i_features |= STYLE_HAS_FLAGS; 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 ) ) if( !strcasecmp ( "underline", psz_val ) )
p_text_style->i_style_flags |= STYLE_UNDERLINE; 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_style_flags &= ~STYLE_STRIKEOUT;
p_text_style->i_features |= STYLE_HAS_FLAGS; 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 *value = strdup( psz_val );
char* psz_saveptr = NULL; 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, static void FillRegionStyle( ttml_context_t *p_ctx,
const char *psz_attr, const char *psz_val, const char *psz_attr, const char *psz_namespace,
ttml_region_t *p_region ) 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); p_region->updt.inner_align &= ~(SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_BOTTOM);
if( !strcasecmp( "after", psz_val ) ) if( !strcasecmp( "after", psz_val ) )
@ -445,12 +452,12 @@ static void FillRegionStyle( ttml_context_t *p_ctx,
/* "before" */ /* "before" */
p_region->updt.inner_align |= SUBPICTURE_ALIGN_TOP; p_region->updt.inner_align |= SUBPICTURE_ALIGN_TOP;
} }
else if( !strcasecmp ( "tts:origin", psz_attr ) || else if( !strcasecmp ( "origin", psz_attr ) ||
!strcasecmp ( "tts:extent", psz_attr ) ) !strcasecmp ( "extent", psz_attr ) )
{ {
ttml_length_t x, y; ttml_length_t x, y;
if( ttml_read_coords( psz_val, &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; p_text_style->i_font_size = len.i_value;
} }
static void FillTTMLStyle( const char *psz_attr, const char *psz_val, static void FillTTMLStyle( const char *psz_attr, const char *psz_namespace,
ttml_style_t *p_ttml_style ) 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, ttml_read_coords( psz_val, &p_ttml_style->extent_h,
&p_ttml_style->extent_v ); &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, ttml_read_coords( psz_val, &p_ttml_style->origin_h,
&p_ttml_style->origin_v ); &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); p_ttml_style->i_text_align &= ~(SUBPICTURE_ALIGN_LEFT|SUBPICTURE_ALIGN_RIGHT);
if( !strcasecmp ( "left", psz_val ) ) 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); printf("**%s %x\n", psz_val, p_ttml_style->i_text_align);
#endif #endif
} }
else if( !strcasecmp( "tts:fontSize", psz_attr ) ) else if( !strcasecmp( "fontSize", psz_attr ) )
{ {
ttml_length_t len = ttml_read_length( psz_val ); ttml_length_t len = ttml_read_length( psz_val );
if( len.unit != TTML_UNIT_UNKNOWN && len.i_value > 0.0 ) if( len.unit != TTML_UNIT_UNKNOWN && len.i_value > 0.0 )
p_ttml_style->font_size = len; p_ttml_style->font_size = len;
} }
else if( !strcasecmp( "tts:direction", psz_attr ) ) else if( !strcasecmp( "direction", psz_attr ) )
{ {
if( !strcasecmp( "rtl", psz_val ) ) 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; 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 ) ) if( !strcasecmp( "bidiOverride", psz_val ) )
p_ttml_style->i_direction |= UNICODE_BIDI_OVERRIDE & ~UNICODE_BIDI_EMBEDDED; p_ttml_style->i_direction |= UNICODE_BIDI_OVERRIDE & ~UNICODE_BIDI_EMBEDDED;
else if( !strcasecmp( "embed", psz_val ) ) else if( !strcasecmp( "embed", psz_val ) )
p_ttml_style->i_direction |= UNICODE_BIDI_EMBEDDED & ~UNICODE_BIDI_OVERRIDE; 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 ) ) 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; 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 ) ) if( !strcmp( "none", psz_val ) )
p_ttml_style->display = TTML_DISPLAY_NONE; p_ttml_style->display = TTML_DISPLAY_NONE;
else else
p_ttml_style->display = TTML_DISPLAY_AUTO; 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 ); 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]; for ( const vlc_dictionary_entry_t* p_entry = p_src->p_entries[i];
p_entry != NULL; p_entry = p_entry->p_next ) p_entry != NULL; p_entry = p_entry->p_next )
{ {
if( !strncmp( "tts:", p_entry->psz_key, 4 ) || if( vlc_dictionary_has_key( p_dst, p_entry->psz_key ) )
!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( b_override )
{ {
if( b_override ) vlc_dictionary_remove_value_for_key( p_dst, p_entry->psz_key, NULL, NULL );
{
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 ); 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, static void DictMergeWithStyleID( ttml_context_t *p_ctx, tt_namespaces_t *p_nss,
vlc_dictionary_t *p_dst ) const char *psz_styles, vlc_dictionary_t *p_dst )
{ {
assert(p_ctx->p_rootnode); assert(p_ctx->p_rootnode);
char *psz_dup; char *psz_dup;
@ -612,8 +620,10 @@ static void DictMergeWithStyleID( ttml_context_t *p_ctx, const char *psz_styles,
while( psz_id ) while( psz_id )
{ {
/* Lookup referenced style ID */ /* Lookup referenced style ID */
const tt_node_t *p_node = FindNode( p_ctx->p_rootnode, const tt_node_t *p_node = FindNode( p_nss,
"style", -1, psz_id ); p_ctx->p_rootnode,
"style", TT_NS,
-1, psz_id );
if( p_node ) if( p_node )
DictionaryMerge( &p_node->attr_dict, &tempdict, true ); 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, static void DictMergeWithRegionID( ttml_context_t *p_ctx, tt_namespaces_t *p_nss,
vlc_dictionary_t *p_dst ) const char *psz_id, vlc_dictionary_t *p_dst )
{ {
assert(p_ctx->p_rootnode); assert(p_ctx->p_rootnode);
if( psz_id && p_ctx->p_rootnode ) if( psz_id && p_ctx->p_rootnode )
{ {
const tt_node_t *p_regionnode = FindNode( p_ctx->p_rootnode, const tt_node_t *p_regionnode = FindNode( p_nss,
"region", -1, psz_id ); p_ctx->p_rootnode,
"region", TT_NS,
-1, psz_id );
if( !p_regionnode ) if( !p_regionnode )
return; return;
DictionaryMerge( &p_regionnode->attr_dict, p_dst, false ); DictionaryMerge( &p_regionnode->attr_dict, p_dst, false );
const char *psz_styleid = (const char *) const char *psz_styleid =
vlc_dictionary_value_for_key( &p_regionnode->attr_dict, "style" ); tt_node_GetAttribute( p_nss, p_regionnode, "style", NULL );
if( psz_styleid ) 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; for( const tt_basenode_t *p_child = p_regionnode->p_child;
p_child; p_child = p_child->p_next ) 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; continue;
const tt_node_t *p_node = (const tt_node_t *) p_child; 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 ); 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 ) ttml_style_t *p_ttml_style )
{ {
for( int i = 0; i < p_dict->i_size; ++i ) 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]; for ( vlc_dictionary_entry_t* p_entry = p_dict->p_entries[i];
p_entry != NULL; p_entry = p_entry->p_next ) 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 ); 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 ); assert( p_node );
ttml_style_t *p_ttml_style = NULL; 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 ); DictionaryMerge( &p_node->attr_dict, &merged, false );
const char *psz_styleid = (const char *) const char *psz_styleid = tt_node_GetAttribute( p_nss, p_node, "style", NULL );
vlc_dictionary_value_for_key( &p_node->attr_dict, "style" );
if( psz_styleid ) if( psz_styleid )
DictMergeWithStyleID( p_ctx, psz_styleid, &merged ); DictMergeWithStyleID( p_ctx, p_nss, psz_styleid, &merged );
const char *psz_regionid = (const char *) const char *psz_regionid = tt_node_GetAttribute( p_nss, p_node, "region", NULL );
vlc_dictionary_value_for_key( &p_node->attr_dict, "region" );
if( psz_regionid ) 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()) ) 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 ); vlc_dictionary_clear( &merged, NULL, NULL );
@ -708,14 +724,15 @@ static ttml_style_t * InheritTTMLStyles( ttml_context_t *p_ctx, tt_node_t *p_nod
return p_ttml_style; 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, *psz_node_namespace; const char *psz_node_name, *psz_node_namespace;
do do
{ {
int i_type = xml_ReaderNextNodeNS( p_reader, &psz_node_name, &psz_node_namespace ); int i_type = xml_ReaderNextNodeNS( p_reader, &psz_node_name, &psz_node_namespace );
fprintf(stderr, "Parse %s %s \n", psz_node_name, psz_node_namespace);
if( i_type <= XML_READER_NONE ) if( i_type <= XML_READER_NONE )
break; break;
@ -725,20 +742,20 @@ static int ParseTTMLChunk( xml_reader_t *p_reader, tt_node_t **pp_rootnode )
break; break;
case XML_READER_STARTELEM: 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 ) *pp_rootnode != NULL )
return VLC_EGENERIC; return VLC_EGENERIC;
*pp_rootnode = tt_node_NewRead( p_reader, p_nss, NULL,
*pp_rootnode = tt_node_NewRead( p_reader, NULL, psz_node_name, psz_node_name, psz_node_namespace );
psz_node_namespace );
if( !*pp_rootnode || 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; return VLC_EGENERIC;
break; break;
case XML_READER_ENDELEM: case XML_READER_ENDELEM:
if( !*pp_rootnode || 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; return VLC_EGENERIC;
break; break;
} }
@ -792,7 +809,8 @@ static void StripSpacing( text_segment_t *p_segment )
*p = ' '; *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 * ) ttml_region_t *p_region = ( ttml_region_t * )
vlc_dictionary_value_for_key( &p_ctx->regions, psz_region_id ? psz_region_id : "" ); vlc_dictionary_value_for_key( &p_ctx->regions, psz_region_id ? psz_region_id : "" );
@ -805,7 +823,7 @@ static ttml_region_t *GetTTMLRegion( ttml_context_t *p_ctx, const char *psz_regi
vlc_dictionary_t merged; vlc_dictionary_t merged;
vlc_dictionary_init( &merged, 0 ); vlc_dictionary_init( &merged, 0 );
/* Get all attributes, including region > style */ /* 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 )) ) if( (p_region = ttml_region_New( false )) )
{ {
/* Fill from its own attributes */ /* Fill from its own attributes */
@ -814,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]; for ( vlc_dictionary_entry_t* p_entry = merged.p_entries[i];
p_entry != NULL; p_entry = p_entry->p_next ) p_entry != NULL; p_entry = p_entry->p_next )
{ {
FillRegionStyle( p_ctx, p_entry->psz_key, p_entry->p_value, const char *psz_namespace = tt_namespaces_GetURI( p_nss, p_entry->psz_key );
p_region ); 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 );
} }
} }
} }
@ -841,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 ) const ttml_style_t *p_set_styles, ttml_region_t *p_region )
{ {
text_segment_t *p_segment; text_segment_t *p_segment;
@ -853,7 +876,7 @@ static void AppendTextToRegion( ttml_context_t *p_ctx, const tt_textnode_t *p_tt
if( p_segment ) if( p_segment )
{ {
bool b_preserve_space = false; 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( s )
{ {
if( p_set_styles ) if( p_set_styles )
@ -900,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; 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 ) if( !p_ctx->p_rootnode )
return NULL; 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 ) if( !p_head )
return NULL; return NULL;
@ -916,10 +940,11 @@ static const char * GetSMPTEImage( ttml_context_t *p_ctx, const char *psz_id )
continue; continue;
tt_node_t *p_node = (tt_node_t *) p_child; 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; 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 ) if( !p_imagenode )
continue; continue;
@ -936,7 +961,8 @@ static const char * GetSMPTEImage( ttml_context_t *p_ctx, const char *psz_id )
return NULL; 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, ttml_region_t *p_region,
const ttml_style_t *p_upper_set_styles, const ttml_style_t *p_upper_set_styles,
tt_time_t playbacktime ) tt_time_t playbacktime )
@ -945,34 +971,29 @@ static void ConvertNodesToRegionContent( ttml_context_t *p_ctx, const tt_node_t
!tt_timings_Contains( &p_node->timings, &playbacktime ) ) !tt_timings_Contains( &p_node->timings, &playbacktime ) )
return; return;
const char *psz_regionid = (const char *) const char *psz_regionid = tt_node_GetAttribute( p_nss, p_node, "region", NULL );
vlc_dictionary_value_for_key( &p_node->attr_dict, "region" );
/* Region isn't set or is changing */ /* Region isn't set or is changing */
if( psz_regionid || p_region == NULL ) 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 */ /* Check for bitmap profile defined by ST2052 / SMPTE-TT */
if( !tt_node_NameCompare( p_node->psz_node_name, "div" ) && if( tt_node_Match( p_node, "div", TT_NS ) )
vlc_dictionary_has_key( &p_node->attr_dict, "smpte:backgroundImage" ) )
{ {
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.. */ /* Seems SMPTE can't make diff between html and xml.. */
if( psz_id && *psz_id == '#' ) const char *psz_base64 = GetSMPTEImage( p_ctx, p_nss, &psz_id[1] );
{ if( psz_base64 )
const char *psz_base64 = GetSMPTEImage( p_ctx, &psz_id[1] ); p_region->bgbitmap.i_bytes =
if( psz_base64 ) vlc_b64_decode_binary( &p_region->bgbitmap.p_bytes, psz_base64 );
p_region->bgbitmap.i_bytes =
vlc_b64_decode_binary( &p_region->bgbitmap.p_bytes, psz_base64 );
}
} }
} }
/* awkward paragraph handling */ /* 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 ) p_region->updt.p_segments )
{ {
AppendLineBreakToRegion( p_region ); AppendLineBreakToRegion( p_region );
@ -988,10 +1009,10 @@ static void ConvertNodesToRegionContent( ttml_context_t *p_ctx, const tt_node_t
{ {
if( p_child->i_type == TT_NODE_TYPE_TEXT ) 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 ); 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; const tt_node_t *p_set = (const tt_node_t *)p_child;
if( !tt_time_Valid( &playbacktime ) || if( !tt_time_Valid( &playbacktime ) ||
@ -1000,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()) ) 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 */ /* 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 ); AppendLineBreakToRegion( p_region );
} }
else 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 ); p_region, p_set_styles, playbacktime );
} }
} }
@ -1019,7 +1040,8 @@ static void ConvertNodesToRegionContent( ttml_context_t *p_ctx, const tt_node_t
ttml_style_Delete( p_set_styles ); 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; stream_t* p_sub;
xml_reader_t* p_xml_reader; xml_reader_t* p_xml_reader;
@ -1036,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; 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 ) if( p_rootnode )
tt_node_RecursiveDelete( p_rootnode ); tt_node_RecursiveDelete( p_rootnode );
@ -1049,7 +1071,8 @@ static tt_node_t *ParseTTML( decoder_t *p_dec, const uint8_t *p_buffer, size_t i
return p_rootnode; 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; p_ctx->p_rootnode = p_rootnode;
/* set defaults required for size/cells computation */ /* set defaults required for size/cells computation */
@ -1060,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_v = TTML_DEFAULT_CELL_RESOLUTION_V;
p_ctx->i_cell_resolution_h = TTML_DEFAULT_CELL_RESOLUTION_H; p_ctx->i_cell_resolution_h = TTML_DEFAULT_CELL_RESOLUTION_H;
/* and override them */ /* and override them */
const char *value = vlc_dictionary_value_for_key( &p_rootnode->attr_dict, const char *value = tt_node_GetAttribute( p_nss, p_rootnode, "extent", TT_NS_STYLING );
"tts:extent" ); if( value )
if( value != kVLCDictionaryNotFound )
{ {
ttml_read_coords( value, &p_ctx->root_extent_h, ttml_read_coords( value, &p_ctx->root_extent_h,
&p_ctx->root_extent_v ); &p_ctx->root_extent_v );
} }
value = vlc_dictionary_value_for_key( &p_rootnode->attr_dict, value = tt_node_GetAttribute( p_nss, p_rootnode, "cellResolution", TT_NS_PARAMETER );
"ttp:cellResolution" ); if( value )
if( value != kVLCDictionaryNotFound )
{ {
unsigned w, h; unsigned w, h;
if( sscanf( value, "%u %u", &w, &h) == 2 && w && h ) if( sscanf( value, "%u %u", &w, &h) == 2 && w && h )
@ -1080,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* p_regions = NULL;
ttml_region_t** pp_region_last = &p_regions; 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 ) if( p_bodynode )
{ {
ttml_context_t context; ttml_context_t context;
InitTTMLContext( p_rootnode, &context ); InitTTMLContext( p_nss, p_rootnode, &context );
context.p_rootnode = p_rootnode; context.p_rootnode = p_rootnode;
vlc_dictionary_init( &context.regions, 1 ); 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 ) for( int i = 0; i < context.regions.i_size; ++i )
{ {
@ -1110,8 +1132,8 @@ static ttml_region_t *GenerateRegions( tt_node_t *p_rootnode, tt_time_t playback
vlc_dictionary_clear( &context.regions, NULL, NULL ); vlc_dictionary_clear( &context.regions, NULL, NULL );
} }
} }
else if ( !tt_node_NameCompare( p_rootnode->psz_node_name, "div" ) || else if ( tt_node_Match( p_rootnode, "div", TT_NS ) ||
!tt_node_NameCompare( p_rootnode->psz_node_name, "p" ) ) tt_node_Match( p_rootnode, "p", TT_NS ) )
{ {
/* TODO */ /* TODO */
} }
@ -1315,9 +1337,14 @@ static int ParseBlock( decoder_t *p_dec, const block_t *p_block )
return VLCDEC_SUCCESS; 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 ) if( !p_rootnode )
{
tt_namespaces_Clean( &namespaces );
return VLCDEC_SUCCESS; return VLCDEC_SUCCESS;
}
tt_timings_Resolve( (tt_basenode_t *) p_rootnode, &temporal_extent, tt_timings_Resolve( (tt_basenode_t *) p_rootnode, &temporal_extent,
&p_timings_array, &i_timings_count ); &p_timings_array, &i_timings_count );
@ -1347,7 +1374,7 @@ static int ParseBlock( decoder_t *p_dec, const block_t *p_block )
bool b_bitmap_regions = false; bool b_bitmap_regions = false;
subpicture_t *p_spu = NULL; 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 )
{ {
if( p_regions->bgbitmap.i_bytes > 0 && p_regions->updt.p_segments == NULL ) if( p_regions->bgbitmap.i_bytes > 0 && p_regions->updt.p_segments == NULL )
@ -1389,6 +1416,7 @@ static int ParseBlock( decoder_t *p_dec, const block_t *p_block )
} }
tt_node_RecursiveDelete( p_rootnode ); tt_node_RecursiveDelete( p_rootnode );
tt_namespaces_Clean( &namespaces );
free( p_timings_array ); free( p_timings_array );

View File

@ -177,11 +177,22 @@ bool tt_node_Match( const tt_node_t *p_node, const char *psz_name, const char *p
return !!psz_namespace == !!psz_nodens; return !!psz_namespace == !!psz_nodens;
} }
int tt_node_NameCompare( const char* psz_tagname, const char* psz_pattern ) const char * tt_node_GetAttribute( tt_namespaces_t *p_nss, const tt_node_t *p_node,
const char *psz_name, const char *psz_namespace )
{ {
if( !strncasecmp( "tt:", psz_tagname, 3 ) ) const void *value;
psz_tagname += 3; char *alloc = NULL;
return strcasecmp( psz_tagname, psz_pattern ); 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 ) bool tt_node_HasChild( const tt_node_t *p_node )
@ -385,7 +396,8 @@ tt_node_t * tt_node_New( tt_node_t* p_parent,
return p_node; return p_node;
} }
tt_node_t * tt_node_NewRead( xml_reader_t* reader, tt_node_t* p_parent, 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 ) const char* psz_node_name, const char *psz_namespace )
{ {
tt_node_t *p_node = tt_node_New( p_parent, psz_node_name, psz_namespace ); tt_node_t *p_node = tt_node_New( p_parent, psz_node_name, psz_namespace );
@ -397,7 +409,8 @@ tt_node_t * tt_node_NewRead( xml_reader_t* reader, tt_node_t* p_parent,
psz_key != NULL; psz_key != NULL;
psz_key = xml_ReaderNextAttrNS( reader, &psz_value, &psz_ns ) ) psz_key = xml_ReaderNextAttrNS( reader, &psz_value, &psz_ns ) )
{ {
fprintf(stderr,"ATTR %s %s %s\n", psz_key, psz_value, psz_ns); if( psz_ns && psz_key )
tt_namespaces_Register( p_nss, psz_key, psz_ns );
char *psz_val = strdup( psz_value ); char *psz_val = strdup( psz_value );
if( psz_val ) if( psz_val )
{ {
@ -474,7 +487,7 @@ static int tt_node_Skip( xml_reader_t *p_reader, const char *psz_skipped )
return VLC_EGENERIC; return VLC_EGENERIC;
} }
#endif #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; size_t i_depth = 0;
tt_node_t *p_node = p_root_node; tt_node_t *p_node = p_root_node;
@ -496,7 +509,9 @@ int tt_nodes_Read( xml_reader_t *p_reader, tt_node_t *p_root_node )
case XML_READER_STARTELEM: 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 ); psz_node_namespace );
if( !p_newnode ) if( !p_newnode )
return VLC_EGENERIC; return VLC_EGENERIC;
@ -517,7 +532,7 @@ int tt_nodes_Read( xml_reader_t *p_reader, tt_node_t *p_root_node )
case XML_READER_ENDELEM: 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; return VLC_EGENERIC;
if( i_depth == 0 ) if( i_depth == 0 )

View File

@ -129,16 +129,17 @@ static inline const char *tt_LocalName( const char *psz_qname )
tt_textnode_t *tt_textnode_New( tt_node_t *p_parent, const char *psz_text ); 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_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, const char *psz_namespace ); 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_node_t* p_parent, const char* psz_node_name, tt_node_t * tt_node_NewRead( xml_reader_t* reader, tt_namespaces_t *, tt_node_t* p_parent,
const char *psz_namespace ); const char* psz_node_name, const char *psz_namespace );
void tt_node_RecursiveDelete( tt_node_t *p_node ); void tt_node_RecursiveDelete( tt_node_t *p_node );
bool tt_node_Match( const tt_node_t *p_node, const char* psz_name, const char* psz_namespace ); bool tt_node_Match( const tt_node_t *p_node, const char* psz_name, const char* psz_namespace );
int tt_node_NameCompare( const char* psz_tagname, const char* psz_pattern ); 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 ); 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 ); 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 ); 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, 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 ); tt_time_t **pp_array, size_t *pi_count );

View File

@ -51,6 +51,7 @@ typedef struct
bool b_first_time; bool b_first_time;
tt_node_t *p_rootnode; tt_node_t *p_rootnode;
tt_namespaces_t namespaces;
tt_timings_t temporal_extent; 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 ) static int ReadTTML( demux_t* p_demux )
{ {
demux_sys_t* p_sys = p_demux->p_sys; demux_sys_t* p_sys = p_demux->p_sys;
const char* psz_node_name, *psz_namespace; const char* psz_node_name, *psz_node_namespace;
do do
{ {
int i_type = xml_ReaderNextNodeNS( p_sys->p_reader, &psz_node_name, &psz_namespace ); int i_type = xml_ReaderNextNodeNS( p_sys->p_reader, &psz_node_name, &psz_node_namespace );
bool b_empty = xml_ReaderIsEmptyElement( p_sys->p_reader ); bool b_empty = xml_ReaderIsEmptyElement( p_sys->p_reader );
if( i_type <= XML_READER_NONE ) if( i_type <= XML_READER_NONE )
@ -173,22 +174,25 @@ static int ReadTTML( demux_t* p_demux )
break; break;
case XML_READER_STARTELEM: 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 ) p_sys->p_rootnode != NULL )
return VLC_EGENERIC; 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_namespace ); psz_node_name,
psz_node_namespace );
if( b_empty ) if( b_empty )
break; break;
if( !p_sys->p_rootnode || 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; return VLC_EGENERIC;
break; break;
case XML_READER_ENDELEM: case XML_READER_ENDELEM:
if( !p_sys->p_rootnode || 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; return VLC_EGENERIC;
break; break;
} }
@ -342,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.end );
tt_time_Init( &p_sys->temporal_extent.dur ); tt_time_Init( &p_sys->temporal_extent.dur );
p_sys->temporal_extent.begin.base = 0; p_sys->temporal_extent.begin.base = 0;
tt_namespaces_Init( &p_sys->namespaces );
p_sys->p_xml = xml_Create( p_demux ); p_sys->p_xml = xml_Create( p_demux );
if( !p_sys->p_xml ) if( !p_sys->p_xml )
@ -419,6 +424,8 @@ void tt_CloseDemux( vlc_object_t* p_this )
if( p_sys->p_xml ) if( p_sys->p_xml )
xml_Delete( 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->times.p_array );
free( p_sys ); free( p_sys );