Coverage Report

Created: 2026-09-03 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/codec/spudec/parse.c
Line
Count
Source
1
/*****************************************************************************
2
 * parse.c: SPU parser
3
 *****************************************************************************
4
 * Copyright (C) 2000-2001, 2005, 2006 VLC authors and VideoLAN
5
 *
6
 * Authors: Sam Hocevar <sam@zoy.org>
7
 *          Laurent Aimar <fenrir@via.ecp.fr>
8
 *          Gildas Bazin <gbazin@videolan.org>
9
 *
10
 * This program is free software; you can redistribute it and/or modify it
11
 * under the terms of the GNU Lesser General Public License as published by
12
 * the Free Software Foundation; either version 2.1 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU Lesser General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public License
21
 * along with this program; if not, write to the Free Software Foundation,
22
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23
 *****************************************************************************/
24
25
/*****************************************************************************
26
 * Preamble
27
 *****************************************************************************/
28
#ifdef HAVE_CONFIG_H
29
# include "config.h"
30
#endif
31
32
#include <vlc_common.h>
33
#include <vlc_codec.h>
34
35
#include "spudec.h"
36
37
/*****************************************************************************
38
 * Local prototypes.
39
 *****************************************************************************/
40
typedef struct
41
{
42
    int i_width;
43
    int i_height;
44
    int i_x;
45
    int i_y;
46
    vlc_tick_t i_start;
47
    vlc_tick_t i_stop;
48
    bool b_ephemer;
49
    bool b_subtitle;
50
    bool b_forced;
51
} spu_properties_t;
52
53
typedef struct
54
{
55
    int   pi_offset[2];                              /* byte offsets to data */
56
57
    /* Color information */
58
    bool b_palette;
59
    uint8_t    pi_alpha[4];
60
    uint8_t    pi_yuv[4][3];
61
    /* PXCTLI commands storage */
62
    const uint8_t *p_pxctli;
63
    size_t i_pxclti;
64
65
    /* Auto crop fullscreen subtitles */
66
    bool b_auto_crop;
67
    int i_y_top_offset;
68
    int i_y_bottom_offset;
69
70
} subpicture_data_t;
71
72
static int  ParseControlSeq( decoder_t *, vlc_tick_t i_pts,
73
                             void(*pf_queue)(decoder_t *, subpicture_t *) );
74
static int  ParseRLE       ( decoder_t *, subpicture_data_t *,
75
                             const spu_properties_t *, uint16_t *, size_t * );
76
static int  Render         ( decoder_t *, subpicture_t *, const uint16_t *, size_t,
77
                             const subpicture_data_t *, const spu_properties_t * );
78
79
/*****************************************************************************
80
 * AddNibble: read a nibble from a source packet and add it to our integer.
81
 *****************************************************************************/
82
static inline unsigned int AddNibble( unsigned int i_code,
83
                                      const uint8_t *p_src, unsigned int *pi_index )
84
10.0k
{
85
10.0k
    if( *pi_index & 0x1 )
86
4.55k
    {
87
4.55k
        return( i_code << 4 | ( p_src[(*pi_index)++ >> 1] & 0xf ) );
88
4.55k
    }
89
5.47k
    else
90
5.47k
    {
91
5.47k
        return( i_code << 4 | p_src[(*pi_index)++ >> 1] >> 4 );
92
5.47k
    }
93
10.0k
}
94
95
static void CLUTIdxToYUV(const struct subs_format_t *subs,
96
                         const uint8_t idx[4], uint8_t yuv[4][3])
97
0
{
98
0
    for( int i = 0; i < 4 ; i++ )
99
0
    {
100
0
        uint32_t i_ayvu = subs->spu.palette[idx[i]];
101
        /* FIXME: this job should be done sooner */
102
0
        yuv[3-i][0] = i_ayvu>>16;
103
0
        yuv[3-i][1] = i_ayvu;
104
0
        yuv[3-i][2] = i_ayvu>>8;
105
0
    }
106
0
}
107
108
static void ParsePXCTLI( decoder_t *p_dec, const subpicture_data_t *p_spu_data,
109
                         subpicture_region_t *p_region )
