Coverage Report

Created: 2026-05-30 08:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/codec/svcdsub.c
Line
Count
Source
1
/*****************************************************************************
2
 * svcdsub.c : Overlay Graphics Text (SVCD subtitles) decoder
3
 *****************************************************************************
4
 * Copyright (C) 2003, 2004 VLC authors and VideoLAN
5
 *
6
 * Authors: Rocky Bernstein
7
 *          Gildas Bazin <gbazin@videolan.org>
8
 *          Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
9
 *          Laurent Aimar <fenrir@via.ecp.fr>
10
 *
11
 * This program is free software; you can redistribute it and/or modify it
12
 * under the terms of the GNU Lesser General Public License as published by
13
 * the Free Software Foundation; either version 2.1 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Lesser General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Lesser General Public License
22
 * along with this program; if not, write to the Free Software Foundation,
23
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24
 *****************************************************************************/
25
26
/*****************************************************************************
27
 * Preamble
28
 *****************************************************************************/
29
#ifdef HAVE_CONFIG_H
30
# include "config.h"
31
#endif
32
33
#include <vlc_common.h>
34
#include <vlc_plugin.h>
35
#include <vlc_codec.h>
36
#include <vlc_bits.h>
37
38
#include "../demux/mpeg/timestamps.h"
39
40
/*****************************************************************************
41
 * Module descriptor.
42
 *****************************************************************************/
43
static int  DecoderOpen   ( vlc_object_t * );
44
static int  PacketizerOpen( vlc_object_t * );
45
static void DecoderClose  ( vlc_object_t * );
46
47
150
vlc_module_begin ()
48
75
    set_description( N_("Philips OGT (SVCD subtitle) decoder") )
49
75
    set_shortname( N_("SVCD subtitles") )
50
75
    set_subcategory( SUBCAT_INPUT_SCODEC )
51
75
    set_capability( "spu decoder", 50 )
52
150
    set_callbacks( DecoderOpen, DecoderClose )
53
54
75
    add_submodule ()
55
75
    set_description( N_("Philips OGT (SVCD subtitle) packetizer") )
56
75
    set_capability( "spu packetizer", 50 )
57
150
    set_callbacks( PacketizerOpen, DecoderClose )
58
75
vlc_module_end ()
59
60
/*****************************************************************************
61
 * Local prototypes
62
 *****************************************************************************/
63
static int      Decode( decoder_t *, block_t * );
64
static block_t *Packetize  ( decoder_t *, block_t ** );
65
static block_t *Reassemble ( decoder_t *, block_t * );
66
static void ParseHeader( decoder_t *, block_t * );
67
static subpicture_t *DecodePacket( decoder_t *, block_t * );
68
static void SVCDSubRenderImage( decoder_t *, block_t *, picture_t * );
69
70
4.93k
#define GETINT16(p) GetWBE(p)  ; p +=2;
71
72
typedef enum  {
73
  SUBTITLE_BLOCK_EMPTY    = 0,
74
  SUBTITLE_BLOCK_PARTIAL  = 1,
75
  SUBTITLE_BLOCK_COMPLETE = 2
76
} packet_state_t;
77
78
typedef struct
79
{
80
  packet_state_t i_state; /* data-gathering state for this subtitle */
81
82
  block_t  *p_spu;        /* Bytes of the packet. */
83
84
  uint16_t i_image;       /* image number in the subtitle stream */
85
  uint8_t  i_packet;      /* packet number for above image number */
86
87
  size_t   i_spu_size;     /* goal for subtitle_data_pos while gathering,
88
                             size of used subtitle_data later */
89
90
  uint16_t i_image_offset;      /* offset from subtitle_data to compressed
91
                                   image data */
92
  size_t second_field_offset;      /* offset of odd raster lines */
93
94
  vlc_tick_t i_duration;   /* how long to display the image, 0 stands
95
                           for "until next subtitle" */
96
97
  uint16_t i_x_start, i_y_start; /* position of top leftmost pixel of
98
                                    image when displayed */
99
  uint16_t i_width, i_height;    /* dimensions in pixels of image */
100
101
  uint8_t p_palette[4][4];       /* Palette of colors used in subtitle */
102
} decoder_sys_t;
103
104
static int OpenCommon( vlc_object_t *p_this, bool b_packetizer )
105
30.0k
{
106
30.0k
    decoder_t     *p_dec = (decoder_t*)p_this;
107
30.0k
    decoder_sys_t *p_sys;
108
109
30.0k
    if( p_dec->fmt_in->i_codec != VLC_CODEC_OGT )
110
29.8k
        return VLC_EGENERIC;
111
112
198
    p_dec->p_sys = p_sys = calloc( 1, sizeof( decoder_sys_t ) );
113
198
    if( p_sys == NULL )
114
0
        return VLC_ENOMEM;
115
116
117
198
    p_sys->i_image = -1;
118
119
198
    p_sys->i_state = SUBTITLE_BLOCK_EMPTY;
120
198
    p_sys->p_spu   = NULL;
121
122
198
    p_dec->fmt_out.i_codec = VLC_CODEC_OGT;
123
124
198
    if( b_packetizer )
125
99
        p_dec->pf_packetize = Packetize;
126
99
    else
127
99
        p_dec->pf_decode    = Decode;
128
129
198
    return VLC_SUCCESS;
130
198
}
131
132
/*****************************************************************************
133
 * DecoderOpen: open/initialize the svcdsub decoder.
134
 *****************************************************************************/
