dts.c 12 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/*****************************************************************************
 * dts.c: parse DTS audio sync info and packetize the stream
 *****************************************************************************
 * Copyright (C) 2001-2016 VLC authors and VideoLAN
 *
 * Authors: Gildas Bazin <gbazin@videolan.org>
 *          Thomas Guillem <thomas@gllm.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_codec.h>
#include <vlc_block_helper.h>
#include <vlc_modules.h>

#include "dts_header.h"

#include "packetizer_helper.h"

static int  Open( vlc_object_t * );
static void Close( vlc_object_t * );

vlc_module_begin ()
    set_category( CAT_SOUT )
    set_subcategory( SUBCAT_SOUT_PACKETIZER )
    set_description( N_("DTS audio packetizer") )
    set_capability( "packetizer", 10 )
    set_callbacks( Open, Close )
vlc_module_end ()

struct decoder_sys_t
{
    /*
     * Input properties
     */
    int i_state;

    block_bytestream_t bytestream;
60
    size_t i_next_offset;
61
62
63
64
65
66
67
68

    /*
     * Common properties
     */
    date_t  end_date;
    bool    b_date_set;

    mtime_t i_pts;
69
    bool    b_discontinuity;
70
71

    vlc_dts_header_t dts;
72
    size_t  i_input_size;
73
74
75
76
77
78
};

static void PacketizeFlush( decoder_t *p_dec )
{
    decoder_sys_t *p_sys = p_dec->p_sys;

79
    p_sys->b_discontinuity = true;
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
    date_Set( &p_sys->end_date, 0 );
    p_sys->i_state = STATE_NOSYNC;
    block_BytestreamEmpty( &p_sys->bytestream );
}

static block_t *GetOutBuffer( decoder_t *p_dec )
{
    decoder_sys_t *p_sys = p_dec->p_sys;

    if( !p_sys->b_date_set
     || p_dec->fmt_out.audio.i_rate != p_sys->dts.i_rate )
    {
        msg_Dbg( p_dec, "DTS samplerate:%d bitrate:%d",
                 p_sys->dts.i_rate, p_sys->dts.i_bitrate );

        date_Init( &p_sys->end_date, p_sys->dts.i_rate, 1 );
        date_Set( &p_sys->end_date, p_sys->i_pts );
        p_sys->b_date_set = true;
    }

    p_dec->fmt_out.audio.i_rate     = p_sys->dts.i_rate;
    if( p_dec->fmt_out.audio.i_bytes_per_frame < p_sys->dts.i_frame_size )
        p_dec->fmt_out.audio.i_bytes_per_frame = p_sys->dts.i_frame_size;
    p_dec->fmt_out.audio.i_frame_length = p_sys->dts.i_frame_length;

    p_dec->fmt_out.audio.i_original_channels = p_sys->dts.i_original_channels;
    p_dec->fmt_out.audio.i_physical_channels = 
        p_sys->dts.i_original_channels & AOUT_CHAN_PHYSMASK;
    p_dec->fmt_out.audio.i_channels =
        popcount( p_dec->fmt_out.audio.i_physical_channels );

    p_dec->fmt_out.i_bitrate = p_sys->dts.i_bitrate;

113
    block_t *p_block = block_Alloc( p_sys->i_input_size );
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
    if( p_block == NULL )
        return NULL;

    p_block->i_nb_samples = p_sys->dts.i_frame_length;
    p_block->i_pts = p_block->i_dts = date_Get( &p_sys->end_date );
    p_block->i_length =
        date_Increment( &p_sys->end_date, p_block->i_nb_samples ) - p_block->i_pts;
    return p_block;
}

