Coverage Report

Created: 2023-03-26 07:08

/src/vlc/modules/codec/substx3g.c
Line
Count
Source (jump to first uncovered line)
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
#include "../demux/mp4/minibox.h"
33
34
/*****************************************************************************
35
 * Module descriptor.
36
 *****************************************************************************/
37
static int OpenDecoder ( vlc_object_t * );
38
static void CloseDecoder ( vlc_object_t * );
39
static int Decode( decoder_t *, block_t * );
40
#ifdef ENABLE_SOUT
41
static int OpenEncoder ( vlc_object_t * );
42
static block_t * Encode( encoder_t *, subpicture_t * );
43
#endif
44
45
0
vlc_module_begin ()
46
0
    set_description( N_("tx3g subtitles decoder") )
47
0
    set_shortname( N_("tx3g subtitles") )
48
0
    set_capability( "spu decoder", 100 )
49
0
    set_subcategory( SUBCAT_INPUT_SCODEC )
50
0
    set_callbacks( OpenDecoder, CloseDecoder )
51
0
#ifdef ENABLE_SOUT
52
0
    add_submodule ()
53
0
        set_description( N_("tx3g subtitles encoder") )
54
0
        set_shortname( N_("tx3g subtitles encoder") )
55
0
        set_capability( "spu encoder", 101 )
56
0
        set_callback( OpenEncoder )
57
0
#endif
58
0
vlc_module_end ()
59
60
/****************************************************************************
61
 * Local structs
62
 ****************************************************************************/
63
64
/*****************************************************************************
65
 * Local:
66
 *****************************************************************************/
67
68
0
#define FONT_FACE_BOLD      0x1
69
0
#define FONT_FACE_ITALIC    0x2
70
0
#define FONT_FACE_UNDERLINE 0x4
71
72
static int ConvertToVLCFlags( int i_atomflags )
73
0
{
74
0
    int i_vlcstyles_flags = 0;
75
0
    if ( i_atomflags & FONT_FACE_BOLD )
76
0
        i_vlcstyles_flags |= STYLE_BOLD;
77
0
    if ( i_atomflags & FONT_FACE_ITALIC )
78
0
        i_vlcstyles_flags |= STYLE_ITALIC;
79
0
    if ( i_atomflags & FONT_FACE_UNDERLINE )
80
0
        i_vlcstyles_flags |= STYLE_UNDERLINE;
81
0
    return i_vlcstyles_flags;
82
0
}
83
84
static size_t str8len( const char *psz_string )
85
0
{
86
0
    const char *psz_tmp = psz_string;
87
0
    size_t i=0;
88
0
    while ( *psz_tmp )
89
0
    {
90
0
        if ( (*psz_tmp & 0xC0) != 0x80 ) i++;
91
0
        psz_tmp++;
92
0
    }
93
0
    return i;
94
0
}
95
96
static char * str8indup( const char *psz_string, size_t i_skip, size_t n )
97
0
{
98
0
    for( size_t i = 0; i< i_skip && *psz_string; i++ )
99
0
    {
100
0
        while( *(++psz_string) && (*psz_string & 0xC0) == 0x80 )
101
0
        {};
102
0
    }
103
104
0
    if ( ! *psz_string )
105
0
        return NULL;
106
107
0
    const char *psz_tmp = psz_string;
108
0
    for( size_t i = 0; i < n && *psz_tmp; i++ )
109
0
    {
110
0
        while( *(++psz_tmp) && (*psz_tmp & 0xC0) == 0x80 )
111
0
        {};
112
0
    }
113
114
0
    return strndup( psz_string, psz_tmp - psz_string );
115
0
}
116
117
typedef struct tx3g_segment_t tx3g_segment_t;
118
119
struct tx3g_segment_t
120
{
121
    text_segment_t *s;
122
    size_t i_size;
123
    tx3g_segment_t *p_next3g;
124
};
125
126
static tx3g_segment_t * tx3g_segment_New( const char *psz_string )
127
0
{
128
0
    tx3g_segment_t *p_seg = malloc( sizeof(tx3g_segment_t) );
129
0
    if( p_seg )
130
0
    {
131
0
        p_seg->i_size = 0;
132
0
        p_seg->p_next3g = NULL;
133
0
        p_seg->s = text_segment_New( psz_string );
134
0
        if( !p_seg->s )
135
0
        {
136
0
            free( p_seg );
137
0
            p_seg = NULL;
138
0
        }
139
0
    }
140
0
    return p_seg;
141
0
}
142
143
static void SegmentDoSplit( tx3g_segment_t *p_segment, uint16_t i_start, uint16_t i_end,
144
                            tx3g_segment_t **pp_segment_left,
145
                            tx3g_segment_t **pp_segment_middle,
146
                            tx3g_segment_t **pp_segment_right )
