Coverage Report

Created: 2026-05-21 08:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/demux/pva.c
Line
Count
Source
1
/*****************************************************************************
2
 * pva.c: PVA demuxer
3
 *****************************************************************************
4
 * Copyright (C) 2004 VLC authors and VideoLAN
5
 *
6
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
7
 *
8
 * This program is free software; you can redistribute it and/or modify it
9
 * under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation; either version 2.1 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program; if not, write to the Free Software Foundation,
20
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21
 *****************************************************************************/
22
23
/*****************************************************************************
24
 * Preamble
25
 *****************************************************************************/
26
27
#ifdef HAVE_CONFIG_H
28
# include "config.h"
29
#endif
30
31
#include <vlc_common.h>
32
#include <vlc_plugin.h>
33
#include <vlc_demux.h>
34
35
#include "mpeg/pes.h"
36
37
/*****************************************************************************
38
 * Module descriptor
39
 *****************************************************************************/
40
static int  Open    ( vlc_object_t * );
41
static void Close  ( vlc_object_t * );
42
43
138
vlc_module_begin ()
44
69
    set_description( N_("PVA demuxer" ) )
45
69
    set_capability( "demux", 10 )
46
69
    set_subcategory( SUBCAT_INPUT_DEMUX )
47
69
    set_callbacks( Open, Close )
48
69
    add_shortcut( "pva" )
49
69
    add_file_extension("pva")
50
69
vlc_module_end ()
51
52
/*****************************************************************************
53
 * Local prototypes
54
 *****************************************************************************/
55
56
typedef struct
57
{
58
    es_out_id_t *p_video;
59
    es_out_id_t *p_audio;
60
61
    /* counter */
62
    int          i_vc;
63
    int          i_ac;
64
65
    /* audio/video block */
66
    block_t     *p_pes; /* audio */
67
    block_t     *p_es;  /* video */
68
69
    int64_t     b_pcr_audio;
70
} demux_sys_t;
71
72
static int  Demux   ( demux_t *p_demux );
73
static int  Control ( demux_t *p_demux, int i_query, va_list args );
74
75
static int  ReSynch ( demux_t * );
76
static void ParsePES( demux_t * );
77
78
/*****************************************************************************
79
 * Open
80
 *****************************************************************************/
81
static int Open( vlc_object_t *p_this )
82
10.1k
{
83
10.1k
    demux_t     *p_demux = (demux_t*)p_this;
84
10.1k
    demux_sys_t *p_sys;
85
10.1k
    es_format_t  fmt;
86
10.1k
    const uint8_t *p_peek;
87
88
10.1k
    if( vlc_stream_Peek( p_demux->s, &p_peek, 8 ) < 8 ) return VLC_EGENERIC;
89
10.1k
    if( p_peek[0] != 'A' || p_peek[1] != 'V' || p_peek[4] != 0x55 )
90
5.77k
    {
91
        /* In case we had forced this demuxer we try to resynch */
92
5.77k
        if( !p_demux->obj.force || ReSynch( p_demux ) )
93
1.09k
            return VLC_EGENERIC;
94
5.77k
    }
95
96
9.06k
    p_sys = malloc( sizeof( demux_sys_t ) );
97
9.06k
    if( unlikely(p_sys == NULL) )
98
0
        return VLC_ENOMEM;
99
100
    /* Fill p_demux field */
101
9.06k
    p_demux->pf_demux = Demux;
102
9.06k
    p_demux->pf_control = Control;
103
9.06k
    p_demux->p_sys = p_sys;
104
105
    /* Register one audio and one video stream */
106
9.06k
    es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_MPGA );
107
9.06k
    fmt.b_packetized = false;
108
9.06k
    p_sys->p_audio = es_out_Add( p_demux->out, &fmt );
109
110
9.06k
    es_format_Init( &fmt, VIDEO_ES, VLC_CODEC_MPGV );
111
9.06k
    fmt.b_packetized = false;