static block_t *PacketizeBlock( decoder_t *p_dec, block_t **pp_block )
{
    decoder_sys_t *p_sys = p_dec->p_sys;
    uint8_t p_header[VLC_DTS_HEADER_SIZE];
    block_t *p_out_buffer;

130
    block_t *p_block = pp_block ? *pp_block : NULL;
131

132
    if( p_block )
133
    {
134
135
136
137
138
139
        if ( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) {
            /* First always drain complete blocks before discontinuity */
            block_t *p_drain = PacketizeBlock( p_dec, NULL );
            if(p_drain)
                return p_drain;

140
            PacketizeFlush( p_dec );
141
142
143
144
145

            if ( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) {
                block_Release( p_block );
                return NULL;
            }
146
        }
147

148
149
150
151
152
        if ( !date_Get( &p_sys->end_date ) && p_block->i_pts <= VLC_TS_INVALID ) {
            /* We've just started the stream, wait for the first PTS. */
            block_Release( p_block );
            return NULL;
        }
153

154
        block_BytestreamPush( &p_sys->bytestream, p_block );
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
    }

    while( 1 )
    {
        switch( p_sys->i_state )
        {
        case STATE_NOSYNC:
            while( block_PeekBytes( &p_sys->bytestream, p_header, 6 )
                   == VLC_SUCCESS )
            {
                if( vlc_dts_header_IsSync( p_header, 6 ) )
                {
                    p_sys->i_state = STATE_SYNC;
                    break;
                }
                block_SkipByte( &p_sys->bytestream );
            }
            if( p_sys->i_state != STATE_SYNC )
            {
                block_BytestreamFlush( &p_sys->bytestream );

                /* Need more data */
                return NULL;
            }

        case STATE_SYNC:
            /* New frame, set the Presentation Time Stamp */
            p_sys->i_pts = p_sys->bytestream.p_block->i_pts;
            if( p_sys->i_pts > VLC_TS_INVALID &&
                p_sys->i_pts != date_Get( &p_sys->end_date ) )
            {
                date_Set( &p_sys->end_date, p_sys->i_pts );
            }
            p_sys->i_state = STATE_HEADER;

        case STATE_HEADER:
            /* Get DTS frame header (VLC_DTS_HEADER_SIZE bytes) */
            if( block_PeekBytes( &p_sys->bytestream, p_header,
                                 VLC_DTS_HEADER_SIZE ) != VLC_SUCCESS )
            {
                /* Need more data */
                return NULL;
            }

            /* Check if frame is valid and get frame info */
            if( vlc_dts_header_Parse( &p_sys->dts, p_header,
                                      VLC_DTS_HEADER_SIZE ) != VLC_SUCCESS )
            {
                msg_Dbg( p_dec, "emulated sync word" );
                block_SkipByte( &p_sys->bytestream );
                p_sys->i_state = STATE_NOSYNC;
                break;
            }

209
210
211
212
213
214
215
216
217
218
            if( p_sys->dts.b_substream  )
            {
                msg_Warn( p_dec, "substream without the paired core stream, "
                          "skip it" );
                p_sys->i_state = STATE_NOSYNC;
                if( block_SkipBytes( &p_sys->bytestream,
                                     p_sys->dts.i_frame_size ) != VLC_SUCCESS )
                    return NULL;
                break;
            }
219
220
            /* Even frame size is likely incorrect FSIZE #18166.
             * Sync minus one byte, we can always sync 1 byte further */
221
            p_sys->i_input_size = p_sys->i_next_offset
222
223
                                = (p_sys->dts.i_frame_size % 2) ? p_sys->dts.i_frame_size - 1
                                                                : p_sys->dts.i_frame_size;
224
225
226
227
            p_sys->i_state = STATE_NEXT_SYNC;

        case STATE_NEXT_SYNC:
            /* Check if next expected frame contains the sync word */
228
            while( p_sys->i_state == STATE_NEXT_SYNC )
229
            {
230
                if( block_PeekOffsetBytes( &p_sys->bytestream,
231
232
                                           p_sys->i_next_offset, p_header,
                                           VLC_DTS_HEADER_SIZE )
233
                                           != VLC_SUCCESS )
234
                {
235
236
237
238
239
240
241
                    if( p_block == NULL ) /* drain */
                    {
                        p_sys->i_state = STATE_GET_DATA;
                        break;
                    }
                    /* Need more data */
                    return NULL;
242
                }
243

244
245
246
247
248
249
                if( p_header[0] == 0 )
                {
                    /* DTS wav files, audio CD's and some mkvs use stuffing */
                    p_sys->i_next_offset++;
                    continue;
                }
250

251
                if( !vlc_dts_header_IsSync( p_header, VLC_DTS_HEADER_SIZE ) )
252
253
254
255
256
257
258
                {
                    msg_Dbg( p_dec, "emulated sync word "
                             "(no sync on following frame)" );
                    p_sys->i_state = STATE_NOSYNC;
                    block_SkipByte( &p_sys->bytestream );
                    break;
                }
259
260
261
262
263
264
265
266
267
268
269
270
271

                /* Check if a DTS substream packet is located just after
                 * the core packet */
                if( p_sys->i_next_offset == p_sys->dts.i_frame_size )
                {
                    vlc_dts_header_t next_header;
                    if( vlc_dts_header_Parse( &next_header, p_header,
                                              VLC_DTS_HEADER_SIZE )
                        == VLC_SUCCESS && next_header.b_substream )
                    {
                        p_sys->i_input_size += next_header.i_frame_size;
                    }
                }
272
                p_sys->i_state = STATE_GET_DATA;
273
274
275
276
            }
            break;

        case STATE_GET_DATA:
277
            /* Make sure we have enough data. */
278
            if( block_WaitBytes( &p_sys->bytestream,
279
                                 p_sys->i_input_size ) != VLC_SUCCESS )
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
            {
                /* Need more data */
                return NULL;
            }
            p_sys->i_state = STATE_SEND_DATA;

        case STATE_SEND_DATA:
            if( !(p_out_buffer = GetOutBuffer( p_dec )) )
            {
                return NULL;
            }

            /* Copy the whole frame into the buffer. When we reach this point
             * we already know we have enough data available. */
            block_GetBytes( &p_sys->bytestream, p_out_buffer->p_buffer,
295
                            p_out_buffer->i_buffer );
296
297
298
299
300

            /* Make sure we don't reuse the same pts twice */
            if( p_sys->i_pts == p_sys->bytestream.p_block->i_pts )
                p_sys->i_pts = p_sys->bytestream.p_block->i_pts = VLC_TS_INVALID;

301
302
303
304
305
306
            if( p_sys->b_discontinuity )
            {
                p_sys->b_discontinuity = false;
                p_out_buffer->i_flags |= BLOCK_FLAG_DISCONTINUITY;
            }

307
            /* So p_block doesn't get re-added several times */
308
309
            if( pp_block )
                *pp_block = block_BytestreamPop( &p_sys->bytestream );
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349

            p_sys->i_state = STATE_NOSYNC;

            return p_out_buffer;
        }
    }
}

