Coverage Report

Created: 2026-09-28 07:27

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