1 /*****************************************************************************
2 * substtml.c : TTML subtitles decoder
3 *****************************************************************************
4 * Copyright (C) 2015 VLC authors and VideoLAN
6 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
7 * Sushma Reddy <sushma.reddy@research.iiit.ac.in>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
28 #include <vlc_common.h>
29 #include <vlc_plugin.h>
30 #include <vlc_modules.h>
31 #include <vlc_codec.h>
33 #include <vlc_stream.h>
34 #include <vlc_text_style.h>
40 #define ALIGN_TEXT N_("Subtitle justification")
41 #define ALIGN_LONGTEXT N_("Set the justification of subtitles")
43 /*****************************************************************************
45 *****************************************************************************/
46 static int OpenDecoder ( vlc_object_t
* );
47 static void CloseDecoder ( vlc_object_t
* );
49 static text_segment_t
*ParseTTMLSubtitles( decoder_t
*, subpicture_updater_sys_t
*, char * );
52 set_capability( "decoder", 10 )
53 set_shortname( N_("TTML decoder"))
54 set_description( N_("TTML subtitles decoder") )
55 set_callbacks( OpenDecoder
, CloseDecoder
)
56 set_category( CAT_INPUT
)
57 set_subcategory( SUBCAT_INPUT_SCODEC
)
58 add_integer( "ttml-align", 0, ALIGN_TEXT
, ALIGN_LONGTEXT
, false )
61 /*****************************************************************************
63 *****************************************************************************/
68 text_style_t
* font_style
;
72 int i_margin_percent_h
;
73 int i_margin_percent_v
;
79 ttml_style_t
** pp_styles
;
83 static void MergeTTMLStyle( ttml_style_t
*p_dst
, const ttml_style_t
*p_src
)
85 text_style_Merge( p_dst
->font_style
, p_src
->font_style
, false );
86 if( !( p_dst
->i_align
& SUBPICTURE_ALIGN_MASK
) )
87 p_dst
->i_align
|= p_src
->i_align
;
89 if( !p_dst
->i_margin_h
)
90 p_dst
->i_margin_h
= p_src
->i_margin_h
;
92 if( !p_dst
->i_margin_v
)
93 p_dst
->i_margin_v
= p_src
->i_margin_v
;
95 if( !p_dst
->i_margin_percent_h
)
96 p_dst
->i_margin_percent_h
= p_src
->i_margin_percent_h
;
98 if( !p_dst
->i_margin_percent_v
)
99 p_dst
->i_margin_percent_v
= p_src
->i_margin_percent_v
;
102 static void CleanupStyle( ttml_style_t
* p_ttml_style
)
104 text_style_Delete( p_ttml_style
->font_style
);
105 free( p_ttml_style
->psz_styleid
);
106 free( p_ttml_style
);
109 static ttml_style_t
*FindTextStyle( decoder_t
*p_dec
, const char *psz_style
)
111 decoder_sys_t
*p_sys
= p_dec
->p_sys
;
113 for( size_t i
= 0; i
< p_sys
->i_styles
; i
++ )
115 if( !strcmp( p_sys
->pp_styles
[i
]->psz_styleid
, psz_style
) )
117 return p_sys
->pp_styles
[i
];
123 typedef struct style_stack style_stack_t
;
126 ttml_style_t
* p_style
;
127 style_stack_t
* p_next
;
130 static bool PushStyle( style_stack_t
**pp_stack
, ttml_style_t
* p_style
)
132 style_stack_t
* p_entry
= malloc( sizeof( *p_entry
) );
133 if ( unlikely( p_entry
== NULL
) )
135 p_entry
->p_style
= p_style
;
136 p_entry
->p_next
= *pp_stack
;
141 static void PopStyle( style_stack_t
** pp_stack
)
143 if ( *pp_stack
== NULL
)
145 style_stack_t
* p_next
= (*pp_stack
)->p_next
;
150 static void ClearStack( style_stack_t
* p_stack
)
152 while ( p_stack
!= NULL
)
154 style_stack_t
* p_next
= p_stack
->p_next
;
160 static text_style_t
* CurrentStyle( style_stack_t
* p_stack
)
162 if ( p_stack
== NULL
)
163 return text_style_Create( STYLE_NO_DEFAULTS
);
164 return text_style_Duplicate( p_stack
->p_style
->font_style
);
167 static ttml_style_t
* ParseTTMLStyle( decoder_t
*p_dec
, xml_reader_t
* p_reader
, const char* psz_node_name
)
169 decoder_sys_t
* p_sys
= p_dec
->p_sys
;
170 ttml_style_t
*p_ttml_style
= NULL
;
171 ttml_style_t
*p_base_style
= NULL
;
173 p_ttml_style
= calloc( 1, sizeof(ttml_style_t
) );
175 if ( unlikely( !p_ttml_style
) )
177 p_ttml_style
->font_style
= text_style_Create( STYLE_NO_DEFAULTS
);
178 if( unlikely( !p_ttml_style
->font_style
) )
180 free( p_ttml_style
);
184 const char *attr
, *val
;
186 while( (attr
= xml_ReaderNextAttr( p_reader
, &val
) ) )
188 /* searching previous styles for inheritence */
189 if( !strcasecmp( attr
, "style" ) || !strcasecmp( attr
, "region" ) )
191 if( !strcasecmp( psz_node_name
, "style" ) || !strcasecmp( psz_node_name
, "tt:style" ) ||
192 !strcasecmp( psz_node_name
, "region" ) || !strcasecmp( psz_node_name
, "tt:region" ) )
194 for( size_t i
= 0; i
< p_sys
->i_styles
; i
++ )
196 if( !strcasecmp( p_sys
->pp_styles
[i
]->psz_styleid
, val
) )
198 p_base_style
= p_sys
->pp_styles
[i
];
204 * In p nodes, style attribute has this format :
205 * style="style1 style2 style3" where style1 and style2 are
206 * style applied on the parents of p in that order.
208 * In span node, we can apply several styles in the same order than
209 * in p nodes with the same inheritance order.
211 * In order to preserve this style predominance, we merge the styles
212 * in the from right to left ( the right one being predominant ) .
214 else if( !strcasecmp( psz_node_name
, "p" ) || !strcasecmp( psz_node_name
, "tt:p" ) ||
215 !strcasecmp( psz_node_name
, "span" ) || !strcasecmp( psz_node_name
, "tt:span" ) )
218 char *value
= strdup( val
);
219 if( unlikely( value
== NULL
) )
222 char *token
= strtok_r( value
, " ", &tmp
);
223 ttml_style_t
* p_style
= FindTextStyle( p_dec
, token
);
224 if( p_style
== NULL
)
226 msg_Warn( p_dec
, "Style \"%s\" not found", token
);
231 while( ( token
= strtok_r( NULL
, " ", &tmp
) ) != NULL
)
233 ttml_style_t
* p_next_style
= FindTextStyle( p_dec
, token
);
234 if( p_next_style
== NULL
)
236 msg_Warn( p_dec
, "Style \"%s\" not found", token
);
240 MergeTTMLStyle( p_next_style
, p_style
);
241 CleanupStyle( p_style
);
242 p_style
= p_next_style
;
244 MergeTTMLStyle( p_style
, p_ttml_style
);
246 CleanupStyle( p_ttml_style
);
247 p_ttml_style
= p_style
;
251 ttml_style_t
* p_style
= FindTextStyle( p_dec
, val
);
252 if( p_style
== NULL
)
254 msg_Warn( p_dec
, "Style \"%s\" not found", val
);
257 MergeTTMLStyle( p_style
, p_ttml_style
);
258 CleanupStyle( p_ttml_style
);
259 p_ttml_style
= p_style
;
262 else if ( !strcasecmp( "xml:id", attr
) )
264 free( p_ttml_style
->psz_styleid
);
265 p_ttml_style
->psz_styleid
= strdup( val
);
267 else if ( !strcasecmp ( "tts:fontFamily", attr
) )
269 free( p_ttml_style
->font_style
->psz_fontname
);
270 p_ttml_style
->font_style
->psz_fontname
= strdup( val
);
272 else if( !strcasecmp( "tts:opacity", attr
) )
274 p_ttml_style
->font_style
->i_background_alpha
= atoi( val
);
275 p_ttml_style
->font_style
->i_font_alpha
= atoi( val
);
276 p_ttml_style
->font_style
->i_features
|= STYLE_HAS_BACKGROUND_ALPHA
| STYLE_HAS_FONT_ALPHA
;
278 else if( !strcasecmp( "tts:fontSize", attr
) )
280 char* psz_end
= NULL
;
281 float size
= strtof( val
, &psz_end
);
282 if( *psz_end
== '%' )
283 p_ttml_style
->font_style
->f_font_relsize
= size
;
285 p_ttml_style
->font_style
->i_font_size
= (int)( size
+ 0.5 );
287 else if ( !strcasecmp( "tts:color", attr
) )
289 unsigned int i_color
= vlc_html_color( val
, NULL
);
290 p_ttml_style
->font_style
->i_font_color
= (i_color
& 0xffffff);
291 p_ttml_style
->font_style
->i_font_alpha
= (i_color
& 0xFF000000) >> 24;
292 p_ttml_style
->font_style
->i_features
|= STYLE_HAS_FONT_COLOR
| STYLE_HAS_FONT_ALPHA
;
294 else if ( !strcasecmp( "tts:backgroundColor", attr
) )
296 unsigned int i_color
= vlc_html_color( val
, NULL
);
297 p_ttml_style
->font_style
->i_background_color
= i_color
& 0xFFFFFF;
298 p_ttml_style
->font_style
->i_background_alpha
= (i_color
& 0xFF000000) >> 24;
299 p_ttml_style
->font_style
->i_features
|= STYLE_HAS_BACKGROUND_COLOR
300 | STYLE_HAS_BACKGROUND_ALPHA
;
301 p_ttml_style
->font_style
->i_style_flags
|= STYLE_BACKGROUND
;
303 else if ( !strcasecmp( "tts:textAlign", attr
) )
305 if ( !strcasecmp ( "left", val
) )
306 p_ttml_style
->i_align
= SUBPICTURE_ALIGN_BOTTOM
| SUBPICTURE_ALIGN_LEFT
;
307 else if ( !strcasecmp ( "right", val
) )
308 p_ttml_style
->i_align
= SUBPICTURE_ALIGN_BOTTOM
| SUBPICTURE_ALIGN_RIGHT
;
309 else if ( !strcasecmp ( "center", val
) )
310 p_ttml_style
->i_align
= SUBPICTURE_ALIGN_BOTTOM
;
311 else if ( !strcasecmp ( "start", val
) )
312 p_ttml_style
->i_align
= SUBPICTURE_ALIGN_TOP
| SUBPICTURE_ALIGN_LEFT
;
313 else if ( !strcasecmp ( "end", val
) )
314 p_ttml_style
->i_align
= SUBPICTURE_ALIGN_BOTTOM
| SUBPICTURE_ALIGN_RIGHT
;
316 else if ( !strcasecmp( "tts:fontStyle", attr
) )
318 if ( !strcasecmp ( "italic", val
) || !strcasecmp ( "oblique", val
) )
319 p_ttml_style
->font_style
->i_style_flags
|= STYLE_ITALIC
;
321 p_ttml_style
->font_style
->i_style_flags
&= ~STYLE_ITALIC
;
322 p_ttml_style
->font_style
->i_features
|= STYLE_HAS_FLAGS
;
324 else if ( !strcasecmp ( "tts:fontWeight", attr
) )
326 if ( !strcasecmp ( "bold", val
) )
327 p_ttml_style
->font_style
->i_style_flags
|= STYLE_BOLD
;
329 p_ttml_style
->font_style
->i_style_flags
&= ~STYLE_BOLD
;
330 p_ttml_style
->font_style
->i_features
|= STYLE_HAS_FLAGS
;
332 else if ( !strcasecmp ( "tts:textDecoration", attr
) )
334 if ( !strcasecmp ( "underline", val
) )
335 p_ttml_style
->font_style
->i_style_flags
|= STYLE_UNDERLINE
;
336 else if ( !strcasecmp ( "noUnderline", val
) )
337 p_ttml_style
->font_style
->i_style_flags
&= ~STYLE_UNDERLINE
;
338 if ( !strcasecmp ( "lineThrough", val
) )
339 p_ttml_style
->font_style
->i_style_flags
|= STYLE_STRIKEOUT
;
340 else if ( !strcasecmp ( "noLineThrough", val
) )
341 p_ttml_style
->font_style
->i_style_flags
&= ~STYLE_STRIKEOUT
;
342 p_ttml_style
->font_style
->i_features
|= STYLE_HAS_FLAGS
;
344 else if ( !strcasecmp ( "tts:origin", attr
) )
346 const char *psz_token
= val
;
347 while ( isspace( *psz_token
) )
350 const char *psz_separator
= strchr( psz_token
, ' ' );
351 if ( psz_separator
== NULL
)
353 msg_Warn( p_dec
, "Invalid origin attribute: \"%s\"", val
);
356 const char *psz_percent_sign
= strchr( psz_token
, '%' );
358 if( psz_percent_sign
!= NULL
&& psz_percent_sign
< psz_separator
)
360 p_ttml_style
->i_margin_h
= 0;
361 p_ttml_style
->i_margin_percent_h
= atoi( psz_token
);
365 p_ttml_style
->i_margin_h
= atoi( psz_token
);
366 p_ttml_style
->i_margin_percent_h
= 0;
368 while ( isspace( *psz_separator
) )
370 psz_token
= psz_separator
;
371 psz_percent_sign
= strchr( psz_token
, '%' );
372 if( psz_percent_sign
!= NULL
)
374 p_ttml_style
->i_margin_v
= 0;
375 p_ttml_style
->i_margin_percent_v
= atoi( val
);
379 p_ttml_style
->i_margin_v
= atoi( val
);
380 p_ttml_style
->i_margin_percent_v
= 0;
383 else if ( !strcasecmp( "tts:textOutline", attr
) )
385 char *value
= strdup( val
);
386 char* psz_saveptr
= NULL
;
387 char* token
= strtok_r( value
, " ", &psz_saveptr
);
388 // <color>? <length> <length>?
390 unsigned int color
= vlc_html_color( token
, &b_ok
);
393 p_ttml_style
->font_style
->i_outline_color
= color
& 0xFFFFFF;
394 p_ttml_style
->font_style
->i_outline_alpha
= (color
& 0xFF000000) >> 24;
395 token
= strtok_r( NULL
, " ", &psz_saveptr
);
397 char* psz_end
= NULL
;
398 int i_outline_width
= strtol( token
, &psz_end
, 10 );
399 if ( psz_end
!= token
)
401 // Assume unit is pixel, and ignore border radius
402 p_ttml_style
->font_style
->i_outline_width
= i_outline_width
;
407 if ( p_base_style
!= NULL
)
409 MergeTTMLStyle( p_ttml_style
, p_base_style
);
411 if ( p_ttml_style
->psz_styleid
== NULL
)
413 CleanupStyle( p_ttml_style
);
419 static void ParseTTMLStyles( decoder_t
* p_dec
)
421 stream_t
* p_stream
= vlc_stream_MemoryNew( p_dec
, (uint8_t*)p_dec
->fmt_in
.p_extra
, p_dec
->fmt_in
.i_extra
, true );
422 if( unlikely( p_stream
== NULL
) )
425 xml_reader_t
* p_reader
= xml_ReaderCreate( p_dec
, p_stream
);
426 if( unlikely( p_reader
== NULL
) )
428 vlc_stream_Delete( p_stream
);
431 const char* psz_node_name
;
432 int i_type
= xml_ReaderNextNode( p_reader
, &psz_node_name
);
434 if( i_type
== XML_READER_STARTELEM
&& ( !strcasecmp( psz_node_name
, "tt" ) || !strcasecmp( psz_node_name
, "tt:tt" ) ) )
436 int i_type
= xml_ReaderNextNode( p_reader
, &psz_node_name
);
437 while( i_type
!= XML_READER_STARTELEM
|| ( strcasecmp( psz_node_name
, "styling" ) && strcasecmp( psz_node_name
, "tt:styling" ) ) )
438 i_type
= xml_ReaderNextNode( p_reader
, &psz_node_name
);
442 /* region and style tag are respectively inside layout and styling tags */
443 if( !strcasecmp( psz_node_name
, "styling" ) || !strcasecmp( psz_node_name
, "layout" ) ||
444 !strcasecmp( psz_node_name
, "tt:styling" ) || !strcasecmp( psz_node_name
, "tt:layout" ) )
446 i_type
= xml_ReaderNextNode( p_reader
, &psz_node_name
);
447 while( i_type
!= XML_READER_ENDELEM
)
449 ttml_style_t
* p_ttml_style
= ParseTTMLStyle( p_dec
, p_reader
, psz_node_name
);
450 if ( p_ttml_style
== NULL
)
452 xml_ReaderDelete( p_reader
);
453 vlc_stream_Delete( p_stream
);
456 decoder_sys_t
* p_sys
= p_dec
->p_sys
;
457 TAB_APPEND( p_sys
->i_styles
, p_sys
->pp_styles
, p_ttml_style
);
458 i_type
= xml_ReaderNextNode( p_reader
, &psz_node_name
);
461 i_type
= xml_ReaderNextNode( p_reader
, &psz_node_name
);
462 }while( i_type
!= XML_READER_ENDELEM
|| ( strcasecmp( psz_node_name
, "head" ) && strcasecmp( psz_node_name
, "tt:head" ) ) );
464 xml_ReaderDelete( p_reader
);
465 vlc_stream_Delete( p_stream
);
468 static text_segment_t
*ParseTTMLSubtitles( decoder_t
*p_dec
, subpicture_updater_sys_t
*p_update_sys
, char *psz_subtitle
)
470 stream_t
* p_sub
= NULL
;
471 xml_reader_t
* p_xml_reader
= NULL
;
472 text_segment_t
* p_first_segment
= NULL
;
473 text_segment_t
* p_current_segment
= NULL
;
474 style_stack_t
* p_style_stack
= NULL
;
475 ttml_style_t
* p_style
= NULL
;
477 p_sub
= vlc_stream_MemoryNew( p_dec
, (uint8_t*)psz_subtitle
, strlen( psz_subtitle
), true );
478 if( unlikely( p_sub
== NULL
) )
481 p_xml_reader
= xml_ReaderCreate( p_dec
, p_sub
);
482 if( unlikely( p_xml_reader
== NULL
) )
484 vlc_stream_Delete( p_sub
);
491 i_type
= xml_ReaderNextNode( p_xml_reader
, &node
);
492 while( i_type
!= XML_READER_NONE
&& i_type
> 0 )
495 * We parse the styles and put them on the style stack
496 * until we reach a text node.
498 if( i_type
== XML_READER_STARTELEM
&& ( !strcasecmp( node
, "p" ) || !strcasecmp( node
, "tt:p" ) ||
499 !strcasecmp( node
, "span") || !strcasecmp( node
, "tt:span") ) )
501 p_style
= ParseTTMLStyle( p_dec
, p_xml_reader
, node
);
502 if( unlikely( p_style
== NULL
) )
505 if( p_style_stack
!= NULL
&& p_style_stack
->p_style
!= NULL
)
506 MergeTTMLStyle( p_style
, p_style_stack
->p_style
);
508 if( PushStyle( &p_style_stack
, p_style
) == false )
512 else if( i_type
== XML_READER_TEXT
)
515 * Once we have a text node, we create a segment, apply the
516 * latest style put on the style stack and fill it with the
517 * content of the node.
519 text_segment_t
* p_segment
= text_segment_New( NULL
);
520 if( unlikely( p_segment
== NULL
) )
523 p_segment
->psz_text
= strdup( node
);
524 if( unlikely( p_segment
->psz_text
== NULL
) )
526 text_segment_Delete( p_segment
);
530 vlc_xml_decode( p_segment
->psz_text
);
531 if( p_segment
->style
== NULL
&& p_style_stack
== NULL
)
533 p_segment
->style
= text_style_Create( STYLE_NO_DEFAULTS
);
535 else if( p_segment
->style
== NULL
)
537 p_segment
->style
= CurrentStyle( p_style_stack
);
538 if( p_segment
->style
->f_font_relsize
&& !p_segment
->style
->i_font_size
)
539 p_segment
->style
->i_font_size
= (int)( ( p_segment
->style
->f_font_relsize
* STYLE_DEFAULT_FONT_SIZE
/ 100 ) + 0.5 );
541 if( p_style_stack
->p_style
->i_margin_h
)
542 p_update_sys
->x
= p_style_stack
->p_style
->i_margin_h
;
544 p_update_sys
->x
= p_style_stack
->p_style
->i_margin_percent_h
;
546 if( p_style_stack
->p_style
->i_margin_v
)
547 p_update_sys
->y
= p_style_stack
->p_style
->i_margin_v
;
549 p_update_sys
->y
= p_style_stack
->p_style
->i_margin_percent_v
;
551 p_update_sys
->align
|= p_style_stack
->p_style
->i_align
;
553 if( p_first_segment
== NULL
)
555 p_first_segment
= p_segment
;
556 p_current_segment
= p_segment
;
558 else if( p_current_segment
->psz_text
!= NULL
)
560 p_current_segment
->p_next
= p_segment
;
561 p_current_segment
= p_segment
;
566 * If p_first_segment isn't NULL but p_current_segment->psz_text is NULL
567 * this means that something went wrong in the decoding of the
568 * first segment text:
570 * Indeed, to allocate p_first_segment ( aka non NULL ), we must have
571 * - i_type == XML_READER_TEXT
572 * - passed the allocation of p_segment->psz_text without any error
574 * This would mean that vlc_xml_decode failed and p_first_segment->psz_text
577 text_segment_Delete( p_segment
);
581 else if( i_type
== XML_READER_ENDELEM
&& ( !strcasecmp( node
, "span" ) || !strcasecmp( node
, "tt:span" ) ) )
583 if( p_style_stack
->p_next
)
584 PopStyle( &p_style_stack
);
586 else if( i_type
== XML_READER_ENDELEM
&& ( !strcasecmp( node
, "p" ) || !strcasecmp( node
, "tt:p" ) ) )
588 PopStyle( &p_style_stack
);
589 p_current_segment
->p_next
= NULL
;
591 else if( i_type
== XML_READER_STARTELEM
&& !strcasecmp( node
, "br" ) )
593 if( p_current_segment
!= NULL
&& p_current_segment
->psz_text
!= NULL
)
595 char* psz_text
= NULL
;
596 if( asprintf( &psz_text
, "%s\n", p_current_segment
->psz_text
) != -1 )
598 free( p_current_segment
->psz_text
);
599 p_current_segment
->psz_text
= psz_text
;
603 i_type
= xml_ReaderNextNode( p_xml_reader
, &node
);
605 ClearStack( p_style_stack
);
606 xml_ReaderDelete( p_xml_reader
);
607 vlc_stream_Delete( p_sub
);
609 return p_first_segment
;
612 text_segment_ChainDelete( p_first_segment
);
613 ClearStack( p_style_stack
);
614 xml_ReaderDelete( p_xml_reader
);
615 vlc_stream_Delete( p_sub
);
619 static subpicture_t
*ParseText( decoder_t
*p_dec
, block_t
*p_block
)
621 decoder_sys_t
*p_sys
= p_dec
->p_sys
;
622 subpicture_t
*p_spu
= NULL
;
623 char *psz_subtitle
= NULL
;
625 if( p_block
->i_flags
& BLOCK_FLAG_CORRUPTED
)
628 /* We cannot display a subpicture with no date */
629 if( p_block
->i_pts
<= VLC_TS_INVALID
)
631 msg_Warn( p_dec
, "subtitle without a date" );
635 /* Check validity of packet data */
636 /* An "empty" line containing only \0 can be used to force
637 and ephemer picture from the screen */
639 if( p_block
->i_buffer
< 1 )
641 msg_Warn( p_dec
, "no subtitle data" );
645 psz_subtitle
= malloc( p_block
->i_buffer
);
646 if ( unlikely( psz_subtitle
== NULL
) )
648 memcpy( psz_subtitle
, p_block
->p_buffer
, p_block
->i_buffer
);
650 /* Create the subpicture unit */
651 p_spu
= decoder_NewSubpictureText( p_dec
);
654 free( psz_subtitle
);
657 p_spu
->i_start
= p_block
->i_pts
;
658 p_spu
->i_stop
= p_block
->i_pts
+ p_block
->i_length
;
659 p_spu
->b_ephemer
= (p_block
->i_length
== 0);
660 p_spu
->b_absolute
= false;
662 subpicture_updater_sys_t
*p_spu_sys
= p_spu
->updater
.p_sys
;
664 p_spu_sys
->align
= SUBPICTURE_ALIGN_BOTTOM
| p_sys
->i_align
;
665 p_spu_sys
->p_segments
= ParseTTMLSubtitles( p_dec
, p_spu_sys
, psz_subtitle
);
666 free( psz_subtitle
);
673 /****************************************************************************
674 * DecodeBlock: the whole thing
675 ****************************************************************************/
676 static subpicture_t
*DecodeBlock( decoder_t
*p_dec
, block_t
**pp_block
)
678 if( !pp_block
|| *pp_block
== NULL
)
681 block_t
* p_block
= *pp_block
;
682 subpicture_t
*p_spu
= ParseText( p_dec
, p_block
);
684 block_Release( p_block
);
690 /*****************************************************************************
691 * OpenDecoder: probe the decoder and return score
692 *****************************************************************************/
693 static int OpenDecoder( vlc_object_t
*p_this
)
695 decoder_t
*p_dec
= (decoder_t
*)p_this
;
696 decoder_sys_t
*p_sys
;
698 if ( p_dec
->fmt_in
.i_codec
!= VLC_CODEC_TTML
)
701 /* Allocate the memory needed to store the decoder's structure */
702 p_dec
->p_sys
= p_sys
= calloc( 1, sizeof( *p_sys
) );
703 if( unlikely( p_sys
== NULL
) )
706 if ( p_dec
->fmt_in
.p_extra
!= NULL
&& p_dec
->fmt_in
.i_extra
> 0 )
707 ParseTTMLStyles( p_dec
);
709 p_dec
->pf_decode_sub
= DecodeBlock
;
710 p_dec
->fmt_out
.i_cat
= SPU_ES
;
711 p_sys
->i_align
= var_InheritInteger( p_dec
, "ttml-align" );
716 /*****************************************************************************
717 * CloseDecoder: clean up the decoder
718 *****************************************************************************/
719 static void CloseDecoder( vlc_object_t
*p_this
)
721 decoder_t
*p_dec
= (decoder_t
*)p_this
;
722 decoder_sys_t
*p_sys
= p_dec
->p_sys
;
724 for ( size_t i
= 0; i
< p_sys
->i_styles
; ++i
)
726 free( p_sys
->pp_styles
[i
]->psz_styleid
);
727 text_style_Delete( p_sys
->pp_styles
[i
]->font_style
);
728 free( p_sys
->pp_styles
[i
] );
730 TAB_CLEAN( p_sys
->i_styles
, p_sys
->pp_styles
);