Coverage Report

Created: 2025-07-23 06:11

/src/vlc/modules/packetizer/dts.c
Line
Count
Source (jump to first uncovered line)
1
/*****************************************************************************
2
 * dts.c: parse DTS audio sync info and packetize the stream
3
 *****************************************************************************
4
 * Copyright (C) 2001-2016 VLC authors and VideoLAN
5
 *
6
 * Authors: Gildas Bazin <gbazin@videolan.org>
7
 *          Thomas Guillem <thomas@gllm.fr>
8
 *
9
 * This program is free software; you can redistribute it and/or modify it
10
 * under the terms of the GNU Lesser General Public License as published by
11
 * the Free Software Foundation; either version 2.1 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with this program; if not, write to the Free Software Foundation,
21
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
 *****************************************************************************/
23
24
/*****************************************************************************
25
 * Preamble
26
 *****************************************************************************/
27
#ifdef HAVE_CONFIG_H
28
# include "config.h"
29
#endif
30
31
#include <stdbit.h>
32
33
#include <vlc_common.h>
34
#include <vlc_plugin.h>
35
#include <vlc_codec.h>
36
#include <vlc_block_helper.h>
37
#include <vlc_modules.h>
38
39
#include "dts_header.h"
40
41
#include "packetizer_helper.h"
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_("DTS audio packetizer") )
49
2
    set_capability( "audio packetizer", 10 )
50
4
    set_callbacks( Open, Close )
