1 /*****************************************************************************
2 * substx3gsub.c : MP4 tx3g subtitles decoder
3 *****************************************************************************
4 * Copyright (C) 2014 VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation, Inc.,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
25 #include <vlc_common.h>
26 #include <vlc_plugin.h>
27 #include <vlc_codec.h>
32 /*****************************************************************************
34 *****************************************************************************/
35 static int Open ( vlc_object_t * );
36 static subpicture_t *Decode( decoder_t *, block_t ** );
39 set_description( N_("tx3g subtitles decoder") )
40 set_shortname( N_("tx3g subtitles") )
41 set_capability( "decoder", 100 )
42 set_category( CAT_INPUT )
43 set_subcategory( SUBCAT_INPUT_SCODEC )
44 set_callbacks( Open, NULL )
47 /****************************************************************************
49 ****************************************************************************/
51 /*****************************************************************************
52 * Open: probe the decoder and return score
53 *****************************************************************************/
54 static int Open( vlc_object_t *p_this )
56 decoder_t *p_dec = (decoder_t *) p_this;
58 if( p_dec->fmt_in.i_codec != VLC_CODEC_TX3G )
61 p_dec->pf_decode_sub = Decode;
63 p_dec->fmt_out.i_cat = SPU_ES;
64 p_dec->fmt_out.i_codec = 0;
69 /*****************************************************************************
71 *****************************************************************************/
73 #define FONT_FACE_BOLD 0x1
74 #define FONT_FACE_ITALIC 0x2
75 #define FONT_FACE_UNDERLINE 0x4
77 static int ConvertFlags( int i_atomflags )
79 int i_vlcstyles_flags = 0;
80 if ( i_atomflags & FONT_FACE_BOLD )
81 i_vlcstyles_flags |= STYLE_BOLD;
82 else if ( i_atomflags & FONT_FACE_ITALIC )
83 i_vlcstyles_flags |= STYLE_ITALIC;
84 else if ( i_atomflags & FONT_FACE_UNDERLINE )
85 i_vlcstyles_flags |= STYLE_UNDERLINE;
86 return i_vlcstyles_flags;
89 static size_t str8len( const char *psz_string )
91 const char *psz_tmp = psz_string;
95 if ( (*psz_tmp & 0xC0) != 0x80 ) i++;
101 static char * str8indup( const char *psz_string, size_t i_skip, size_t n )
103 while( i_skip && *psz_string )
105 if ( (*psz_string & 0xC0) != 0x80 ) i_skip--;
108 if ( ! *psz_string || i_skip ) return NULL;
110 const char *psz_tmp = psz_string;
111 while( n && *psz_tmp )
113 if ( (*psz_tmp & 0xC0) != 0x80 ) n--;
116 return strndup( psz_string, psz_tmp - psz_string );
119 static void SegmentDoSplit( segment_t *p_segment, uint16_t i_start, uint16_t i_end,
120 segment_t **pp_segment_left,
121 segment_t **pp_segment_middle,
122 segment_t **pp_segment_right )
124 segment_t *p_segment_left = *pp_segment_left;
125 segment_t *p_segment_right = *pp_segment_right;
126 segment_t *p_segment_middle = *pp_segment_middle;
127 p_segment_left = p_segment_middle = p_segment_right = NULL;
129 if ( (p_segment->i_size - i_start < 1) || (p_segment->i_size - i_end < 1) )
134 p_segment_left = calloc( 1, sizeof(segment_t) );
135 if ( !p_segment_left ) goto error;
136 memcpy( &p_segment_left->styles, &p_segment->styles, sizeof(segment_style_t) );
137 p_segment_left->psz_string = str8indup( p_segment->psz_string, 0, i_start );
138 p_segment_left->i_size = str8len( p_segment_left->psz_string );
141 p_segment_middle = calloc( 1, sizeof(segment_t) );
142 if ( !p_segment_middle ) goto error;
143 memcpy( &p_segment_middle->styles, &p_segment->styles, sizeof(segment_style_t) );
144 p_segment_middle->psz_string = str8indup( p_segment->psz_string, i_start, i_end - i_start + 1 );
145 p_segment_middle->i_size = str8len( p_segment_middle->psz_string );
147 if ( i_end < (p_segment->i_size - 1) )
149 p_segment_right = calloc( 1, sizeof(segment_t) );
150 if ( !p_segment_right ) goto error;
151 memcpy( &p_segment_right->styles, &p_segment->styles, sizeof(segment_style_t) );
152 p_segment_right->psz_string = str8indup( p_segment->psz_string, i_end + 1, p_segment->i_size - i_end - 1 );
153 p_segment_right->i_size = str8len( p_segment_right->psz_string );
156 if ( p_segment_left ) p_segment_left->p_next = p_segment_middle;
157 if ( p_segment_right ) p_segment_middle->p_next = p_segment_right;
159 *pp_segment_left = p_segment_left;
160 *pp_segment_middle = p_segment_middle;
161 *pp_segment_right = p_segment_right;
166 SegmentFree( p_segment_left );
167 SegmentFree( p_segment_middle );
168 SegmentFree( p_segment_right );
171 static bool SegmentSplit( segment_t *p_prev, segment_t **pp_segment,
172 const uint16_t i_start, const uint16_t i_end,
173 const segment_style_t *p_styles )
175 segment_t *p_segment_left = NULL, *p_segment_middle = NULL, *p_segment_right = NULL;
177 if ( (*pp_segment)->i_size == 0 ) return false;
178 if ( i_start > i_end ) return false;
179 if ( (size_t)(i_end - i_start) > (*pp_segment)->i_size - 1 ) return false;
180 if ( i_end > (*pp_segment)->i_size - 1 ) return false;
182 SegmentDoSplit( *pp_segment, i_start, i_end, &p_segment_left, &p_segment_middle, &p_segment_right );
183 if ( !p_segment_middle )
186 SegmentFree( p_segment_left );
187 SegmentFree( p_segment_right );
191 segment_t *p_next = (*pp_segment)->p_next;
192 SegmentFree( *pp_segment );
193 *pp_segment = ( p_segment_left ) ? p_segment_left : p_segment_middle ;
194 if ( p_prev ) p_prev->p_next = *pp_segment;
196 if ( p_segment_right )
197 p_segment_right->p_next = p_next;
199 p_segment_middle->p_next = p_next;
201 p_segment_middle->styles = *p_styles;
206 /* Creates a new segment using the given style and split existing ones according
207 to the start & end offsets */
208 static void ApplySegmentStyle( segment_t **pp_segment, const uint16_t i_absstart,
209 const uint16_t i_absend, const segment_style_t *p_styles )
211 /* find the matching segment */
212 uint16_t i_curstart = 0;
213 segment_t *p_prev = NULL;
214 segment_t *p_cur = *pp_segment;
217 uint16_t i_curend = i_curstart + p_cur->i_size - 1;
218 if ( (i_absstart >= i_curstart) && (i_absend <= i_curend) )
221 if ( !SegmentSplit( p_prev, &p_cur, i_absstart - i_curstart,
222 i_absend - i_curstart, p_styles ) )
224 if ( !p_prev ) *pp_segment = p_cur;
229 i_curstart += p_cur->i_size;
231 p_cur = p_cur->p_next;
236 /*****************************************************************************
238 *****************************************************************************/
239 static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block )
242 subpicture_t *p_spu = NULL;
244 if( ( pp_block == NULL ) || ( *pp_block == NULL ) ) return NULL;
248 if( ( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) ||
249 p_block->i_buffer < sizeof(uint16_t) )
251 block_Release( p_block );
255 uint8_t *p_buf = p_block->p_buffer;
257 /* Read our raw string and create the styled segment for HTML */
258 uint16_t i_psz_length = GetWBE( p_buf );
259 char *psz_subtitle = malloc( i_psz_length + 1 );
260 if ( !psz_subtitle ) return NULL;
261 memcpy( psz_subtitle, p_block->p_buffer + sizeof(uint16_t), i_psz_length );
262 psz_subtitle[ i_psz_length ] = '\0';
263 p_buf += i_psz_length + sizeof(uint16_t);
265 for( uint16_t i=0; i < i_psz_length; i++ )
266 if ( psz_subtitle[i] == '\r' ) psz_subtitle[i] = '\n';
268 segment_t *p_segment = calloc( 1, sizeof(segment_t) );
271 free( psz_subtitle );
274 p_segment->psz_string = strdup( psz_subtitle );
275 p_segment->i_size = str8len( psz_subtitle );
276 if ( p_dec->fmt_in.subs.p_style )
278 p_segment->styles.i_color = p_dec->fmt_in.subs.p_style->i_font_color;
279 p_segment->styles.i_color |= p_dec->fmt_in.subs.p_style->i_font_alpha << 24;
280 if ( p_dec->fmt_in.subs.p_style->i_style_flags )
281 p_segment->styles.i_flags = p_dec->fmt_in.subs.p_style->i_style_flags;
282 p_segment->styles.i_fontsize = p_dec->fmt_in.subs.p_style->i_font_size;
285 if ( !p_segment->psz_string )
287 SegmentFree( p_segment );
288 free( psz_subtitle );
292 /* Create the subpicture unit */
293 p_spu = decoder_NewSubpictureText( p_dec );
296 free( psz_subtitle );
297 SegmentFree( p_segment );
300 subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
302 /* Parse our styles */
303 while( (size_t)(p_buf - p_block->p_buffer) + 8 < p_block->i_buffer )
305 uint32_t i_atomsize = GetDWBE( p_buf );
306 vlc_fourcc_t i_atomtype = VLC_FOURCC(p_buf[4],p_buf[5],p_buf[6],p_buf[7]);
311 case VLC_FOURCC('s','t','y','l'):
313 if ( (size_t)(p_buf - p_block->p_buffer) < 14 ) break;
314 uint16_t i_nbrecords = GetWBE(p_buf);
315 uint16_t i_cur_record = 0;
317 while( i_cur_record++ < i_nbrecords )
319 if ( (size_t)(p_buf - p_block->p_buffer) < 12 ) break;
320 uint16_t i_start = __MIN( GetWBE(p_buf), i_psz_length - 1 );
321 uint16_t i_end = __MIN( GetWBE(p_buf + 2), i_psz_length - 1 );
323 segment_style_t style;
324 style.i_flags = ConvertFlags( p_buf[6] );
325 style.i_fontsize = p_buf[7];
326 style.i_color = GetDWBE(p_buf+8) >> 8;// RGBA -> ARGB
327 style.i_color |= (GetDWBE(p_buf+8) & 0xFF) << 24;
328 ApplySegmentStyle( &p_segment, i_start, i_end, &style );
330 if ( i_nbrecords == 1 )
334 p_spu_sys->style_flags.i_value = ConvertFlags( p_buf[6] );
335 p_spu_sys->style_flags.b_set = true;
337 p_spu_sys->i_font_height_abs_to_src = p_buf[7];
338 p_spu_sys->font_color.i_value = GetDWBE(p_buf+8) >> 8;// RGBA -> ARGB
339 p_spu_sys->font_color.i_value |= (GetDWBE(p_buf+8) & 0xFF) << 24;
340 p_spu_sys->font_color.b_set = true;
347 case VLC_FOURCC('d','r','p','o'):
348 if ( (size_t)(p_buf - p_block->p_buffer) < 4 ) break;
349 p_spu_sys->i_drop_shadow = __MAX( GetWBE(p_buf), GetWBE(p_buf+2) );
352 case VLC_FOURCC('d','r','p','t'):
353 if ( (size_t)(p_buf - p_block->p_buffer) < 2 ) break;
354 p_spu_sys->i_drop_shadow_alpha = GetWBE(p_buf);
364 p_spu->i_start = p_block->i_pts;
365 p_spu->i_stop = p_block->i_pts + p_block->i_length;
366 p_spu->b_ephemer = (p_block->i_length == 0);
367 p_spu->b_absolute = false;
369 p_spu_sys->align = SUBPICTURE_ALIGN_BOTTOM;
370 p_spu_sys->text = psz_subtitle;
371 p_spu_sys->p_htmlsegments = p_segment;
373 block_Release( p_block );