110
0
{
111
0
    if (p_region == NULL)
112
0
        return;
113
114
0
    plane_t *p_plane = &p_region->p_picture->p[0];
115
0
    video_palette_t *p_palette = p_region->p_picture->format.p_palette;
116
117
0
    if( !p_dec->fmt_in->subs.spu.b_palette )
118
0
        return;
119
120
0
    for( size_t i=0;i<p_spu_data->i_pxclti; i++ )
121
0
    {
122
0
        uint16_t i_col = GetWBE(&p_spu_data->p_pxctli[i*6 + 0]);
123
0
        uint16_t i_color = GetWBE(&p_spu_data->p_pxctli[i*6 + 2]);
124
0
        uint16_t i_contrast = GetWBE(&p_spu_data->p_pxctli[i*6 + 4]);
125
126
        /* Lookup the CLUT palette for the YUV values */
127
0
        uint8_t idx[4];
128
0
        uint8_t yuv[4][3];
129
0
        uint8_t alpha[4];
130
0
        idx[0] = (i_color >> 12)&0x0f;
131
0
        idx[1] = (i_color >>  8)&0x0f;
132
0
        idx[2] = (i_color >>  4)&0x0f;
133
0
        idx[3] = i_color&0x0f;
134
0
        CLUTIdxToYUV( &p_dec->fmt_in->subs, idx, yuv );
135
136
        /* Process the contrast */
137
0
        alpha[3] = (i_contrast >> 12)&0x0f;
138
0
        alpha[2] = (i_contrast >>  8)&0x0f;
139
0
        alpha[1] = (i_contrast >>  4)&0x0f;
140
0
        alpha[0] = i_contrast&0x0f;
141
142
        /* Create a new YUVA palette entries for the picture */
143
0
        int index_map[4];
144
0
        for( int j=0; j<4; j++ )
145
0
        {
146
0
            uint8_t yuvaentry[4];
147
0
            yuvaentry[0] = yuv[j][0];
148
0
            yuvaentry[1] = yuv[j][1];
149
0
            yuvaentry[2] = yuv[j][2];
150
0
            yuvaentry[3] = alpha[j] * 0x11;
151
0
            int i_index = VIDEO_PALETTE_COLORS_MAX;
152
0
            for( int k = p_palette->i_entries; k > 0; k-- )
153
0
            {
154
0
                if( !memcmp( p_palette->palette[k], yuvaentry, sizeof(uint8_t [4]) ) )
155
0
                {
156
0
                    i_index = i;
157
0
                    break;
158
0
                }
159
0
            }
160
161
            /* Add an entry in out palette */
162
0
            if( i_index == VIDEO_PALETTE_COLORS_MAX )
163
0
            {
164
0
                if(p_palette->i_entries == VIDEO_PALETTE_COLORS_MAX)
165
0
                {
166
0
                    msg_Warn( p_dec, "Cannot create new color, skipping PXCTLI" );
167
0
                    return;
168
0
                }
169
0
                i_index = p_palette->i_entries++;
170
0
                memcpy( p_palette->palette[ i_index ], yuvaentry, sizeof(uint8_t [4]) );
171
0
            }
172
0
            index_map[j] = i_index;
173
0
        }
174
175
0
        if( p_region->i_x >= i_col )
176
0
            i_col -= p_region->i_x;
177
178
0
        for( int j=0; j<p_plane->i_visible_lines; j++ )
179
0
        {
180
0
            uint8_t *p_line = &p_plane->p_pixels[j * p_plane->i_pitch];
181
            /* Extends to end of the line */
182
0
            for( int k=i_col; k<p_plane->i_visible_pitch; k++ )
183
0
            {
184
0
                if( p_line[k] < 4 ) /* can forge write-again */
185
0
                    p_line[k] = index_map[ p_line[k] ];
186
0
            }
187
0
        }
188
0
    }
189
0
}
190
191
static void UpdateDvdSpu(subpicture_t * p_spu,
192
                         const struct vlc_spu_updater_configuration * cfg)
193
0
{
194
0
    p_spu->i_original_picture_width  = cfg->current.video_src->i_visible_width;
195
0
    p_spu->i_original_picture_height = cfg->current.video_src->i_visible_height;
196
0
}
197
198
/*****************************************************************************
199
 * OutputPicture:
200
 *****************************************************************************
201
 *****************************************************************************/
202
static void OutputPicture( decoder_t *p_dec,
203
                           const subpicture_data_t *p_spu_data,
204
                           const spu_properties_t *p_spu_properties,
205
                           void(*pf_queue)(decoder_t *, subpicture_t *) )
206
1.39k
{
207
1.39k
    decoder_sys_t *p_sys = p_dec->p_sys;
208
1.39k
    subpicture_t *p_spu;
209
1.39k
    uint16_t *p_pixeldata;
210
211
    /* Allocate the subpicture internal data. */
212
1.39k
    static const struct vlc_spu_updater_ops spu_ops =
213
1.39k
    {
214
1.39k
        .update = UpdateDvdSpu,
215
1.39k
    };
216
217
1.39k
    static const subpicture_updater_t updater = {
218
1.39k
        .ops = &spu_ops,
219
1.39k
    };
220
1.39k
    p_spu = decoder_NewSubpicture( p_dec, &updater );
221
1.39k
    if( !p_spu ) return;
222
223
1.39k
    p_spu->i_original_picture_width =
224
1.39k
        p_dec->fmt_in->subs.spu.i_original_frame_width;
225
1.39k
    p_spu->i_original_picture_height =
226
1.39k
        p_dec->fmt_in->subs.spu.i_original_frame_height;
227
1.39k
    p_spu->i_start = p_spu_properties->i_start;
228
1.39k
    p_spu->i_stop = p_spu_properties->i_stop;
229
1.39k
    p_spu->b_ephemer = p_spu_properties->b_ephemer;
230
1.39k
    p_spu->b_subtitle = p_spu_properties->b_subtitle;
231
232
1.39k
    if( (p_spu->i_stop == VLC_TICK_INVALID || p_spu->i_stop <= p_spu->i_start) &&
233
773
        !p_spu->b_ephemer )
234
734
    {
235
        /* This subtitle will live for 5 seconds or until the next subtitle */
236
734
        p_spu->i_stop = p_spu->i_start + VLC_TICK_FROM_MS(500 * 11);
237
734
        p_spu->b_ephemer = true;
238
734
    }
239
240
    /* we are going to expand the RLE stuff so that we won't need to read
241
     * nibbles later on. This will speed things up a lot. Plus, we'll only
242
     * need to do this stupid interlacing stuff once.
243
     *
244
     * Rationale for the "p_spudec->i_rle_size * 4*sizeof(*spu_data.p_data)":
245
     *  one byte gaves two nibbles and may be used twice (once per field)
246
     * generating 4 codes.
247
     */
248
1.39k
    p_pixeldata = vlc_alloc( p_sys->i_rle_size, sizeof(*p_pixeldata) * 2 * 2 );
249
250
1.39k
    size_t pixeldata_size = p_sys->i_rle_size * 2 * 2; // in 16-bit
251
252
    /* We try to display it */
253
1.39k
    subpicture_data_t render_spu_data = *p_spu_data; /* Need a copy */
254
1.39k
    if( ParseRLE( p_dec, &render_spu_data, p_spu_properties, p_pixeldata, &pixeldata_size ) )
255
1.36k
    {
256
        /* There was a parse error, delete the subpicture */
257
1.36k
        subpicture_Delete( p_spu );
258
1.36k
        free( p_pixeldata );
259
1.36k
        return;
260
1.36k
    }
261
262
#ifdef DEBUG_SPUDEC
263
    msg_Dbg( p_dec, "total size: 0x%x, RLE offsets: 0x%x 0x%x",
264
             p_sys->i_spu_size,
265
             render_spu_data.pi_offset[0], render_spu_data.pi_offset[1] );
266
#endif
267
268
36
    if( Render( p_dec, p_spu, p_pixeldata, pixeldata_size, &render_spu_data, p_spu_properties ) )
269
0
    {
270
0
        subpicture_Delete( p_spu );
271
0
        free( p_pixeldata );
272
0
        return;
273
0
    }
274
275
36
    free( p_pixeldata );
276
277
36
    if( p_spu_data->p_pxctli && p_spu )
278
0
        ParsePXCTLI( p_dec, p_spu_data,
279
0
                    vlc_spu_regions_first_or_null(&p_spu->regions) );
280
281
36
    pf_queue( p_dec, p_spu );
282
36
}
283
284
static int Validate( decoder_t *p_dec, unsigned i_index,
285
                     unsigned i_cur_seq, unsigned i_next_seq,
286
                     const subpicture_data_t *p_spu_data,
287
                     const spu_properties_t *p_spu_properties )
