demux: mp4: avoid audio cuts on seek
[vlc.git] / modules / codec / substx3g.c
blob0035b974220e3cc7f3173b06a20ace4d0705fd8e
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 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include <vlc_common.h>
26 #include <vlc_plugin.h>
27 #include <vlc_codec.h>
28 #include <vlc_sout.h>
29 #include <vlc_charset.h>
31 #include "substext.h"
33 /*****************************************************************************
34 * Module descriptor.
35 *****************************************************************************/
36 static int Open ( vlc_object_t * );
37 static int Decode( decoder_t *, block_t * );
39 vlc_module_begin ()
40 set_description( N_("tx3g subtitles decoder") )
41 set_shortname( N_("tx3g subtitles") )
42 set_capability( "spu decoder", 100 )
43 set_category( CAT_INPUT )
44 set_subcategory( SUBCAT_INPUT_SCODEC )
45 set_callbacks( Open, NULL )
46 vlc_module_end ()
48 /****************************************************************************
49 * Local structs
50 ****************************************************************************/
52 /*****************************************************************************
53 * Open: probe the decoder and return score
54 *****************************************************************************/
55 static int Open( vlc_object_t *p_this )
57 decoder_t *p_dec = (decoder_t *) p_this;
59 if( p_dec->fmt_in.i_codec != VLC_CODEC_TX3G )
60 return VLC_EGENERIC;
62 p_dec->pf_decode = Decode;
64 p_dec->fmt_out.i_codec = 0;
65 if( p_dec->fmt_out.subs.p_style )
67 p_dec->fmt_out.subs.p_style->i_font_size = 0;
68 p_dec->fmt_out.subs.p_style->f_font_relsize = 5.0;
71 return VLC_SUCCESS;
74 /*****************************************************************************
75 * Local:
76 *****************************************************************************/
78 #define FONT_FACE_BOLD 0x1
79 #define FONT_FACE_ITALIC 0x2
80 #define FONT_FACE_UNDERLINE 0x4
82 static int ConvertFlags( int i_atomflags )
84 int i_vlcstyles_flags = 0;
85 if ( i_atomflags & FONT_FACE_BOLD )
86 i_vlcstyles_flags |= STYLE_BOLD;
87 if ( i_atomflags & FONT_FACE_ITALIC )
88 i_vlcstyles_flags |= STYLE_ITALIC;
89 if ( i_atomflags & FONT_FACE_UNDERLINE )
90 i_vlcstyles_flags |= STYLE_UNDERLINE;
91 return i_vlcstyles_flags;
94 static size_t str8len( const char *psz_string )
96 const char *psz_tmp = psz_string;
97 size_t i=0;
98 while ( *psz_tmp )
100 if ( (*psz_tmp & 0xC0) != 0x80 ) i++;
101 psz_tmp++;
103 return i;
106 static char * str8indup( const char *psz_string, size_t i_skip, size_t n )
108 while( i_skip && *psz_string )
110 if ( (*psz_string & 0xC0) != 0x80 ) i_skip--;
111 psz_string++;
113 if ( ! *psz_string || i_skip ) return NULL;
115 const char *psz_tmp = psz_string;
116 while( n && *psz_tmp )
118 if ( (*psz_tmp & 0xC0) != 0x80 ) n--;
119 psz_tmp++;
121 return strndup( psz_string, psz_tmp - psz_string );
124 typedef struct tx3g_segment_t tx3g_segment_t;
126 struct tx3g_segment_t
128 text_segment_t *s;
129 size_t i_size;
130 tx3g_segment_t *p_next3g;
133 static tx3g_segment_t * tx3g_segment_New( const char *psz_string )
135 tx3g_segment_t *p_seg = malloc( sizeof(tx3g_segment_t) );
136 if( p_seg )
138 p_seg->i_size = 0;
139 p_seg->p_next3g = NULL;
140 p_seg->s = text_segment_New( psz_string );
141 if( !p_seg->s )
143 free( p_seg );
144 p_seg = NULL;
147 return p_seg;
150 static void SegmentDoSplit( tx3g_segment_t *p_segment, uint16_t i_start, uint16_t i_end,
151 tx3g_segment_t **pp_segment_left,
152 tx3g_segment_t **pp_segment_middle,
153 tx3g_segment_t **pp_segment_right )
155 tx3g_segment_t *p_segment_left = NULL, *p_segment_right = NULL, *p_segment_middle = NULL;
157 if ( (p_segment->i_size - i_start < 1) || (p_segment->i_size - i_end < 1) )
158 goto error;
160 if ( i_start > 0 )
162 char* psz_text = str8indup( p_segment->s->psz_text, 0, i_start );
163 p_segment_left = tx3g_segment_New( psz_text );
164 free( psz_text );
165 if ( !p_segment_left ) goto error;
166 p_segment_left->s->style = text_style_Duplicate( p_segment->s->style );
167 p_segment_left->i_size = str8len( p_segment_left->s->psz_text );
170 char* psz_text = str8indup( p_segment->s->psz_text, i_start, i_end - i_start + 1 );
171 p_segment_middle = tx3g_segment_New( psz_text );
172 free( psz_text );
173 if ( !p_segment_middle ) goto error;
174 p_segment_middle->s->style = text_style_Duplicate( p_segment->s->style );
175 p_segment_middle->i_size = str8len( p_segment_middle->s->psz_text );
177 if ( i_end < (p_segment->i_size - 1) )
179 char* psz_text = str8indup( p_segment->s->psz_text, i_end + 1, p_segment->i_size - i_end - 1 );
180 p_segment_right = tx3g_segment_New( psz_text );
181 free( psz_text );
182 if ( !p_segment_right ) goto error;
183 p_segment_right->s->style = text_style_Duplicate( p_segment->s->style );
184 p_segment_right->i_size = str8len( p_segment_right->s->psz_text );
187 if ( p_segment_left ) p_segment_left->p_next3g = p_segment_middle;
188 if ( p_segment_right ) p_segment_middle->p_next3g = p_segment_right;
190 *pp_segment_left = p_segment_left;
191 *pp_segment_middle = p_segment_middle;
192 *pp_segment_right = p_segment_right;
194 return;
196 error:
197 if( p_segment_middle )
199 text_segment_Delete( p_segment_middle->s );
200 free( p_segment_middle );
202 if( p_segment_left )
204 text_segment_Delete( p_segment_left->s );
205 free( p_segment_left );
207 *pp_segment_left = *pp_segment_middle = *pp_segment_right = NULL;
210 static bool SegmentSplit( tx3g_segment_t *p_prev, tx3g_segment_t **pp_segment,
211 const uint16_t i_start, const uint16_t i_end,
212 const text_style_t *p_styles )
214 tx3g_segment_t *p_segment_left = NULL, *p_segment_middle = NULL, *p_segment_right = NULL;
216 if ( (*pp_segment)->i_size == 0 ) return false;
217 if ( i_start > i_end ) return false;
218 if ( (size_t)(i_end - i_start) > (*pp_segment)->i_size - 1 ) return false;
219 if ( i_end > (*pp_segment)->i_size - 1 ) return false;
221 SegmentDoSplit( *pp_segment, i_start, i_end, &p_segment_left, &p_segment_middle, &p_segment_right );
222 if ( !p_segment_middle )
224 /* Failed */
225 text_segment_Delete( p_segment_left->s );
226 free( p_segment_left );
227 text_segment_Delete( p_segment_right->s );
228 free( p_segment_right );
229 return false;
232 tx3g_segment_t *p_next3g = (*pp_segment)->p_next3g;
233 text_segment_Delete( (*pp_segment)->s );
234 free( *pp_segment );
235 *pp_segment = ( p_segment_left ) ? p_segment_left : p_segment_middle ;
236 if ( p_prev ) p_prev->p_next3g = *pp_segment;
238 if ( p_segment_right )
239 p_segment_right->p_next3g = p_next3g;
240 else
241 p_segment_middle->p_next3g = p_next3g;
243 text_style_Delete( p_segment_middle->s->style );
244 p_segment_middle->s->style = text_style_Duplicate( p_styles );
246 return true;
249 /* Creates a new segment using the given style and split existing ones according
250 to the start & end offsets */
251 static void ApplySegmentStyle( tx3g_segment_t **pp_segment, const uint16_t i_absstart,
252 const uint16_t i_absend, const text_style_t *p_styles )
254 /* find the matching segment */
255 uint16_t i_curstart = 0;
256 tx3g_segment_t *p_prev = NULL;
257 tx3g_segment_t *p_cur = *pp_segment;
258 while ( p_cur )
260 uint16_t i_curend = i_curstart + p_cur->i_size - 1;
261 if ( (i_absstart >= i_curstart) && (i_absend <= i_curend) )
263 /* segment found */
264 if ( !SegmentSplit( p_prev, &p_cur, i_absstart - i_curstart,
265 i_absend - i_curstart, p_styles ) )
266 return;
267 if ( !p_prev ) *pp_segment = p_cur;
268 break;
270 else
272 i_curstart += p_cur->i_size;
273 p_prev = p_cur;
274 p_cur = p_cur->p_next3g;
279 /* Do relative size conversion using default style size (from stsd),
280 as the line should always be 5%. Apply to each segment specific text size */
281 static void FontSizeConvert( const text_style_t *p_default_style, text_style_t *p_style )
283 if( unlikely(!p_style) )
285 return;
287 else if( unlikely(!p_default_style) || p_default_style->i_font_size == 0 )
289 p_style->i_font_size = 0;
290 p_style->f_font_relsize = 5.0;
292 else
294 p_style->f_font_relsize = 5.0 * (float) p_style->i_font_size / p_default_style->i_font_size;
295 p_style->i_font_size = 0;
299 /*****************************************************************************
300 * Decode:
301 *****************************************************************************/
302 static int Decode( decoder_t *p_dec, block_t *p_block )
304 subpicture_t *p_spu = NULL;
306 if( p_block == NULL ) /* No Drain */
307 return VLCDEC_SUCCESS;
309 if( ( p_block->i_flags & (BLOCK_FLAG_CORRUPTED) ) ||
310 p_block->i_buffer < sizeof(uint16_t) )
312 block_Release( p_block );
313 return VLCDEC_SUCCESS;
316 uint8_t *p_buf = p_block->p_buffer;
318 /* Read our raw string and create the styled segment for HTML */
319 uint16_t i_psz_bytelength = GetWBE( p_buf );
320 const uint8_t *p_pszstart = p_block->p_buffer + sizeof(uint16_t);
321 char *psz_subtitle;
322 if ( i_psz_bytelength > 2 &&
323 ( !memcmp( p_pszstart, "\xFE\xFF", 2 ) || !memcmp( p_pszstart, "\xFF\xFE", 2 ) )
326 psz_subtitle = FromCharset( "UTF-16", p_pszstart, i_psz_bytelength );
327 if ( !psz_subtitle )
328 return VLCDEC_SUCCESS;
330 else
332 psz_subtitle = malloc( i_psz_bytelength + 1 );
333 if ( !psz_subtitle )
334 return VLCDEC_SUCCESS;
335 memcpy( psz_subtitle, p_pszstart, i_psz_bytelength );
336 psz_subtitle[ i_psz_bytelength ] = '\0';
338 p_buf += i_psz_bytelength + sizeof(uint16_t);
340 for( uint16_t i=0; i < i_psz_bytelength; i++ )
341 if ( psz_subtitle[i] == '\r' ) psz_subtitle[i] = '\n';
343 tx3g_segment_t *p_segment3g = tx3g_segment_New( psz_subtitle );
344 p_segment3g->i_size = str8len( psz_subtitle );
345 if ( p_dec->fmt_in.subs.p_style )
346 p_segment3g->s->style = text_style_Duplicate( p_dec->fmt_in.subs.p_style );
348 free( psz_subtitle );
350 if ( !p_segment3g->s->psz_text )
352 text_segment_Delete( p_segment3g->s );
353 free( p_segment3g );
354 return VLCDEC_SUCCESS;
357 /* Create the subpicture unit */
358 p_spu = decoder_NewSubpictureText( p_dec );
359 if( !p_spu )
361 text_segment_Delete( p_segment3g->s );
362 free( p_segment3g );
363 return VLCDEC_SUCCESS;
365 subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
367 /* Parse our styles */
368 while( (size_t)(p_buf - p_block->p_buffer) + 8 < p_block->i_buffer )
370 uint32_t i_atomsize = GetDWBE( p_buf );
371 vlc_fourcc_t i_atomtype = VLC_FOURCC(p_buf[4],p_buf[5],p_buf[6],p_buf[7]);
372 p_buf += 8;
373 switch( i_atomtype )
376 case VLC_FOURCC('s','t','y','l'):
378 if ( (size_t)(p_buf - p_block->p_buffer) < 14 ) break;
379 uint16_t i_nbrecords = GetWBE(p_buf);
380 uint16_t i_cur_record = 0;
381 p_buf += 2;
382 while( i_cur_record++ < i_nbrecords )
384 if ( (size_t)(p_buf - p_block->p_buffer) < 12 ) break;
385 uint16_t i_start = __MIN( GetWBE(p_buf), i_psz_bytelength - 1 );
386 uint16_t i_end = __MIN( GetWBE(p_buf + 2), i_psz_bytelength - 1 );
388 text_style_t style;
389 memset( &style, 0, sizeof(text_style_t) );
390 style.i_style_flags = ConvertFlags( p_buf[6] );
391 style.i_font_size = p_buf[7];
392 style.i_font_color = GetDWBE(p_buf+8) >> 8;// RGBA -> RGB
393 style.i_font_alpha = GetDWBE(p_buf+8) & 0xFF;
394 style.i_features = STYLE_HAS_FONT_COLOR | STYLE_HAS_FONT_ALPHA;
395 ApplySegmentStyle( &p_segment3g, i_start, i_end, &style );
397 if ( i_nbrecords == 1 )
399 if ( p_buf[6] )
401 if( (p_spu_sys->p_default_style->i_style_flags = ConvertFlags( p_buf[6] )) )
402 p_spu_sys->p_default_style->i_features |= STYLE_HAS_FLAGS;
404 p_spu_sys->p_default_style->i_font_size = p_buf[7];
405 p_spu_sys->p_default_style->i_font_color = GetDWBE(p_buf+8) >> 8;// RGBA -> ARGB
406 p_spu_sys->p_default_style->i_font_alpha = (GetDWBE(p_buf+8) & 0xFF) << 24;
407 p_spu_sys->p_default_style->i_features |= (STYLE_HAS_FONT_COLOR | STYLE_HAS_FONT_ALPHA);
410 p_buf += 12;
412 } break;
414 case VLC_FOURCC('d','r','p','o'):
415 if ( (size_t)(p_buf - p_block->p_buffer) < 4 ) break;
416 p_spu_sys->p_default_style->i_shadow_width = __MAX( GetWBE(p_buf), GetWBE(p_buf+2) );
417 break;
419 case VLC_FOURCC('d','r','p','t'):
420 if ( (size_t)(p_buf - p_block->p_buffer) < 2 ) break;
421 p_spu_sys->p_default_style->i_shadow_alpha = GetWBE(p_buf);
422 p_spu_sys->p_default_style->i_features |= STYLE_HAS_SHADOW_ALPHA;
423 break;
425 default:
426 break;
429 p_buf += i_atomsize;
432 p_spu->i_start = p_block->i_pts;
433 p_spu->i_stop = p_block->i_pts + p_block->i_length;
434 p_spu->b_ephemer = (p_block->i_length == 0);
435 p_spu->b_absolute = false;
437 p_spu_sys->region.inner_align = SUBPICTURE_ALIGN_BOTTOM;
439 FontSizeConvert( p_dec->fmt_in.subs.p_style, p_spu_sys->p_default_style );
441 /* Unwrap */
442 text_segment_t *p_text_segments = p_segment3g->s;
443 text_segment_t *p_cur = p_text_segments;
444 while( p_segment3g )
446 FontSizeConvert( p_dec->fmt_in.subs.p_style, p_segment3g->s->style );
448 tx3g_segment_t * p_old = p_segment3g;
449 p_segment3g = p_segment3g->p_next3g;
450 free( p_old );
451 if( p_segment3g )
452 p_cur->p_next = p_segment3g->s;
453 p_cur = p_cur->p_next;
456 p_spu_sys->region.p_segments = p_text_segments;
458 block_Release( p_block );
460 decoder_QueueSub( p_dec, p_spu );
461 return VLCDEC_SUCCESS;