112
9.06k
    p_sys->p_video = es_out_Add( p_demux->out, &fmt );
113
114
9.06k
    p_sys->i_vc    = -1;
115
9.06k
    p_sys->i_ac    = -1;
116
9.06k
    p_sys->p_pes   = NULL;
117
9.06k
    p_sys->p_es    = NULL;
118
119
9.06k
    p_sys->b_pcr_audio = false;
120
121
9.06k
    return VLC_SUCCESS;
122
9.06k
}
123
124
/*****************************************************************************
125
 * Close
126
 *****************************************************************************/
127
static void Close( vlc_object_t *p_this )
128
9.06k
{
129
9.06k
    demux_t     *p_demux = (demux_t*)p_this;
130
9.06k
    demux_sys_t *p_sys = p_demux->p_sys;
131
132
9.06k
    block_ChainRelease( p_sys->p_es );
133
9.06k
    block_ChainRelease( p_sys->p_pes );
134
135
9.06k
    free( p_sys );
136
9.06k
}
137
138
139
/*****************************************************************************
140
 * Demux:
141
 *****************************************************************************
142
 * See http://multimedia.cx/mirror/av_format_v1.pdf
143
 *****************************************************************************/
144
static int Demux( demux_t *p_demux )
145
2.27M
{
146
2.27M
    demux_sys_t *p_sys = p_demux->p_sys;
147
148
2.27M
    const uint8_t *p_peek;
149
2.27M
    int         i_size;
150
2.27M
    block_t     *p_frame;
151
2.27M
    ts_90khz_t  i_pts;
152
2.27M
    int         i_skip;
153
154
2.27M
    if( vlc_stream_Peek( p_demux->s, &p_peek, 8 ) < 8 )
155
7.82k
    {
156
7.82k
        msg_Warn( p_demux, "eof ?" );
157
7.82k
        return VLC_DEMUXER_EOF;
158
7.82k
    }
159
2.26M
    if( p_peek[0] != 'A' || p_peek[1] != 'V' || p_peek[4] != 0x55 )
160
1.61M
    {
161
1.61M
        msg_Warn( p_demux, "lost synchro" );
162
1.61M
        if( ReSynch( p_demux ) )
163
1.08k
        {
164
1.08k
            return VLC_DEMUXER_EGENERIC;
165
1.08k
        }
166
1.61M
        if( vlc_stream_Peek( p_demux->s, &p_peek, 8 ) < 8 )
167
70
        {
168
70
            msg_Warn( p_demux, "eof ?" );
169
70
            return VLC_DEMUXER_EOF;
170
70
        }
171
1.61M
    }
172
173
2.26M
    i_size = GetWBE( &p_peek[6] );
174
2.26M
    switch( p_peek[2] )
175
2.26M
    {
176
385k
        case 0x01:  /* VideoStream */
177
385k
            if( p_sys->i_vc < 0 )
178
4.92k
            {
179
4.92k
                msg_Dbg( p_demux, "first packet for video" );
180
4.92k
            }
181
380k
            else if( ((p_sys->i_vc + 1)&0xff) != p_peek[3] )
182
233k
            {
183
233k
                msg_Dbg( p_demux, "packet lost (video)" );
184
233k
                if( p_sys->p_es )
185
57.0k
                {
186
57.0k
                    block_ChainRelease( p_sys->p_es );
187
57.0k
                    p_sys->p_es = NULL;
188
57.0k
                }
189
233k
            }
190
385k
            p_sys->i_vc = p_peek[3];
191
192
            /* read the PTS and potential extra bytes TODO: make it a bit more optimised */
193
385k
            i_pts = TS_90KHZ_INVALID;
194
385k
            i_skip = 8;
195
385k
            if( p_peek[5]&0x10 )
196
338k
            {
197
338k
                int i_pre = p_peek[5]&0x3;
198
199
338k
                if( ( p_frame = vlc_stream_Block( p_demux->s, 8 + 4 + i_pre ) ) )
200
338k
                {
201
338k
                    i_pts = GetDWBE( &p_frame->p_buffer[8] );
202
338k
                    if( p_frame->i_buffer > 12 )
203
317k
                    {
204
317k
                        p_frame->p_buffer += 12;
205
317k
                        p_frame->i_buffer -= 12;
206
317k
                        block_ChainAppend( &p_sys->p_es, p_frame );
207
317k
                    }
208
21.5k
                    else
209
21.5k
                    {
210
21.5k
                        block_Release( p_frame );
211
21.5k
                    }
212
338k
                }
213
338k
                i_size -= 4 + i_pre;
214
338k
                i_skip  = 0;
215
                /* Set PCR */
216
338k
                if( ( p_frame = p_sys->p_es ) )
217
327k
                {
218
219
327k
                    if( p_frame->i_pts != VLC_TICK_INVALID && !p_sys->b_pcr_audio )
220
59.2k
                    {
221
59.2k
                        es_out_SetPCR( p_demux->out, p_frame->i_pts);
222
59.2k
                    }
223
224
327k
                    p_frame = block_ChainGather( p_frame );
225
327k
                    if( likely(p_frame) )
226
327k
                    {
227
327k
                        if( likely(p_sys->p_video) )
228
327k
                            es_out_Send( p_demux->out, p_sys->p_video, p_frame );
229
0
                        else
230
0
                            block_Release( p_frame );
231
327k
                    }
232
233
327k
                    p_sys->p_es = NULL;
234
327k
                }
235
338k
            }
236
237
385k
            if( ( p_frame = vlc_stream_Block( p_demux->s, i_size + i_skip ) ) )
238
188k
            {
239
188k
                p_frame->p_buffer += i_skip;
240
188k
                p_frame->i_buffer -= i_skip;
241
188k
                if( i_pts != TS_90KHZ_INVALID )
242
142k
                    p_frame->i_pts = FROM_SCALE(i_pts);
243
188k
                block_ChainAppend( &p_sys->p_es, p_frame );
244
188k
            }
245
385k
            break;
246
247
1.87M
        case 0x02:  /* MainAudioStream */
248
1.87M
            if( p_sys->i_ac < 0 )
249
5.59k
            {
250
5.59k
                msg_Dbg( p_demux, "first packet for audio" );
251
5.59k
            }
252
1.86M
            else if( ((p_sys->i_ac + 1)&0xff) != p_peek[3] )
253
925k
            {
254
925k
                msg_Dbg( p_demux, "packet lost (audio)" );
255
925k
                if( p_sys->p_pes )
256
925k
                {
257
925k
                    block_ChainRelease( p_sys->p_pes );
258
925k
                    p_sys->p_pes = NULL;
259
925k
                }
260
925k
            }
261
1.87M
            p_sys->i_ac = p_peek[3];
262
263
1.87M
            if( p_peek[5]&0x10 && p_sys->p_pes )
264
604k
            {
265
604k
                ParsePES( p_demux );
266
604k
            }
267
1.87M
            if( ( p_frame = vlc_stream_Block( p_demux->s, i_size + 8 ) ) )
268
1.87M
            {
269
1.87M
                p_frame->p_buffer += 8;
270
1.87M
                p_frame->i_buffer -= 8;
271
                /* XXX this a hack, some streams aren't compliant and
272
                 * don't set pes_start flag */
273
1.87M
                if( p_sys->p_pes && p_frame->i_buffer > 4 &&
274
317k
                    p_frame->p_buffer[0] == 0x00 &&
275
298k
                    p_frame->p_buffer[1] == 0x00 &&
276
290k
                    p_frame->p_buffer[2] == 0x01 )
277
271k
                {
278
271k
                    ParsePES( p_demux );
279
271k
                }
280
1.87M
                block_ChainAppend( &p_sys->p_pes, p_frame );
281
1.87M
            }
282
1.87M
            break;
283
284
10.6k
        default:
285
10.6k
            msg_Warn( p_demux, "unknown id=0x%x", p_peek[2] );
286
10.6k
            if( vlc_stream_Read( p_demux->s, NULL, i_size + 8 ) != (i_size + 8) )
287
87
                return VLC_DEMUXER_EOF;
288
10.6k
            break;
289
2.26M
    }
290
2.26M
    return VLC_DEMUXER_SUCCESS;
291
2.26M
}
292
293
/*****************************************************************************
294
 * Control:
295
 *****************************************************************************/
