a52.c 13.5 KB
Newer Older
1
/*****************************************************************************
gbazin's avatar
 
gbazin committed
2
 * a52.c: parse A/52 audio sync info and packetize the stream
3
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2001-2002 VLC authors and VideoLAN
gbazin's avatar
gbazin committed
5
 * $Id$
6
 *
7
 * Authors: Stéphane Borel <stef@via.ecp.fr>
8
 *          Christophe Massiot <massiot@via.ecp.fr>
gbazin's avatar
gbazin committed
9
 *          Gildas Bazin <gbazin@videolan.org>
10
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
11 12 13
 * 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
14
 * (at your option) any later version.
15
 *
16 17
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
18 19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
20
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
21 22 23
 * 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.
24 25 26 27 28
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
29 30 31 32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33
#include <vlc_common.h>
34
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
35 36
#include <vlc_codec.h>
#include <vlc_block_helper.h>
37
#include <vlc_modules.h>
38 39

#include "a52.h"
gbazin's avatar
 
gbazin committed
40

41 42
#include "../packetizer/packetizer_helper.h"

43 44 45 46 47 48 49
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int  OpenDecoder   ( vlc_object_t * );
static int  OpenPacketizer( vlc_object_t * );
static void CloseCommon   ( vlc_object_t * );

50 51 52 53 54 55
vlc_module_begin ()
    set_description( N_("A/52 parser") )
    set_capability( "decoder", 100 )
    set_callbacks( OpenDecoder, CloseCommon )
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_ACODEC )
56

57 58 59 60 61
    add_submodule ()
    set_description( N_("A/52 audio packetizer") )
    set_capability( "packetizer", 10 )
    set_callbacks( OpenPacketizer, CloseCommon )
vlc_module_end ()
gbazin's avatar
 
gbazin committed
62

Christophe Massiot's avatar
Christophe Massiot committed
63
/*****************************************************************************
gbazin's avatar
 
gbazin committed
64
 * decoder_sys_t : decoder descriptor
Christophe Massiot's avatar
Christophe Massiot committed
65
 *****************************************************************************/
66

gbazin's avatar
 
gbazin committed
67
struct decoder_sys_t
Christophe Massiot's avatar
Christophe Massiot committed
68
{
gbazin's avatar
 
gbazin committed
69
    /* Module mode */
70
    bool b_packetizer;
gbazin's avatar
 
gbazin committed
71

Christophe Massiot's avatar
Christophe Massiot committed
72
    /*
gbazin's avatar
 
gbazin committed
73
     * Input properties
Christophe Massiot's avatar
Christophe Massiot committed
74
     */
gbazin's avatar
 
gbazin committed
75
    int i_state;
gbazin's avatar
 
gbazin committed
76

gbazin's avatar
 
gbazin committed
77
    block_bytestream_t bytestream;
Christophe Massiot's avatar
Christophe Massiot committed
78 79

    /*
gbazin's avatar
 
gbazin committed
80
     * Common properties
Christophe Massiot's avatar
Christophe Massiot committed
81
     */
82
    date_t  end_date;
gbazin's avatar
 
gbazin committed
83

gbazin's avatar
 
gbazin committed
84
    mtime_t i_pts;
85 86

    vlc_a52_header_t frame;
gbazin's avatar
 
gbazin committed
87 88
};

89
/****************************************************************************
90
 * Local prototypes
91
 ****************************************************************************/
92
static block_t *DecodeBlock  ( decoder_t *, block_t ** );
93

94 95 96
static uint8_t *GetOutBuffer ( decoder_t *, block_t ** );
static block_t *GetAoutBuffer( decoder_t * );
static block_t *GetSoutBuffer( decoder_t * );
97 98

/*****************************************************************************
99
 * OpenCommon: probe the decoder/packetizer and return score
100
 *****************************************************************************/
