Coverage Report

Created: 2023-03-26 07:08

/src/vlc/modules/packetizer/mlp.c
Line
Count
Source (jump to first uncovered line)
1
/*****************************************************************************
2
 * mlp.c: packetize MLP/TrueHD audio
3
 *****************************************************************************
4
 * Copyright (C) 2008 Laurent Aimar
5
 *
6
 * Authors: Laurent Aimar < fenrir _AT videolan _DOT_ org >
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
#ifdef HAVE_CONFIG_H
27
# include "config.h"
28
#endif
29
30
#include <vlc_common.h>
31
#include <vlc_plugin.h>
32
#include <vlc_codec.h>
33
#include <vlc_block_helper.h>
34
#include <vlc_bits.h>
35
#include <assert.h>
36
37
#include "packetizer_helper.h"
38
#include "a52.h"
39
40
/*****************************************************************************
41
 * Module descriptor
42
 *****************************************************************************/
43
static int  Open ( vlc_object_t * );
44
static void Close( vlc_object_t * );
45
46
4
vlc_module_begin ()
47
2
    set_subcategory( SUBCAT_SOUT_PACKETIZER )
48
2
    set_description( N_("MLP/TrueHD parser") )
49
2
    set_capability( "packetizer", 50 )
50
4
    set_callbacks( Open, Close )
51
2
vlc_module_end ()
52
53
/*****************************************************************************
54
 *
55
 *****************************************************************************/
56
typedef struct
57
{
58
    int i_type;
59
    unsigned i_rate;
60
    unsigned i_channels;
61
    int i_channels_conf;
62
    unsigned i_samples;
63
64
    bool b_vbr;
65
    unsigned  i_bitrate;
66
67
    unsigned  i_substreams;
68
69
} mlp_header_t;
70
71
typedef struct
72
{
73
    /*
74
     * Input properties
75
     */
76
    int i_state;
77
78
    block_bytestream_t bytestream;
79
80
    /*
81
     * Common properties
82
     */
83
    date_t  end_date;
84
    bool    b_discontinuity;
85
86
    vlc_tick_t i_pts;
87
    int i_frame_size;
88
89
    bool         b_mlp;
90
    mlp_header_t mlp;
91
} decoder_sys_t;
92
93
496k
#define MLP_MAX_SUBSTREAMS (16)
94
496k
#define MLP_HEADER_SYNC (28)
95
496k
#define MLP_HEADER_SIZE (4 + MLP_HEADER_SYNC + 4 * MLP_MAX_SUBSTREAMS)
96
97
static const uint8_t pu_start_code[3] = { 0xf8, 0x72, 0x6f };
98
99
/**
100
 * It parse MLP sync info.
101
 *
102
 * TODO handle CRC (at offset 26)
103
 */