288
5.56k
{
289
5.56k
    decoder_sys_t *p_sys = p_dec->p_sys;
290
291
    /* Check that the next sequence index matches the current one */
292
5.56k
    if( i_next_seq < i_cur_seq )
293
892
    {
294
892
        msg_Err( p_dec, "index mismatch (0x%.4x < 0x%.4x)",
295
892
                 i_next_seq, i_cur_seq );
296
892
        return VLC_EGENERIC;
297
892
    }
298
299
4.66k
    if( i_index > p_sys->i_spu_size )
300
0
    {
301
0
        msg_Err( p_dec, "uh-oh, we went too far (0x%.4x > 0x%.4x)",
302
0
                 i_index, p_sys->i_spu_size );
303
0
        return VLC_EGENERIC;
304
0
    }
305
306
4.66k
    const int i_spu_size = p_sys->i_spu - 4;
307
4.66k
    if( p_spu_data->pi_offset[0] < 0 || p_spu_data->pi_offset[0] >= i_spu_size ||
308
1.42k
        p_spu_data->pi_offset[1] < 0 || p_spu_data->pi_offset[1] >= i_spu_size )
309
3.25k
    {
310
3.25k
        msg_Err( p_dec, "invalid offset values" );
311
3.25k
        return VLC_EGENERIC;
312
3.25k
    }
313
314
1.41k
    if( p_spu_properties->i_start == VLC_TICK_INVALID )
315
14
    {
316
14
        msg_Err( p_dec, "no `start display' command" );
317
14
        return VLC_EGENERIC;
318
14
    }
319
320
    /* Get rid of padding bytes */
321
1.39k
    if( i_index > i_next_seq && p_sys->i_spu_size > i_index + 1 )
322
20
    {
323
        /* Zero or one padding byte are quite usual
324
         * More than one padding byte - this is very strange, but
325
         * we can ignore them. */
326
20
        msg_Warn( p_dec, "%i padding bytes, we usually get 0 or 1 of them",
327
20
                  p_sys->i_spu_size - i_index );
328
20
    }
329
330
1.39k
    return VLC_SUCCESS;
331
1.41k
}
332
333
/*****************************************************************************
334
 * ParsePacket: parse an SPU packet and send it to the video output
335
 *****************************************************************************
336
 * This function parses the SPU packet and, if valid, sends it to the
337
 * video output.
338
 *****************************************************************************/
339
void ParsePacket( decoder_t *p_dec, void(*pf_queue)(decoder_t *, subpicture_t *) )
340
14.1k
{
341
14.1k
    decoder_sys_t *p_sys = p_dec->p_sys;
342
343
    /* Getting the control part */
344
14.1k
    if( ParseControlSeq( p_dec, p_sys->i_pts, pf_queue ) )
345
5.48k
        return;
346
14.1k
}
347
348
/*****************************************************************************
349
 * ParseControlSeq: parse all SPU control sequences
350
 *****************************************************************************
351
 * This is the most important part in SPU decoding. We get dates, palette
352
 * information, coordinates, and so on. For more information on the
353
 * subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html
354
 *****************************************************************************/
355
static int ParseControlSeq( decoder_t *p_dec, vlc_tick_t i_pts,
356
                            void(*pf_queue)(decoder_t *, subpicture_t *) )
