]> git.sesse.net Git - vlc/blob - modules/codec/substx3g.c
mediacodec: fix warning
[vlc] / modules / codec / substx3g.c
1 /*****************************************************************************
2  * substx3gsub.c : MP4 tx3g subtitles decoder
3  *****************************************************************************
4  * Copyright (C) 2014 VLC authors and VideoLAN
5  *
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.
10  *
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.
15  *
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  *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
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>
30
31 #include "substext.h"
32
33 /*****************************************************************************
34  * Module descriptor.
35  *****************************************************************************/
36 static int  Open ( vlc_object_t * );
37 static subpicture_t *Decode( decoder_t *, block_t ** );
38
39 vlc_module_begin ()
40     set_description( N_("tx3g subtitles decoder") )
41     set_shortname( N_("tx3g subtitles") )
42     set_capability( "decoder", 100 )
43     set_category( CAT_INPUT )
44     set_subcategory( SUBCAT_INPUT_SCODEC )
45     set_callbacks( Open, NULL )
46 vlc_module_end ()
47
48 /****************************************************************************
49  * Local structs
50  ****************************************************************************/
51
52 /*****************************************************************************
53  * Open: probe the decoder and return score
54  *****************************************************************************/
55 static int Open( vlc_object_t *p_this )
56 {
57     decoder_t     *p_dec = (decoder_t *) p_this;
58
59     if( p_dec->fmt_in.i_codec != VLC_CODEC_TX3G )
60         return VLC_EGENERIC;
61
62     p_dec->pf_decode_sub = Decode;
63
64     p_dec->fmt_out.i_cat = SPU_ES;
65     p_dec->fmt_out.i_codec = 0;
66
67     return VLC_SUCCESS;
68 }
69
70 /*****************************************************************************
71  * Local:
72  *****************************************************************************/
73
74 #define FONT_FACE_BOLD      0x1
75 #define FONT_FACE_ITALIC    0x2
76 #define FONT_FACE_UNDERLINE 0x4
77
78 static int ConvertFlags( int i_atomflags )
79 {
80     int i_vlcstyles_flags = 0;
81     if ( i_atomflags & FONT_FACE_BOLD )
82         i_vlcstyles_flags |= STYLE_BOLD;
83     if ( i_atomflags & FONT_FACE_ITALIC )
84         i_vlcstyles_flags |= STYLE_ITALIC;
85     if ( i_atomflags & FONT_FACE_UNDERLINE )
86         i_vlcstyles_flags |= STYLE_UNDERLINE;
87     return i_vlcstyles_flags;
88 }
89
90 static size_t str8len( const char *psz_string )
91 {
92     const char *psz_tmp = psz_string;
93     size_t i=0;
94     while ( *psz_tmp )
95     {
96         if ( (*psz_tmp & 0xC0) != 0x80 ) i++;
97         psz_tmp++;
98     }
99     return i;
100 }
101
102 static char * str8indup( const char *psz_string, size_t i_skip, size_t n )
103 {
104     while( i_skip && *psz_string )
105     {
106         if ( (*psz_string & 0xC0) != 0x80 ) i_skip--;
107         psz_string++;
108     }
109     if ( ! *psz_string || i_skip ) return NULL;
110
111     const char *psz_tmp = psz_string;
112     while( n && *psz_tmp )
113     {
114         if ( (*psz_tmp & 0xC0) != 0x80 ) n--;
115         psz_tmp++;
116     }
117     return strndup( psz_string, psz_tmp - psz_string );
118 }
119
120 static void SegmentDoSplit( segment_t *p_segment, uint16_t i_start, uint16_t i_end,
121                             segment_t **pp_segment_left,
122                             segment_t **pp_segment_middle,
123                             segment_t **pp_segment_right )
124 {
125     segment_t *p_segment_left = *pp_segment_left;
126     segment_t *p_segment_right = *pp_segment_right;
127     segment_t *p_segment_middle = *pp_segment_middle;
128     p_segment_left = p_segment_middle = p_segment_right = NULL;
129
130     if ( (p_segment->i_size - i_start < 1) || (p_segment->i_size - i_end < 1) )
131         return;
132
133     if ( i_start > 0 )
134     {
135         p_segment_left = calloc( 1, sizeof(segment_t) );
136         if ( !p_segment_left ) goto error;
137         memcpy( &p_segment_left->styles, &p_segment->styles, sizeof(segment_style_t) );
138         p_segment_left->psz_string = str8indup( p_segment->psz_string, 0, i_start );
139         p_segment_left->i_size = str8len( p_segment_left->psz_string );
140     }
141
142     p_segment_middle = calloc( 1, sizeof(segment_t) );
143     if ( !p_segment_middle ) goto error;
144     memcpy( &p_segment_middle->styles, &p_segment->styles, sizeof(segment_style_t) );
145     p_segment_middle->psz_string = str8indup( p_segment->psz_string, i_start, i_end - i_start + 1 );
146     p_segment_middle->i_size = str8len( p_segment_middle->psz_string );
147
148     if ( i_end < (p_segment->i_size - 1) )
149     {
150         p_segment_right = calloc( 1, sizeof(segment_t) );
151         if ( !p_segment_right ) goto error;
152         memcpy( &p_segment_right->styles, &p_segment->styles, sizeof(segment_style_t) );
153         p_segment_right->psz_string = str8indup( p_segment->psz_string, i_end + 1, p_segment->i_size - i_end - 1 );
154         p_segment_right->i_size = str8len( p_segment_right->psz_string );
155     }
156
157     if ( p_segment_left ) p_segment_left->p_next = p_segment_middle;
158     if ( p_segment_right ) p_segment_middle->p_next = p_segment_right;
159
160     *pp_segment_left = p_segment_left;
161     *pp_segment_middle = p_segment_middle;
162     *pp_segment_right = p_segment_right;
163
164     return;
165
166 error:
167     SegmentFree( p_segment_left );
168     SegmentFree( p_segment_middle );
169     SegmentFree( p_segment_right );
170 }
171
172 static bool SegmentSplit( segment_t *p_prev, segment_t **pp_segment,
173                           const uint16_t i_start, const uint16_t i_end,
174                           const segment_style_t *p_styles )
175 {
176     segment_t *p_segment_left = NULL, *p_segment_middle = NULL, *p_segment_right = NULL;
177
178     if ( (*pp_segment)->i_size == 0 ) return false;
179     if ( i_start > i_end ) return false;
180     if ( (size_t)(i_end - i_start) > (*pp_segment)->i_size - 1 ) return false;
181     if ( i_end > (*pp_segment)->i_size - 1 ) return false;
182
183     SegmentDoSplit( *pp_segment, i_start, i_end, &p_segment_left, &p_segment_middle, &p_segment_right );
184     if ( !p_segment_middle )
185     {
186         /* Failed */
187         SegmentFree( p_segment_left );
188         SegmentFree( p_segment_right );
189         return false;
190     }
191
192     segment_t *p_next = (*pp_segment)->p_next;
193     SegmentFree( *pp_segment );
194     *pp_segment = ( p_segment_left ) ? p_segment_left : p_segment_middle ;
195     if ( p_prev ) p_prev->p_next = *pp_segment;
196
197     if ( p_segment_right )
198         p_segment_right->p_next = p_next;
199     else
200         p_segment_middle->p_next = p_next;
201
202     p_segment_middle->styles = *p_styles;
203
204     return true;
205 }
206
207 /* Creates a new segment using the given style and split existing ones according
208    to the start & end offsets */
209 static void ApplySegmentStyle( segment_t **pp_segment, const uint16_t i_absstart,
210                                const uint16_t i_absend, const segment_style_t *p_styles )
211 {
212     /* find the matching segment */
213     uint16_t i_curstart = 0;
214     segment_t *p_prev = NULL;
215     segment_t *p_cur = *pp_segment;
216     while ( p_cur )
217     {
218         uint16_t i_curend = i_curstart + p_cur->i_size - 1;
219         if ( (i_absstart >= i_curstart) && (i_absend <= i_curend) )
220         {
221             /* segment found */
222             if ( !SegmentSplit( p_prev, &p_cur, i_absstart - i_curstart,
223                                 i_absend - i_curstart, p_styles ) )
224                 return;
225             if ( !p_prev ) *pp_segment = p_cur;
226             break;
227         }
228         else
229         {
230             i_curstart += p_cur->i_size;
231             p_prev = p_cur;
232             p_cur = p_cur->p_next;
233         }
234     }
235 }
236
237 /*****************************************************************************
238  * Decode:
239  *****************************************************************************/
240 static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block )
241 {
242     block_t       *p_block;
243     subpicture_t  *p_spu = NULL;
244
245     if( ( pp_block == NULL ) || ( *pp_block == NULL ) ) return NULL;
246     p_block = *pp_block;
247     *pp_block = NULL;
248
249     if( ( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) ||
250           p_block->i_buffer < sizeof(uint16_t) )
251     {
252         block_Release( p_block );
253         return NULL;
254     }
255
256     uint8_t *p_buf = p_block->p_buffer;
257
258     /* Read our raw string and create the styled segment for HTML */
259     uint16_t i_psz_bytelength = GetWBE( p_buf );
260     const uint8_t *p_pszstart = p_block->p_buffer + sizeof(uint16_t);
261     char *psz_subtitle;
262     if ( i_psz_bytelength > 2 &&
263          ( !memcmp( p_pszstart, "\xFE\xFF", 2 ) || !memcmp( p_pszstart, "\xFF\xFE", 2 ) )
264        )
265     {
266         psz_subtitle = FromCharset( "UTF-16", p_pszstart, i_psz_bytelength );
267         if ( !psz_subtitle ) return NULL;
268     }
269     else
270     {
271         psz_subtitle = malloc( i_psz_bytelength + 1 );
272         if ( !psz_subtitle ) return NULL;
273         memcpy( psz_subtitle, p_pszstart, i_psz_bytelength );
274         psz_subtitle[ i_psz_bytelength ] = '\0';
275     }
276     p_buf += i_psz_bytelength + sizeof(uint16_t);
277
278     for( uint16_t i=0; i < i_psz_bytelength; i++ )
279      if ( psz_subtitle[i] == '\r' ) psz_subtitle[i] = '\n';
280
281     segment_t *p_segment = calloc( 1, sizeof(segment_t) );
282     if ( !p_segment )
283     {
284         free( psz_subtitle );
285         return NULL;
286     }
287     p_segment->psz_string = strdup( psz_subtitle );
288     p_segment->i_size = str8len( psz_subtitle );
289     if ( p_dec->fmt_in.subs.p_style )
290     {
291         p_segment->styles.i_color = p_dec->fmt_in.subs.p_style->i_font_color;
292         p_segment->styles.i_color |= p_dec->fmt_in.subs.p_style->i_font_alpha << 24;
293         if ( p_dec->fmt_in.subs.p_style->i_style_flags )
294             p_segment->styles.i_flags = p_dec->fmt_in.subs.p_style->i_style_flags;
295         p_segment->styles.i_fontsize = p_dec->fmt_in.subs.p_style->i_font_size;
296     }
297
298     if ( !p_segment->psz_string )
299     {
300         SegmentFree( p_segment );
301         free( psz_subtitle );
302         return NULL;
303     }
304
305     /* Create the subpicture unit */
306     p_spu = decoder_NewSubpictureText( p_dec );
307     if( !p_spu )
308     {
309         free( psz_subtitle );
310         SegmentFree( p_segment );
311         return NULL;
312     }
313     subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
314
315     /* Parse our styles */
316     while( (size_t)(p_buf - p_block->p_buffer) + 8 < p_block->i_buffer )
317     {
318         uint32_t i_atomsize = GetDWBE( p_buf );
319         vlc_fourcc_t i_atomtype = VLC_FOURCC(p_buf[4],p_buf[5],p_buf[6],p_buf[7]);
320         p_buf += 8;
321         switch( i_atomtype )
322         {
323
324         case VLC_FOURCC('s','t','y','l'):
325         {
326             if ( (size_t)(p_buf - p_block->p_buffer) < 14 ) break;
327             uint16_t i_nbrecords = GetWBE(p_buf);
328             uint16_t i_cur_record = 0;
329             p_buf += 2;
330             while( i_cur_record++ < i_nbrecords )
331             {
332                 if ( (size_t)(p_buf - p_block->p_buffer) < 12 ) break;
333                 uint16_t i_start = __MIN( GetWBE(p_buf), i_psz_bytelength - 1 );
334                 uint16_t i_end =  __MIN( GetWBE(p_buf + 2), i_psz_bytelength - 1 );
335
336                 segment_style_t style;
337                 style.i_flags = ConvertFlags( p_buf[6] );
338                 style.i_fontsize = p_buf[7];
339                 style.i_color = GetDWBE(p_buf+8) >> 8;// RGBA -> ARGB
340                 style.i_color |= (GetDWBE(p_buf+8) & 0xFF) << 24;
341                 ApplySegmentStyle( &p_segment, i_start, i_end, &style );
342
343                 if ( i_nbrecords == 1 )
344                 {
345                     if ( p_buf[6] )
346                     {
347                         p_spu_sys->style_flags.i_value = ConvertFlags( p_buf[6] );
348                         p_spu_sys->style_flags.b_set = true;
349                     }
350                     p_spu_sys->i_font_height_abs_to_src = p_buf[7];
351                     p_spu_sys->font_color.i_value = GetDWBE(p_buf+8) >> 8;// RGBA -> ARGB
352                     p_spu_sys->font_color.i_value |= (GetDWBE(p_buf+8) & 0xFF) << 24;
353                     p_spu_sys->font_color.b_set = true;
354                 }
355
356                 p_buf += 12;
357             }
358         }   break;
359
360         case VLC_FOURCC('d','r','p','o'):
361             if ( (size_t)(p_buf - p_block->p_buffer) < 4 ) break;
362             p_spu_sys->i_drop_shadow = __MAX( GetWBE(p_buf), GetWBE(p_buf+2) );
363             break;
364
365         case VLC_FOURCC('d','r','p','t'):
366             if ( (size_t)(p_buf - p_block->p_buffer) < 2 ) break;
367             p_spu_sys->i_drop_shadow_alpha = GetWBE(p_buf);
368             break;
369
370         default:
371             break;
372
373         }
374         p_buf += i_atomsize;
375     }
376
377     p_spu->i_start    = p_block->i_pts;
378     p_spu->i_stop     = p_block->i_pts + p_block->i_length;
379     p_spu->b_ephemer  = (p_block->i_length == 0);
380     p_spu->b_absolute = false;
381
382     p_spu_sys->align = SUBPICTURE_ALIGN_BOTTOM;
383     p_spu_sys->text  = psz_subtitle;
384     p_spu_sys->p_htmlsegments = p_segment;
385
386     block_Release( p_block );
387
388     return p_spu;
389 }