Coverage Report

Created: 2026-08-13 07:43

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
168
vlc_module_begin ()
48
84
    set_description( N_("Philips OGT (SVCD subtitle) decoder") )
49
84
    set_shortname( N_("SVCD subtitles") )
50
84
    set_subcategory( SUBCAT_INPUT_SCODEC )
51
84
    set_capability( "spu decoder", 50 )
52
168
    set_callbacks( DecoderOpen, DecoderClose )
53
54
84
    add_submodule ()
55
84
    set_description( N_("Philips OGT (SVCD subtitle) packetizer") )
56
84
    set_capability( "spu packetizer", 50 )
57
168
    set_callbacks( PacketizerOpen, DecoderClose )
58
84
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
24.7k
#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
31.3k
{
106
31.3k
    decoder_t     *p_dec = (decoder_t*)p_this;
107
31.3k
    decoder_sys_t *p_sys;
108
109
31.3k
    if( p_dec->fmt_in->i_codec != VLC_CODEC_OGT )
110
30.5k
        return VLC_EGENERIC;
111
112
810
    p_dec->p_sys = p_sys = calloc( 1, sizeof( decoder_sys_t ) );
113
810
    if( p_sys == NULL )
114
0
        return VLC_ENOMEM;
115
116
117
810
    p_sys->i_image = -1;
118
119
810
    p_sys->i_state = SUBTITLE_BLOCK_EMPTY;
120
810
    p_sys->p_spu   = NULL;
121
122
810
    p_dec->fmt_out.i_codec = VLC_CODEC_OGT;
123
124
810
    if( b_packetizer )
125
405
        p_dec->pf_packetize = Packetize;
126
405
    else
127
405
        p_dec->pf_decode    = Decode;
128
129
810
    return VLC_SUCCESS;
130
810
}
131
132
/*****************************************************************************
133
 * DecoderOpen: open/initialize the svcdsub decoder.
134
 *****************************************************************************/
135
static int DecoderOpen( vlc_object_t *p_this )
136
10.1k
{
137
10.1k
    return OpenCommon( p_this, false );
138
10.1k
}
139
140
/*****************************************************************************
141
 * PacketizerOpen: open/initialize the svcdsub packetizer.
142
 *****************************************************************************/
143
static int PacketizerOpen( vlc_object_t *p_this )
144
21.1k
{
145
21.1k
    return OpenCommon( p_this, true );
146
21.1k
}
147
148
/*****************************************************************************
149
 * DecoderClose: closes the svcdsub decoder/packetizer.
150
 *****************************************************************************/
151
void DecoderClose( vlc_object_t *p_this )
152
810
{
153
810
    decoder_t     *p_dec = (decoder_t*)p_this;
154
810
    decoder_sys_t *p_sys = p_dec->p_sys;
155
156
810
    if( p_sys->p_spu ) block_ChainRelease( p_sys->p_spu );
157
810
    free( p_sys );
158
810
}
159
160
/*****************************************************************************
161
 * Decode:
162
 *****************************************************************************/
163
static int Decode( decoder_t *p_dec, block_t *p_block )
164
23.6k
{
165
23.6k
#ifndef NDEBUG
166
23.6k
    msg_Dbg( p_dec, "Decode" );
167
23.6k
#endif
168
169
23.6k
    if( p_block == NULL ) /* No Drain */
170
15.0k
        return VLCDEC_SUCCESS;
171
172
8.52k
    if( !(p_block = Reassemble( p_dec, p_block )) )
173
6.69k
        return VLCDEC_SUCCESS;
174
175
    /* Parse and decode */
176
1.83k
    subpicture_t *p_spu = DecodePacket( p_dec, p_block );
177
1.83k
    block_Release( p_block );
178
1.83k
    if( p_spu != NULL )
179
1.49k
        decoder_QueueSub( p_dec, p_spu );
180
1.83k
    return VLCDEC_SUCCESS;
181
8.52k
}
182
183
/*****************************************************************************
184
 * Packetize:
185
 *****************************************************************************/