135
static int DecoderOpen( vlc_object_t *p_this )
136
8.22k
{
137
8.22k
    return OpenCommon( p_this, false );
138
8.22k
}
139
140
/*****************************************************************************
141
 * PacketizerOpen: open/initialize the svcdsub packetizer.
142
 *****************************************************************************/
143
static int PacketizerOpen( vlc_object_t *p_this )
144
21.8k
{
145
21.8k
    return OpenCommon( p_this, true );
146
21.8k
}
147
148
/*****************************************************************************
149
 * DecoderClose: closes the svcdsub decoder/packetizer.
150
 *****************************************************************************/
151
void DecoderClose( vlc_object_t *p_this )
152
198
{
153
198
    decoder_t     *p_dec = (decoder_t*)p_this;
154
198
    decoder_sys_t *p_sys = p_dec->p_sys;
155
156
198
    if( p_sys->p_spu ) block_ChainRelease( p_sys->p_spu );
157
198
    free( p_sys );
158
198
}
159
160
/*****************************************************************************
161
 * Decode:
162
 *****************************************************************************/
163
static int Decode( decoder_t *p_dec, block_t *p_block )
164
3.53k
{
165
3.53k
#ifndef NDEBUG
166
3.53k
    msg_Dbg( p_dec, "Decode" );
167
3.53k
#endif
168
169
3.53k
    if( p_block == NULL ) /* No Drain */
170
2.48k
        return VLCDEC_SUCCESS;
171
172
1.04k
    if( !(p_block = Reassemble( p_dec, p_block )) )
173
709
        return VLCDEC_SUCCESS;
174
175
    /* Parse and decode */
176
338
    subpicture_t *p_spu = DecodePacket( p_dec, p_block );
177
338
    block_Release( p_block );
178
338
    if( p_spu != NULL )
179
224
        decoder_QueueSub( p_dec, p_spu );
180
338
    return VLCDEC_SUCCESS;
181
1.04k
}
182
183
/*****************************************************************************
184
 * Packetize:
185
 *****************************************************************************/