147
0
{
148
0
    tx3g_segment_t *p_segment_left = NULL, *p_segment_right = NULL, *p_segment_middle = NULL;
149
150
0
    if ( (p_segment->i_size - i_start < 1) || (p_segment->i_size - i_end < 1) )
151
0
        goto error;
152
153
0
    if ( i_start > 0 )
154
0
    {
155
0
        char* psz_text = str8indup( p_segment->s->psz_text, 0, i_start );
156
0
        p_segment_left = tx3g_segment_New( psz_text );
157
0
        free( psz_text );
158
0
        if ( !p_segment_left ) goto error;
159
0
        p_segment_left->s->style = text_style_Duplicate( p_segment->s->style );
160
0
        p_segment_left->i_size = str8len( p_segment_left->s->psz_text );
161
0
    }
162
163
0
    char* psz_midtext = str8indup( p_segment->s->psz_text, i_start, i_end - i_start + 1 );
164
0
    p_segment_middle = tx3g_segment_New( psz_midtext );
165
0
    free( psz_midtext );
166
0
    if ( !p_segment_middle ) goto error;
167
0
    p_segment_middle->s->style = text_style_Duplicate( p_segment->s->style );
168
0
    p_segment_middle->i_size = str8len( p_segment_middle->s->psz_text );
169
170
0
    if ( i_end < (p_segment->i_size - 1) )
171
0
    {
172
0
        char* psz_text = str8indup( p_segment->s->psz_text, i_end + 1, p_segment->i_size - i_end - 1 );
173
0
        p_segment_right = tx3g_segment_New( psz_text );
174
0
        free( psz_text );
175
0
        if ( !p_segment_right ) goto error;
176
0
        p_segment_right->s->style = text_style_Duplicate( p_segment->s->style );
177
0
        p_segment_right->i_size = str8len( p_segment_right->s->psz_text );
178
0
    }
179
180
0
    if ( p_segment_left ) p_segment_left->p_next3g = p_segment_middle;
181
0
    if ( p_segment_right ) p_segment_middle->p_next3g = p_segment_right;
182
183
0
    *pp_segment_left = p_segment_left;
184
0
    *pp_segment_middle = p_segment_middle;
185
0
    *pp_segment_right = p_segment_right;
186
187
0
    return;
188
189
0
error:
190
0
    if( p_segment_middle )
191
0
    {
192
0
        text_segment_Delete( p_segment_middle->s );
193
0
        free( p_segment_middle );
194
0
    }
195
0
    if( p_segment_left )
196
0
    {
197
0
        text_segment_Delete( p_segment_left->s );
198
0
        free( p_segment_left );
199
0
    }
200
0
    *pp_segment_left = *pp_segment_middle = *pp_segment_right = NULL;
201
0
}
202
203
static bool SegmentSplit( tx3g_segment_t *p_prev, tx3g_segment_t **pp_segment,
204
                          const uint16_t i_start, const uint16_t i_end,
205
                          const text_style_t *p_styles )
