1 /*****************************************************************************
2 * subsusf.c : USF subtitles decoder
3 *****************************************************************************
4 * Copyright (C) 2000-2006 the VideoLAN team
7 * Authors: Bernie Purcell <bitmap@videolan.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 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 General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, 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>
32 #include <vlc_input.h>
33 #include <vlc_charset.h>
34 #include <vlc_image.h>
36 #include <vlc_stream.h>
38 /*****************************************************************************
40 *****************************************************************************/
41 static int OpenDecoder ( vlc_object_t
* );
42 static void CloseDecoder ( vlc_object_t
* );
45 set_capability( "decoder", 40 )
46 set_shortname( N_("USFSubs"))
47 set_description( N_("USF subtitles decoder") )
48 set_callbacks( OpenDecoder
, CloseDecoder
)
49 set_category( CAT_INPUT
)
50 set_subcategory( SUBCAT_INPUT_SCODEC
)
51 /* We inherit subsdec-align and subsdec-formatted from subsdec.c */
55 /*****************************************************************************
57 *****************************************************************************/
60 ATTRIBUTE_ALIGNMENT
= (1 << 0),
61 ATTRIBUTE_X
= (1 << 1),
62 ATTRIBUTE_X_PERCENT
= (1 << 2),
63 ATTRIBUTE_Y
= (1 << 3),
64 ATTRIBUTE_Y_PERCENT
= (1 << 4),
75 char * psz_stylename
; /* The name of the style, no comma's allowed */
76 text_style_t font_style
;
80 int i_margin_percent_h
;
81 int i_margin_percent_v
;
86 int i_original_height
;
88 int i_align
; /* Subtitles alignment on the vout */
90 ssa_style_t
**pp_ssa_styles
;
93 image_attach_t
**pp_images
;
97 static subpicture_t
*DecodeBlock ( decoder_t
*, block_t
** );
98 static char *CreatePlainText( char * );
99 static int ParseImageAttachments( decoder_t
*p_dec
);
101 static subpicture_t
*ParseText ( decoder_t
*, block_t
* );
102 static void ParseUSFHeader( decoder_t
* );
103 static subpicture_region_t
*ParseUSFString( decoder_t
*, char * );
104 static subpicture_region_t
*LoadEmbeddedImage( decoder_t
*p_dec
, const char *psz_filename
, int i_transparent_color
);
106 /*****************************************************************************
107 * OpenDecoder: probe the decoder and return score
108 *****************************************************************************
109 * Tries to launch a decoder and return score so that the interface is able
111 *****************************************************************************/
112 static int OpenDecoder( vlc_object_t
*p_this
)
114 decoder_t
*p_dec
= (decoder_t
*)p_this
;
115 decoder_sys_t
*p_sys
;
117 if( p_dec
->fmt_in
.i_codec
!= VLC_CODEC_USF
)
120 /* Allocate the memory needed to store the decoder's structure */
121 if( ( p_dec
->p_sys
= p_sys
= calloc(1, sizeof(decoder_sys_t
)) ) == NULL
)
124 p_dec
->pf_decode_sub
= DecodeBlock
;
125 p_dec
->fmt_out
.i_cat
= SPU_ES
;
126 p_dec
->fmt_out
.i_codec
= 0;
130 p_sys
->i_original_height
= 0;
131 p_sys
->i_original_width
= 0;
132 TAB_INIT( p_sys
->i_ssa_styles
, p_sys
->pp_ssa_styles
);
133 TAB_INIT( p_sys
->i_images
, p_sys
->pp_images
);
135 /* USF subtitles are mandated to be UTF-8, so don't need vlc_iconv */
137 p_sys
->i_align
= var_CreateGetInteger( p_dec
, "subsdec-align" );
139 ParseImageAttachments( p_dec
);
141 if( var_CreateGetBool( p_dec
, "subsdec-formatted" ) )
143 if( p_dec
->fmt_in
.i_extra
> 0 )
144 ParseUSFHeader( p_dec
);
150 /****************************************************************************
151 * DecodeBlock: the whole thing
152 ****************************************************************************
153 * This function must be fed with complete subtitles units.
154 ****************************************************************************/
155 static subpicture_t
*DecodeBlock( decoder_t
*p_dec
, block_t
**pp_block
)
160 if( !pp_block
|| *pp_block
== NULL
)
165 p_spu
= ParseText( p_dec
, p_block
);
167 block_Release( p_block
);
173 /*****************************************************************************
174 * CloseDecoder: clean up the decoder
175 *****************************************************************************/
176 static void CloseDecoder( vlc_object_t
*p_this
)
178 decoder_t
*p_dec
= (decoder_t
*)p_this
;
179 decoder_sys_t
*p_sys
= p_dec
->p_sys
;
181 if( p_sys
->pp_ssa_styles
)
184 for( i
= 0; i
< p_sys
->i_ssa_styles
; i
++ )
186 if( !p_sys
->pp_ssa_styles
[i
] )
189 free( p_sys
->pp_ssa_styles
[i
]->psz_stylename
);
190 //FIXME: Make font_style a pointer and use text_style_* functions
191 free( p_sys
->pp_ssa_styles
[i
]->font_style
.psz_fontname
);
192 free( p_sys
->pp_ssa_styles
[i
] );
194 TAB_CLEAN( p_sys
->i_ssa_styles
, p_sys
->pp_ssa_styles
);
196 if( p_sys
->pp_images
)
199 for( i
= 0; i
< p_sys
->i_images
; i
++ )
201 if( !p_sys
->pp_images
[i
] )
204 if( p_sys
->pp_images
[i
]->p_pic
)
205 picture_Release( p_sys
->pp_images
[i
]->p_pic
);
206 free( p_sys
->pp_images
[i
]->psz_filename
);
208 free( p_sys
->pp_images
[i
] );
210 TAB_CLEAN( p_sys
->i_images
, p_sys
->pp_images
);
216 /*****************************************************************************
217 * ParseText: parse an text subtitle packet and send it to the video output
218 *****************************************************************************/
219 static subpicture_t
*ParseText( decoder_t
*p_dec
, block_t
*p_block
)
221 decoder_sys_t
*p_sys
= p_dec
->p_sys
;
222 subpicture_t
*p_spu
= NULL
;
223 char *psz_subtitle
= NULL
;
225 /* We cannot display a subpicture with no date */
226 if( p_block
->i_pts
<= VLC_TS_INVALID
)
228 msg_Warn( p_dec
, "subtitle without a date" );
232 /* Check validity of packet data */
233 /* An "empty" line containing only \0 can be used to force
234 and ephemer picture from the screen */
235 if( p_block
->i_buffer
< 1 )
237 msg_Warn( p_dec
, "no subtitle data" );
241 /* Should be resiliant against bad subtitles */
242 psz_subtitle
= strndup( (const char *)p_block
->p_buffer
,
244 if( psz_subtitle
== NULL
)
247 /* USF Subtitles are mandated to be UTF-8 -- make sure it is */
248 if (EnsureUTF8( psz_subtitle
) == NULL
)
250 msg_Err( p_dec
, "USF subtitles must be in UTF-8 format.\n"
251 "This stream contains USF subtitles which aren't." );
254 /* Create the subpicture unit */
255 p_spu
= decoder_NewSubpicture( p_dec
, NULL
);
258 msg_Warn( p_dec
, "can't get spu buffer" );
259 free( psz_subtitle
);
263 /* Decode USF strings */
264 p_spu
->p_region
= ParseUSFString( p_dec
, psz_subtitle
);
266 p_spu
->i_start
= p_block
->i_pts
;
267 p_spu
->i_stop
= p_block
->i_pts
+ p_block
->i_length
;
268 p_spu
->b_ephemer
= (p_block
->i_length
== 0);
269 p_spu
->b_absolute
= false;
270 p_spu
->i_original_picture_width
= p_sys
->i_original_width
;
271 p_spu
->i_original_picture_height
= p_sys
->i_original_height
;
273 free( psz_subtitle
);
278 static char *GrabAttributeValue( const char *psz_attribute
,
279 const char *psz_tag_start
)
281 if( psz_attribute
&& psz_tag_start
)
283 char *psz_tag_end
= strchr( psz_tag_start
, '>' );
284 char *psz_found
= strcasestr( psz_tag_start
, psz_attribute
);
288 psz_found
+= strlen( psz_attribute
);
290 if(( *(psz_found
++) == '=' ) &&
291 ( *(psz_found
++) == '\"' ))
293 if( psz_found
< psz_tag_end
)
295 int i_len
= strcspn( psz_found
, "\"" );
296 return strndup( psz_found
, i_len
);
304 static ssa_style_t
*ParseStyle( decoder_sys_t
*p_sys
, char *psz_subtitle
)
306 ssa_style_t
*p_ssa_style
= NULL
;
307 char *psz_style
= GrabAttributeValue( "style", psz_subtitle
);
313 for( i
= 0; i
< p_sys
->i_ssa_styles
; i
++ )
315 if( !strcmp( p_sys
->pp_ssa_styles
[i
]->psz_stylename
, psz_style
) )
316 p_ssa_style
= p_sys
->pp_ssa_styles
[i
];
323 static int ParsePositionAttributeList( char *psz_subtitle
, int *i_align
,
328 char *psz_align
= GrabAttributeValue( "alignment", psz_subtitle
);
329 char *psz_margin_x
= GrabAttributeValue( "horizontal-margin", psz_subtitle
);
330 char *psz_margin_y
= GrabAttributeValue( "vertical-margin", psz_subtitle
);
332 char *psz_relative = GrabAttributeValue( "relative-to", psz_subtitle );
333 char *psz_rotate_x = GrabAttributeValue( "rotate-x", psz_subtitle );
334 char *psz_rotate_y = GrabAttributeValue( "rotate-y", psz_subtitle );
335 char *psz_rotate_z = GrabAttributeValue( "rotate-z", psz_subtitle );
338 *i_align
= SUBPICTURE_ALIGN_BOTTOM
;
344 if( !strcasecmp( "TopLeft", psz_align
) )
345 *i_align
= SUBPICTURE_ALIGN_TOP
| SUBPICTURE_ALIGN_LEFT
;
346 else if( !strcasecmp( "TopCenter", psz_align
) )
347 *i_align
= SUBPICTURE_ALIGN_TOP
;
348 else if( !strcasecmp( "TopRight", psz_align
) )
349 *i_align
= SUBPICTURE_ALIGN_TOP
| SUBPICTURE_ALIGN_RIGHT
;
350 else if( !strcasecmp( "MiddleLeft", psz_align
) )
351 *i_align
= SUBPICTURE_ALIGN_LEFT
;
352 else if( !strcasecmp( "MiddleCenter", psz_align
) )
354 else if( !strcasecmp( "MiddleRight", psz_align
) )
355 *i_align
= SUBPICTURE_ALIGN_RIGHT
;
356 else if( !strcasecmp( "BottomLeft", psz_align
) )
357 *i_align
= SUBPICTURE_ALIGN_BOTTOM
| SUBPICTURE_ALIGN_LEFT
;
358 else if( !strcasecmp( "BottomCenter", psz_align
) )
359 *i_align
= SUBPICTURE_ALIGN_BOTTOM
;
360 else if( !strcasecmp( "BottomRight", psz_align
) )
361 *i_align
= SUBPICTURE_ALIGN_BOTTOM
| SUBPICTURE_ALIGN_RIGHT
;
363 i_mask
|= ATTRIBUTE_ALIGNMENT
;
368 *i_x
= atoi( psz_margin_x
);
369 if( strchr( psz_margin_x
, '%' ) )
370 i_mask
|= ATTRIBUTE_X_PERCENT
;
372 i_mask
|= ATTRIBUTE_X
;
374 free( psz_margin_x
);
378 *i_y
= atoi( psz_margin_y
);
379 if( strchr( psz_margin_y
, '%' ) )
380 i_mask
|= ATTRIBUTE_Y_PERCENT
;
382 i_mask
|= ATTRIBUTE_Y
;
384 free( psz_margin_y
);
389 static void SetupPositions( subpicture_region_t
*p_region
, char *psz_subtitle
)
395 i_mask
= ParsePositionAttributeList( psz_subtitle
, &i_align
, &i_x
, &i_y
);
397 if( i_mask
& ATTRIBUTE_ALIGNMENT
)
398 p_region
->i_align
= i_align
;
400 /* TODO: Setup % based offsets properly, without adversely affecting
401 * everything else in vlc. Will address with separate patch, to
402 * prevent this one being any more complicated.
404 if( i_mask
& ATTRIBUTE_X
)
406 else if( i_mask
& ATTRIBUTE_X_PERCENT
)
409 if( i_mask
& ATTRIBUTE_Y
)
411 else if( i_mask
& ATTRIBUTE_Y_PERCENT
)
415 static subpicture_region_t
*CreateTextRegion( decoder_t
*p_dec
,
420 decoder_sys_t
*p_sys
= p_dec
->p_sys
;
421 subpicture_region_t
*p_text_region
;
424 /* Create a new subpicture region */
425 memset( &fmt
, 0, sizeof(video_format_t
) );
426 fmt
.i_chroma
= VLC_CODEC_TEXT
;
427 fmt
.i_width
= fmt
.i_height
= 0;
428 fmt
.i_x_offset
= fmt
.i_y_offset
= 0;
429 p_text_region
= subpicture_region_New( &fmt
);
431 if( p_text_region
!= NULL
)
433 ssa_style_t
*p_ssa_style
= NULL
;
435 p_text_region
->psz_text
= NULL
;
436 p_text_region
->psz_html
= strndup( psz_subtitle
, i_len
);
437 if( ! p_text_region
->psz_html
)
439 subpicture_region_Delete( p_text_region
);
443 p_ssa_style
= ParseStyle( p_sys
, p_text_region
->psz_html
);
448 for( i
= 0; i
< p_sys
->i_ssa_styles
; i
++ )
450 if( !strcasecmp( p_sys
->pp_ssa_styles
[i
]->psz_stylename
, "Default" ) )
451 p_ssa_style
= p_sys
->pp_ssa_styles
[i
];
457 msg_Dbg( p_dec
, "style is: %s", p_ssa_style
->psz_stylename
);
459 p_text_region
->p_style
= text_style_Duplicate( &p_ssa_style
->font_style
);
460 p_text_region
->i_align
= p_ssa_style
->i_align
;
462 /* TODO: Setup % based offsets properly, without adversely affecting
463 * everything else in vlc. Will address with separate patch,
464 * to prevent this one being any more complicated.
466 * p_ssa_style->i_margin_percent_h;
467 * p_ssa_style->i_margin_percent_v;
469 p_text_region
->i_x
= p_ssa_style
->i_margin_h
;
470 p_text_region
->i_y
= p_ssa_style
->i_margin_v
;
475 p_text_region
->i_align
= SUBPICTURE_ALIGN_BOTTOM
| i_sys_align
;
476 p_text_region
->i_x
= i_sys_align
? 20 : 0;
477 p_text_region
->i_y
= 10;
479 /* Look for position arguments which may override the style-based
482 SetupPositions( p_text_region
, psz_subtitle
);
484 p_text_region
->p_next
= NULL
;
486 return p_text_region
;
489 static int ParseImageAttachments( decoder_t
*p_dec
)
491 decoder_sys_t
*p_sys
= p_dec
->p_sys
;
492 input_attachment_t
**pp_attachments
;
493 int i_attachments_cnt
;
496 if( VLC_SUCCESS
!= decoder_GetInputAttachments( p_dec
, &pp_attachments
, &i_attachments_cnt
))
499 for( k
= 0; k
< i_attachments_cnt
; k
++ )
501 input_attachment_t
*p_attach
= pp_attachments
[k
];
503 vlc_fourcc_t type
= image_Mime2Fourcc( p_attach
->psz_mime
);
506 ( p_attach
->i_data
> 0 ) &&
507 ( p_attach
->p_data
!= NULL
) )
509 picture_t
*p_pic
= NULL
;
510 image_handler_t
*p_image
;
512 p_image
= image_HandlerCreate( p_dec
);
513 if( p_image
!= NULL
)
517 p_block
= block_New( p_image
->p_parent
, p_attach
->i_data
);
519 if( p_block
!= NULL
)
521 video_format_t fmt_in
;
522 video_format_t fmt_out
;
524 memcpy( p_block
->p_buffer
, p_attach
->p_data
, p_attach
->i_data
);
526 memset( &fmt_in
, 0, sizeof( video_format_t
));
527 memset( &fmt_out
, 0, sizeof( video_format_t
));
529 fmt_in
.i_chroma
= type
;
530 fmt_out
.i_chroma
= VLC_CODEC_YUVA
;
532 /* Find a suitable decoder module */
533 if( module_exists( "sdl_image" ) )
535 /* ffmpeg thinks it can handle bmp properly but it can't (at least
536 * not all of them), so use sdl_image if it is available */
538 var_Create( p_dec
, "codec", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
539 var_SetString( p_dec
, "codec", "sdl_image" );
542 p_pic
= image_Read( p_image
, p_block
, &fmt_in
, &fmt_out
);
543 var_Destroy( p_dec
, "codec" );
546 image_HandlerDelete( p_image
);
550 image_attach_t
*p_picture
= malloc( sizeof(image_attach_t
) );
554 p_picture
->psz_filename
= strdup( p_attach
->psz_name
);
555 p_picture
->p_pic
= p_pic
;
557 TAB_APPEND( p_sys
->i_images
, p_sys
->pp_images
, p_picture
);
561 vlc_input_attachment_Delete( pp_attachments
[ k
] );
563 free( pp_attachments
);
568 static void ParseUSFHeaderTags( decoder_t
*p_dec
, xml_reader_t
*p_xml_reader
)
570 decoder_sys_t
*p_sys
= p_dec
->p_sys
;
572 ssa_style_t
*p_ssa_style
= NULL
;
573 int i_style_level
= 0;
574 int i_metadata_level
= 0;
577 while( (type
= xml_ReaderNextNode( p_xml_reader
, &node
)) > 0 )
581 case XML_READER_ENDELEM
:
582 switch (i_style_level
)
585 if( !strcasecmp( "metadata", node
) && (i_metadata_level
== 1) )
589 if( !strcasecmp( "styles", node
) )
593 if( !strcasecmp( "style", node
) )
595 TAB_APPEND( p_sys
->i_ssa_styles
, p_sys
->pp_ssa_styles
, p_ssa_style
);
604 case XML_READER_STARTELEM
:
605 if( !strcasecmp( "metadata", node
) && (i_style_level
== 0) )
607 else if( !strcasecmp( "resolution", node
) &&
608 ( i_metadata_level
== 1) )
610 const char *attr
, *val
;
611 while( (attr
= xml_ReaderNextAttr( p_xml_reader
, &val
)) )
613 if( !strcasecmp( "x", attr
) )
614 p_sys
->i_original_width
= atoi( val
);
615 else if( !strcasecmp( "y", attr
) )
616 p_sys
->i_original_height
= atoi( val
);
619 else if( !strcasecmp( "styles", node
) && (i_style_level
== 0) )
623 else if( !strcasecmp( "style", node
) && (i_style_level
== 1) )
627 p_ssa_style
= calloc( 1, sizeof(ssa_style_t
) );
628 if( unlikely(!p_ssa_style
) )
630 /* All styles are supposed to default to Default, and then
631 * one or more settings are over-ridden.
632 * At the moment this only effects styles defined AFTER
635 for( int i
= 0; i
< p_sys
->i_ssa_styles
; i
++ )
637 if( !strcasecmp( p_sys
->pp_ssa_styles
[i
]->psz_stylename
, "Default" ) )
639 ssa_style_t
*p_default_style
= p_sys
->pp_ssa_styles
[i
];
641 memcpy( p_ssa_style
, p_default_style
, sizeof( ssa_style_t
) );
642 //FIXME: Make font_style a pointer. Actually we double copy some data here,
643 // we use text_style_Copy to avoid copying psz_fontname, though .
644 text_style_Copy( &p_ssa_style
->font_style
, &p_default_style
->font_style
);
645 p_ssa_style
->psz_stylename
= NULL
;
649 const char *attr
, *val
;
650 while( (attr
= xml_ReaderNextAttr( p_xml_reader
, &val
)) )
652 if( !strcasecmp( "name", attr
) )
654 free( p_ssa_style
->psz_stylename
);
655 p_ssa_style
->psz_stylename
= strdup( val
);
659 else if( !strcasecmp( "fontstyle", node
) && (i_style_level
== 2) )
661 const char *attr
, *val
;
662 while( (attr
= xml_ReaderNextAttr( p_xml_reader
, &val
)) )
664 if( !strcasecmp( "face", attr
) )
666 free( p_ssa_style
->font_style
.psz_fontname
);
667 p_ssa_style
->font_style
.psz_fontname
= strdup( val
);
669 else if( !strcasecmp( "size", attr
) )
671 if( ( *val
== '+' ) || ( *val
== '-' ) )
673 int i_value
= atoi( val
);
675 if( ( i_value
>= -5 ) && ( i_value
<= 5 ) )
676 p_ssa_style
->font_style
.i_font_size
+=
677 ( i_value
* p_ssa_style
->font_style
.i_font_size
) / 10;
678 else if( i_value
< -5 )
679 p_ssa_style
->font_style
.i_font_size
= - i_value
;
680 else if( i_value
> 5 )
681 p_ssa_style
->font_style
.i_font_size
= i_value
;
684 p_ssa_style
->font_style
.i_font_size
= atoi( val
);
686 else if( !strcasecmp( "italic", attr
) )
688 if( !strcasecmp( "yes", val
))
689 p_ssa_style
->font_style
.i_style_flags
|= STYLE_ITALIC
;
691 p_ssa_style
->font_style
.i_style_flags
&= ~STYLE_ITALIC
;
693 else if( !strcasecmp( "weight", attr
) )
695 if( !strcasecmp( "bold", val
))
696 p_ssa_style
->font_style
.i_style_flags
|= STYLE_BOLD
;
698 p_ssa_style
->font_style
.i_style_flags
&= ~STYLE_BOLD
;
700 else if( !strcasecmp( "underline", attr
) )
702 if( !strcasecmp( "yes", val
))
703 p_ssa_style
->font_style
.i_style_flags
|= STYLE_UNDERLINE
;
705 p_ssa_style
->font_style
.i_style_flags
&= ~STYLE_UNDERLINE
;
707 else if( !strcasecmp( "color", attr
) )
711 unsigned long col
= strtol(val
+1, NULL
, 16);
712 p_ssa_style
->font_style
.i_font_color
= (col
& 0x00ffffff);
713 p_ssa_style
->font_style
.i_font_alpha
= (col
>> 24) & 0xff;
716 else if( !strcasecmp( "outline-color", attr
) )
720 unsigned long col
= strtol(val
+1, NULL
, 16);
721 p_ssa_style
->font_style
.i_outline_color
= (col
& 0x00ffffff);
722 p_ssa_style
->font_style
.i_outline_alpha
= (col
>> 24) & 0xff;
725 else if( !strcasecmp( "outline-level", attr
) )
727 p_ssa_style
->font_style
.i_outline_width
= atoi( val
);
729 else if( !strcasecmp( "shadow-color", attr
) )
733 unsigned long col
= strtol(val
+1, NULL
, 16);
734 p_ssa_style
->font_style
.i_shadow_color
= (col
& 0x00ffffff);
735 p_ssa_style
->font_style
.i_shadow_alpha
= (col
>> 24) & 0xff;
738 else if( !strcasecmp( "shadow-level", attr
) )
740 p_ssa_style
->font_style
.i_shadow_width
= atoi( val
);
742 else if( !strcasecmp( "back-color", attr
) )
746 unsigned long col
= strtol(val
+1, NULL
, 16);
747 p_ssa_style
->font_style
.i_karaoke_background_color
= (col
& 0x00ffffff);
748 p_ssa_style
->font_style
.i_karaoke_background_alpha
= (col
>> 24) & 0xff;
751 else if( !strcasecmp( "spacing", attr
) )
753 p_ssa_style
->font_style
.i_spacing
= atoi( val
);
757 else if( !strcasecmp( "position", node
) && (i_style_level
== 2) )
759 const char *attr
, *val
;
760 while( (attr
= xml_ReaderNextAttr( p_xml_reader
, &val
)) )
762 if( !strcasecmp( "alignment", attr
) )
764 if( !strcasecmp( "TopLeft", val
) )
765 p_ssa_style
->i_align
= SUBPICTURE_ALIGN_TOP
| SUBPICTURE_ALIGN_LEFT
;
766 else if( !strcasecmp( "TopCenter", val
) )
767 p_ssa_style
->i_align
= SUBPICTURE_ALIGN_TOP
;
768 else if( !strcasecmp( "TopRight", val
) )
769 p_ssa_style
->i_align
= SUBPICTURE_ALIGN_TOP
| SUBPICTURE_ALIGN_RIGHT
;
770 else if( !strcasecmp( "MiddleLeft", val
) )
771 p_ssa_style
->i_align
= SUBPICTURE_ALIGN_LEFT
;
772 else if( !strcasecmp( "MiddleCenter", val
) )
773 p_ssa_style
->i_align
= 0;
774 else if( !strcasecmp( "MiddleRight", val
) )
775 p_ssa_style
->i_align
= SUBPICTURE_ALIGN_RIGHT
;
776 else if( !strcasecmp( "BottomLeft", val
) )
777 p_ssa_style
->i_align
= SUBPICTURE_ALIGN_BOTTOM
| SUBPICTURE_ALIGN_LEFT
;
778 else if( !strcasecmp( "BottomCenter", val
) )
779 p_ssa_style
->i_align
= SUBPICTURE_ALIGN_BOTTOM
;
780 else if( !strcasecmp( "BottomRight", val
) )
781 p_ssa_style
->i_align
= SUBPICTURE_ALIGN_BOTTOM
| SUBPICTURE_ALIGN_RIGHT
;
783 else if( !strcasecmp( "horizontal-margin", attr
) )
785 if( strchr( val
, '%' ) )
787 p_ssa_style
->i_margin_h
= 0;
788 p_ssa_style
->i_margin_percent_h
= atoi( val
);
792 p_ssa_style
->i_margin_h
= atoi( val
);
793 p_ssa_style
->i_margin_percent_h
= 0;
796 else if( !strcasecmp( "vertical-margin", attr
) )
798 if( strchr( val
, '%' ) )
800 p_ssa_style
->i_margin_v
= 0;
801 p_ssa_style
->i_margin_percent_v
= atoi( val
);
805 p_ssa_style
->i_margin_v
= atoi( val
);
806 p_ssa_style
->i_margin_percent_v
= 0;
819 static subpicture_region_t
*ParseUSFString( decoder_t
*p_dec
,
822 decoder_sys_t
*p_sys
= p_dec
->p_sys
;
823 subpicture_region_t
*p_region_first
= NULL
;
824 subpicture_region_t
*p_region_upto
= p_region_first
;
826 while( *psz_subtitle
)
828 if( *psz_subtitle
== '<' )
830 char *psz_end
= NULL
;
832 if(( !strncasecmp( psz_subtitle
, "<text ", 6 )) ||
833 ( !strncasecmp( psz_subtitle
, "<text>", 6 )))
835 psz_end
= strcasestr( psz_subtitle
, "</text>" );
839 subpicture_region_t
*p_text_region
;
841 psz_end
+= strcspn( psz_end
, ">" ) + 1;
843 p_text_region
= CreateTextRegion( p_dec
,
845 psz_end
- psz_subtitle
,
850 p_text_region
->psz_text
= CreatePlainText( p_text_region
->psz_html
);
852 if( ! var_CreateGetBool( p_dec
, "subsdec-formatted" ) )
854 free( p_text_region
->psz_html
);
855 p_text_region
->psz_html
= NULL
;
859 if( !p_region_first
)
861 p_region_first
= p_region_upto
= p_text_region
;
863 else if( p_text_region
)
865 p_region_upto
->p_next
= p_text_region
;
866 p_region_upto
= p_region_upto
->p_next
;
870 else if(( !strncasecmp( psz_subtitle
, "<karaoke ", 9 )) ||
871 ( !strncasecmp( psz_subtitle
, "<karaoke>", 9 )))
873 psz_end
= strcasestr( psz_subtitle
, "</karaoke>" );
877 subpicture_region_t
*p_text_region
;
879 psz_end
+= strcspn( psz_end
, ">" ) + 1;
881 p_text_region
= CreateTextRegion( p_dec
,
883 psz_end
- psz_subtitle
,
888 if( ! var_CreateGetBool( p_dec
, "subsdec-formatted" ) )
890 free( p_text_region
->psz_html
);
891 p_text_region
->psz_html
= NULL
;
894 if( !p_region_first
)
896 p_region_first
= p_region_upto
= p_text_region
;
898 else if( p_text_region
)
900 p_region_upto
->p_next
= p_text_region
;
901 p_region_upto
= p_region_upto
->p_next
;
905 else if(( !strncasecmp( psz_subtitle
, "<image ", 7 )) ||
906 ( !strncasecmp( psz_subtitle
, "<image>", 7 )))
908 subpicture_region_t
*p_image_region
= NULL
;
910 char *psz_end
= strcasestr( psz_subtitle
, "</image>" );
911 char *psz_content
= strchr( psz_subtitle
, '>' );
912 int i_transparent
= -1;
914 /* If a colorkey parameter is specified, then we have to map
915 * that index in the picture through as transparent (it is
916 * required by the USF spec but is also recommended that if the
917 * creator really wants a transparent colour that they use a
918 * type like PNG that properly supports it; this goes doubly
919 * for VLC because the pictures are stored internally in YUV
920 * and the resulting colour-matching may not produce the
923 char *psz_tmp
= GrabAttributeValue( "colorkey", psz_subtitle
);
926 if( *psz_tmp
== '#' )
927 i_transparent
= strtol( psz_tmp
+ 1, NULL
, 16 ) & 0x00ffffff;
930 if( psz_content
&& ( psz_content
< psz_end
) )
932 char *psz_filename
= strndup( &psz_content
[1], psz_end
- &psz_content
[1] );
935 p_image_region
= LoadEmbeddedImage( p_dec
,
936 psz_filename
, i_transparent
);
937 free( psz_filename
);
941 if( psz_end
) psz_end
+= strcspn( psz_end
, ">" ) + 1;
945 SetupPositions( p_image_region
, psz_subtitle
);
947 p_image_region
->p_next
= NULL
;
948 p_image_region
->psz_text
= NULL
;
949 p_image_region
->psz_html
= NULL
;
952 if( !p_region_first
)
954 p_region_first
= p_region_upto
= p_image_region
;
956 else if( p_image_region
)
958 p_region_upto
->p_next
= p_image_region
;
959 p_region_upto
= p_region_upto
->p_next
;
963 psz_subtitle
= psz_end
- 1;
965 psz_subtitle
+= strcspn( psz_subtitle
, ">" );
971 return p_region_first
;
974 /*****************************************************************************
975 * ParseUSFHeader: Retrieve global formatting information etc
976 *****************************************************************************/
977 static void ParseUSFHeader( decoder_t
*p_dec
)
979 stream_t
*p_sub
= NULL
;
980 xml_reader_t
*p_xml_reader
= NULL
;
982 p_sub
= stream_MemoryNew( VLC_OBJECT(p_dec
),
983 p_dec
->fmt_in
.p_extra
,
984 p_dec
->fmt_in
.i_extra
,
989 p_xml_reader
= xml_ReaderCreate( p_dec
, p_sub
);
990 if( likely(p_xml_reader
) )
994 /* Look for Root Node */
995 if( xml_ReaderNextNode( p_xml_reader
, &node
) == XML_READER_STARTELEM
996 && !strcasecmp( "usfsubtitles", node
) )
997 ParseUSFHeaderTags( p_dec
, p_xml_reader
);
999 xml_ReaderDelete( p_xml_reader
);
1001 stream_Delete( p_sub
);
1004 /* Function now handles tags which has attribute values, and tries
1005 * to deal with &' commands too. It no longer modifies the string
1006 * in place, so that the original text can be reused
1008 static char *StripTags( char *psz_subtitle
)
1010 char *psz_text_start
;
1013 psz_text
= psz_text_start
= malloc( strlen( psz_subtitle
) + 1 );
1014 if( !psz_text_start
)
1017 while( *psz_subtitle
)
1019 /* Mask out any pre-existing LFs in the subtitle */
1020 if( *psz_subtitle
== '\n' )
1021 *psz_subtitle
= ' ';
1023 if( *psz_subtitle
== '<' )
1025 if( strncasecmp( psz_subtitle
, "<br/>", 5 ) == 0 )
1028 psz_subtitle
+= strcspn( psz_subtitle
, ">" );
1030 else if( *psz_subtitle
== '&' )
1032 if( !strncasecmp( psz_subtitle
, "<", 4 ))
1035 psz_subtitle
+= strcspn( psz_subtitle
, ";" );
1037 else if( !strncasecmp( psz_subtitle
, ">", 4 ))
1040 psz_subtitle
+= strcspn( psz_subtitle
, ";" );
1042 else if( !strncasecmp( psz_subtitle
, "&", 5 ))
1045 psz_subtitle
+= strcspn( psz_subtitle
, ";" );
1047 else if( !strncasecmp( psz_subtitle
, """, 6 ))
1050 psz_subtitle
+= strcspn( psz_subtitle
, ";" );
1054 /* Assume it is just a normal ampersand */
1060 *psz_text
++ = *psz_subtitle
;
1063 /* Security fix: Account for the case where input ends early */
1064 if( *psz_subtitle
== '\0' ) break;
1070 char *psz
= realloc( psz_text_start
, strlen( psz_text_start
) + 1 );
1071 if( psz
) psz_text_start
= psz
;
1073 return psz_text_start
;
1076 /* Turn a HTML subtitle, turn into a plain-text version,
1077 * complete with sensible whitespace compaction
1080 static char *CreatePlainText( char *psz_subtitle
)
1082 char *psz_text
= StripTags( psz_subtitle
);
1088 s
= strpbrk( psz_text
, "\t\r\n " );
1093 int i_whitespace
= strspn( s
, "\t\r\n " );
1095 /* Favour '\n' over other whitespaces - if one of these
1096 * occurs in the whitespace use a '\n' as our value,
1097 * otherwise just use a ' '
1099 for( k
= 0; k
< i_whitespace
; k
++ )
1100 if( s
[k
] == '\n' ) spc
= '\n';
1102 if( i_whitespace
> 1 )
1106 strlen( s
) - i_whitespace
+ 1 );
1110 s
= strpbrk( s
, "\t\r\n " );
1115 /****************************************************************************
1116 * download and resize image located at psz_url
1117 ***************************************************************************/
1118 static subpicture_region_t
*LoadEmbeddedImage( decoder_t
*p_dec
,
1119 const char *psz_filename
,
1120 int i_transparent_color
)
1122 decoder_sys_t
*p_sys
= p_dec
->p_sys
;
1123 subpicture_region_t
*p_region
;
1124 video_format_t fmt_out
;
1125 picture_t
*p_pic
= NULL
;
1127 for( int k
= 0; k
< p_sys
->i_images
; k
++ )
1129 if( p_sys
->pp_images
&&
1130 !strcmp( p_sys
->pp_images
[k
]->psz_filename
, psz_filename
) )
1132 p_pic
= p_sys
->pp_images
[k
]->p_pic
;
1139 msg_Err( p_dec
, "Unable to read image %s", psz_filename
);
1143 /* Display the feed's image */
1144 memset( &fmt_out
, 0, sizeof( video_format_t
));
1146 fmt_out
.i_chroma
= VLC_CODEC_YUVA
;
1147 fmt_out
.i_sar_num
= fmt_out
.i_sar_den
= 1;
1149 fmt_out
.i_visible_width
= p_pic
->format
.i_visible_width
;
1151 fmt_out
.i_visible_height
= p_pic
->format
.i_visible_height
;
1153 p_region
= subpicture_region_New( &fmt_out
);
1156 msg_Err( p_dec
, "cannot allocate SPU region" );
1159 assert( p_pic
->format
.i_chroma
== VLC_CODEC_YUVA
);
1160 /* FIXME the copy is probably not needed anymore */
1161 picture_CopyPixels( p_region
->p_picture
, p_pic
);
1163 /* This isn't the best way to do this - if you really want transparency, then
1164 * you're much better off using an image type that supports it like PNG. The
1165 * spec requires this support though.
1167 if( i_transparent_color
> 0 )
1169 int i_r
= ( i_transparent_color
>> 16 ) & 0xff;
1170 int i_g
= ( i_transparent_color
>> 8 ) & 0xff;
1171 int i_b
= ( i_transparent_color
) & 0xff;
1173 /* FIXME it cannot work as the yuv conversion code will probably NOT match
1175 int i_y
= ( ( ( 66 * i_r
+ 129 * i_g
+ 25 * i_b
+ 128 ) >> 8 ) + 16 );
1176 int i_u
= ( ( -38 * i_r
- 74 * i_g
+ 112 * i_b
+ 128 ) >> 8 ) + 128 ;
1177 int i_v
= ( ( 112 * i_r
- 94 * i_g
- 18 * i_b
+ 128 ) >> 8 ) + 128 ;
1179 assert( p_region
->fmt
.i_chroma
== VLC_CODEC_YUVA
);
1180 for( unsigned int y
= 0; y
< p_region
->fmt
.i_height
; y
++ )
1182 for( unsigned int x
= 0; x
< p_region
->fmt
.i_width
; x
++ )
1184 if( p_region
->p_picture
->Y_PIXELS
[y
*p_region
->p_picture
->Y_PITCH
+ x
] != i_y
||
1185 p_region
->p_picture
->U_PIXELS
[y
*p_region
->p_picture
->U_PITCH
+ x
] != i_u
||
1186 p_region
->p_picture
->V_PIXELS
[y
*p_region
->p_picture
->V_PITCH
+ x
] != i_v
)
1188 p_region
->p_picture
->A_PIXELS
[y
*p_region
->p_picture
->A_PITCH
+ x
] = 0;