104
static int TrueHdChannels( int i_map )
105
67
{
106
67
    static const uint8_t pu_thd[13] =
107
67
    {
108
67
         2, 1, 1, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1
109
67
    };
110
67
    int i_count = 0;
111
112
938
    for( int i = 0; i < 13; i++ )
113
871
    {
114
871
        if( i_map & (1<<i) )
115
326
            i_count += pu_thd[i];
116
871
    }
117
67
    return i_count;
118
67
}
119
120
static int MlpParse( mlp_header_t *p_mlp, const uint8_t p_hdr[MLP_HEADER_SYNC] )
121
68
{
122
68
    bs_t s;
123
124
68
    assert( !memcmp( p_hdr, pu_start_code, 3 ) );
125
126
    /* TODO Checksum ? */
127
128
    /* */
129
68
    bs_init( &s, &p_hdr[3], MLP_HEADER_SYNC - 3 );
130
131
    /* Stream type */
132
68
    p_mlp->i_type = bs_read( &s, 8 );
133
68
    int i_rate_idx1;
134
135
68
    if( p_mlp->i_type == 0xbb )        /* MLP */
136
0
    {
137
0
        static const unsigned pu_channels[32] = {
138
0
            1, 2, 3, 4, 3, 4, 5, 3, 4, 5, 4, 5, 6, 4, 5, 4,
139
0
            5, 6, 5, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
140
0
        };
141
142
0
        bs_skip( &s, 4 + 4 );
143
144
0
        i_rate_idx1 = bs_read( &s, 4 );
145
146
        // Just skip the 4 following, since we don't use it
147
        // const int i_rate_idx2 = bs_read( &s, 4 );
148
0
        bs_skip( &s, 4 );
149
150
0
        bs_skip( &s, 11 );
151
152
0
        const int i_channel_idx = bs_read( &s, 5 );
153
0
        p_mlp->i_channels = pu_channels[i_channel_idx];
154
0
    }
155
68
    else if( p_mlp->i_type == 0xba )   /* True HD */
156
67
    {
157
67
        i_rate_idx1 = bs_read( &s, 4 );
158
159
67
        bs_skip( &s, 8 );
160
161
67
        const int i_channel1 = bs_read( &s, 5 );
162
163
67
        bs_skip( &s, 2 );
164
165
67
        const int i_channel2 = bs_read( &s, 13 );
166
67
        if( i_channel2 )
167
67
            p_mlp->i_channels = TrueHdChannels( i_channel2 );
168
0
        else
169
0
            p_mlp->i_channels = TrueHdChannels( i_channel1 );
170
67
    }
171
1
    else
172
1
    {
173
1
        return VLC_EGENERIC;
174
1
    }
175
176
67
    if( i_rate_idx1 == 0x0f )
177
0
        p_mlp->i_rate = 0;
178
67
    else
179
67
        p_mlp->i_rate = ( ( i_rate_idx1 & 0x8 ) ? 44100 : 48000 ) << (i_rate_idx1 & 0x7);
180
67
    p_mlp->i_channels_conf = 0; /* TODO ? */
181
182
67
    p_mlp->i_samples = 40 << ( i_rate_idx1 & 0x07 );
183
184
67
    bs_skip( &s, 48 );
185
186
67
    p_mlp->b_vbr = bs_read( &s, 1 );
187
67
    p_mlp->i_bitrate = ( bs_read( &s, 15 ) * p_mlp->i_rate + 8) / 16;
188
189
67
    p_mlp->i_substreams = bs_read( &s, 4 );
190
67
    bs_skip( &s, 4 + 11 * 8 );
191
192
    //fprintf( stderr, "i_samples = %d channels:%d rate:%d bitsrate=%d substreams=%d\n",
193
    //        p_mlp->i_samples, p_mlp->i_channels, p_mlp->i_rate, p_mlp->i_bitrate, p_mlp->i_substreams );
194
67
    return VLC_SUCCESS;
195
68
}
196
197
static int SyncInfo( const uint8_t *p_hdr, bool *pb_mlp, mlp_header_t *p_mlp )
198
172k
{
199
    /* Check major sync presence */
200
172k
    const bool b_has_sync = !memcmp( &p_hdr[4], pu_start_code, 3 );
201
202
    /* Wait for a major sync */
203
172k
    if( !b_has_sync && !*pb_mlp )
204
171k
        return 0;
205
206
    /* Parse major sync if present */
207
1.33k
    if( b_has_sync )
208
68
    {
209
68
        *pb_mlp = !MlpParse( p_mlp, &p_hdr[4] );
210
211
68
        if( !*pb_mlp )
212
1
            return 0;
213
68
    }
214
215
1.33k
    if( !b_has_sync )
216
1.26k
    {
217
1.26k
        int i_tmp = 0 ^ p_hdr[0] ^ p_hdr[1] ^ p_hdr[2] ^ p_hdr[3];
218
1.26k
        const uint8_t *p = &p_hdr[4];
219
220
2.52k
        for( unsigned i = 0; i < p_mlp->i_substreams; i++ )
221
1.26k
        {
222
1.26k
            i_tmp ^= *p++;
223
1.26k
            i_tmp ^= *p++;
224
1.26k
            if( p[-2] & 0x80 )
225
32
            {
226
32
                i_tmp ^= *p++;
227
32
                i_tmp ^= *p++;
228
32
            }
229
1.26k
        }
230
1.26k
        i_tmp = ( i_tmp >> 4 ) ^ i_tmp;
231
232
1.26k
        if( ( i_tmp & 0x0f ) != 0x0f )
233
1.26k
            return 0;
234
1.26k
    }
235
236
    /* */
237
67
    const int i_word = ( ( p_hdr[0] << 8 ) | p_hdr[1] ) & 0xfff;
238
67
    return i_word * 2;
239
1.33k
}
240
241
/**
242
 * It returns the size of an AC3/EAC3 frame (or 0 if invalid)
243
 */