206
0
{
207
0
    tx3g_segment_t *p_segment_left = NULL, *p_segment_middle = NULL, *p_segment_right = NULL;
208
209
0
    if ( (*pp_segment)->i_size == 0 ) return false;
210
0
    if ( i_start > i_end ) return false;
211
0
    if ( (size_t)(i_end - i_start) > (*pp_segment)->i_size - 1 ) return false;
212
0
    if ( i_end > (*pp_segment)->i_size - 1 ) return false;
213
214
0
    SegmentDoSplit( *pp_segment, i_start, i_end, &p_segment_left, &p_segment_middle, &p_segment_right );
215
0
    if ( !p_segment_middle )
216
0
    {
217
        /* Failed */
218
0
        text_segment_Delete( p_segment_left->s );
219
0
        free( p_segment_left );
220
0
        text_segment_Delete( p_segment_right->s );
221
0
        free( p_segment_right );
222
0
        return false;
223
0
    }
224
225
0
    tx3g_segment_t *p_next3g = (*pp_segment)->p_next3g;
226
0
    text_segment_Delete( (*pp_segment)->s );
227
0
    free( *pp_segment );
228
0
    *pp_segment = ( p_segment_left ) ? p_segment_left : p_segment_middle ;
229
0
    if ( p_prev ) p_prev->p_next3g = *pp_segment;
230
231
0
    if ( p_segment_right )
232
0
        p_segment_right->p_next3g = p_next3g;
233
0
    else
234
0
        p_segment_middle->p_next3g = p_next3g;
235
236
0
    if( p_segment_middle->s->style )
237
0
        text_style_Merge( p_segment_middle->s->style, p_styles, true );
238
0
    else
239
0
        p_segment_middle->s->style = text_style_Duplicate( p_styles );
240
241
0
    return true;
242
0
}
243
244
/* Creates a new segment using the given style and split existing ones according
245
   to the start & end offsets */
246
static void ApplySegmentStyle( tx3g_segment_t **pp_segment, const uint16_t i_absstart,
247
                               const uint16_t i_absend, const text_style_t *p_styles )
248
0
{
249
    /* find the matching segment */
250
0
    uint16_t i_curstart = 0;
251
0
    tx3g_segment_t *p_prev = NULL;
252
0
    tx3g_segment_t *p_cur = *pp_segment;
253
0
    while ( p_cur )
254
0
    {
255
0
        uint16_t i_curend = i_curstart + p_cur->i_size - 1;
256
0
        if ( (i_absstart >= i_curstart) && (i_absend <= i_curend) )
257
0
        {
258
            /* segment found */
259
0
            if ( !SegmentSplit( p_prev, &p_cur, i_absstart - i_curstart,
260
0
                                i_absend - i_curstart, p_styles ) )
261
0
                return;
262
0
            if ( !p_prev ) *pp_segment = p_cur;
263
0
            break;
264
0
        }
265
0
        else
266
0
        {
267
0
            i_curstart += p_cur->i_size;
268
0
            p_prev = p_cur;
269
0
            p_cur = p_cur->p_next3g;
270
0
        }
271
0
    }
272
0
}
273
274
/* Do relative size conversion using default style size (from stsd),
275
   as the line should always be 5%. Apply to each segment specific text size */
276
static void FontSizeConvert( const text_style_t *p_reference, text_style_t *p_style )
277
0
{
278
0
    if( unlikely(!p_style) )
279
0
    {
280
0
        return;
281
0
    }
282
0
    else if( unlikely(!p_reference) || p_reference->i_font_size == 0 )
283
0
    {
284
0
        p_style->i_font_size = 0;
285
0
        p_style->f_font_relsize = 5.0;
286
0
    }
287
0
    else
288
0
    {
289
0
        p_style->f_font_relsize = 5.0 * (float) p_style->i_font_size / p_reference->i_font_size;
290
0
        p_style->i_font_size = 0;
291
0
    }
292
0
}
293
294
/*****************************************************************************
295
 * Decode:
296
 *****************************************************************************/