357
14.1k
{
358
14.1k
    decoder_sys_t *p_sys = p_dec->p_sys;
359
360
    /* Our current index in the SPU packet */
361
14.1k
    unsigned int i_index;
362
363
    /* The next start-of-control-sequence index and the previous one */
364
14.1k
    unsigned int i_next_seq = 0, i_cur_seq = 0;
365
366
    /* Command and date */
367
14.1k
    uint8_t i_command = SPU_CMD_END;
368
14.1k
    vlc_tick_t date = 0;
369
14.1k
    bool b_cmd_offset = false;
370
14.1k
    bool b_cmd_alpha = false;
371
372
    /* Create working space for spu data */
373
14.1k
    subpicture_data_t spu_data_cmd;
374
14.1k
    memset( &spu_data_cmd, 0, sizeof(spu_data_cmd) );
375
14.1k
    spu_data_cmd.pi_offset[0] = -1;
376
14.1k
    spu_data_cmd.pi_offset[1] = -1;
377
14.1k
    spu_data_cmd.b_palette = false;
378
14.1k
    spu_data_cmd.b_auto_crop = false;
379
14.1k
    spu_data_cmd.i_y_top_offset = 0;
380
14.1k
    spu_data_cmd.i_y_bottom_offset = 0;
381
14.1k
    spu_data_cmd.pi_alpha[0] = 0x00;
382
14.1k
    spu_data_cmd.pi_alpha[1] = 0x0f;
383
14.1k
    spu_data_cmd.pi_alpha[2] = 0x0f;
384
14.1k
    spu_data_cmd.pi_alpha[3] = 0x0f;
385
386
14.1k
    subpicture_data_t spu_data = spu_data_cmd;
387
388
    /* Initialize the structure */
389
14.1k
    spu_properties_t spu_properties;
390
14.1k
    memset( &spu_properties, 0, sizeof(spu_properties) );
391
14.1k
    spu_properties.i_start = VLC_TICK_INVALID;
392
14.1k
    spu_properties.i_stop = VLC_TICK_INVALID;
393
14.1k
    spu_properties.b_subtitle = true;
394
14.1k
    spu_properties.b_forced = false;
395
396
136k
    for( i_index = 4 + p_sys->i_rle_size; i_index < p_sys->i_spu_size ; )
397
134k
    {
398
        /* If we just read a command sequence, read the next one;
399
         * otherwise, go on with the commands of the current sequence. */
400
134k
        if( i_command == SPU_CMD_END )
401
14.2k
        {
402
14.2k
            if( i_index + 4 > p_sys->i_spu_size )
403
79
            {
404
79
                msg_Err( p_dec, "overflow in SPU command sequence" );
405
79
                return VLC_EGENERIC;
406
79
            }
407
408
            /* */
409
14.1k
            b_cmd_offset = false;
410
14.1k
            b_cmd_alpha = false;
411
            /* Get the control sequence date */
412
14.1k
            date = VLC_TICK_FROM_MS(GetWBE( &p_sys->buffer[i_index] ) * 11);
413
414
            /* Next offset */
415
14.1k
            i_cur_seq = i_index;
416
14.1k
            i_next_seq = GetWBE( &p_sys->buffer[i_index+2] );
417
418
14.1k
            if( i_next_seq > p_sys->i_spu_size )
419
1.61k
            {
420
1.61k
                msg_Err( p_dec, "overflow in SPU next command sequence" );
421
1.61k
                return VLC_EGENERIC;
422
1.61k
            }
423
424
            /* Skip what we just read */
425
12.5k
            i_index += 4;
426
12.5k
        }
427
428
133k
        i_command = i_index < p_sys->buffer_size ? p_sys->buffer[i_index] : (SPU_CMD_END - 1);
429
430
133k
        switch( i_command )
431
133k
        {
432
78.4k
        case SPU_CMD_FORCE_DISPLAY: /* 00 (force displaying) */
433
78.4k
            spu_properties.i_start = i_pts + date;
434
78.4k
            spu_properties.b_ephemer = true;
435
            /* ignores picture date as display start time
436
             * works around non displayable (offset by few ms)
437
             * spu menu over still frame in SPU_Select */
438
78.4k
            spu_properties.b_subtitle = false;
439
78.4k
            spu_properties.b_forced = true;
440
78.4k
            i_index += 1;
441
78.4k
            break;
442
443
        /* Convert the dates in seconds to PTS values */
444
14.9k
        case SPU_CMD_START_DISPLAY: /* 01 (start displaying) */
445
14.9k
            spu_properties.i_start = i_pts + date;
446
14.9k
            i_index += 1;
447
14.9k
            break;
448
449
684
        case SPU_CMD_STOP_DISPLAY: /* 02 (stop displaying) */
450
684
            spu_properties.i_stop = i_pts + date;
451
684
            i_index += 1;
452
684
            break;
453
454
6.94k
        case SPU_CMD_SET_PALETTE:
455
            /* 03xxxx (palette) */
456
6.94k
            if( i_index + 3 > p_sys->i_spu_size )
457
323
            {
458
323
                msg_Err( p_dec, "overflow in SPU command" );
459
323
                return VLC_EGENERIC;
460
323
            }
461
462
6.61k
            if( p_dec->fmt_in->subs.spu.b_palette )
463
0
            {
464
0
                uint8_t idx[4];
465
466
0
                spu_data_cmd.b_palette = true;
467
468
0
                idx[0] = (p_sys->buffer[i_index+1]>>4)&0x0f;
469
0
                idx[1] = (p_sys->buffer[i_index+1])&0x0f;
470
0
                idx[2] = (p_sys->buffer[i_index+2]>>4)&0x0f;
471
0
                idx[3] = (p_sys->buffer[i_index+2])&0x0f;
472
473
0
                CLUTIdxToYUV( &p_dec->fmt_in->subs, idx, spu_data_cmd.pi_yuv );
474
0
            }
475
476
6.61k
            i_index += 3;
477
6.61k
            break;
478
479
11.3k
        case SPU_CMD_SET_ALPHACHANNEL: /* 04xxxx (alpha channel) */
480
11.3k
            if( i_index + 3 > p_sys->i_spu_size )
481
83
            {
482
83
                msg_Err( p_dec, "overflow in SPU command" );
483
83
                return VLC_EGENERIC;
484
83
            }
485
486
11.2k
            if(!p_sys->b_disabletrans)
487
11.2k
            { /* If we want to use original transparency values */
488
11.2k
                b_cmd_alpha = true;
489
11.2k
                spu_data_cmd.pi_alpha[3] = (p_sys->buffer[i_index+1]>>4)&0x0f;
490
11.2k
                spu_data_cmd.pi_alpha[2] = (p_sys->buffer[i_index+1])&0x0f;
491
11.2k
                spu_data_cmd.pi_alpha[1] = (p_sys->buffer[i_index+2]>>4)&0x0f;
492
11.2k
                spu_data_cmd.pi_alpha[0] = (p_sys->buffer[i_index+2])&0x0f;
493
11.2k
            }
494
495
11.2k
            i_index += 3;
496
11.2k
            break;
497
498
6.63k
        case SPU_CMD_SET_COORDINATES: /* 05xxxyyyxxxyyy (coordinates) */
499
6.63k
            if( i_index + 7 > p_sys->i_spu_size )
500
280
            {
501
280
                msg_Err( p_dec, "overflow in SPU command" );
502
280
                return VLC_EGENERIC;
503
280
            }
504
505
6.35k
            spu_properties.i_x = (p_sys->buffer[i_index+1]<<4)|
506
6.35k
                         ((p_sys->buffer[i_index+2]>>4)&0x0f);
507
6.35k
            spu_properties.i_width = (((p_sys->buffer[i_index+2]&0x0f)<<8)|
508
6.35k
                              p_sys->buffer[i_index+3]) - spu_properties.i_x + 1;
509
510
6.35k
            spu_properties.i_y = (p_sys->buffer[i_index+4]<<4)|
511
6.35k
                         ((p_sys->buffer[i_index+5]>>4)&0x0f);
512
6.35k
            spu_properties.i_height = (((p_sys->buffer[i_index+5]&0x0f)<<8)|
513
6.35k
                              p_sys->buffer[i_index+6]) - spu_properties.i_y + 1;
514
6.35k
            if (spu_properties.i_width < 0 || spu_properties.i_height < 0) {
515
234
                msg_Err( p_dec, "integer overflow in SPU command" );
516
234
                return VLC_EGENERIC;
517
234
            }
518
519
            /* Auto crop fullscreen subtitles */
520
6.11k
            if( spu_properties.i_height > 250 )
521
5.30k
                spu_data.b_auto_crop = true;
522
523
6.11k
            i_index += 7;
524
6.11k
            break;
525
526
1.39k
        case SPU_CMD_SET_OFFSETS: /* 06xxxxyyyy (byte offsets) */
527
1.39k
            if( i_index + 5 > p_sys->i_spu_size )
528
99
            {
529
99
                msg_Err( p_dec, "overflow in SPU command" );
530
99
                return VLC_EGENERIC;
531
99
            }
532
533
1.29k
            b_cmd_offset = true;
534
1.29k
            spu_data.pi_offset[0] = GetWBE(&p_sys->buffer[i_index+1]) - 4;
535
1.29k
            spu_data.pi_offset[1] = GetWBE(&p_sys->buffer[i_index+3]) - 4;
536
1.29k
            i_index += 5;
537
1.29k
            break;
538
539
1.88k
        case SPU_CMD_SET_COLCON:
540
1.88k
            if( i_index + 7 > p_sys->i_spu_size )
541
368
            {
542
368
                msg_Err( p_dec, "overflow in SPU command" );
543
368
                return VLC_EGENERIC;
544
368
            }
545
546
1.51k
            spu_properties.i_start = i_pts + date;
547
548
1.51k
            uint8_t pcxtli_count = p_sys->buffer[i_index+5] >> 4;
549
1.51k
            if( p_sys->i_spu_size > i_index + 3 + 4 + pcxtli_count )
550
1.48k
            {
551
1.48k
                spu_data.p_pxctli = &p_sys->buffer[i_index+3 + 4];
552
1.48k
                spu_data.i_pxclti = __MIN(pcxtli_count, (p_sys->i_spu_size - (i_index + 3 + 4)) / 6 );
553
1.48k
            }
554
555
1.51k
            i_index += 1 + GetWBE(&p_sys->buffer[i_index+1]);
556
1.51k
            break;
557
558
5.56k
        case SPU_CMD_END: /* ff (end) */
559
560
5.56k
            if( b_cmd_offset )
561
824
            {
562
                /* It seems that palette and alpha from the block having
563
                 * the cmd offset have to be used
564
                 * XXX is it all ? */
565
824
                spu_data.b_palette = spu_data_cmd.b_palette;
566
824
                if( spu_data_cmd.b_palette )
567
0
                    memcpy( spu_data.pi_yuv, spu_data_cmd.pi_yuv, sizeof(spu_data_cmd.pi_yuv) );
568
824
                if( b_cmd_alpha )
569
795
                    memcpy( spu_data.pi_alpha, spu_data_cmd.pi_alpha, sizeof(spu_data_cmd.pi_alpha) );
570
824
            }
571
572
5.56k
            i_index += 1;
573
574
5.56k
            if( Validate( p_dec, i_index, i_cur_seq, i_next_seq,
575
5.56k
                          &spu_data, &spu_properties ) == VLC_SUCCESS && (!p_sys->b_forcedonly || spu_properties.b_forced))
576
1.39k
                OutputPicture( p_dec, &spu_data, &spu_properties, pf_queue );
577
578
5.56k
            break;
579
580
5.40k
        default: /* xx (unknown command) */
581
5.40k
            msg_Warn( p_dec, "unknown SPU command 0x%.2x", i_command );
582
5.40k
            if( i_index + 1 < i_next_seq )
583
2.81k
            {
584
                 /* There is at least one other command sequence */
585
2.81k
                 if( p_sys->buffer[i_next_seq - 1] == SPU_CMD_END )
586
414
                 {
587
                     /* This is consistent. Skip to that command sequence. */
588
414
                     i_index = i_next_seq;
589
414
                 }
590
2.40k
                 else
591
2.40k
                 {
592
                     /* There were other commands. */
593
2.40k
                     msg_Warn( p_dec, "cannot recover, dropping subtitle" );
594
2.40k
                     return VLC_EGENERIC;
595
2.40k
                 }
596
2.81k
            }
597
2.58k
            else
598
2.58k
            {
599
                /* We were in the last command sequence. Stop parsing by
600
                 * pretending we met an SPU_CMD_END command. */
601
2.58k
                i_command = SPU_CMD_END;
602
2.58k
                i_index++;
603
2.58k
            }
604
133k
        }
605
606
        /* */
607
129k
        if( i_command == SPU_CMD_END && i_index != i_next_seq )
608
7.39k
            break;
609
129k
    }
610
611
    /* Successfully parsed ! */
612
613
8.61k
    return VLC_SUCCESS;
614
14.1k
}
615
616
/*****************************************************************************
617
 * ParseRLE: parse the RLE part of the subtitle
618
 *****************************************************************************
619
 * This part parses the subtitle graphical data and stores it in a more
620
 * convenient structure for later decoding. For more information on the
621
 * subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html
622
 *****************************************************************************/