186
static block_t *Packetize( decoder_t *p_dec, block_t **pp_block )
187
23.6k
{
188
23.6k
    block_t *p_block, *p_spu;
189
190
23.6k
    if( pp_block == NULL || *pp_block == NULL ) return NULL;
191
192
14.6k
    p_block = *pp_block;
193
14.6k
    *pp_block = NULL;
194
195
14.6k
    if( !(p_spu = Reassemble( p_dec, p_block )) ) return NULL;
196
197
8.52k
    p_spu->i_dts = p_spu->i_pts;
198
8.52k
    p_spu->i_length = VLC_TICK_INVALID;
199
200
8.52k
    return p_spu;
201
14.6k
}
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
58.7k
#define SPU_HEADER_LEN 5
227
228
static block_t *Reassemble( decoder_t *p_dec, block_t *p_block )
229
23.2k
{
230
23.2k
    decoder_sys_t *p_sys = p_dec->p_sys;
231
23.2k
    uint8_t *p_buffer;
232
23.2k
    uint16_t i_expected_image;
233
23.2k
    uint8_t  i_packet, i_expected_packet;
234
235
23.2k
    if( p_block->i_flags & (BLOCK_FLAG_CORRUPTED) )
236
0
    {
237
0
        block_Release( p_block );
238
0
        return NULL;
239
0
    }
240
241
23.2k
    if( p_block->i_buffer < SPU_HEADER_LEN )
242
5.46k
    {
243
5.46k
        msg_Dbg( p_dec, "invalid packet header (size %zu < %u)" ,
244
5.46k
                 p_block->i_buffer, SPU_HEADER_LEN );
245
5.46k
        block_Release( p_block );
246
5.46k
        return NULL;
247
5.46k
    }
248
249
17.7k
    p_buffer = p_block->p_buffer;
250
251
17.7k
    if( p_sys->i_state == SUBTITLE_BLOCK_EMPTY )
252
10.5k
    {
253
10.5k
        i_expected_image  = p_sys->i_image + 1;
254
10.5k
        i_expected_packet = 0;
255
10.5k
    }
256
7.17k
    else
257
7.17k
    {
258
7.17k
        i_expected_image  = p_sys->i_image;
259
7.17k
        i_expected_packet = p_sys->i_packet + 1;
260
7.17k
    }
261
262
    /* The dummy ES that the menu selection uses has an 0x70 at
263
       the head which we need to strip off. */
264
17.7k
    p_buffer += 2;
265
266
17.7k
    if( *p_buffer & 0x80 )
267
10.3k
    {
268
10.3k
        p_sys->i_state = SUBTITLE_BLOCK_COMPLETE;
269
10.3k
        i_packet       = *p_buffer++ & 0x7F;
270
10.3k
    }
271
7.38k
    else
272
7.38k
    {
273
7.38k
        p_sys->i_state = SUBTITLE_BLOCK_PARTIAL;
274
7.38k
        i_packet       = *p_buffer++;
275
7.38k
    }
276
277
17.7k
    p_sys->i_image = GETINT16(p_buffer);
278
279
17.7k
    if( p_sys->i_image != i_expected_image )
280
15.8k
    {
281
15.8k
        msg_Warn( p_dec, "expected subtitle image %u but found %u",
282
15.8k
                  i_expected_image, p_sys->i_image );
283
15.8k
    }
284
285
17.7k
    if( i_packet != i_expected_packet )
286
12.7k
    {
287
12.7k
        msg_Warn( p_dec, "expected subtitle image packet %u but found %u",
288
12.7k
                  i_expected_packet, i_packet );
289
12.7k
    }
290
291
17.7k
    p_block->p_buffer += SPU_HEADER_LEN;
292
17.7k
    p_block->i_buffer -= SPU_HEADER_LEN;
293
294
17.7k
    p_sys->i_packet = i_packet;
295
    /* First packet in the subtitle block */
296
17.7k
    if( !p_sys->i_packet ) ParseHeader( p_dec, p_block );
297
298
17.7k
    block_ChainAppend( &p_sys->p_spu, p_block );
299
300
17.7k
    if( p_sys->i_state == SUBTITLE_BLOCK_COMPLETE )
301
10.3k
    {
302
10.3k
        block_t *p_spu = block_ChainGather( p_sys->p_spu );
303
304
10.3k
        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
10.3k
        if( p_spu->i_buffer != p_sys->i_spu_size )
315
4.89k
        {
316
4.89k
            msg_Warn( p_dec, "subtitle packets size=%zu should be %zu",
317
4.89k
                      p_spu->i_buffer, p_sys->i_spu_size );
318
4.89k
        }
319
320
10.3k
        msg_Dbg( p_dec, "subtitle packet complete, size=%zu", p_spu->i_buffer );
321
322
10.3k
        p_sys->i_state = SUBTITLE_BLOCK_EMPTY;
323
10.3k
        p_sys->p_spu = NULL;
324
10.3k
        return p_spu;
325
10.3k
    }
326
327
7.38k
    return NULL;
328
17.7k
}
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
6.55k
{
362
6.55k
    decoder_sys_t *p_sys = p_dec->p_sys;
363
6.55k
    uint8_t *p = p_block->p_buffer;
364
6.55k
    size_t i_buffer = p_block->i_buffer;
365
6.55k
    uint8_t i_options, i_cmd;
366
6.55k
    int i;
367
368
6.55k
    if (i_buffer < 4) return;
369
370
1.31k
    p_sys->i_spu_size = GETINT16(p);
371
1.31k
    i_options  = *p++;
372
    // Skip over unused value
373
1.31k
    p++;
374
375
1.31k
    i_buffer -= 4;
376
377
1.31k
    if( i_options & 0x08 ) {
378
865
      if (i_buffer < 4) return;
379
853
      p_sys->i_duration = FROM_SCALE_NZ(GetDWBE(p));
380
853
      p += 4;
381
853
      i_buffer -= 4;
382
853
    }
383
451
    else p_sys->i_duration = 0; /* Ephemer subtitle */
384
385
1.30k
    if (i_buffer < 25) return;
386
387
1.14k
    p_sys->i_x_start = GETINT16(p);
388
1.14k
    p_sys->i_y_start = GETINT16(p);
389
1.14k
    p_sys->i_width   = GETINT16(p);
390
1.14k
    p_sys->i_height  = GETINT16(p);
391
392
5.72k
    for( i = 0; i < 4; i++ )
393
4.58k
    {
394
4.58k
        p_sys->p_palette[i][0] = *p++; /* Y */
395
4.58k
        p_sys->p_palette[i][2] = *p++; /* Cr / V */
396
4.58k
        p_sys->p_palette[i][1] = *p++; /* Cb / U */
397
4.58k
        p_sys->p_palette[i][3] = *p++; /* T */
398
4.58k
    }
399
400
1.14k
    i_cmd = *p++;
401
402
1.14k
    i_buffer -= 25;
403
404
    /* We do not really know this, FIXME */
405
1.14k
    if( i_cmd ) {
406
763
      if (i_buffer < 4) return;
407
739
      p += 4;
408
739
      i_buffer -= 4;
409
739
    }
410
411
    /* Actually, this is measured against a different origin, so we have to
412
     * adjust it */
413
1.12k
    if (i_buffer < 2) return;
414
1.10k
    p_sys->second_field_offset = GETINT16(p);
415
1.10k
    i_buffer -= 2;
416
1.10k
    p_sys->i_image_offset  = p - p_block->p_buffer;
417
418
1.10k
#ifndef NDEBUG
419
1.10k
    msg_Dbg( p_dec, "x-start: %d, y-start: %d, width: %d, height %d, "
420
1.10k
             "spu size: %zu, duration: %"PRIu64" (p:%"PRIu16")",
421
1.10k
             p_sys->i_x_start, p_sys->i_y_start,
422
1.10k
             p_sys->i_width, p_sys->i_height,
423
1.10k
             p_sys->i_spu_size, p_sys->i_duration,
424
1.10k
             p_sys->i_image_offset);
425
426
5.51k
    for( i = 0; i < 4; i++ )
427
4.41k
    {
428
4.41k
        msg_Dbg( p_dec, "palette[%d]= T: %2x, Y: %2x, u: %2x, v: %2x", i,
429
4.41k
                 p_sys->p_palette[i][3], p_sys->p_palette[i][0],
430
4.41k
                 p_sys->p_palette[i][1], p_sys->p_palette[i][2] );
431
4.41k
    }
432
1.10k
#endif
433
1.10k
}
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
1.83k
{
443
1.83k
    decoder_sys_t *p_sys = p_dec->p_sys;
444
1.83k
    subpicture_t  *p_spu;
445
1.83k
    subpicture_region_t *p_region;
446
1.83k
    video_format_t fmt;
447
1.83k
    video_palette_t palette;
448
1.83k
    int i;
449
450
    /* Allocate the subpicture internal data. */
451
1.83k
    p_spu = decoder_NewSubpicture( p_dec, NULL );
452
1.83k
    if( !p_spu ) return NULL;
453
454
1.83k
    p_spu->i_start = p_data->i_pts;
455
1.83k
    p_spu->i_stop  = p_data->i_pts + p_sys->i_duration;
456
1.83k
    p_spu->b_ephemer = true;
457
458
    /* Create new subtitle region */
459
1.83k
    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
1.83k
    fmt.i_sar_num = p_sys->i_height;
470
1.83k
    fmt.i_sar_den = p_sys->i_width;
471
472
1.83k
    fmt.i_width = fmt.i_visible_width = p_sys->i_width;
473
1.83k
    fmt.i_height = fmt.i_visible_height = p_sys->i_height;
474
1.83k
    fmt.i_x_offset = fmt.i_y_offset = 0;
475
1.83k
    fmt.p_palette = &palette;
476
1.83k
    fmt.p_palette->i_entries = 4;
477
9.17k
    for( i = 0; i < fmt.p_palette->i_entries; i++ )
478
7.33k
        memcpy( fmt.p_palette->palette[i], p_sys->p_palette[i], 4);
479
480
1.83k
    p_region = subpicture_region_New( &fmt );
481
1.83k
    fmt.p_palette = NULL;
482
1.83k
    video_format_Clean( &fmt );
483
1.83k
    if( !p_region )
484
338
    {
485
338
        msg_Err( p_dec, "cannot allocate SVCD subtitle region" );
486
338
        subpicture_Delete( p_spu );
487
338
        return NULL;
488
338
    }
489
490
1.49k
    vlc_spu_regions_push(&p_spu->regions, p_region);
491
1.49k
    p_region->b_absolute = true; p_region->b_in_window = false;
492
1.49k
    p_region->i_x = p_sys->i_x_start;
493
1.49k
    p_region->i_y = p_sys->i_y_start;
494
495
1.49k
    SVCDSubRenderImage( p_dec, p_data, p_region->p_picture );
496
497
1.49k
    return p_spu;
498
1.83k
}
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
1.49k
{
521
1.49k
    decoder_sys_t *p_sys = p_dec->p_sys;
522
1.49k
    uint8_t *p_dest = dst_pic->Y_PIXELS;
523
1.49k
    int i_field;            /* The subtitles are interlaced */
524
1.49k
    int i_row, i_column;    /* scanline row/column number */
525
1.49k
    uint8_t i_color, i_count;
526
1.49k
    bs_t bs;
527
528
1.49k
    bs_init( &bs, p_data->p_buffer + p_sys->i_image_offset,
529
1.49k
             p_data->i_buffer - p_sys->i_image_offset );
530
531
4.48k
    for( i_field = 0; i_field < 2; i_field++ )
532
2.99k
    {
533
181k
        for( i_row = i_field; i_row < p_sys->i_height; i_row += 2 )
534
178k
        {
535
50.2M
            for( i_column = 0; i_column < p_sys->i_width; i_column++ )
536
50.0M
            {
537
50.0M
                i_color = bs_read( &bs, 2 );
538
50.0M
                if( i_color == 0 && (i_count = bs_read( &bs, 2 )) )
539
15.4k
                {
540
15.4k
                    i_count = __MIN( i_count, p_sys->i_width - i_column - 1 );
541
15.4k
                    memset( &p_dest[i_row * dst_pic->Y_PITCH +
542
15.4k
                                    i_column], 0, i_count + 1 );
543
15.4k
                    i_column += i_count;
544
15.4k
                    continue;
545
15.4k
                }
546
547
50.0M
                p_dest[i_row * dst_pic->Y_PITCH + i_column] = i_color;
548
50.0M
            }
549
550
178k
            bs_align( &bs );
551
178k
        }
552
553
        /* odd field */
554
2.99k
        bs_init( &bs, p_data->p_buffer + p_sys->i_image_offset +
555
2.99k
                 p_sys->second_field_offset,
556
2.99k
                 p_data->i_buffer - p_sys->i_image_offset -
557
2.99k
                 p_sys->second_field_offset );
558
2.99k
    }
559
1.49k
}