297
static int Decode( decoder_t *p_dec, block_t *p_block )
298
0
{
299
0
    subpicture_t  *p_spu = NULL;
300
301
0
    if( p_block == NULL ) /* No Drain */
302
0
        return VLCDEC_SUCCESS;
303
304
0
    if( ( p_block->i_flags & (BLOCK_FLAG_CORRUPTED) ) ||
305
0
          p_block->i_buffer < sizeof(uint16_t) )
306
0
    {
307
0
        block_Release( p_block );
308
0
        return VLCDEC_SUCCESS;
309
0
    }
310
311
0
    uint8_t *p_buf = p_block->p_buffer;
312
313
    /* Read our raw string and create the styled segment for HTML */
314
0
    uint16_t i_psz_bytelength = GetWBE( p_buf );
315
0
    if( p_block->i_buffer < i_psz_bytelength + 2U )
316
0
    {
317
0
        block_Release( p_block );
318
0
        return VLCDEC_SUCCESS;
319
0
    }
320
321
0
    const uint8_t *p_pszstart = p_block->p_buffer + sizeof(uint16_t);
322
0
    char *psz_subtitle;
323
0
    if ( i_psz_bytelength > 2 &&
324
0
         ( !memcmp( p_pszstart, "\xFE\xFF", 2 ) || !memcmp( p_pszstart, "\xFF\xFE", 2 ) )
325
0
       )
326
0
    {
327
0
        psz_subtitle = FromCharset( "UTF-16", p_pszstart, i_psz_bytelength );
328
0
        if ( !psz_subtitle )
329
0
            return VLCDEC_SUCCESS;
330
0
    }
331
0
    else
332
0
    {
333
0
        psz_subtitle = strndup( (const char*) p_pszstart, i_psz_bytelength );
334
0
        if ( !psz_subtitle )
335
0
            return VLCDEC_SUCCESS;
336
0
    }
337
0
    p_buf += i_psz_bytelength + sizeof(uint16_t);
338
339
0
    for( uint16_t i=0; i < i_psz_bytelength; i++ )
340
0
     if ( psz_subtitle[i] == '\r' ) psz_subtitle[i] = '\n';
341
342
0
    tx3g_segment_t *p_segment3g = tx3g_segment_New( psz_subtitle );
343
0
    p_segment3g->i_size = str8len( psz_subtitle );
344
0
    free( psz_subtitle );
345
346
0
    if ( !p_segment3g->s->psz_text )
347
0
    {
348
0
        text_segment_Delete( p_segment3g->s );
349
0
        free( p_segment3g );
350
0
        return VLCDEC_SUCCESS;
351
0
    }
352
353
    /* Create the subpicture unit */
354
0
    p_spu = decoder_NewSubpictureText( p_dec );
355
0
    if( !p_spu )
356
0
    {
357
0
        text_segment_Delete( p_segment3g->s );
358
0
        free( p_segment3g );
359
0
        return VLCDEC_SUCCESS;
360
0
    }
361
362
0
    subtext_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
363
0
    const text_style_t *p_root_style = (text_style_t *) p_dec->p_sys;
364
365
0
    mp4_box_iterator_t it;
366
0
    mp4_box_iterator_Init( &it, p_buf,
367
0
                           p_block->i_buffer - (p_buf - p_block->p_buffer) );
368
    /* Parse our styles */
369
0
    if( p_dec->fmt_in->i_codec != VLC_CODEC_QTXT )
370
0
    while( mp4_box_iterator_Next( &it ) )
371
0
    {
372
0
        switch( it.i_type )
373
0
        {
374
375
0
        case VLC_FOURCC('s','t','y','l'):
376
0
        {
377
0
            if( it.i_payload < 14 )
378
0
                break;
379
380
0
            uint16_t i_nbrecords = GetWBE(it.p_payload);
381
0
            uint16_t i_cur_record = 0;
382
383
0
            it.p_payload += 2; it.i_payload -= 2;
384
0
            while( i_cur_record++ < i_nbrecords && it.i_payload >= 12 )
385
0
            {
386
0
                uint16_t i_start = __MIN( GetWBE(it.p_payload), i_psz_bytelength - 1 );
387
0
                uint16_t i_end =  GetWBE(it.p_payload + 2); /* index is past last char */
388
0
                if( i_start < i_end )
389
0
                {
390
0
                    i_end = VLC_CLIP( i_end - 1, i_start, i_psz_bytelength - 1 );
391
392
0
                    text_style_t *p_style = text_style_Create( STYLE_NO_DEFAULTS );
393
0
                    if( p_style )
394
0
                    {
395
0
                        if( (p_style->i_style_flags = ConvertToVLCFlags( it.p_payload[6] )) )
396
0
                            p_style->i_features |= STYLE_HAS_FLAGS;
397
0
                        p_style->i_font_size = it.p_payload[7];
398
0
                        p_style->i_font_color = GetDWBE(&it.p_payload[8]) >> 8;// RGBA -> RGB
399
0
                        p_style->i_font_alpha = GetDWBE(&it.p_payload[8]) & 0xFF;
400
0
                        p_style->i_features |= STYLE_HAS_FONT_COLOR | STYLE_HAS_FONT_ALPHA;
401
0
                        ApplySegmentStyle( &p_segment3g, i_start, i_end, p_style );
402
0
                        text_style_Delete( p_style );
403
0
                    }
404
0
                }
405
406
0
                it.p_payload += 12; it.i_payload -= 12;
407
0
            }
408
0
        }   break;
409
410
411
0
        default:
412
0
            break;
413
414
0
        }
415
0
    }
416
417
0
    p_spu->i_start    = p_block->i_pts;
418
0
    p_spu->i_stop     = p_block->i_pts + p_block->i_length;
419
0
    p_spu->b_ephemer  = (p_block->i_length == VLC_TICK_INVALID);
420
0
    p_spu->b_absolute = false;
421
422
0
    p_spu_sys->region.align = SUBPICTURE_ALIGN_BOTTOM;
423
424
0
    text_style_Merge( p_spu_sys->p_default_style, p_root_style, true );
425
0
    FontSizeConvert( p_root_style, p_spu_sys->p_default_style );
426
427
    /* Unwrap */
428
0
    text_segment_t *p_text_segments = p_segment3g->s;
429
0
    text_segment_t *p_cur = p_text_segments;
430
0
    while( p_segment3g )
431
0
    {
432
0
        FontSizeConvert( p_root_style, p_segment3g->s->style );
433
434
0
        tx3g_segment_t * p_old = p_segment3g;
435
0
        p_segment3g = p_segment3g->p_next3g;
436
0
        free( p_old );
437
0
        if( p_segment3g )
438
0
            p_cur->p_next = p_segment3g->s;
439
0
        p_cur = p_cur->p_next;
440
0
    }
441
442
0
    p_spu_sys->region.p_segments = p_text_segments;
443
444
0
    block_Release( p_block );
445
446
0
    decoder_QueueSub( p_dec, p_spu );
447
0
    return VLCDEC_SUCCESS;
448
0
}
449
450
/*****************************************************************************
451
 * Extradata Parsing
452
 *****************************************************************************/