296
static int Control( demux_t *p_demux, int i_query, va_list args )
297
0
{
298
    /* demux_sys_t *p_sys = p_demux->p_sys; */
299
0
    double f, *pf;
300
0
    int64_t i64;
301
0
    switch( i_query )
302
0
    {
303
0
        case DEMUX_CAN_SEEK:
304
0
            return vlc_stream_vaControl( p_demux->s, i_query, args );
305
306
0
        case DEMUX_GET_POSITION:
307
0
            if( ( i64 = stream_Size( p_demux->s ) ) > 0 )
308
0
            {
309
0
                pf = va_arg( args, double * );
310
0
                double current = vlc_stream_Tell( p_demux->s );
311
0
                *pf = current / (double)i64;
312
0
                return VLC_SUCCESS;
313
0
            }
314
0
            return VLC_EGENERIC;
315
316
0
        case DEMUX_SET_POSITION:
317
0
            f = va_arg( args, double );
318
0
            i64 = stream_Size( p_demux->s );
319
320
0
            if( vlc_stream_Seek( p_demux->s, (int64_t)(i64 * f) ) || ReSynch( p_demux ) )
321
0
            {
322
0
                return VLC_EGENERIC;
323
0
            }
324
0
            return VLC_SUCCESS;
325
326
#if 0
327
        case DEMUX_GET_TIME:
328
            pi64 = va_arg( args, vlc_tick_t * );
329
            if( p_sys->i_time < 0 )
330
            {
331
                *pi64 = 0;
332
                return VLC_EGENERIC;
333
            }
334
            *pi64 = p_sys->i_time;
335
            return VLC_SUCCESS;
336
337
#if 0
338
        case DEMUX_GET_LENGTH:
339
            pi64 = va_arg( args, vlc_tick_t * );
340
            if( p_sys->i_mux_rate > 0 )
341
            {
342
                *pi64 = vlc_tick_from_samples( stream_Size( p_demux->s ) / 50, p_sys->i_mux_rate);
343
                return VLC_SUCCESS;
344
            }
345
            *pi64 = 0;
346
            return VLC_EGENERIC;
347
348
#endif
349
        case DEMUX_GET_FPS:
350
            pf = va_arg( args, double * );
351
            *pf = (double)1000000.0 / (double)p_sys->i_pcr_inc;
352
            return VLC_SUCCESS;
353
#endif
354
0
        case DEMUX_CAN_PAUSE:
355
0
        case DEMUX_SET_PAUSE_STATE:
356
0
        case DEMUX_CAN_CONTROL_PACE:
357
0
        case DEMUX_GET_PTS_DELAY:
358
0
            return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
359
360
0
        case DEMUX_SET_TIME:
361
0
        default:
362
0
            return VLC_EGENERIC;
363
0
    }
364
0
}
365
366
/*****************************************************************************
367
 * ReSynch:
368
 *****************************************************************************/