623
static int ParseRLE( decoder_t *p_dec,
624
                     subpicture_data_t *p_spu_data,
625
                     const spu_properties_t *p_spu_properties,
626
                     uint16_t *p_pixeldata, size_t * pixeldata_size )
627
1.39k
{
628
1.39k
    decoder_sys_t *p_sys = p_dec->p_sys;
629
630
1.39k
    const unsigned int i_width = p_spu_properties->i_width;
631
1.39k
    const unsigned int i_height = p_spu_properties->i_height;
632
1.39k
    unsigned int i_x, i_y;
633
634
1.39k
    uint16_t *p_dest = p_pixeldata;
635
1.39k
    size_t dest_left = *pixeldata_size;
636
1.39k
#define WRITE_CODE(x)         \
637
4.93k
    if (dest_left == 0)       \
638
4.93k
        break;                \
639
4.93k
    do {                      \
640
4.89k
        *p_dest++ = (x);      \
641
4.89k
        dest_left--;          \
642
4.89k
    } while(0)
643
644
    /* The subtitles are interlaced, we need two offsets */
645
1.39k
    unsigned int  i_id = 0;                   /* Start on the even SPU layer */
646
1.39k
    unsigned int  pi_table[ 2 ];
647
1.39k
    unsigned int *pi_offset;
648
649
    /* Cropping */
650
1.39k
    bool b_empty_top = true;
651
1.39k
    unsigned int i_skipped_top = 0, i_skipped_bottom = 0;
652
1.39k
    unsigned int i_transparent_code = 0;
653
654
    /* Colormap statistics */
655
1.39k
    int i_border = -1;
656
1.39k
    int stats[4]; stats[0] = stats[1] = stats[2] = stats[3] = 0;
657
658
1.39k
    pi_table[ 0 ] = p_spu_data->pi_offset[ 0 ] << 1;
659
1.39k
    pi_table[ 1 ] = p_spu_data->pi_offset[ 1 ] << 1;
660
661
2.46k
    for( i_y = 0 ; i_y < i_height && dest_left != 0 ; i_y++ )
662
2.38k
    {
663
2.38k
        unsigned int i_code;
664
2.38k
        pi_offset = pi_table + i_id;
665
666
7.66k
        for( i_x = 0 ; i_x < i_width && dest_left != 0 ; i_x += i_code >> 2 )
667
5.28k
        {
668
5.28k
            i_code = 0;
669
15.3k
            for( unsigned int i_min = 1; i_min <= 0x40 && i_code < i_min; i_min <<= 2 )
670
10.0k
            {
671
10.0k
                if( (*pi_offset >> 1) + 4 >= p_sys->i_spu_size )
672
11
                {
673
11
                    msg_Err( p_dec, "out of bounds while reading rle" );
674
11
                    return VLC_EGENERIC;
675
11
                }
676
10.0k
                i_code = AddNibble( i_code, &p_sys->buffer[4], pi_offset );
677
10.0k
            }
678
5.27k
            if( i_code < 0x0004 )
679
228
            {
680
                /* If the 14 first bits are set to 0, then it's a
681
                 * new line. We emulate it. */
682
228
                i_code |= ( i_width - i_x ) << 2;
683
228
            }
684
685
5.27k
            if( ( (i_code >> 2) + i_x + i_y * i_width ) > i_height * i_width )
686
0
            {
687
0
                msg_Err( p_dec, "out of bounds, %i at (%i,%i) is out of %ix%i",
688
0
                         i_code >> 2, i_x, i_y, i_width, i_height );
689
0
                return VLC_EGENERIC;
690
0
            }
691
692
            /* Try to find the border color */
693
5.27k
            if( p_spu_data->pi_alpha[ i_code & 0x3 ] != 0x00 )
694
204
            {
695
204
                i_border = i_code & 0x3;
696
204
                stats[i_border] += i_code >> 2;
697
204
            }
698
699
            /* Auto crop subtitles (a lot more optimized) */
700
5.27k
            if( p_spu_data->b_auto_crop )
701
942
            {
702
942
                if( !i_y )
703
95
                {
704
                    /* We assume that if the first line is transparent, then
705
                     * it is using the palette index for the
706
                     * (background) transparent color */
707
95
                    if( (i_code >> 2) == i_width &&
708
77
                        p_spu_data->pi_alpha[ i_code & 0x3 ] == 0x00 )
709
77
                    {
710
77
                        i_transparent_code = i_code;
711
77
                    }
712
18
                    else
713
18
                    {
714
18
                        p_spu_data->b_auto_crop = false;
715
18
                    }
716
95
                }
717
718
942
                if( i_code == i_transparent_code )
719
693
                {
720
693
                    if( b_empty_top )
721
385
                    {
722
                        /* This is a blank top line, we skip it */
723
385
                      i_skipped_top++;
724
385
                    }
725
308
                    else
726
308
                    {
727
                        /* We can't be sure the current lines will be skipped,
728
                         * so we store the code just in case. */
729
308
                      WRITE_CODE( i_code );
730
308
                      i_skipped_bottom++;
731
308
                    }
732
693
                }
733
249
                else
734
249
                {
735
                    /* We got a valid code, store it */
736
249
                    WRITE_CODE( i_code );
737
738
                    /* Valid code means no blank line */
739
249
                    b_empty_top = false;
740
249
                    i_skipped_bottom = 0;
741
249
                }
742
942
            }
743
4.33k
            else
744
4.33k
            {
745
4.33k
                WRITE_CODE( i_code );
746
4.33k
            }
747
5.27k
        }
748
749
        /* Check that we didn't go too far */
750
2.37k
        if( i_x > i_width )
751
1.31k
        {
752
1.31k
            msg_Err( p_dec, "i_x overflowed, %i > %i", i_x, i_width );
753
1.31k
            return VLC_EGENERIC;
754
1.31k
        }
755
756
        /* Byte-align the stream */
757
1.06k
        if( *pi_offset & 0x1 )
758
924
        {
759
924
            (*pi_offset)++;
760
924
        }
761
762
        /* Swap fields */
763
1.06k
        i_id = ~i_id & 0x1;
764
1.06k
    }
765
766
    /* We shouldn't get any padding bytes */
767
75
    if( i_y < i_height )
768
39
    {
769
39
        msg_Err( p_dec, "padding bytes found in RLE sequence" );
770
39
        msg_Err( p_dec, "send mail to <sam@zoy.org> if you "
771
39
                        "want to help debugging this" );
772
773
        /* Skip them just in case */
774
39
        while( i_y < i_height )
775
39
        {
776
39
            WRITE_CODE( i_width << 2 );
777
0
            i_y++;
778
0
        }
779
780
39
        return VLC_EGENERIC;
781
39
    }
782
783
#ifdef DEBUG_SPUDEC
784
    msg_Dbg( p_dec, "valid subtitle, size: %ix%i, position: %i,%i",
785
             p_spu_properties->i_width, p_spu_properties->i_height,
786
             p_spu_properties->i_x, p_spu_properties->i_y );
787
#endif
788
789
    /* Crop if necessary */
790
36
    if( i_skipped_top || i_skipped_bottom )
791
0
    {
792
0
        p_spu_data->i_y_top_offset = i_skipped_top;
793
0
        p_spu_data->i_y_bottom_offset = i_skipped_bottom;
794
#ifdef DEBUG_SPUDEC
795
        msg_Dbg( p_dec, "cropped to: %ix%i, position: %i,%i",
796
                 p_spu_properties->i_width,
797
                 p_spu_properties->i_height - (i_skipped_top + i_skipped_bottom),
798
                 p_spu_properties->i_x, p_spu_properties->i_y + i_skipped_top );
799
#endif
800
0
    }
801
802
    /* Handle color if no palette was found */
803
36
    if( !p_spu_data->b_palette )
804
36
    {
805
36
        int i, i_inner = -1, i_shade = -1;
806
807
        /* Set the border color */
808
36
        if( i_border != -1 )
809
0
        {
810
0
            p_spu_data->pi_yuv[i_border][0] = 0x00;
811
0
            p_spu_data->pi_yuv[i_border][1] = 0x80;
812
0
            p_spu_data->pi_yuv[i_border][2] = 0x80;
813
0
            stats[i_border] = 0;
814
0
        }
815
816
        /* Find the inner colors */
817
180
        for( i = 0 ; i < 4 && i_inner == -1 ; i++ )
818
144
        {
819
144
            if( stats[i] )
820
0
            {
821
0
                i_inner = i;
822
0
            }
823
144
        }
824
825
36
        for(       ; i < 4 && i_shade == -1 ; i++ )
826
0
        {
827
0
            if( stats[i] )
828
0
            {
829
0
                if( stats[i] > stats[i_inner] )
830
0
                {
831
0
                    i_shade = i_inner;
832
0
                    i_inner = i;
833
0
                }
834
0
                else
835
0
                {
836
0
                    i_shade = i;
837
0
                }
838
0
            }
839
0
        }
840
841
        /* Set the inner color */
842
36
        if( i_inner != -1 )
843
0
        {
844
0
            p_spu_data->pi_yuv[i_inner][0] = 0xff;
845
0
            p_spu_data->pi_yuv[i_inner][1] = 0x80;
846
0
            p_spu_data->pi_yuv[i_inner][2] = 0x80;
847
0
        }
848
849
        /* Set the anti-aliasing color */
850
36
        if( i_shade != -1 )
851
0
        {
852
0
            p_spu_data->pi_yuv[i_shade][0] = 0x80;
853
0
            p_spu_data->pi_yuv[i_shade][1] = 0x80;
854
0
            p_spu_data->pi_yuv[i_shade][2] = 0x80;
855
0
        }
856
857
#ifdef DEBUG_SPUDEC
858
        msg_Dbg( p_dec, "using custom palette (border %i, inner %i, shade %i)",
859
                 i_border, i_inner, i_shade );
860
#endif
861
36
    }
862
863
36
    *pixeldata_size = *pixeldata_size - dest_left;
864
865
36
    return VLC_SUCCESS;
866
75
}
867
868
static int Render( decoder_t *p_dec, subpicture_t *p_spu,
869
                    const uint16_t *p_pixeldata, size_t pixeldata_size,
870
                    const subpicture_data_t *p_spu_data,
871
                    const spu_properties_t *p_spu_properties )