453
static void ParseExtradataTx3g( decoder_t *p_dec )
454
0
{
455
0
    text_style_t *p_style = (text_style_t *) p_dec->p_sys;
456
0
    const uint8_t *p_extra = p_dec->fmt_in->p_extra;
457
458
0
    if( p_dec->fmt_in->i_extra < 32 )
459
0
        return;
460
461
    /* DF @0 */
462
    /* Just @4 */
463
464
    /* BGColor @6 */
465
0
    p_style->i_background_color = GetDWBE(&p_extra[6]) >> 8;
466
0
    p_style->i_background_alpha = p_extra[9];
467
0
    p_style->i_features |= STYLE_HAS_BACKGROUND_COLOR|STYLE_HAS_BACKGROUND_ALPHA;
468
469
    /* BoxRecord @10 */
470
471
    /* StyleRecord @18 */
472
0
    p_style->i_style_flags = ConvertToVLCFlags( p_extra[24] );
473
0
    if( p_style->i_style_flags )
474
0
        p_style->i_features |= STYLE_HAS_FLAGS;
475
0
    p_style->i_font_size = p_extra[25];
476
0
    p_style->i_font_color = GetDWBE(&p_extra[26]) >> 8;// RGBA -> RGB
477
0
    p_style->i_font_alpha = p_extra[29];
478
0
    p_style->i_features |= STYLE_HAS_FONT_COLOR | STYLE_HAS_FONT_ALPHA;
479
480
    /* FontTableBox @30 */
481
0
}
482
483
static void ParseExtradataTextMedia( decoder_t *p_dec )
484
0
{
485
0
    text_style_t *p_style = (text_style_t *) p_dec->p_sys;
486
0
    const uint8_t *p_extra = p_dec->fmt_in->p_extra;
487
488
0
    if( p_dec->fmt_in->i_extra < 44 )
489
0
        return;
490
491
    /* DF @0 */
492
0
    uint32_t i_flags = GetDWBE(p_extra);
493
0
    if(i_flags & 0x1000) /* drop shadow */
494
0
    {
495
0
        p_style->i_style_flags |= STYLE_SHADOW;
496
0
        p_style->i_features    |= STYLE_HAS_SHADOW_COLOR|STYLE_HAS_FLAGS|STYLE_HAS_SHADOW_ALPHA;
497
0
        p_style->i_shadow_color = 0xC0C0C0;
498
0
        p_style->i_shadow_alpha = STYLE_ALPHA_OPAQUE;
499
0
    }
500
0
    if(i_flags & 0x4000) /* key text*/
501
0
    {
502
        /*Controls background color. If this flag is set to 1, the text media handler does not display the
503
          background color, so that the text overlay background tracks.*/
504
0
        p_style->i_style_flags &= ~STYLE_BACKGROUND;
505
0
    }
506
507
    /* Just @4 */
508
509
    /* BGColor @8, read top of 16 bits */
510
0
    p_style->i_background_color = (p_extra[8]  << 16) |
511
0
                                  (p_extra[10] <<  8) |
512
0
                                   p_extra[12];
513
0
    p_style->i_features |= STYLE_HAS_BACKGROUND_COLOR | STYLE_HAS_BACKGROUND_ALPHA;
514
0
    p_style->i_background_alpha = STYLE_ALPHA_OPAQUE;
515
516
    /* BoxRecord @14 */
517
    /* Reserved 64 @22 */
518
    /* Font # @30 */
519
520
    /* Font Face @32 */
521
0
    p_style->i_style_flags |= ConvertToVLCFlags( GetWBE(&p_extra[32]) );
522
0
    if( p_style->i_style_flags )
523
0
        p_style->i_features |= STYLE_HAS_FLAGS;
524
    /* Reserved 8 @34 */
525
    /* Reserved 16 @35 */
526
    /* FGColor @37 */
527
0
    p_style->i_font_color = (p_extra[37] << 16) |
528
0
                            (p_extra[39] <<  8) |
529
0
                             p_extra[41];
530
0
    p_style->i_features |= STYLE_HAS_FONT_COLOR;
531
532
    /* FontName Pascal (8 + string) @43 */
533
0
}
534
/*****************************************************************************
535
 * Decoder entry/exit points
536
 *****************************************************************************/