186
static block_t *Packetize( decoder_t *p_dec, block_t **pp_block )
187
3.53k
{
188
3.53k
    block_t *p_block, *p_spu;
189
190
3.53k
    if( pp_block == NULL || *pp_block == NULL ) return NULL;
191
192
2.38k
    p_block = *pp_block;
193
2.38k
    *pp_block = NULL;
194
195
2.38k
    if( !(p_spu = Reassemble( p_dec, p_block )) ) return NULL;
196
197
1.04k
    p_spu->i_dts = p_spu->i_pts;
198
1.04k
    p_spu->i_length = VLC_TICK_INVALID;
199
200
1.04k
    return p_spu;
201
2.38k
}
202
203
/*****************************************************************************
204
 Reassemble:
205
206
 The data for single screen subtitle may come in one of many
207
 non-contiguous packets of a stream. This routine is called when the
208
 next packet in the stream comes in. The job of this routine is to
209
 parse the header, if this is the beginning, and combine the packets
210
 into one complete subtitle unit.
211
212
 If everything is complete, we will return a block. Otherwise return
213
 NULL.
214
215
216
 The format of the beginning of the subtitle packet that is used here.
217
218
   size    description
219
   -------------------------------------------
220
   byte    subtitle channel (0..7) in bits 0-3
221
   byte    subtitle packet number of this subtitle image 0-N,
222
           if the subtitle packet is complete, the top bit of the byte is 1.
223
   uint16  subtitle image number
224
225
 *****************************************************************************/