244
static int SyncInfoDolby( const uint8_t *p_buf )
245
271k
{
246
271k
    vlc_a52_header_t a52;
247
271k
    if( vlc_a52_header_Parse( &a52, p_buf, MLP_HEADER_SIZE ) == VLC_SUCCESS )
248
197k
        return a52.i_size;
249
73.6k
    else
250
73.6k
        return 0;
251
271k
}
252
253
static void Flush( decoder_t *p_dec )
254
0
{
255
0
    decoder_sys_t *p_sys = p_dec->p_sys;
256
257
0
    p_sys->b_mlp = false;
258
0
    p_sys->i_state = STATE_NOSYNC;
259
0
    p_sys->b_discontinuity = true;
260
0
    block_BytestreamEmpty( &p_sys->bytestream );
261
0
    date_Set( &p_sys->end_date, VLC_TICK_INVALID );
262
0
}
263
264
static block_t *Packetize( decoder_t *p_dec, block_t **pp_block )
265
282
{
266
282
    decoder_sys_t *p_sys = p_dec->p_sys;
267
282
    uint8_t p_header[MLP_HEADER_SIZE];
268
282
    block_t *p_out_buffer;
269
270
282
    block_t *p_block = pp_block ? *pp_block : NULL;
271
272
282
    if ( p_block )
273
279
    {
274
279
        if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) )
275
0
        {
276
            /* First always drain complete blocks before discontinuity */
277
0
            block_t *p_drain = Packetize( p_dec, NULL );
278
0
            if( p_drain )
279
0
                return p_drain;
280
281
0
            Flush( p_dec );
282
283
0
            if( p_block->i_flags & BLOCK_FLAG_CORRUPTED )
284
0
            {
285
0
                block_Release( p_block );
286
0
                return NULL;
287
0
            }
288
0
        }
289
290
279
        if( p_block->i_pts == VLC_TICK_INVALID &&
291
279
            date_Get( &p_sys->end_date ) == VLC_TICK_INVALID )
292
139
        {
293
            /* We've just started the stream, wait for the first PTS. */
294
139
            msg_Dbg( p_dec, "waiting for PTS" );
295
139
            block_Release( p_block );
296
139
            return NULL;
297
139
        }
298
299
140
        block_BytestreamPush( &p_sys->bytestream, p_block );
300
140
    }
301
302
143
    for( ;; )