537
static void CloseDecoder( vlc_object_t *p_this )
538
0
{
539
0
    decoder_t     *p_dec = (decoder_t *) p_this;
540
0
    text_style_Delete( (text_style_t *) p_dec->p_sys );
541
0
}
542
543
static int OpenDecoder( vlc_object_t *p_this )
544
0
{
545
0
    decoder_t     *p_dec = (decoder_t *) p_this;
546
547
0
    if( p_dec->fmt_in->i_codec != VLC_CODEC_TX3G &&
548
0
        p_dec->fmt_in->i_codec != VLC_CODEC_QTXT )
549
0
        return VLC_EGENERIC;
550
551
0
    p_dec->pf_decode = Decode;
552
553
0
    p_dec->p_sys = text_style_Create( STYLE_NO_DEFAULTS );
554
0
    if( !p_dec->p_sys )
555
0
        return VLC_ENOMEM;
556
557
0
    text_style_t *p_default_style = p_dec->p_sys;
558
0
    p_default_style->i_style_flags |= STYLE_BACKGROUND;
559
0
    p_default_style->i_features |= STYLE_HAS_FLAGS;
560
561
0
    if( p_dec->fmt_in->i_codec == VLC_CODEC_TX3G )
562
0
        ParseExtradataTx3g( p_dec );
563
0
    else
564
0
        ParseExtradataTextMedia( p_dec );
565
566
0
    p_dec->fmt_out.i_codec = VLC_CODEC_TEXT;
567
568
0
    return VLC_SUCCESS;
569
0
}
570
571
/*****************************************************************************
572
 * Encoder entry/exit
573
 *****************************************************************************/