226
9.64k
#define SPU_HEADER_LEN 5
227
228
static block_t *Reassemble( decoder_t *p_dec, block_t *p_block )
229
3.43k
{
230
3.43k
    decoder_sys_t *p_sys = p_dec->p_sys;
231
3.43k
    uint8_t *p_buffer;
232
3.43k
    uint16_t i_expected_image;
233
3.43k
    uint8_t  i_packet, i_expected_packet;
234
235
3.43k
    if( p_block->i_flags & (BLOCK_FLAG_CORRUPTED) )
236
0
    {
237
0
        block_Release( p_block );
238
0
        return NULL;
239
0
    }
240
241
3.43k
    if( p_block->i_buffer < SPU_HEADER_LEN )
242
328
    {
243
328
        msg_Dbg( p_dec, "invalid packet header (size %zu < %u)" ,
244
328
                 p_block->i_buffer, SPU_HEADER_LEN );
245
328
        block_Release( p_block );
246
328
        return NULL;
247
328
    }
248
249
3.10k
    p_buffer = p_block->p_buffer;
250
251
3.10k
    if( p_sys->i_state == SUBTITLE_BLOCK_EMPTY )
252
1.44k
    {
253
1.44k
        i_expected_image  = p_sys->i_image + 1;
254
1.44k
        i_expected_packet = 0;
255
1.44k
    }
256
1.66k
    else
257
1.66k
    {
258
1.66k
        i_expected_image  = p_sys->i_image;
259
1.66k
        i_expected_packet = p_sys->i_packet + 1;
260
1.66k
    }
261
262
    /* The dummy ES that the menu selection uses has an 0x70 at
263
       the head which we need to strip off. */
264
3.10k
    p_buffer += 2;
265
266
3.10k
    if( *p_buffer & 0x80 )
267
1.38k
    {
268
1.38k
        p_sys->i_state = SUBTITLE_BLOCK_COMPLETE;
269
1.38k
        i_packet       = *p_buffer++ & 0x7F;
270
1.38k
    }
271
1.72k
    else
272
1.72k
    {
273
1.72k
        p_sys->i_state = SUBTITLE_BLOCK_PARTIAL;
274
1.72k
        i_packet       = *p_buffer++;
275
1.72k
    }
276
277
3.10k
    p_sys->i_image = GETINT16(p_buffer);
278
279
3.10k
    if( p_sys->i_image != i_expected_image )
280
2.81k
    {
281
2.81k
        msg_Warn( p_dec, "expected subtitle image %u but found %u",
282
2.81k
                  i_expected_image, p_sys->i_image );
283
2.81k
    }
284
285
3.10k
    if( i_packet != i_expected_packet )
286
2.93k
    {
287
2.93k
        msg_Warn( p_dec, "expected subtitle image packet %u but found %u",
288
2.93k
                  i_expected_packet, i_packet );
289
2.93k
    }
290
291
3.10k
    p_block->p_buffer += SPU_HEADER_LEN;
292
3.10k
    p_block->i_buffer -= SPU_HEADER_LEN;
293
294
3.10k
    p_sys->i_packet = i_packet;
295
    /* First packet in the subtitle block */
296
3.10k
    if( !p_sys->i_packet ) ParseHeader( p_dec, p_block );
297
298
3.10k
    block_ChainAppend( &p_sys->p_spu, p_block );
299
300
3.10k
    if( p_sys->i_state == SUBTITLE_BLOCK_COMPLETE )
301
1.38k
    {
302
1.38k
        block_t *p_spu = block_ChainGather( p_sys->p_spu );
303
304
1.38k
        if( unlikely( !p_spu ) )
305
0
        {
306
0
            block_ChainRelease( p_sys->p_spu );
307
0
            p_sys->i_state = SUBTITLE_BLOCK_EMPTY;
308
0
            p_sys->p_spu = NULL;
309
310
0
            msg_Warn( p_dec, "unable to assemble blocks, discarding" );
311
0
            return NULL;
312
0
        }
313
314
1.38k
        if( p_spu->i_buffer != p_sys->i_spu_size )
315
1.34k
        {
316
1.34k
            msg_Warn( p_dec, "subtitle packets size=%zu should be %zu",
317
1.34k
                      p_spu->i_buffer, p_sys->i_spu_size );
318
1.34k
        }
319
320
1.38k
        msg_Dbg( p_dec, "subtitle packet complete, size=%zu", p_spu->i_buffer );
321
322
1.38k
        p_sys->i_state = SUBTITLE_BLOCK_EMPTY;
323
1.38k
        p_sys->p_spu = NULL;
324
1.38k
        return p_spu;
325
1.38k
    }
326
327
1.72k
    return NULL;
328
3.10k
}
329
330
/******************************************************************************
331
  The format is roughly as follows (everything is big-endian):
332
333
   size     description
334
   -------------------------------------------
335
   byte     subtitle channel (0..7) in bits 0-3
336
   byte     subtitle packet number of this subtitle image 0-N,
337
            if the subtitle packet is complete, the top bit of the byte is 1.
338
   u_int16  subtitle image number
339
   u_int16  length in bytes of the rest
340
   byte     option flags, unknown meaning except bit 3 (0x08) indicates
341
            presence of the duration field
342
   byte     unknown
343
   u_int32  duration in 1/90000ths of a second (optional), start time
344
            is as indicated by the PTS in the PES header
345
   u_int32  xpos
346
   u_int32  ypos
347
   u_int32  width (must be even)
348
   u_int32  height (must be even)
349
   byte[16] palette, 4 palette entries, each contains values for
350
            Y, U, V and transparency, 0 standing for transparent
351
   byte     command,
352
            cmd>>6==1 indicates shift
353
            (cmd>>4)&3 is direction from, (0=top,1=left,2=right,3=bottom)
354
   u_int32  shift duration in 1/90000ths of a second
355
   u_int16  offset of odd-numbered scanlines - subtitle images are
356
            given in interlace order
357
   byte[]   limited RLE image data in interlace order (0,2,4... 1,3,5) with
358
            2-bits per palette number
359
******************************************************************************/
360
static void ParseHeader( decoder_t *p_dec, block_t *p_block )
361
769
{
362
769
    decoder_sys_t *p_sys = p_dec->p_sys;
363
769
    uint8_t *p = p_block->p_buffer;
364
769
    size_t i_buffer = p_block->i_buffer;
365
769
    uint8_t i_options, i_cmd;
366
769
    int i;
367
368
769
    if (i_buffer < 4) return;
369
370
586
    p_sys->i_spu_size = GETINT16(p);
371
586
    i_options  = *p++;
372
    // Skip over unused value
373
586
    p++;
374
375
586
    i_buffer -= 4;
376
377
586
    if( i_options & 0x08 ) {
378
141
      if (i_buffer < 4) return;
379
141
      p_sys->i_duration = FROM_SCALE_NZ(GetDWBE(p));
380
141
      p += 4;
381
141
      i_buffer -= 4;
382
141
    }
383
445
    else p_sys->i_duration = 0; /* Ephemer subtitle */
384
385
586
    if (i_buffer < 25) return;
386
387
249
    p_sys->i_x_start = GETINT16(p);
388
249
    p_sys->i_y_start = GETINT16(p);
389
249
    p_sys->i_width   = GETINT16(p);
390
249
    p_sys->i_height  = GETINT16(p);
391
392
1.24k
    for( i = 0; i < 4; i++ )
393
996
    {
394
996
        p_sys->p_palette[i][0] = *p++; /* Y */
395
996
        p_sys->p_palette[i][2] = *p++; /* Cr / V */
396
996
        p_sys->p_palette[i][1] = *p++; /* Cb / U */
397
996
        p_sys->p_palette[i][3] = *p++; /* T */
398
996
    }
399
400
249
    i_cmd = *p++;
401
402
249
    i_buffer -= 25;
403
404
    /* We do not really know this, FIXME */
405
249
    if( i_cmd ) {
406
133
      if (i_buffer < 4) return;
407
133
      p += 4;
408
133
      i_buffer -= 4;
409
133
    }
410
411
    /* Actually, this is measured against a different origin, so we have to
412
     * adjust it */
413
249
    if (i_buffer < 2) return;
414
248
    p_sys->second_field_offset = GETINT16(p);
415
248
    i_buffer -= 2;
416
248
    p_sys->i_image_offset  = p - p_block->p_buffer;
417
418
248
#ifndef NDEBUG
419
248
    msg_Dbg( p_dec, "x-start: %d, y-start: %d, width: %d, height %d, "
420
248
             "spu size: %zu, duration: %"PRIu64" (p:%"PRIu16")",
421
248
             p_sys->i_x_start, p_sys->i_y_start,
422
248
             p_sys->i_width, p_sys->i_height,
423
248
             p_sys->i_spu_size, p_sys->i_duration,
424
248
             p_sys->i_image_offset);
425
426
1.24k
    for( i = 0; i < 4; i++ )
427
992
    {
428
992
        msg_Dbg( p_dec, "palette[%d]= T: %2x, Y: %2x, u: %2x, v: %2x", i,
429
992
                 p_sys->p_palette[i][3], p_sys->p_palette[i][0],
430
992
                 p_sys->p_palette[i][1], p_sys->p_palette[i][2] );
431
992
    }
432
248
#endif
433
248
}
434
435
/*****************************************************************************
436
 * DecodePacket: parse and decode an subtitle packet
437
 *****************************************************************************
438
 * This function parses and decodes an SPU packet and, if valid, returns a
439
 * subpicture.
440
 *****************************************************************************/