872
36
{
873
36
    uint8_t *p_p;
874
36
    int i_x, i_y, i_len, i_color, i_pitch;
875
36
    const uint16_t *p_source = p_pixeldata;
876
36
    size_t source_left = pixeldata_size;
877
36
    video_format_t fmt;
878
36
    video_palette_t palette;
879
880
36
    if (p_spu_properties->i_width <= 0)
881
34
        return VLC_SUCCESS;
882
2
    if (p_spu_properties->i_height <= (p_spu_data->i_y_top_offset + p_spu_data->i_y_bottom_offset))
883
0
        return VLC_SUCCESS;
884
885
2
    const int width = p_spu_properties->i_width;
886
2
    const int height = p_spu_properties->i_height -
887
2
        p_spu_data->i_y_top_offset - p_spu_data->i_y_bottom_offset;
888
889
    /* Create a new subpicture region */
890
2
    video_format_Init( &fmt, VLC_CODEC_YUVP );
891
2
    video_format_Setup( &fmt, VLC_CODEC_YUVP, width, height, width, height,
892
2
                        0, /* 0 means use aspect ratio of background video */
893
2
                        1 );
894
2
    fmt.p_palette = &palette;
895
2
    fmt.p_palette->i_entries = 4;
896
10
    for( i_x = 0; i_x < fmt.p_palette->i_entries; i_x++ )
897
8
    {
898
8
        fmt.p_palette->palette[i_x][0] = p_spu_data->pi_yuv[i_x][0];
899
8
        fmt.p_palette->palette[i_x][1] = p_spu_data->pi_yuv[i_x][1];
900
8
        fmt.p_palette->palette[i_x][2] = p_spu_data->pi_yuv[i_x][2];
901
8
        fmt.p_palette->palette[i_x][3] = p_spu_data->pi_alpha[i_x] * 0x11;
902
8
    }
903
2
    fmt.color_range = COLOR_RANGE_LIMITED;
904
905
2
    subpicture_region_t *p_region = subpicture_region_New( &fmt );
906
2
    fmt.p_palette = NULL;
907
2
    video_format_Clean( &fmt );
908
2
    if( !p_region )
909
0
    {
910
0
        msg_Err( p_dec, "cannot allocate SPU region" );
911
0
        return VLC_EGENERIC;
912
0
    }
913
914
2
    p_region->b_absolute = true; p_region->b_in_window = false;
915
2
    p_region->i_x = p_spu_properties->i_x;
916
2
    p_region->i_y = p_spu_properties->i_y + p_spu_data->i_y_top_offset;
917
2
    p_p = p_region->p_picture->p->p_pixels;
918
2
    i_pitch = p_region->p_picture->p->i_pitch;
919
920
    /* Draw until we reach the bottom of the subtitle */
921
20
    for( i_y = 0; i_y < height * i_pitch; i_y += i_pitch )
922
18
    {
923
        /* Draw until we reach the end of the line */
924
36
        for( i_x = 0 ; i_x < width; i_x += i_len )
925
18
        {
926
18
            if( source_left == 0 )
927
0
            {
928
0
                msg_Err( p_dec, "missing RLE data" );
929
0
                subpicture_region_Delete( p_region );
930
0
                return VLC_EGENERIC;
931
0
            }
932
            /* Get the RLE part, then draw the line */
933
18
            i_color = *p_source & 0x3;
934
18
            i_len = *p_source++ >> 2;
935
18
            source_left--;
936
18
            memset( p_p + i_x + i_y, i_color, i_len );
937
18
        }
938
18
    }
939
2
    vlc_spu_regions_push(&p_spu->regions, p_region);
940
941
2
    return VLC_SUCCESS;
942
2
}