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