441
static subpicture_t *DecodePacket( decoder_t *p_dec, block_t *p_data )
442
338
{
443
338
    decoder_sys_t *p_sys = p_dec->p_sys;
444
338
    subpicture_t  *p_spu;
445
338
    subpicture_region_t *p_region;
446
338
    video_format_t fmt;
447
338
    video_palette_t palette;
448
338
    int i;
449
450
    /* Allocate the subpicture internal data. */
451
338
    p_spu = decoder_NewSubpicture( p_dec, NULL );
452
338
    if( !p_spu ) return NULL;
453
454
338
    p_spu->i_start = p_data->i_pts;
455
338
    p_spu->i_stop  = p_data->i_pts + p_sys->i_duration;
456
338
    p_spu->b_ephemer = true;
457
458
    /* Create new subtitle region */
459
338
    video_format_Init( &fmt, VLC_CODEC_YUVP );
460
461
    /**
462
       The video on which the subtitle sits, is scaled, probably
463
       4:3. However subtitle bitmaps assume an 1:1 aspect ratio.
464
465
       FIXME: We should get the video aspect ratio from somewhere.
466
       Two candidates are the video and the other possibility would be
467
       the access module.
468
    */
469
338
    fmt.i_sar_num = p_sys->i_height;
470
338
    fmt.i_sar_den = p_sys->i_width;
471
472
338
    fmt.i_width = fmt.i_visible_width = p_sys->i_width;
473
338
    fmt.i_height = fmt.i_visible_height = p_sys->i_height;
474
338
    fmt.i_x_offset = fmt.i_y_offset = 0;
475
338
    fmt.p_palette = &palette;
476
338
    fmt.p_palette->i_entries = 4;
477
1.69k
    for( i = 0; i < fmt.p_palette->i_entries; i++ )
478
1.35k
        memcpy( fmt.p_palette->palette[i], p_sys->p_palette[i], 4);
479
480
338
    p_region = subpicture_region_New( &fmt );
481
338
    fmt.p_palette = NULL;
482
338
    video_format_Clean( &fmt );
483
338
    if( !p_region )
484
114
    {
485
114
        msg_Err( p_dec, "cannot allocate SVCD subtitle region" );
486
114
        subpicture_Delete( p_spu );
487
114
        return NULL;
488
114
    }
489
490
224
    vlc_spu_regions_push(&p_spu->regions, p_region);
491
224
    p_region->b_absolute = true; p_region->b_in_window = false;
492
224
    p_region->i_x = p_sys->i_x_start;
493
224
    p_region->i_y = p_sys->i_y_start;
494
495
224
    SVCDSubRenderImage( p_dec, p_data, p_region->p_picture );
496
497
224
    return p_spu;
498
338
}
499
500
/*****************************************************************************
501
 * SVCDSubRenderImage: reorders bytes of image data in subpicture region.
502
 *****************************************************************************
503
504
 The image is encoded using two bits per pixel that select a palette
505
 entry except that value 0 starts a limited run-length encoding for
506
 color 0.  When 0 is seen, the next two bits encode one less than the
507
 number of pixels, so we can encode run lengths from 1 to 4. These get
508
 filled with the color in palette entry 0.
509
510
 The encoding of each line is padded to a whole number of bytes.  The
511
 first field is padded to an even byte length and the complete subtitle
512
 is padded to a 4-byte multiple that always include one zero byte at
513
 the end.
514
515
 However we'll transform this so that that the RLE is expanded and
516
 interlacing will also be removed.
517
 *****************************************************************************/