369
static int ReSynch( demux_t *p_demux )
370
1.62M
{
371
1.62M
    for( ;; )
372
1.62M
    {
373
1.62M
        const uint8_t *p_peek;
374
1.62M
        int i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 1024 );
375
1.62M
        if( i_peek < 8 )
376
1.14k
            break;
377
378
1.62M
        int i_skip = 0;
379
380
44.0M
        while( i_skip < i_peek - 5 )
381
44.0M
        {
382
44.0M
            if( p_peek[0] == 'A' && p_peek[1] == 'V' && p_peek[4] == 0x55 )
383
1.62M
            {
384
1.62M
                if( i_skip > 0
385
1.62M
                 && vlc_stream_Read( p_demux->s, NULL, i_skip ) != i_skip )
386
0
                    return VLC_EGENERIC;
387
1.62M
                return VLC_SUCCESS;
388
1.62M
            }
389
42.4M
            p_peek++;
390
42.4M
            i_skip++;
391
42.4M
        }
392
393
7.67k
        if( vlc_stream_Read( p_demux->s, NULL, i_skip ) != i_skip )
394
0
            break;
395
7.67k
    }
396
397
1.14k
    return VLC_EGENERIC;
398
1.62M
}
399
400
static void ParsePES( demux_t *p_demux )
401
876k
{
402
876k
    demux_sys_t *p_sys = p_demux->p_sys;
403
876k
    block_t     *p_pes = p_sys->p_pes;
404
876k
    uint8_t     hdr[20];
405
406
876k
    unsigned    i_skip;
407
876k
    ts_90khz_t  i_dts;
408
876k
    ts_90khz_t  i_pts;
409
410
876k
    p_sys->p_pes = NULL;
411
412
    /* FIXME find real max size */
413
876k
    size_t hdr_read = block_ChainExtract( p_pes, hdr, ARRAY_SIZE(hdr) );
414
415
    /* See ยง2.4.3.6 of ISO 13818-1 */
416
876k
    if( hdr_read < 9 ) {
417
8.24k
        msg_Warn( p_demux, "invalid hdr size (%zu)", hdr_read);
418
8.24k
        block_ChainRelease( p_pes );
419
8.24k
        return;
420
8.24k
    }
421
867k
    if( hdr[0] != 0 || hdr[1] != 0 || hdr[2] != 1 ) {
422
25.5k
        msg_Warn( p_demux, "invalid hdr [0x%2.2x:%2.2x:%2.2x]",
423
25.5k
                  hdr[0], hdr[1], hdr[2] );
424
25.5k
        block_ChainRelease( p_pes );
425
25.5k
        return;
426
25.5k
    }
427
    // hdr[4] i_pes_size, 2 bytes
428
    // hdr[6] Marker -> original_or_copy
429
430
    /* we assume mpeg2 PES */
431
842k
    i_skip = hdr[8] + 9;
432
433
842k
    p_pes = block_ChainGather( p_pes );
434
842k
    if( unlikely(p_pes == NULL) )
435
0
        return;
436
842k
    if( p_pes->i_buffer <= i_skip )
437
37.4k
    {
438
37.4k
        block_ChainRelease( p_pes );
439
37.4k
        return;
440
37.4k
    }
441
442
804k
    p_pes->i_buffer -= i_skip;
443
804k
    p_pes->p_buffer += i_skip;
444
445
804k
    if( hdr[7]&0x80 && hdr_read >= (9+1+5) )    /* has pts */
446
759k
    {
447
759k
        i_pts = GetPESTimestamp( &hdr[9] );
448
759k
        p_pes->i_pts = FROM_SCALE(i_pts);
449
450
759k
        if( hdr[7]&0x40 && hdr_read >= (14+1+5) )    /* has dts */
451
601k
        {
452
601k
            i_dts = GetPESTimestamp( &hdr[14] );
453
601k
            p_pes->i_dts = FROM_SCALE(i_dts);
454
601k
        }
455
759k
    }
456
457
    /* Set PCR */
458
804k
    if( p_pes->i_pts != VLC_TICK_INVALID )
459
759k
    {
460
759k
        es_out_SetPCR( p_demux->out, p_pes->i_pts);
461
759k
        p_sys->b_pcr_audio = true;
462
759k
    }
463
804k
    if( likely(p_sys->p_audio) )
464
804k
        es_out_Send( p_demux->out, p_sys->p_audio, p_pes );
465
0
    else
466
0
        block_Release( p_pes );
467
804k
}