101
static int OpenCommon( vlc_object_t *p_this, bool b_packetizer )
102
{
gbazin's avatar
 
gbazin committed
103
    decoder_t *p_dec = (decoder_t*)p_this;
gbazin's avatar
 
gbazin committed
104
    decoder_sys_t *p_sys;
105
    vlc_fourcc_t i_codec;
gbazin's avatar
 
gbazin committed
106

107
    switch( p_dec->fmt_in.i_codec )
gbazin's avatar
 
gbazin committed
108
    {
109 110
    case VLC_CODEC_A52:
        i_codec = VLC_CODEC_A52;
111
        break;
112
    case VLC_CODEC_EAC3:
113 114 115 116
        /* XXX ugly hack, a52 does not support eac3 so no eac3 pass-through
         * support */
        if( !b_packetizer )
            return VLC_EGENERIC;
117
        i_codec = VLC_CODEC_EAC3;
118 119
        break;
    default:
gbazin's avatar
 
gbazin committed
120 121
        return VLC_EGENERIC;
    }
122

gbazin's avatar
 
gbazin committed
123
    /* Allocate the memory needed to store the decoder's structure */
gbazin's avatar
 
gbazin committed
124
    if( ( p_dec->p_sys = p_sys =
gbazin's avatar
 
gbazin committed
125
          (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL )
126
        return VLC_ENOMEM;
gbazin's avatar
 
gbazin committed
127 128

    /* Misc init */
129
    p_sys->b_packetizer = b_packetizer;
gbazin's avatar
 
gbazin committed
130
    p_sys->i_state = STATE_NOSYNC;
131
    date_Set( &p_sys->end_date, 0 );
132
    p_sys->i_pts = VLC_TS_INVALID;
gbazin's avatar
 
gbazin committed
133

134
    block_BytestreamInit( &p_sys->bytestream );
gbazin's avatar
 
gbazin committed
135 136 137

    /* Set output properties */
    p_dec->fmt_out.i_cat = AUDIO_ES;
138
    p_dec->fmt_out.i_codec = i_codec;
gbazin's avatar
gbazin committed
139
    p_dec->fmt_out.audio.i_rate = 0; /* So end_date gets initialized */
140
    p_dec->fmt_out.audio.i_bytes_per_frame = 0;
gbazin's avatar
 
gbazin committed
141 142

    /* Set callback */
143
    if( b_packetizer )
144
        p_dec->pf_packetize    = DecodeBlock;
145
    else
146
        p_dec->pf_decode_audio = DecodeBlock;
gbazin's avatar
 
gbazin committed
147 148 149
    return VLC_SUCCESS;
}

150
static int OpenDecoder( vlc_object_t *p_this )
gbazin's avatar
 
gbazin committed
151
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
152
    /* HACK: Don't use this codec if we don't have an a52 audio filter */
153 154
    if( !module_exists( "a52tofloat32" ) )
        return VLC_EGENERIC;
155 156
    return OpenCommon( p_this, false );
}
gbazin's avatar
 
gbazin committed
157

158 159 160
static int OpenPacketizer( vlc_object_t *p_this )
{
    return OpenCommon( p_this, true );
gbazin's avatar
 
gbazin committed
161 162
}

163
/****************************************************************************
gbazin's avatar
 
gbazin committed
164
 * DecodeBlock: the whole thing
165 166 167
 ****************************************************************************
 * This function is called just after the thread is launched.
 ****************************************************************************/
168
static block_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
169
{
gbazin's avatar
 
gbazin committed
170
    decoder_sys_t *p_sys = p_dec->p_sys;
171
    uint8_t p_header[VLC_A52_HEADER_SIZE];
gbazin's avatar
 
gbazin committed
172
    uint8_t *p_buf;
173
    block_t *p_out_buffer;
gbazin's avatar
 
gbazin committed
174

gbazin's avatar
 
gbazin committed
175 176
    if( !pp_block || !*pp_block ) return NULL;

177
    if( (*pp_block)->i_flags&(BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) )
gbazin's avatar
 
gbazin committed
178
    {
179
        if( (*pp_block)->i_flags&BLOCK_FLAG_CORRUPTED )
180 181
        {
            p_sys->i_state = STATE_NOSYNC;
182
            block_BytestreamEmpty( &p_sys->bytestream );
183
        }
184 185 186 187
        date_Set( &p_sys->end_date, 0 );
        block_Release( *pp_block );
        return NULL;
    }
gbazin's avatar
 
gbazin committed
188

189
    if( !date_Get( &p_sys->end_date ) && (*pp_block)->i_pts <= VLC_TS_INVALID)
gbazin's avatar
 
gbazin committed
190
    {
191 192 193
        /* We've just started the stream, wait for the first PTS. */
        block_Release( *pp_block );
        return NULL;
gbazin's avatar
 
gbazin committed
194 195
    }

gbazin's avatar
 
gbazin committed
196
    block_BytestreamPush( &p_sys->bytestream, *pp_block );
197

gbazin's avatar
 
gbazin committed
198
    while( 1 )
199
    {
gbazin's avatar
 
gbazin committed
200 201 202
        switch( p_sys->i_state )
        {
        case STATE_NOSYNC:
gbazin's avatar
 
gbazin committed
203 204
            while( block_PeekBytes( &p_sys->bytestream, p_header, 2 )
                   == VLC_SUCCESS )
gbazin's avatar
 
gbazin committed
205
            {
gbazin's avatar
 
gbazin committed
206 207 208 209 210 211
                if( p_header[0] == 0x0b && p_header[1] == 0x77 )
                {
                    p_sys->i_state = STATE_SYNC;
                    break;
                }
                block_SkipByte( &p_sys->bytestream );
gbazin's avatar
 
gbazin committed
212
            }
gbazin's avatar
 
gbazin committed
213
            if( p_sys->i_state != STATE_SYNC )
gbazin's avatar
 
gbazin committed
214
            {
215
                block_BytestreamFlush( &p_sys->bytestream );
216

gbazin's avatar
 
gbazin committed
217
                /* Need more data */
gbazin's avatar
 
gbazin committed
218
                return NULL;
gbazin's avatar
 
gbazin committed
219
            }
gbazin's avatar
 
gbazin committed
220

gbazin's avatar
 
gbazin committed
221 222
        case STATE_SYNC:
            /* New frame, set the Presentation Time Stamp */
gbazin's avatar
 
gbazin committed
223
            p_sys->i_pts = p_sys->bytestream.p_block->i_pts;
224
            if( p_sys->i_pts > VLC_TS_INVALID &&
225
                p_sys->i_pts != date_Get( &p_sys->end_date ) )
gbazin's avatar
 
gbazin committed
226
            {
227
                date_Set( &p_sys->end_date, p_sys->i_pts );
gbazin's avatar
 
gbazin committed
228 229 230 231
            }
            p_sys->i_state = STATE_HEADER;

        case STATE_HEADER:
232
            /* Get A/52 frame header (VLC_A52_HEADER_SIZE bytes) */
gbazin's avatar
 
gbazin committed
233
            if( block_PeekBytes( &p_sys->bytestream, p_header,
234
                                 VLC_A52_HEADER_SIZE ) != VLC_SUCCESS )
gbazin's avatar
 
gbazin committed
235
            {
gbazin's avatar
 
gbazin committed
236
                /* Need more data */
gbazin's avatar
 
gbazin committed
237
                return NULL;
gbazin's avatar
 
gbazin committed
238 239
            }

gbazin's avatar
 
gbazin committed
240
            /* Check if frame is valid and get frame info */
241
            if( vlc_a52_header_Parse( &p_sys->frame, p_header, VLC_A52_HEADER_SIZE ) )
gbazin's avatar
 
gbazin committed
242
            {
gbazin's avatar
 
gbazin committed
243 244
                msg_Dbg( p_dec, "emulated sync word" );
                block_SkipByte( &p_sys->bytestream );
gbazin's avatar
 
gbazin committed
245 246 247
                p_sys->i_state = STATE_NOSYNC;
                break;
            }
248

gbazin's avatar
 
gbazin committed
249
            p_sys->i_state = STATE_NEXT_SYNC;
gbazin's avatar
 
gbazin committed
250

gbazin's avatar
 
gbazin committed
251 252
        case STATE_NEXT_SYNC:
            /* TODO: If pp_block == NULL, flush the buffer without checking the
gbazin's avatar
 
gbazin committed
253 254
             * next sync word */

gbazin's avatar
 
gbazin committed
255 256
            /* Check if next expected frame contains the sync word */
            if( block_PeekOffsetBytes( &p_sys->bytestream,
257
                                       p_sys->frame.i_size, p_header, 2 )
gbazin's avatar
 
gbazin committed
258 259 260 261
                != VLC_SUCCESS )
            {
                /* Need more data */
                return NULL;
gbazin's avatar
 
gbazin committed
262
            }
gbazin's avatar
 
gbazin committed
263

gbazin's avatar
 
gbazin committed
264 265 266 267 268 269 270 271
            if( p_sys->b_packetizer &&
                p_header[0] == 0 && p_header[1] == 0 )
            {
                /* A52 wav files and audio CD's use stuffing */
                p_sys->i_state = STATE_GET_DATA;
                break;
            }

gbazin's avatar
 
gbazin committed
272
            if( p_header[0] != 0x0b || p_header[1] != 0x77 )
gbazin's avatar
 
gbazin committed
273
            {
gbazin's avatar
 
gbazin committed
274 275 276 277 278
                msg_Dbg( p_dec, "emulated sync word "
                         "(no sync on following frame)" );
                p_sys->i_state = STATE_NOSYNC;
                block_SkipByte( &p_sys->bytestream );
                break;
gbazin's avatar
 
gbazin committed
279
            }
gbazin's avatar
 
gbazin committed
280 281
            p_sys->i_state = STATE_SEND_DATA;
            break;
gbazin's avatar
 
gbazin committed
282

gbazin's avatar
 
gbazin committed
283 284 285 286
        case STATE_GET_DATA:
            /* Make sure we have enough data.
             * (Not useful if we went through NEXT_SYNC) */
            if( block_WaitBytes( &p_sys->bytestream,
287
                                 p_sys->frame.i_size ) != VLC_SUCCESS )
gbazin's avatar
 
gbazin committed
288 289
            {
                /* Need more data */
gbazin's avatar
 
gbazin committed
290
                return NULL;
gbazin's avatar
 
gbazin committed
291
            }
gbazin's avatar
 
gbazin committed
292
            p_sys->i_state = STATE_SEND_DATA;
gbazin's avatar
 
gbazin committed
293

gbazin's avatar
 
gbazin committed
294 295 296
        case STATE_SEND_DATA:
            if( !(p_buf = GetOutBuffer( p_dec, &p_out_buffer )) )
            {
297
                //p_dec->b_error = true;
gbazin's avatar
 
gbazin committed
298 299
                return NULL;
            }
gbazin's avatar
 
gbazin committed
300

gbazin's avatar
 
gbazin committed
301 302
            /* Copy the whole frame into the buffer. When we reach this point
             * we already know we have enough data available. */
303 304
            block_GetBytes( &p_sys->bytestream,
                            p_buf, __MIN( p_sys->frame.i_size, p_out_buffer->i_buffer ) );
gbazin's avatar
 
gbazin committed
305 306

            /* Make sure we don't reuse the same pts twice */
gbazin's avatar
 
gbazin committed
307
            if( p_sys->i_pts == p_sys->bytestream.p_block->i_pts )
308
                p_sys->i_pts = p_sys->bytestream.p_block->i_pts = VLC_TS_INVALID;
309

gbazin's avatar
 
gbazin committed
310 311
            /* So p_block doesn't get re-added several times */
            *pp_block = block_BytestreamPop( &p_sys->bytestream );
312

gbazin's avatar
 
gbazin committed
313
            p_sys->i_state = STATE_NOSYNC;
gbazin's avatar
 
gbazin committed
314

gbazin's avatar
 
gbazin committed
315
            return p_out_buffer;
316
        }
gbazin's avatar
 
gbazin committed
317 318
    }
}
319

gbazin's avatar
 
gbazin committed
320
/*****************************************************************************
321
 * CloseCommon: clean up the decoder
gbazin's avatar
 
gbazin committed
322
 *****************************************************************************/
323
static void CloseCommon( vlc_object_t *p_this )
gbazin's avatar
 
gbazin committed
324
{
gbazin's avatar
 
gbazin committed
325
    decoder_t *p_dec = (decoder_t*)p_this;
gbazin's avatar
 
gbazin committed
326
    decoder_sys_t *p_sys = p_dec->p_sys;
327

gbazin's avatar
 
gbazin committed
328
    block_BytestreamRelease( &p_sys->bytestream );
329

gbazin's avatar
 
gbazin committed
330
    free( p_sys );
gbazin's avatar
 
gbazin committed
331
}
332

gbazin's avatar
 
gbazin committed
333
/*****************************************************************************
gbazin's avatar
 
gbazin committed
334
 * GetOutBuffer:
gbazin's avatar
 
gbazin committed
335
 *****************************************************************************/
336
static uint8_t *GetOutBuffer( decoder_t *p_dec, block_t **pp_out_buffer )
gbazin's avatar
 
gbazin committed
337 338
{
    decoder_sys_t *p_sys = p_dec->p_sys;
gbazin's avatar
 
gbazin committed
339
    uint8_t *p_buf;
gbazin's avatar
 
gbazin committed
340

341
    if( p_dec->fmt_out.audio.i_rate != p_sys->frame.i_rate )
gbazin's avatar
 
gbazin committed
342
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
343 344
        msg_Dbg( p_dec, "A/52 channels:%d samplerate:%d bitrate:%d",
                 p_sys->frame.i_channels, p_sys->frame.i_rate, p_sys->frame.i_bitrate );
gbazin's avatar
 
gbazin committed
345

346 347
        date_Init( &p_sys->end_date, p_sys->frame.i_rate, 1 );
        date_Set( &p_sys->end_date, p_sys->i_pts );
gbazin's avatar
 
gbazin committed
348 349
    }

350 351 352 353
    p_dec->fmt_out.audio.i_rate     = p_sys->frame.i_rate;
    p_dec->fmt_out.audio.i_channels = p_sys->frame.i_channels;
    if( p_dec->fmt_out.audio.i_bytes_per_frame < p_sys->frame.i_size )
        p_dec->fmt_out.audio.i_bytes_per_frame = p_sys->frame.i_size;
354
    p_dec->fmt_out.audio.i_frame_length = p_sys->frame.i_samples;
gbazin's avatar
 
gbazin committed
355

356
    p_dec->fmt_out.audio.i_original_channels = p_sys->frame.i_channels_conf;
gbazin's avatar
 
gbazin committed
357
    p_dec->fmt_out.audio.i_physical_channels =
358
        p_sys->frame.i_channels_conf & AOUT_CHAN_PHYSMASK;
gbazin's avatar
 
gbazin committed
359

360
    p_dec->fmt_out.i_bitrate = p_sys->frame.i_bitrate;
gbazin's avatar
 
gbazin committed
361

gbazin's avatar
 
gbazin committed
362
    if( p_sys->b_packetizer )
gbazin's avatar
 
gbazin committed
363
    {
gbazin's avatar
 
gbazin committed
364 365 366 367 368 369
        block_t *p_sout_buffer = GetSoutBuffer( p_dec );
        p_buf = p_sout_buffer ? p_sout_buffer->p_buffer : NULL;
        *pp_out_buffer = p_sout_buffer;
    }
    else
    {
370
        block_t *p_aout_buffer = GetAoutBuffer( p_dec );
gbazin's avatar
 
gbazin committed
371 372
        p_buf = p_aout_buffer ? p_aout_buffer->p_buffer : NULL;
        *pp_out_buffer = p_aout_buffer;
gbazin's avatar
 
gbazin committed
373 374
    }

gbazin's avatar
 
gbazin committed
375
    return p_buf;
gbazin's avatar
 
gbazin committed
376 377 378
}