303
99.1k
    {
304
99.1k
        switch( p_sys->i_state )
305
99.1k
        {
306
51.8k
        case STATE_NOSYNC:
307
120k
            while( !block_PeekBytes( &p_sys->bytestream, p_header, MLP_HEADER_SIZE ) )
308
120k
            {
309
120k
                if( SyncInfo( p_header, &p_sys->b_mlp, &p_sys->mlp ) > 0 )
310
33
                {
311
33
                    p_sys->i_state = STATE_SYNC;
312
33
                    break;
313
33
                }
314
120k
                else if( SyncInfoDolby( p_header ) > 0 )
315
51.8k
                {
316
51.8k
                    p_sys->i_state = STATE_SYNC;
317
51.8k
                    break;
318
51.8k
                }
319
68.8k
                block_SkipByte( &p_sys->bytestream );
320
68.8k
            }
321
51.8k
            if( p_sys->i_state != STATE_SYNC )
322
4
            {
323
4
                block_BytestreamFlush( &p_sys->bytestream );
324
325
                /* Need more data */
326
4
                return NULL;
327
4
            }
328
            /* fallthrough */
329
330
51.8k
        case STATE_SYNC:
331
            /* New frame, set the Presentation Time Stamp */
332
51.8k
            p_sys->i_pts = p_sys->bytestream.p_block->i_pts;
333
51.8k
            if( p_sys->i_pts != VLC_TICK_INVALID &&
334
51.8k
                p_sys->i_pts != date_Get( &p_sys->end_date ) )
335
1
            {
336
1
                date_Set( &p_sys->end_date, p_sys->i_pts );
337
1
            }
338
51.8k
            p_sys->i_state = STATE_HEADER;
339
            /* fallthrough */
340
341
51.8k
        case STATE_HEADER:
342
            /* Get a MLP header */
343
51.8k
            if( block_PeekBytes( &p_sys->bytestream, p_header, MLP_HEADER_SIZE ) )
344
0
            {
345
                /* Need more data */
346
0
                return NULL;
347
0
            }
348
349
            /* Check if frame is valid and get frame info */
350
51.8k
            p_sys->i_frame_size = SyncInfoDolby( p_header );
351
51.8k
            if( p_sys->i_frame_size <= 0 )
352
33
                p_sys->i_frame_size = SyncInfo( p_header, &p_sys->b_mlp, &p_sys->mlp );
353
51.8k
            if( p_sys->i_frame_size <= 0 )
354
0
            {
355
0
                msg_Dbg( p_dec, "emulated sync word" );
356
0
                block_SkipByte( &p_sys->bytestream );
357
0
                p_sys->b_mlp = false;
358
0
                p_sys->i_state = STATE_NOSYNC;
359
0
                break;
360
0
            }
361
51.8k
            p_sys->i_state = STATE_NEXT_SYNC;
362
            /* fallthrough */
363
364
51.9k
        case STATE_NEXT_SYNC:
365
            /* Check if next expected frame contains the sync word */
366
51.9k
            if( block_PeekOffsetBytes( &p_sys->bytestream,
367
51.9k
                                       p_sys->i_frame_size, p_header, MLP_HEADER_SIZE ) )
368
138
            {
369
138
                if( p_block == NULL ) /* drain */
370
1
                {
371
1
                    p_sys->i_state = STATE_GET_DATA;
372
1
                    break;
373
1
                }
374
                /* Need more data */
375
137
                return NULL;
376
138
            }
377
378
51.8k
            bool b_mlp = p_sys->b_mlp;
379
51.8k
            mlp_header_t mlp = p_sys->mlp;
380
51.8k
            if( SyncInfo( p_header, &b_mlp, &mlp ) <= 0 && SyncInfoDolby( p_header ) <= 0 )
381
4.71k
            {
382
4.71k
                msg_Dbg( p_dec, "emulated sync word "
383
4.71k
                         "(no sync on following frame)" );
384
4.71k
                p_sys->b_mlp = false;
385
4.71k
                p_sys->i_state = STATE_NOSYNC;
386
4.71k
                block_SkipByte( &p_sys->bytestream );
387
4.71k
                break;
388
4.71k
            }
389
47.1k
            p_sys->i_state = STATE_GET_DATA;
390
47.1k
            break;
391
392
47.1k
        case STATE_GET_DATA:
393
            /* Make sure we have enough data. */
394
47.1k
            if( block_WaitBytes( &p_sys->bytestream, p_sys->i_frame_size ) )
395
1
            {
396
                /* Need more data */
397
1
                return NULL;
398
1
            }
399
47.1k
            p_sys->i_state = STATE_SEND_DATA;
400
            /* fallthrough */
401
402
47.1k
        case STATE_SEND_DATA:
403
            /* When we reach this point we already know we have enough
404
             * data available. */
405
47.1k
            p_out_buffer = block_Alloc( p_sys->i_frame_size );
406
47.1k
            if( !p_out_buffer )
407
0
                return NULL;
408
409
            /* Copy the whole frame into the buffer */
410
47.1k
            block_GetBytes( &p_sys->bytestream,
411
47.1k
                            p_out_buffer->p_buffer, p_out_buffer->i_buffer );
412
413
            /* Just ignore (E)AC3 frames */
414
47.1k
            if( SyncInfoDolby( p_out_buffer->p_buffer ) > 0 )
415
47.1k
            {
416
47.1k
                block_Release( p_out_buffer );
417
47.1k
                p_sys->i_state = STATE_NOSYNC;
418
47.1k
                break;
419
47.1k
            }
420
421
            /* Setup output */
422
1
            if( p_dec->fmt_out.audio.i_rate != p_sys->mlp.i_rate )
423
1
            {
424
1
                msg_Info( p_dec, "MLP channels: %d samplerate: %d",
425
1
                          p_sys->mlp.i_channels, p_sys->mlp.i_rate );
426
427
1
                if( p_sys->mlp.i_rate > 0 )
428
1
                    date_Change( &p_sys->end_date, p_sys->mlp.i_rate, 1 );
429
1
            }
430
431
1
            p_dec->fmt_out.audio.i_rate     = p_sys->mlp.i_rate;
432
1
            p_dec->fmt_out.audio.i_channels = p_sys->mlp.i_channels;
433
1
            p_dec->fmt_out.audio.i_physical_channels = p_sys->mlp.i_channels_conf;
434
1
            p_dec->fmt_out.audio.i_bytes_per_frame = p_sys->i_frame_size;
435
1
            p_dec->fmt_out.audio.i_frame_length = p_sys->mlp.i_samples;
436
437
1
            p_out_buffer->i_pts = p_out_buffer->i_dts = date_Get( &p_sys->end_date );
438
1
            p_out_buffer->i_nb_samples = p_sys->mlp.i_samples;
439
440
1
            p_out_buffer->i_length =
441
1
                date_Increment( &p_sys->end_date, p_sys->mlp.i_samples ) - p_out_buffer->i_pts;
442
443
            /* Make sure we don't reuse the same pts twice */
444
1
            if( p_sys->i_pts == p_sys->bytestream.p_block->i_pts )
445
1
                p_sys->i_pts = p_sys->bytestream.p_block->i_pts = VLC_TICK_INVALID;
446
447
1
            if( p_sys->b_discontinuity )
448
0
            {
449
0
                p_out_buffer->i_flags |= BLOCK_FLAG_DISCONTINUITY;
450
0
                p_sys->b_discontinuity = false;
451
0
            }
452
453
            /* So p_block doesn't get re-added several times */
454
1
            if( pp_block )
455
1
                *pp_block = block_BytestreamPop( &p_sys->bytestream );
456
457
1
            p_sys->i_state = STATE_NOSYNC;
458
459
1
            return p_out_buffer;
460
99.1k
        }
461
99.1k
    }