51
2
vlc_module_end ()
52
53
typedef struct
54
{
55
    /*
56
     * Input properties
57
     */
58
    int i_state;
59
60
    block_bytestream_t bytestream;
61
    size_t i_next_offset;
62
63
    /*
64
     * Common properties
65
     */
66
    date_t  end_date;
67
    bool    b_date_set;
68
69
    vlc_tick_t i_pts;
70
    bool    b_discontinuity;
71
72
    vlc_dts_header_t first, second;
73
    size_t  i_input_size;
74
} decoder_sys_t;
75
76
enum
77
{
78
    STATE_SYNC_SUBSTREAM_EXTENSIONS = STATE_CUSTOM_FIRST,
79
    STATE_NEXT_SYNC_SUBSTREAM_EXTENSIONS,
80
};
81
82
static void PacketizeFlush( decoder_t *p_dec )
83
0
{
84
0
    decoder_sys_t *p_sys = p_dec->p_sys;
85
86
0
    p_sys->b_discontinuity = true;
87
0
    date_Set( &p_sys->end_date, VLC_TICK_INVALID );
88
0
    p_sys->i_state = STATE_NOSYNC;
89
0
    block_BytestreamEmpty( &p_sys->bytestream );
90
0
}
91
92
static block_t *GetOutBuffer( decoder_t *p_dec )
93
0
{
94
0
    decoder_sys_t *p_sys = p_dec->p_sys;
95
96
0
    if( !p_sys->b_date_set
97
0
     || p_dec->fmt_out.audio.i_rate != p_sys->first.i_rate )
98
0
    {
99
0
        msg_Dbg( p_dec, "DTS samplerate:%d bitrate:%d",
100
0
                 p_sys->first.i_rate, p_sys->first.i_bitrate );
101
102
0
        date_Init( &p_sys->end_date, p_sys->first.i_rate, 1 );
103
0
        date_Set( &p_sys->end_date, p_sys->i_pts );
104
0
        p_sys->b_date_set = true;
105
0
    }
106
107
0
    p_dec->fmt_out.audio.i_rate     = p_sys->first.i_rate;
108
0
    if( p_dec->fmt_out.audio.i_bytes_per_frame < p_sys->first.i_frame_size )
109
0
        p_dec->fmt_out.audio.i_bytes_per_frame = p_sys->first.i_frame_size;
110
0
    p_dec->fmt_out.audio.i_frame_length = p_sys->first.i_frame_length;
111
112
0
    p_dec->fmt_out.audio.i_chan_mode = p_sys->first.i_chan_mode;
113
0
    p_dec->fmt_out.audio.i_physical_channels = p_sys->first.i_physical_channels;
114
0
    p_dec->fmt_out.audio.i_channels =
115
0
        stdc_count_ones( p_dec->fmt_out.audio.i_physical_channels );
116
117
0
    p_dec->fmt_out.i_bitrate = p_sys->first.i_bitrate;
118
119
0
    block_t *p_block = block_Alloc( p_sys->i_input_size );
120
0
    if( p_block == NULL )
121
0
        return NULL;
122
123
0
    p_block->i_nb_samples = p_sys->first.i_frame_length;
124
0
    p_block->i_pts = p_block->i_dts = date_Get( &p_sys->end_date );
125
0
    p_block->i_length =
126
0
        date_Increment( &p_sys->end_date, p_block->i_nb_samples ) - p_block->i_pts;
127
0
    return p_block;
128
0
}
129
130
static block_t *PacketizeBlock( decoder_t *p_dec, block_t **pp_block )
131
0
{
132
0
    decoder_sys_t *p_sys = p_dec->p_sys;
133
0
    uint8_t p_header[VLC_DTS_HEADER_SIZE];
134
0
    block_t *p_out_buffer;
135
136
0
    block_t *p_block = pp_block ? *pp_block : NULL;
137
138
0
    if( p_block )
139
0
    {
140
0
        if ( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) {
141
            /* First always drain complete blocks before discontinuity */
142
0
            block_t *p_drain = PacketizeBlock( p_dec, NULL );
143
0
            if(p_drain)
144
0
                return p_drain;
145
146
0
            PacketizeFlush( p_dec );
147
148
0
            if ( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) {
149
0
                block_Release( p_block );
150
0
                return NULL;
151
0
            }
152
0
        }
153
154
0
        if ( p_block->i_pts == VLC_TICK_INVALID &&
155
0
             date_Get( &p_sys->end_date ) == VLC_TICK_INVALID ) {
156
            /* We've just started the stream, wait for the first PTS. */
157
0
            block_Release( p_block );
158
0
            return NULL;
159
0
        }
160
161
0
        block_BytestreamPush( &p_sys->bytestream, p_block );
162
0
    }
163
164
0
    while( 1 )
165
0
    {
166
0
        switch( p_sys->i_state )
167
0
        {
168
0
        case STATE_NOSYNC:
169
0
            while( block_PeekBytes( &p_sys->bytestream, p_header, 6 )
170
0
                   == VLC_SUCCESS )
171
0
            {
172
0
                if( vlc_dts_header_IsSync( p_header, 6 ) )
173
0
                {
174
0
                    p_sys->i_state = STATE_SYNC;
175
0
                    break;
176
0
                }
177
0
                block_SkipByte( &p_sys->bytestream );
178
0
            }
179
0
            if( p_sys->i_state != STATE_SYNC )
180
0
            {
181
0
                block_BytestreamFlush( &p_sys->bytestream );
182
183
                /* Need more data */
184
0
                return NULL;
185
0
            }
186
            /* fallthrough */
187
188
0
        case STATE_SYNC:
189
            /* New frame, set the Presentation Time Stamp */
190
0
            p_sys->i_pts = p_sys->bytestream.p_block->i_pts;
191
0
            if( p_sys->i_pts != VLC_TICK_INVALID &&
192
0
                p_sys->i_pts != date_Get( &p_sys->end_date ) )
193
0
            {
194
0
                date_Set( &p_sys->end_date, p_sys->i_pts );
195
0
            }
196
0
            p_sys->i_state = STATE_HEADER;
197
            /* fallthrough */
198
199
0
        case STATE_HEADER:
200
            /* Get DTS frame header (VLC_DTS_HEADER_SIZE bytes) */
201
0
            if( block_PeekBytes( &p_sys->bytestream, p_header,
202
0
                                 VLC_DTS_HEADER_SIZE ) != VLC_SUCCESS )
203
0
            {
204
                /* Need more data */
205
0
                return NULL;
206
0
            }
207
208
            /* Check if frame is valid and get frame info */
209
0
            if( vlc_dts_header_Parse( &p_sys->first, p_header,
210
0
                                      VLC_DTS_HEADER_SIZE ) != VLC_SUCCESS
211
0
             || p_sys->first.i_frame_size == 0 )
212
0
            {
213
0
                msg_Dbg( p_dec, "emulated sync word" );
214
0
                block_SkipByte( &p_sys->bytestream );
215
0
                p_sys->i_state = STATE_NOSYNC;
216
0
                break;
217
0
            }
218
219
0
            if( p_sys->first.syncword == DTS_SYNC_SUBSTREAM )
220
0
                p_sys->i_state = STATE_SYNC_SUBSTREAM_EXTENSIONS;
221
0
            else
222
0
                p_sys->i_state = STATE_NEXT_SYNC;
223
0
            p_sys->i_input_size = p_sys->i_next_offset = p_sys->first.i_frame_size;
224
0
            break;
225
226
0
        case STATE_SYNC_SUBSTREAM_EXTENSIONS:
227
            /* Peek into the substream extension (sync + header size < frame_size) */
228
0
            if( block_PeekOffsetBytes( &p_sys->bytestream,
229
0
                                       p_sys->first.i_substream_header_size,
230
0
                                       p_header,
231
0
                                       VLC_DTS_HEADER_SIZE ) != VLC_SUCCESS )
232
0
            {
233
                /* Need more data */
234
0
                return NULL;
235
0
            }
236
237
0
            vlc_dts_header_t xssheader;
238
0
            if( vlc_dts_header_Parse( &xssheader, p_header,
239
0
                                      VLC_DTS_HEADER_SIZE ) != VLC_SUCCESS )
240
0
            {
241
0
                msg_Dbg( p_dec, "emulated substream sync word, can't find extension" );
242
0
                block_SkipByte( &p_sys->bytestream );
243
0
                p_sys->i_state = STATE_NOSYNC;
244
0
                break;
245
0
            }
246
247
0
            if( xssheader.syncword == DTS_SYNC_SUBSTREAM_LBR )
248
0
            {
249
                /*
250
                 * LBR exists as independent SUBSTREAM. It is seen valid
251
                 * only when SUBSTREAM[LBR]..SUBTREAM.
252
                 * CORE...SUBSTREAM is regular extension.
253
                 * SUBSTREAM...CORE is sync issue.
254
                 */
255
0
                p_dec->fmt_out.i_profile = PROFILE_DTS_EXPRESS;
256
0
                p_sys->first.i_rate = xssheader.i_rate;
257
0
                p_sys->first.i_frame_length = xssheader.i_frame_length;
258
0
                p_sys->i_state = STATE_NEXT_SYNC;
259
0
                break;
260
0
            }
261
262
0
            msg_Warn( p_dec, "substream without the paired core stream, skip it" );
263
0
            p_sys->i_state = STATE_NOSYNC;
264
0
            p_dec->fmt_out.i_profile = PROFILE_DTS;
265
0
            if( block_SkipBytes( &p_sys->bytestream,
266
0
                                 p_sys->first.i_frame_size ) != VLC_SUCCESS )
267
0
                return NULL;
268
0
            break;
269
270
0
        case STATE_NEXT_SYNC:
271
            /* Check if next expected frame contains the sync word */
272
0
            while( p_sys->i_state == STATE_NEXT_SYNC )
273
0
            {
274
0
                if( block_PeekOffsetBytes( &p_sys->bytestream,
275
0
                                           p_sys->i_next_offset, p_header,
276
0
                                           VLC_DTS_HEADER_SIZE )
277
0
                                           != VLC_SUCCESS )
278
0
                {
279
0
                    if( p_block == NULL ) /* drain */
280
0
                    {
281
0
                        p_sys->i_state = STATE_GET_DATA;
282
0
                        break;
283
0
                    }
284
                    /* Need more data */
285
0
                    return NULL;
286
0
                }
287
288
0
                if( p_header[0] == 0 )
289
0
                {
290
                    /* DTS wav files, audio CD's and some mkvs use stuffing */
291
0
                    p_sys->i_next_offset++;
292
0
                    continue;
293
0
                }
294
295
0
                if( !vlc_dts_header_IsSync( p_header, VLC_DTS_HEADER_SIZE ) )
296
0
                {
297
                    /* Even frame size is likely incorrect FSIZE #18166 */
298
0
                    if( (p_sys->first.i_frame_size % 2) && p_sys->i_next_offset > 0 &&
299
0
                        block_PeekOffsetBytes( &p_sys->bytestream,
300
0
                                               p_sys->i_next_offset - 1, p_header,
301
0
                                               VLC_DTS_HEADER_SIZE ) == 0 &&
302
0
                         vlc_dts_header_IsSync( p_header, VLC_DTS_HEADER_SIZE ) )
303
0
                    {
304
0
                        p_sys->i_input_size = p_sys->i_next_offset = p_sys->first.i_frame_size - 1;
305
                        /* reenter */
306
0
                        break;
307
0
                    }
308
0
                    msg_Dbg( p_dec, "emulated sync word "
309
0
                             "(no sync on following frame)" );
310
0
                    p_sys->i_state = STATE_NOSYNC;
311
0
                    block_SkipByte( &p_sys->bytestream );
312
0
                    break;
313
0
                }
314
315
                /* Check if a DTS substream packet is located just after
316
                 * the core packet */
317
0
                if( p_sys->i_next_offset == p_sys->first.i_frame_size &&
318
0
                    vlc_dts_header_Parse( &p_sys->second,
319
0
                                          p_header, VLC_DTS_HEADER_SIZE ) == VLC_SUCCESS &&
320
0
                    p_sys->second.syncword == DTS_SYNC_SUBSTREAM )
321
0
                {
322
0
                    p_sys->i_state = STATE_NEXT_SYNC_SUBSTREAM_EXTENSIONS;
323
0
                }
324
0
                else
325
0
                {
326
0
                    p_dec->fmt_out.i_profile = PROFILE_DTS;
327
0
                    p_sys->i_state = STATE_GET_DATA;
328
0
                }
329
0
            }
330
0
            break;
331
332
0
        case STATE_NEXT_SYNC_SUBSTREAM_EXTENSIONS:
333
0
            assert(p_sys->second.syncword == DTS_SYNC_SUBSTREAM);
334
0
            if( p_sys->first.syncword == DTS_SYNC_SUBSTREAM )
335
0
            {
336
                /* First substream must have been LBR */
337
0
                p_dec->fmt_out.i_profile = PROFILE_DTS_EXPRESS;
338
0
            }
339
0
            else /* Otherwise that's core + extensions, we need to output both */
340
0
            {
341
0
                p_dec->fmt_out.i_profile = PROFILE_DTS_HD;
342
0
                p_sys->i_input_size += p_sys->second.i_frame_size;
343
0
            }
344
0
            p_sys->i_state = STATE_GET_DATA;
345
0
            break;
346
347
0
        case STATE_GET_DATA:
348
            /* Make sure we have enough data. */
349
0
            if( block_WaitBytes( &p_sys->bytestream,
350
0
                                 p_sys->i_input_size ) != VLC_SUCCESS )
351
0
            {
352
                /* Need more data */
353
0
                return NULL;
354
0
            }
355
0
            p_sys->i_state = STATE_SEND_DATA;
356
            /* fallthrough */
357
358
0
        case STATE_SEND_DATA:
359
0
            if( !(p_out_buffer = GetOutBuffer( p_dec )) )
360
0
            {
361
0
                return NULL;
362
0
            }
363
364
            /* Copy the whole frame into the buffer. When we reach this point
365
             * we already know we have enough data available. */
366
0
            block_GetBytes( &p_sys->bytestream, p_out_buffer->p_buffer,
367
0
                            p_out_buffer->i_buffer );
368
369
            /* Make sure we don't reuse the same pts twice */
370
0
            if( p_sys->i_pts == p_sys->bytestream.p_block->i_pts )
371
0
                p_sys->i_pts = p_sys->bytestream.p_block->i_pts = VLC_TICK_INVALID;
372
373
0
            if( p_sys->b_discontinuity )
374
0
            {
375
0
                p_sys->b_discontinuity = false;
376
0
                p_out_buffer->i_flags |= BLOCK_FLAG_DISCONTINUITY;
377
0
            }
378
379
            /* So p_block doesn't get re-added several times */
380
0
            if( pp_block )
381
0
                *pp_block = block_BytestreamPop( &p_sys->bytestream );
382
383
0
            p_sys->i_state = STATE_NOSYNC;
384
385
0
            return p_out_buffer;
386
0
        }
387
0
    }
388
0
}
389
390
static void Close( vlc_object_t *p_this )
391
0
{
392
0
    decoder_t *p_dec = (decoder_t*)p_this;
393
0
    decoder_sys_t *p_sys = p_dec->p_sys;
394
395
0
    block_BytestreamRelease( &p_sys->bytestream );
396
397
0
    free( p_sys );
398
0
}
399
400
static int Open( vlc_object_t *p_this )
401
0
{
402
0
    decoder_t *p_dec = (decoder_t*)p_this;
403
0
    decoder_sys_t *p_sys;
404
405
0
    if( p_dec->fmt_in->i_codec != VLC_CODEC_DTS )
406
0
        return VLC_EGENERIC;
407
408
    /* Allocate the memory needed to store the decoder's structure */
409
0
    if( ( p_dec->p_sys = p_sys = malloc(sizeof(decoder_sys_t)) ) == NULL )
410
0
        return VLC_ENOMEM;
411
412
    /* Misc init */
413
0
    p_sys->i_state = STATE_NOSYNC;
414
0
    date_Set( &p_sys->end_date, VLC_TICK_INVALID );
415
0
    p_sys->i_pts = VLC_TICK_INVALID;
416
0
    p_sys->b_date_set = false;
417
0
    p_sys->b_discontinuity = false;
418
0
    memset(&p_sys->first, 0, sizeof(vlc_dts_header_t));
419
0
    memset(&p_sys->second, 0, sizeof(vlc_dts_header_t));
420
0
    block_BytestreamInit( &p_sys->bytestream );
421
422
    /* Set output properties (passthrough only) */
423
0
    p_dec->fmt_out.i_codec = p_dec->fmt_in->i_codec;
424
0
    p_dec->fmt_out.audio = p_dec->fmt_in->audio;
425
426
    /* Set callback */
427
0
    p_dec->pf_packetize = PacketizeBlock;
428
0
    p_dec->pf_flush     = PacketizeFlush;
429
0
    p_dec->pf_get_cc    = NULL;
430
0
    return VLC_SUCCESS;
431
0
}