/*****************************************************************************
gbazin's avatar
 
gbazin committed
379
 * GetAoutBuffer:
gbazin's avatar
 
gbazin committed
380
 *****************************************************************************/
381
static block_t *GetAoutBuffer( decoder_t *p_dec )
gbazin's avatar
 
gbazin committed
382 383 384
{
    decoder_sys_t *p_sys = p_dec->p_sys;

385
    block_t *p_buf = decoder_NewAudioBuffer( p_dec, p_sys->frame.i_samples );
Rafaël Carré's avatar
Rafaël Carré committed
386 387 388 389 390 391
    if( p_buf )
    {
        p_buf->i_pts = date_Get( &p_sys->end_date );
        p_buf->i_length = date_Increment( &p_sys->end_date,
                                          p_sys->frame.i_samples ) - p_buf->i_pts;
    }
gbazin's avatar
 
gbazin committed
392

gbazin's avatar
 
gbazin committed
393
    return p_buf;
394 395 396
}

/*****************************************************************************
gbazin's avatar
 
gbazin committed
397
 * GetSoutBuffer:
398
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
399
static block_t *GetSoutBuffer( decoder_t *p_dec )
400
{
gbazin's avatar
 
gbazin committed
401 402
    decoder_sys_t *p_sys = p_dec->p_sys;

403
    block_t *p_block = block_Alloc( p_sys->frame.i_size );
Rafaël Carré's avatar
Rafaël Carré committed
404 405 406 407 408 409
    if( p_block )
    {
        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_sys->frame.i_samples ) - p_block->i_pts;
    }
gbazin's avatar
 
gbazin committed
410 411

    return p_block;
412
}
413