Coverage Report

Created: 2026-03-31 07:49

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