462
463
0
    return NULL;
464
143
}
465
466
static int Open( vlc_object_t *p_this )
467
18
{
468
18
    decoder_t *p_dec = (decoder_t*)p_this;
469
18
    decoder_sys_t *p_sys;
470
471
18
    if( p_dec->fmt_in->i_codec != VLC_CODEC_MLP &&
472
18
        p_dec->fmt_in->i_codec != VLC_CODEC_TRUEHD )
473
16
        return VLC_EGENERIC;
474
475
    /* */
476
2
    p_dec->p_sys = p_sys = malloc( sizeof(*p_sys) );
477
2
    if( !p_sys )
478
0
        return VLC_ENOMEM;
479
480
    /* */
481
2
    p_sys->i_state = STATE_NOSYNC;
482
2
    date_Init( &p_sys->end_date, 1, 1 );
483
484
2
    block_BytestreamInit( &p_sys->bytestream );
485
2
    p_sys->b_mlp = false;
486
2
    p_sys->b_discontinuity = false;
487
488
    /* Set output properties (Passthrough only) */
489
2
    p_dec->fmt_out.i_codec = p_dec->fmt_in->i_codec;
490
2
    p_dec->fmt_out.audio.i_rate = 0;
491
492
    /* Set callback */
493
2
    p_dec->pf_packetize = Packetize;
494
2
    p_dec->pf_flush     = Flush;
495
2
    p_dec->pf_get_cc    = NULL;
496
2
    return VLC_SUCCESS;
497
2
}
498
499
static void Close( vlc_object_t *p_this )
500
2
{
501
2
    decoder_t *p_dec = (decoder_t*)p_this;
502
2
    decoder_sys_t *p_sys = p_dec->p_sys;
503
504
2
    block_BytestreamRelease( &p_sys->bytestream );
505
506
2
    free( p_sys );
507
2
}