574
#ifdef ENABLE_SOUT
575
static void FillExtradataTx3g( void **pp_extra, int *pi_extra )
576
0
{
577
0
    size_t i_extra = 32 + 37;
578
0
    uint8_t *p_extra = calloc( 1, i_extra );
579
0
    if( p_extra )
580
0
    {
581
0
        p_extra[4] = 0x01;/* 1  center, horizontal */
582
0
        p_extra[5] = 0xFF;/* -1 bottom, vertical */
583
0
        SetDWBE( &p_extra[6],  0x000000FFU ); /* bgcolor */
584
0
        p_extra[25] = STYLE_DEFAULT_FONT_SIZE;
585
0
        SetDWBE( &p_extra[26], 0xFFFFFFFFU ); /* fgcolor */
586
587
        /* FontTableBox */
588
0
        SetDWBE(&p_extra[32], 8 + 2 + 6 + 11 + 10);
589
0
        memcpy(&p_extra[36], "ftab", 4);
590
591
0
        SetWBE(&p_extra[40], 3); /* entry count */
592
        /* Font Record 0 */
593
0
        p_extra[41] = 5;
594
0
        memcpy(&p_extra[42], "Serif", 5);
595
        /* Font Record 1 */
596
0
        p_extra[47] = 10;
597
0
        memcpy(&p_extra[48], "Sans-serif", 10);
598
        /* Font Record 2 */
599
0
        p_extra[58] = 9;
600
0
        memcpy(&p_extra[59], "Monospace", 9);
601
602
0
        *pp_extra = p_extra;
603
0
        *pi_extra = i_extra;
604
0
    }
605
0
}
606
607
static int OpenEncoder( vlc_object_t *p_this )
608
0
{
609
0
    encoder_t *p_enc = (encoder_t *)p_this;
610
611
0
    if( p_enc->fmt_out.i_codec != VLC_CODEC_TX3G )
612
0
        return VLC_EGENERIC;
613
614
0
    p_enc->fmt_in.i_codec = VLC_CODEC_TEXT;
615
616
0
    if( !p_enc->fmt_out.i_extra )
617
0
        FillExtradataTx3g( &p_enc->fmt_out.p_extra, &p_enc->fmt_out.i_extra );
618
619
0
    static const struct vlc_encoder_operations ops =
620
0
    {
621
0
        .encode_sub = Encode,
622
0
    };
623
0
    p_enc->ops = &ops;
624
625
0
    return VLC_SUCCESS;
626
0
}
627
628
static int ConvertFromVLCFlags( const text_style_t *p_style )
629
0
{
630
0
    int i_atomflags = 0;
631
0
    if( p_style->i_features & STYLE_HAS_FLAGS )
632
0
    {
633
0
        if ( p_style->i_style_flags & STYLE_BOLD )
634
0
            i_atomflags |= FONT_FACE_BOLD;
635
0
        if ( p_style->i_style_flags & STYLE_ITALIC )
636
0
            i_atomflags |= FONT_FACE_ITALIC;
637
0
        if ( p_style->i_style_flags & STYLE_UNDERLINE )
638
0
            i_atomflags |= FONT_FACE_UNDERLINE;
639
0
    }
640
0
    return i_atomflags;
641
0
}
642
643
static uint32_t ConvertFromVLCColor( const text_style_t *p_style )
644
0
{
645
0
    uint32_t rgba = 0;
646
0
    if( p_style->i_features & STYLE_HAS_FONT_COLOR )
647
0
        rgba = ((uint32_t)p_style->i_font_color) << 8;
648
0
    else
649
0
        rgba = 0xFFFFFF00U;
650
0
    if( p_style->i_features & STYLE_HAS_FONT_ALPHA )
651
0
        rgba |= p_style->i_font_alpha;
652
0
    else
653
0
        rgba |= 0xFF;
654
0
    return rgba;
655
0
}
656
657
static bool NeedStyling( const text_segment_t *p_segment )
658
0
{
659
0
    const text_style_t *p_style = p_segment->style;
660
0
    if( !p_style )
661
0
        return false;
662
663
0
    if( p_style->i_features & STYLE_HAS_FLAGS )
664
0
    {
665
0
        if( p_style->i_style_flags & (STYLE_BOLD|STYLE_ITALIC|STYLE_UNDERLINE) )
666
0
            return true;
667
0
    }
668
669
0
    if( p_style->i_features & (STYLE_HAS_FONT_COLOR|STYLE_HAS_FONT_ALPHA) )
670
0
        return true;
671
672
0
    return false;
673
0
}
674
675
static block_t *GetStylBlock( const text_segment_t *p_segment, size_t i_styles )
676
0
{
677
0
    size_t i_start = 0;
678
0
    block_t *p_styl = block_Alloc( 10 + 12 * i_styles );
679
0
    if( p_styl )
680
0
    {
681
0
        SetDWBE( p_styl->p_buffer, p_styl->i_buffer );
682
0
        memcpy( &p_styl->p_buffer[4], "styl", 4 );
683
0
        SetWBE( &p_styl->p_buffer[8], i_styles );
684
0
        p_styl->i_buffer = 10;
685
0
        for( ; p_segment; p_segment = p_segment->p_next )
686
0
        {
687
0
            size_t i_len = str8len( p_segment->psz_text );
688
0
            if( NeedStyling( p_segment ) )
689
0
            {
690
0
                uint8_t *p = &p_styl->p_buffer[p_styl->i_buffer];
691
0
                SetWBE( &p[0], i_start );
692
0
                SetWBE( &p[2], i_start + i_len );
693
0
                SetWBE( &p[4], 0 );
694
0
                p[6] = ConvertFromVLCFlags( p_segment->style );
695
0
                p[7] = STYLE_DEFAULT_FONT_SIZE;
696
0
                SetDWBE(&p[8], ConvertFromVLCColor( p_segment->style ) );
697
0
                p_styl->i_buffer += 12;
698
0
            }
699
0
            i_start += i_len;
700
0
        }
701
0
    }
702
0
    return p_styl;
703
0
}
704
705
static block_t * Encode( encoder_t *p_enc, subpicture_t *p_spu )
706
0
{
707
0
    VLC_UNUSED(p_enc);
708
0
    const text_segment_t *p_segments = (p_spu->p_region)
709
0
                                     ? p_spu->p_region->p_text
710
0
                                     : NULL;
711
0
    size_t i_len = 0;
712
0
    size_t i_styles = 0;
713
714
0
    for(const text_segment_t  *p_segment = p_segments;
715
0
                               p_segment; p_segment = p_segment->p_next )
716
0
    {
717
0
        if( p_segment->style )
718
0
            i_styles++;
719
0
        i_len += strlen( p_segment->psz_text );
720
0
    }
721
722
0
    block_t *p_block = block_Alloc( i_len + 2 );
723
0
    if( !p_block )
724
0
        return NULL;
725
726
0
    SetWBE(p_block->p_buffer, i_len);
727
0
    p_block->i_buffer = 2;
728
0
    for(const text_segment_t  *p_segment = p_segments;
729
0
                               p_segment; p_segment = p_segment->p_next )
730
0
    {
731
0
        size_t i_seglen = strlen(p_segment->psz_text);
732
0
        memcpy(&p_block->p_buffer[p_block->i_buffer],
733
0
                p_segment->psz_text, i_seglen);
734
0
        p_block->i_buffer += i_seglen;
735
0
    }
736
0
    p_block->i_dts = p_block->i_pts = p_spu->i_start;
737
0
    p_block->i_length = p_spu->i_stop - p_spu->i_start;
738
739
0
    if( i_styles > 0 )
740
0
        p_block->p_next = GetStylBlock( p_segments, i_styles );
741
742
0
    return block_ChainGather( p_block );
743
0
}
744
#endif