static void Close( vlc_object_t *p_this )
{
    decoder_t *p_dec = (decoder_t*)p_this;
    decoder_sys_t *p_sys = p_dec->p_sys;

    block_BytestreamRelease( &p_sys->bytestream );

    free( p_sys );
}

static int Open( vlc_object_t *p_this )
{
    decoder_t *p_dec = (decoder_t*)p_this;
    decoder_sys_t *p_sys;

    switch( p_dec->fmt_in.i_codec )
    {
        case VLC_CODEC_DTS:
            break;
        default:
            return VLC_EGENERIC;
    }

    /* Allocate the memory needed to store the decoder's structure */
    if( ( p_dec->p_sys = p_sys = malloc(sizeof(decoder_sys_t)) ) == NULL )
        return VLC_ENOMEM;

    /* Misc init */
    p_sys->i_state = STATE_NOSYNC;
    date_Set( &p_sys->end_date, 0 );
    p_sys->i_pts = VLC_TS_INVALID;
    p_sys->b_date_set = false;
350
    p_sys->b_discontinuity = false;
351
    memset(&p_sys->dts, 0, sizeof(vlc_dts_header_t));
352
353
    block_BytestreamInit( &p_sys->bytestream );

354
    /* Set output properties (passthrough only) */
355
356
357
358
359
360
361
362
363
    p_dec->fmt_out.i_cat = AUDIO_ES;
    p_dec->fmt_out.i_codec = p_dec->fmt_in.i_codec;
    p_dec->fmt_out.audio = p_dec->fmt_in.audio;

    /* Set callback */
    p_dec->pf_packetize = PacketizeBlock;
    p_dec->pf_flush     = PacketizeFlush;
    return VLC_SUCCESS;
}