518
static void SVCDSubRenderImage( decoder_t *p_dec, block_t *p_data,
519
                                picture_t *dst_pic )
520
224
{
521
224
    decoder_sys_t *p_sys = p_dec->p_sys;
522
224
    uint8_t *p_dest = dst_pic->Y_PIXELS;
523
224
    int i_field;            /* The subtitles are interlaced */
524
224
    int i_row, i_column;    /* scanline row/column number */
525
224
    uint8_t i_color, i_count;
526
224
    bs_t bs;
527
528
224
    bs_init( &bs, p_data->p_buffer + p_sys->i_image_offset,
529
224
             p_data->i_buffer - p_sys->i_image_offset );
530
531
672
    for( i_field = 0; i_field < 2; i_field++ )
532
448
    {
533
237k
        for( i_row = i_field; i_row < p_sys->i_height; i_row += 2 )
534
236k
        {
535
283M
            for( i_column = 0; i_column < p_sys->i_width; i_column++ )
536
283M
            {
537
283M
                i_color = bs_read( &bs, 2 );
538
283M
                if( i_color == 0 && (i_count = bs_read( &bs, 2 )) )
539
36.0k
                {
540
36.0k
                    i_count = __MIN( i_count, p_sys->i_width - i_column - 1 );
541
36.0k
                    memset( &p_dest[i_row * dst_pic->Y_PITCH +
542
36.0k
                                    i_column], 0, i_count + 1 );
543
36.0k
                    i_column += i_count;
544
36.0k
                    continue;
545
36.0k
                }
546
547
283M
                p_dest[i_row * dst_pic->Y_PITCH + i_column] = i_color;
548
283M
            }
549
550
236k
            bs_align( &bs );
551
236k
        }
552
553
        /* odd field */
554
448
        bs_init( &bs, p_data->p_buffer + p_sys->i_image_offset +
555
448
                 p_sys->second_field_offset,
556
448
                 p_data->i_buffer - p_sys->i_image_offset -
557
448
                 p_sys->second_field_offset );
558
448
    }
559
224
}