ttml codec: add span and p style attributes parsing for inheritence
[vlc.git] / modules / codec / substtml.c
blobe38bb3348db696b20638fe5bbbaba94d20ac0380
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 *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <vlc_common.h>
29 #include <vlc_plugin.h>
30 #include <vlc_modules.h>
31 #include <vlc_codec.h>
32 #include <vlc_xml.h>
33 #include <vlc_stream.h>
34 #include <vlc_text_style.h>
36 #include "substext.h"
38 #include <ctype.h>
40 #define ALIGN_TEXT N_("Subtitle justification")
41 #define ALIGN_LONGTEXT N_("Set the justification of subtitles")
43 /*****************************************************************************
44 * Module descriptor.
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 * );
51 vlc_module_begin ()
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 )
59 vlc_module_end ();
61 /*****************************************************************************
62 * Local prototypes
63 *****************************************************************************/
65 typedef struct
67 char* psz_styleid;
68 text_style_t* font_style;
69 int i_align;
70 int i_margin_h;
71 int i_margin_v;
72 int i_margin_percent_h;
73 int i_margin_percent_v;
74 } ttml_style_t;
76 struct decoder_sys_t
78 int i_align;
79 ttml_style_t** pp_styles;
80 size_t i_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];
120 return NULL;
123 typedef struct style_stack style_stack_t;
124 struct style_stack
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 ) )
134 return false;
135 p_entry->p_style = p_style;
136 p_entry->p_next = *pp_stack;
137 *pp_stack = p_entry;
138 return true;
141 static void PopStyle( style_stack_t** pp_stack )
143 if ( *pp_stack == NULL )
144 return;
145 style_stack_t* p_next = (*pp_stack)->p_next;
146 free( *pp_stack );
147 *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;
155 free( p_stack );
156 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 ) )
176 return ;
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 );
181 return NULL;
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];
199 break;
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" ) )
217 char *tmp;
218 char *value = strdup( val );
219 if( unlikely( value == NULL ) )
220 return 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 );
227 free( value );
228 break;
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 );
237 free( value );
238 break;
240 MergeTTMLStyle( p_next_style, p_style );
241 CleanupStyle( p_style );
242 p_style = p_next_style;
244 MergeTTMLStyle( p_style, p_ttml_style );
245 free( value );
246 CleanupStyle( p_ttml_style );
247 p_ttml_style = p_style;
249 else
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 );
255 break;
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;
284 else
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;
320 else
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;
328 else
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 ) )
348 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 );
354 continue;
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 );
363 else
365 p_ttml_style->i_margin_h = atoi( psz_token );
366 p_ttml_style->i_margin_percent_h = 0;
368 while ( isspace( *psz_separator ) )
369 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 );
377 else
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>?
389 bool b_ok = false;
390 unsigned int color = vlc_html_color( token, &b_ok );
391 if ( 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;
404 free( value );
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 );
414 return NULL;
416 return 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 ) )
423 return ;
425 xml_reader_t* p_reader = xml_ReaderCreate( p_dec, p_stream );
426 if( unlikely( p_reader == NULL ) )
428 vlc_stream_Delete( p_stream );
429 return ;
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 );
454 return;
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 ) )
479 return NULL;
481 p_xml_reader = xml_ReaderCreate( p_dec, p_sub );
482 if( unlikely( p_xml_reader == NULL ) )
484 vlc_stream_Delete( p_sub );
485 return NULL;
488 const char *node;
489 int i_type;
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 ) )
503 goto fail;
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 )
509 goto fail;
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 ) )
521 goto fail;
523 p_segment->psz_text = strdup( node );
524 if( unlikely( p_segment->psz_text == NULL ) )
526 text_segment_Delete( p_segment );
527 goto fail;
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;
543 else
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;
548 else
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;
563 else
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
575 * is NULL.
577 text_segment_Delete( p_segment );
578 goto fail;
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;
611 fail:
612 text_segment_ChainDelete( p_first_segment );
613 ClearStack( p_style_stack );
614 xml_ReaderDelete( p_xml_reader );
615 vlc_stream_Delete( p_sub );
616 return NULL;
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 )
626 return NULL;
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" );
632 return NULL;
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" );
642 return NULL;
645 psz_subtitle = malloc( p_block->i_buffer );
646 if ( unlikely( psz_subtitle == NULL ) )
647 return 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 );
652 if( !p_spu )
654 free( psz_subtitle );
655 return NULL;
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 );
668 return p_spu;
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 )
679 return NULL;
681 block_t* p_block = *pp_block;
682 subpicture_t *p_spu = ParseText( p_dec, p_block );
684 block_Release( p_block );
685 *pp_block = NULL;
687 return p_spu;
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 )
699 return VLC_EGENERIC;
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 ) )
704 return VLC_ENOMEM;
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" );
713 return VLC_SUCCESS;
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 );
732 free( p_sys );