Coverage Report

Created: 2025-07-11 06:16

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