[vlc-devel] [RFC PATCH 04/17] a52: split decoder and packetizer

Thomas Guillem thomas at gllm.fr
Mon Jul 25 17:23:53 CEST 2016


When the A52 packetizer find a valid fmt_out, the core will reload the decoder
module: the A52 decoder will be closed and opened again. The A52 decoder will
be able to know if an audio output can handle SPDIF in the open function: if
decoder_UpdateAudioFormat() doesn't fail. If decoder_UpdateAudioFormat() fails,
the A52 decoder will abort and an other decoder will be used to output PCM
(avcodec, and the future a52tofloat32 that will be moved to decoder).

The same thing can be done for DTS, or every other passthrough format.  Maybe
this can be done if the same module.
---
 extras/package/rpm/vlc.altlinux.spec |   3 +-
 modules/MODULES_LIST                 |   1 +
 modules/codec/Makefile.am            |   5 +-
 modules/codec/a52.c                  | 430 -----------------------------------
 modules/codec/a52.h                  | 216 ------------------
 modules/codec/avcodec/avcodec.c      |   2 +
 modules/codec/spdif.c                | 105 +++++++++
 modules/demux/mpeg/es.c              |   2 +-
 modules/packetizer/Makefile.am       |   3 +-
 modules/packetizer/a52.c             | 328 ++++++++++++++++++++++++++
 modules/packetizer/a52.h             | 216 ++++++++++++++++++
 po/POTFILES.in                       |   3 +-
 12 files changed, 660 insertions(+), 654 deletions(-)
 delete mode 100644 modules/codec/a52.c
 delete mode 100644 modules/codec/a52.h
 create mode 100644 modules/codec/spdif.c
 create mode 100644 modules/packetizer/a52.c
 create mode 100644 modules/packetizer/a52.h

diff --git a/extras/package/rpm/vlc.altlinux.spec b/extras/package/rpm/vlc.altlinux.spec
index 33511ad..021ca24 100644
--- a/extras/package/rpm/vlc.altlinux.spec
+++ b/extras/package/rpm/vlc.altlinux.spec
@@ -1033,7 +1033,6 @@ strfile %buildroot%_gamesdatadir/fortune/vlc %buildroot%_gamesdatadir/fortune/vl
 %_vlc_pluginsdir/audio_output/libaout_file_plugin.so*
 
 %dir %_vlc_pluginsdir/codec
-%_vlc_pluginsdir/codec/liba52_plugin.so*
 %_vlc_pluginsdir/codec/libadpcm_plugin.so*
 %_vlc_pluginsdir/codec/libaraw_plugin.so*
 %_vlc_pluginsdir/codec/librawvideo_plugin.so*
@@ -1135,7 +1134,7 @@ strfile %buildroot%_gamesdatadir/fortune/vlc %buildroot%_gamesdatadir/fortune/vl
 #%_vlc_pluginsdir/mux/libmux_ts_plugin.so*
 
 %dir %_vlc_pluginsdir/packetizer
-#_vlc_pluginsdir/packetizer/libpacketizer_a52_plugin.so*
+%_vlc_pluginsdir/packetizer/libpacketizer_a52_plugin.so*
 %_vlc_pluginsdir/packetizer/libpacketizer_copy_plugin.so*
 %_vlc_pluginsdir/packetizer/libpacketizer_mpeg4audio_plugin.so*
 %_vlc_pluginsdir/packetizer/libpacketizer_mpeg4video_plugin.so*
diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST
index 50fc92c..17ed8a1 100644
--- a/modules/MODULES_LIST
+++ b/modules/MODULES_LIST
@@ -354,6 +354,7 @@ $Id$
  * sndio: OpenBSD sndio audio output
  * soxr: SoX Resampler library audio filter
  * spatializer: A spatializer audio filter
+ * spdif: S/PDIF audio pass-throught decoder
  * speex: a speex audio decoder/packetizer using the libspeex library
  * speex_resampler: audio resampler using the libspeexdsp library
  * spudec: RLE DVD subtitles decoder
diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am
index a77476c..ef0aa74 100644
--- a/modules/codec/Makefile.am
+++ b/modules/codec/Makefile.am
@@ -13,9 +13,8 @@ endif
 
 
 ### Audio codecs ###
-
-liba52_plugin_la_SOURCES = codec/a52.c codec/a52.h
-codec_LTLIBRARIES += liba52_plugin.la
+libspdif_plugin_la_SOURCES = codec/spdif.c
+codec_LTLIBRARIES += libspdif_plugin.la
 
 libadpcm_plugin_la_SOURCES = codec/adpcm.c
 codec_LTLIBRARIES += libadpcm_plugin.la
diff --git a/modules/codec/a52.c b/modules/codec/a52.c
deleted file mode 100644
index 044905b..0000000
--- a/modules/codec/a52.c
+++ /dev/null
@@ -1,430 +0,0 @@
-/*****************************************************************************
- * a52.c: parse A/52 audio sync info and packetize the stream
- *****************************************************************************
- * Copyright (C) 2001-2002 VLC authors and VideoLAN
- * $Id$
- *
- * Authors: Stéphane Borel <stef at via.ecp.fr>
- *          Christophe Massiot <massiot at via.ecp.fr>
- *          Gildas Bazin <gbazin at videolan.org>
- *
- * 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 "a52.h"
-
-#include "../packetizer/packetizer_helper.h"
-
-/*****************************************************************************
- * Module descriptor
- *****************************************************************************/
-static int  OpenDecoder   ( vlc_object_t * );
-static int  OpenPacketizer( vlc_object_t * );
-static void CloseCommon   ( vlc_object_t * );
-
-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 )
-
-    add_submodule ()
-    set_description( N_("A/52 audio packetizer") )
-    set_capability( "packetizer", 10 )
-    set_callbacks( OpenPacketizer, CloseCommon )
-vlc_module_end ()
-
-/*****************************************************************************
- * decoder_sys_t : decoder descriptor
- *****************************************************************************/
-
-struct decoder_sys_t
-{
-    /* Module mode */
-    bool b_packetizer;
-
-    /*
-     * Input properties
-     */
-    int i_state;
-
-    block_bytestream_t bytestream;
-
-    /*
-     * Common properties
-     */
-    date_t  end_date;
-
-    mtime_t i_pts;
-
-    vlc_a52_header_t frame;
-};
-
-/****************************************************************************
- * Local prototypes
- ****************************************************************************/
-static block_t *DecodeBlock  ( decoder_t *, block_t ** );
-static void Flush( decoder_t * );
-
-static uint8_t *GetOutBuffer ( decoder_t *, block_t ** );
-static block_t *GetAoutBuffer( decoder_t * );
-static block_t *GetSoutBuffer( decoder_t * );
-
-/*****************************************************************************
- * OpenCommon: probe the decoder/packetizer and return score
- *****************************************************************************/
-static int OpenCommon( vlc_object_t *p_this, bool b_packetizer )
-{
-    decoder_t *p_dec = (decoder_t*)p_this;
-    decoder_sys_t *p_sys;
-    vlc_fourcc_t i_codec;
-
-    switch( p_dec->fmt_in.i_codec )
-    {
-    case VLC_CODEC_A52:
-        i_codec = VLC_CODEC_A52;
-        break;
-    case VLC_CODEC_EAC3:
-        /* XXX ugly hack, a52 does not support eac3 so no eac3 pass-through
-         * support */
-        if( !b_packetizer )
-            return VLC_EGENERIC;
-        i_codec = VLC_CODEC_EAC3;
-        break;
-    default:
-        return VLC_EGENERIC;
-    }
-
-    /* Allocate the memory needed to store the decoder's structure */
-    if( ( p_dec->p_sys = p_sys =
-          (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL )
-        return VLC_ENOMEM;
-
-    /* Misc init */
-    p_sys->b_packetizer = b_packetizer;
-    p_sys->i_state = STATE_NOSYNC;
-    date_Set( &p_sys->end_date, 0 );
-    p_sys->i_pts = VLC_TS_INVALID;
-
-    block_BytestreamInit( &p_sys->bytestream );
-
-    /* Set output properties */
-    p_dec->fmt_out.i_cat = AUDIO_ES;
-    p_dec->fmt_out.i_codec = i_codec;
-    p_dec->fmt_out.audio.i_rate = 0; /* So end_date gets initialized */
-    p_dec->fmt_out.audio.i_bytes_per_frame = 0;
-
-    /* Set callback */
-    if( b_packetizer )
-        p_dec->pf_packetize    = DecodeBlock;
-    else
-        p_dec->pf_decode_audio = DecodeBlock;
-    p_dec->pf_flush            = Flush;
-    return VLC_SUCCESS;
-}
-
-static int OpenDecoder( vlc_object_t *p_this )
-{
-    /* HACK: Don't use this codec if we don't have an a52 audio filter */
-    if( !module_exists( "a52tofloat32" ) )
-        return VLC_EGENERIC;
-    return OpenCommon( p_this, false );
-}
-
-static int OpenPacketizer( vlc_object_t *p_this )
-{
-    return OpenCommon( p_this, true );
-}
-
-/*****************************************************************************
- * Flush:
- *****************************************************************************/
-static void Flush( decoder_t *p_dec )
-{
-    decoder_sys_t *p_sys = p_dec->p_sys;
-
-    date_Set( &p_sys->end_date, 0 );
-    p_sys->i_state = STATE_NOSYNC;
-    block_BytestreamEmpty( &p_sys->bytestream );
-}
-
-/****************************************************************************
- * DecodeBlock: the whole thing
- ****************************************************************************
- * This function is called just after the thread is launched.
- ****************************************************************************/
-static block_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
-{
-    decoder_sys_t *p_sys = p_dec->p_sys;
-    uint8_t p_header[VLC_A52_HEADER_SIZE];
-    uint8_t *p_buf;
-    block_t *p_out_buffer;
-
-    if( !pp_block || !*pp_block ) return NULL;
-
-    if( (*pp_block)->i_flags & (BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED) )
-    {
-        if( (*pp_block)->i_flags & BLOCK_FLAG_CORRUPTED )
-        {
-            Flush( p_dec );
-            block_Release( *pp_block );
-            *pp_block = NULL;
-            return NULL;
-        }
-        else /* BLOCK_FLAG_DISCONTINUITY */
-            date_Set( &p_sys->end_date, 0 );
-
-    }
-
-    if( !date_Get( &p_sys->end_date ) && (*pp_block)->i_pts <= VLC_TS_INVALID)
-    {
-        /* We've just started the stream, wait for the first PTS. */
-        block_Release( *pp_block );
-        *pp_block = NULL;
-        return NULL;
-    }
-
-    block_BytestreamPush( &p_sys->bytestream, *pp_block );
-
-    while( 1 )
-    {
-        switch( p_sys->i_state )
-        {
-        case STATE_NOSYNC:
-            while( block_PeekBytes( &p_sys->bytestream, p_header, 2 )
-                   == VLC_SUCCESS )
-            {
-                if( p_header[0] == 0x0b && p_header[1] == 0x77 )
-                {
-                    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 A/52 frame header (VLC_A52_HEADER_SIZE bytes) */
-            if( block_PeekBytes( &p_sys->bytestream, p_header,
-                                 VLC_A52_HEADER_SIZE ) != VLC_SUCCESS )
-            {
-                /* Need more data */
-                return NULL;
-            }
-
-            /* Check if frame is valid and get frame info */
-            if( vlc_a52_header_Parse( &p_sys->frame, p_header, VLC_A52_HEADER_SIZE ) )
-            {
-                msg_Dbg( p_dec, "emulated sync word" );
-                block_SkipByte( &p_sys->bytestream );
-                p_sys->i_state = STATE_NOSYNC;
-                break;
-            }
-
-            p_sys->i_state = STATE_NEXT_SYNC;
-
-        case STATE_NEXT_SYNC:
-            /* TODO: If pp_block == NULL, flush the buffer without checking the
-             * next sync word */
-
-            /* Check if next expected frame contains the sync word */
-            if( block_PeekOffsetBytes( &p_sys->bytestream,
-                                       p_sys->frame.i_size, p_header, 2 )
-                != VLC_SUCCESS )
-            {
-                /* Need more data */
-                return NULL;
-            }
-
-            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;
-            }
-
-            if( p_header[0] != 0x0b || p_header[1] != 0x77 )
-            {
-                msg_Dbg( p_dec, "emulated sync word "
-                         "(no sync on following frame)" );
-                p_sys->i_state = STATE_NOSYNC;
-                block_SkipByte( &p_sys->bytestream );
-                break;
-            }
-            p_sys->i_state = STATE_SEND_DATA;
-            break;
-
-        case STATE_GET_DATA:
-            /* Make sure we have enough data.
-             * (Not useful if we went through NEXT_SYNC) */
-            if( block_WaitBytes( &p_sys->bytestream,
-                                 p_sys->frame.i_size ) != VLC_SUCCESS )
-            {
-                /* Need more data */
-                return NULL;
-            }
-            p_sys->i_state = STATE_SEND_DATA;
-
-        case STATE_SEND_DATA:
-            if( !(p_buf = GetOutBuffer( p_dec, &p_out_buffer )) )
-            {
-                //p_dec->b_error = true;
-                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_buf, __MIN( p_sys->frame.i_size, p_out_buffer->i_buffer ) );
-
-            /* 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;
-
-            /* So p_block doesn't get re-added several times */
-            *pp_block = block_BytestreamPop( &p_sys->bytestream );
-
-            p_sys->i_state = STATE_NOSYNC;
-
-            return p_out_buffer;
-        }
-    }
-}
-
-/*****************************************************************************
- * CloseCommon: clean up the decoder
- *****************************************************************************/
-static void CloseCommon( 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 );
-}
-
-/*****************************************************************************
- * GetOutBuffer:
- *****************************************************************************/
-static uint8_t *GetOutBuffer( decoder_t *p_dec, block_t **pp_out_buffer )
-{
-    decoder_sys_t *p_sys = p_dec->p_sys;
-    uint8_t *p_buf;
-
-    if( p_dec->fmt_out.audio.i_rate != p_sys->frame.i_rate )
-    {
-        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 );
-
-        date_Init( &p_sys->end_date, p_sys->frame.i_rate, 1 );
-        date_Set( &p_sys->end_date, p_sys->i_pts );
-    }
-
-    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;
-    p_dec->fmt_out.audio.i_frame_length = p_sys->frame.i_samples;
-
-    p_dec->fmt_out.audio.i_original_channels = p_sys->frame.i_channels_conf;
-    p_dec->fmt_out.audio.i_physical_channels =
-        p_sys->frame.i_channels_conf & AOUT_CHAN_PHYSMASK;
-
-    p_dec->fmt_out.i_bitrate = p_sys->frame.i_bitrate;
-
-    if( p_sys->b_packetizer )
-    {
-        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
-    {
-        block_t *p_aout_buffer = GetAoutBuffer( p_dec );
-        p_buf = p_aout_buffer ? p_aout_buffer->p_buffer : NULL;
-        *pp_out_buffer = p_aout_buffer;
-    }
-
-    return p_buf;
-}
-
-/*****************************************************************************
- * GetAoutBuffer:
- *****************************************************************************/
-static block_t *GetAoutBuffer( decoder_t *p_dec )
-{
-    decoder_sys_t *p_sys = p_dec->p_sys;
-
-    block_t *p_buf = decoder_NewAudioBuffer( p_dec, p_sys->frame.i_samples );
-    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;
-    }
-
-    return p_buf;
-}
-
-/*****************************************************************************
- * GetSoutBuffer:
- *****************************************************************************/
-static block_t *GetSoutBuffer( decoder_t *p_dec )
-{
-    decoder_sys_t *p_sys = p_dec->p_sys;
-
-    block_t *p_block = block_Alloc( p_sys->frame.i_size );
-    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;
-    }
-
-    return p_block;
-}
-
diff --git a/modules/codec/a52.h b/modules/codec/a52.h
deleted file mode 100644
index 3b7315c..0000000
--- a/modules/codec/a52.h
+++ /dev/null
@@ -1,216 +0,0 @@
-/*****************************************************************************
- * a52.h
- *****************************************************************************
- * Copyright (C) 2001-2009 Laurent Aimar
- * $Id$
- *
- * Authors: Stéphane Borel <stef at via.ecp.fr>
- *          Christophe Massiot <massiot at via.ecp.fr>
- *          Gildas Bazin <gbazin at videolan.org>
- *          Laurent Aimar <fenrir at via.ecp.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.
- *****************************************************************************/
-
-#ifndef VLC_A52_H_
-#define VLC_A52_H_
-
-#include <vlc_bits.h>
-
-/**
- * Minimum AC3 header size that vlc_a52_header_Parse needs.
- */
-#define VLC_A52_HEADER_SIZE (8)
-
-/**
- * AC3 header information.
- */
-typedef struct
-{
-    bool b_eac3;
-
-    unsigned int i_channels;
-    unsigned int i_channels_conf;
-    unsigned int i_rate;
-    unsigned int i_bitrate;
-
-    unsigned int i_size;
-    unsigned int i_samples;
-
-} vlc_a52_header_t;
-
-/**
- * It parse AC3 sync info.
- *
- * This code is borrowed from liba52 by Aaron Holtzman & Michel Lespinasse,
- * since we don't want to oblige S/PDIF people to use liba52 just to get
- * their SyncInfo...
- */
-static inline int vlc_a52_header_ParseAc3( vlc_a52_header_t *p_header,
-                                           const uint8_t *p_buf,
-                                           const uint32_t *p_acmod )
-{
-    static const uint8_t pi_halfrate[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 };
-    static const unsigned int pi_bitrate[] = { 32,  40,  48,  56,  64,  80,  96, 112,
-                                128, 160, 192, 224, 256, 320, 384, 448,
-                                512, 576, 640 };
-    static const uint8_t pi_lfeon[8] = { 0x10, 0x10, 0x04, 0x04,
-                                      0x04, 0x01, 0x04, 0x01 };
-
-    /* */
-    const unsigned i_rate_shift = pi_halfrate[p_buf[5] >> 3];
-
-    /* acmod, dsurmod and lfeon */
-    const unsigned i_acmod = p_buf[6] >> 5;
-    if( (p_buf[6] & 0xf8) == 0x50 )
-        /* Dolby surround = stereo + Dolby */
-        p_header->i_channels_conf = AOUT_CHANS_STEREO | AOUT_CHAN_DOLBYSTEREO;
-    else
-        p_header->i_channels_conf = p_acmod[i_acmod];
-    if( p_buf[6] & pi_lfeon[i_acmod] )
-        p_header->i_channels_conf |= AOUT_CHAN_LFE;
-
-    p_header->i_channels = popcount(p_header->i_channels_conf
-                                                         & AOUT_CHAN_PHYSMASK);
-
-    const unsigned i_frmsizecod = p_buf[4] & 63;
-    if( i_frmsizecod >= 38 )
-        return VLC_EGENERIC;
-    const unsigned i_bitrate_base = pi_bitrate[i_frmsizecod >> 1];
-    p_header->i_bitrate = (i_bitrate_base * 1000) >> i_rate_shift;
-
-    switch( p_buf[4] & 0xc0 )
-    {
-    case 0:
-        p_header->i_rate = 48000 >> i_rate_shift;
-        p_header->i_size = 4 * i_bitrate_base;
-        break;
-    case 0x40:
-        p_header->i_rate = 44100 >> i_rate_shift;
-        p_header->i_size = 2 * (320 * i_bitrate_base / 147 + (i_frmsizecod & 1));
-        break;
-    case 0x80:
-        p_header->i_rate = 32000 >> i_rate_shift;
-        p_header->i_size = 6 * i_bitrate_base;
-        break;
-    default:
-        return VLC_EGENERIC;
-    }
-    p_header->i_samples = 6*256;
-
-    p_header->b_eac3 = false;
-    return VLC_SUCCESS;
-}
-
-/**
- * It parse E-AC3 sync info
- */
-static inline int vlc_a52_header_ParseEac3( vlc_a52_header_t *p_header,
-                                            const uint8_t *p_buf,
-                                            const uint32_t *p_acmod )
-{
-    static const unsigned pi_samplerate[3] = { 48000, 44100, 32000 };
-    unsigned i_numblkscod;
-    bs_t s;
-
-
-    bs_init( &s, (void*)p_buf, VLC_A52_HEADER_SIZE );
-    bs_skip( &s, 16 +   /* start code */
-                 2 +    /* stream type */
-                 3 );   /* substream id */
-    const unsigned i_frame_size = bs_read( &s, 11 );
-    if( i_frame_size < 2 )
-        return VLC_EGENERIC;
-    p_header->i_size = 2 * ( i_frame_size + 1 );
-
-    const unsigned i_fscod = bs_read( &s, 2 );
-    if( i_fscod == 0x03 )
-    {
-        const unsigned i_fscod2 = bs_read( &s, 2 );
-        if( i_fscod2 == 0X03 )
-            return VLC_EGENERIC;
-        p_header->i_rate = pi_samplerate[i_fscod2] / 2;
-        i_numblkscod = 6;
-    }
-    else
-    {
-        static const int pi_blocks[4] = { 1, 2, 3, 6 };
-
-        p_header->i_rate = pi_samplerate[i_fscod];
-        i_numblkscod = pi_blocks[bs_read( &s, 2 )];
-    }
-
-    const unsigned i_acmod = bs_read( &s, 3 );
-    const unsigned i_lfeon = bs_read1( &s );
-
-    p_header->i_channels_conf = p_acmod[i_acmod];
-    if( i_lfeon )
-        p_header->i_channels_conf |= AOUT_CHAN_LFE;
-    p_header->i_channels = popcount(p_header->i_channels_conf
-                                                         & AOUT_CHAN_PHYSMASK);
-    p_header->i_bitrate = 8 * p_header->i_size * (p_header->i_rate)
-                                               / (i_numblkscod * 256);
-    p_header->i_samples = i_numblkscod * 256;
-
-    p_header->b_eac3 = true;
-    return VLC_SUCCESS;
-}
-
-/**
- * It will parse the header AC3 frame and fill vlc_a52_header_t* if
- * it is valid or return VLC_EGENERIC.
- *
- * XXX It will only recognize big endian bitstream ie starting with 0x0b, 0x77
- */
-static inline int vlc_a52_header_Parse( vlc_a52_header_t *p_header,
-                                        const uint8_t *p_buffer, int i_buffer )
-{
-    static const uint32_t p_acmod[8] = {
-        AOUT_CHANS_2_0 | AOUT_CHAN_DUALMONO,
-        AOUT_CHAN_CENTER,
-        AOUT_CHANS_2_0,
-        AOUT_CHANS_3_0,
-        AOUT_CHANS_FRONT | AOUT_CHAN_REARCENTER, /* 2F1R */
-        AOUT_CHANS_FRONT | AOUT_CHANS_CENTER,    /* 3F1R */
-        AOUT_CHANS_4_0,
-        AOUT_CHANS_5_0,
-    };
-
-    if( i_buffer < VLC_A52_HEADER_SIZE )
-        return VLC_EGENERIC;
-
-    /* Check synword */
-    if( p_buffer[0] != 0x0b || p_buffer[1] != 0x77 )
-        return VLC_EGENERIC;
-
-    /* Check bsid */
-    const int bsid = p_buffer[5] >> 3;
-    if( bsid > 16 )
-        return VLC_EGENERIC;
-
-    if( bsid <= 10 )
-    {
-        if( vlc_a52_header_ParseAc3( p_header, p_buffer, p_acmod ) )
-            return VLC_EGENERIC;
-    }
-    else
-    {
-        if( vlc_a52_header_ParseEac3( p_header, p_buffer, p_acmod ) )
-            return VLC_EGENERIC;
-    }
-    return VLC_SUCCESS;
-}
-
-#endif
diff --git a/modules/codec/avcodec/avcodec.c b/modules/codec/avcodec/avcodec.c
index 5402f69..fae2db1 100644
--- a/modules/codec/avcodec/avcodec.c
+++ b/modules/codec/avcodec/avcodec.c
@@ -256,6 +256,7 @@ static int OpenDecoder( vlc_object_t *p_this )
 
     const AVCodec  *p_codec = NULL;
 
+fprintf(stderr, "avcodec?\n");
     /* *** determine codec type *** */
     if( !GetFfmpegCodec( p_dec->fmt_in.i_codec, &i_cat, &i_codec_id,
                              &psz_namecodec )
@@ -326,6 +327,7 @@ static int OpenDecoder( vlc_object_t *p_this )
     if( avctx->level != FF_LEVEL_UNKNOWN)
         p_dec->fmt_in.i_level = avctx->level;
 
+fprintf(stderr, "avcodec!\n");
     return VLC_SUCCESS;
 }
 
diff --git a/modules/codec/spdif.c b/modules/codec/spdif.c
new file mode 100644
index 0000000..71f0a58
--- /dev/null
+++ b/modules/codec/spdif.c
@@ -0,0 +1,105 @@
+/*****************************************************************************
+ * spdif.c: pass-thought decoder
+ *****************************************************************************
+ * Copyright (C) 2016 VLC authors and VideoLAN
+ *
+ * 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.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_codec.h>
+#include <vlc_modules.h>
+
+static int  OpenDecoder( vlc_object_t * );
+
+vlc_module_begin ()
+    set_category( CAT_INPUT )
+    set_subcategory( SUBCAT_INPUT_ACODEC )
+    set_description( N_("S/PDIF pass-throught decoder") )
+    set_capability( "decoder", 100 )
+    set_callbacks( OpenDecoder, NULL )
+vlc_module_end ()
+
+static block_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
+{
+    (void) p_dec;
+    if( pp_block != NULL )
+    {
+        block_t *p_block = *pp_block;
+        *pp_block = NULL;
+        return p_block;
+    }
+    else
+        return NULL;
+}
+
+static int OpenDecoder( vlc_object_t *p_this )
+{
+    decoder_t *p_dec = (decoder_t*)p_this;
+
+    switch( p_dec->fmt_in.i_codec )
+    {
+    case VLC_CODEC_A52:
+    case VLC_CODEC_EAC3:
+        /* Formats accepted if forced (--codec=spdif) OR spdif (--spdif) is
+         * enabled */
+        if( !p_dec->obj.force && !var_InheritBool( p_dec, "spdif" ) )
+            return VLC_EGENERIC;
+        break;
+    default:
+        return VLC_EGENERIC;
+    }
+
+    /* Set output properties */
+    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;
+    p_dec->fmt_out.audio.i_format = p_dec->fmt_out.i_codec;
+
+fprintf(stderr,
+"i_format: %4.4s, i_rate: %u, i_physical_channels: %u, i_original_channels: %u, "
+"i_bytes_per_frame: %u, i_frame_length: %u, i_bitspersample: %u, i_blockalign: %u, i_channels: %u\n",
+(const char *)&p_dec->fmt_out.audio.i_format,
+p_dec->fmt_out.audio.i_rate,
+p_dec->fmt_out.audio.i_physical_channels,
+p_dec->fmt_out.audio.i_original_channels,
+p_dec->fmt_out.audio.i_bytes_per_frame,
+p_dec->fmt_out.audio.i_frame_length,
+p_dec->fmt_out.audio.i_bitspersample,
+p_dec->fmt_out.audio.i_blockalign,
+p_dec->fmt_out.audio.i_channels);
+
+    if( p_dec->fmt_out.audio.i_physical_channels == 0
+     || p_dec->fmt_out.audio.i_original_channels == 0
+     || p_dec->fmt_out.audio.i_bytes_per_frame == 0
+     || p_dec->fmt_out.audio.i_frame_length == 0
+     || decoder_UpdateAudioFormat( p_dec ) )
+    {
+        fprintf(stderr, "A52: decoder_UpdateAudioFormat failed\n");
+        es_format_Init( &p_dec->fmt_out, UNKNOWN_ES, 0 );
+        return VLC_EGENERIC;
+    }
+
+    p_dec->pf_decode_audio = DecodeBlock;
+    p_dec->pf_flush        = NULL;
+
+    fprintf(stderr, "A52: OpenDecoder!\n");
+    return VLC_SUCCESS;
+}
diff --git a/modules/demux/mpeg/es.c b/modules/demux/mpeg/es.c
index ba80bb7..69785be 100644
--- a/modules/demux/mpeg/es.c
+++ b/modules/demux/mpeg/es.c
@@ -37,7 +37,7 @@
 #include <vlc_codecs.h>
 #include <vlc_input.h>
 
-#include "../../codec/a52.h"
+#include "../../packetizer/a52.h"
 #include "../../codec/dts_header.h"
 
 /*****************************************************************************
diff --git a/modules/packetizer/Makefile.am b/modules/packetizer/Makefile.am
index c0b2ca8..fced538 100644
--- a/modules/packetizer/Makefile.am
+++ b/modules/packetizer/Makefile.am
@@ -16,6 +16,7 @@ libpacketizer_hevc_plugin_la_SOURCES = packetizer/hevc.c \
         packetizer/hevc_nal.h packetizer/hevc_nal.c \
         packetizer/hxxx_nal.h \
         packetizer/hxxx_common.c packetizer/hxxx_common.h
+libpacketizer_a52_plugin_la_SOURCES = packetizer/a52.c packetizer/a52.h
 
 libpacketizer_avparser_plugin_la_SOURCES = packetizer/avparser.c \
 	packetizer/avparser.h \
@@ -25,7 +26,6 @@ libpacketizer_avparser_plugin_la_SOURCES = packetizer/avparser.c \
 libpacketizer_avparser_plugin_la_CFLAGS = $(AVCODEC_CFLAGS) $(AVUTIL_CFLAGS) $(AM_CFLAGS)
 libpacketizer_avparser_plugin_la_LIBADD = $(AVCODEC_LIBS) $(AVUTIL_LIBS) $(LIBM)
 
-
 noinst_HEADERS += packetizer/packetizer_helper.h packetizer/startcode_helper.h
 
 packetizer_LTLIBRARIES = \
@@ -39,6 +39,7 @@ packetizer_LTLIBRARIES = \
 	libpacketizer_flac_plugin.la \
 	libpacketizer_hevc_plugin.la \
 	libpacketizer_copy_plugin.la \
+	libpacketizer_a52_plugin.la
 	$(NULL)
 
 if HAVE_AVCODEC
diff --git a/modules/packetizer/a52.c b/modules/packetizer/a52.c
new file mode 100644
index 0000000..660decf
--- /dev/null
+++ b/modules/packetizer/a52.c
@@ -0,0 +1,328 @@
+/*****************************************************************************
+ * a52.c: parse A/52 audio sync info and packetize the stream
+ *****************************************************************************
+ * Copyright (C) 2001-2016 VLC authors and VideoLAN
+ * $Id$
+ *
+ * Authors: Stéphane Borel <stef at via.ecp.fr>
+ *          Christophe Massiot <massiot at via.ecp.fr>
+ *          Gildas Bazin <gbazin at videolan.org>
+ *
+ * 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 "a52.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_("A/52 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;
+
+    /*
+     * Common properties
+     */
+    date_t  end_date;
+    bool    b_date_set;
+
+    mtime_t i_pts;
+
+    vlc_a52_header_t frame;
+};
+
+static void Flush( decoder_t *p_dec )
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    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->frame.i_rate )
+    {
+        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 );
+
+        date_Init( &p_sys->end_date, p_sys->frame.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->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;
+    p_dec->fmt_out.audio.i_frame_length = p_sys->frame.i_samples;
+
+    p_dec->fmt_out.audio.i_original_channels = p_sys->frame.i_channels_conf;
+    p_dec->fmt_out.audio.i_physical_channels =
+        p_sys->frame.i_channels_conf & AOUT_CHAN_PHYSMASK;
+
+    p_dec->fmt_out.i_bitrate = p_sys->frame.i_bitrate;
+
+    block_t *p_block = block_Alloc( p_sys->frame.i_size );
+    if( p_block == NULL )
+        return NULL;
+
+    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;
+    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_A52_HEADER_SIZE];
+    block_t *p_out_buffer;
+
+    if( !pp_block || !*pp_block ) return NULL;
+
+    if( (*pp_block)->i_flags & (BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED) )
+    {
+        if( (*pp_block)->i_flags & BLOCK_FLAG_CORRUPTED )
+        {
+            Flush( p_dec );
+            block_Release( *pp_block );
+            *pp_block = NULL;
+            return NULL;
+        }
+        else /* BLOCK_FLAG_DISCONTINUITY */
+            date_Set( &p_sys->end_date, 0 );
+
+    }
+
+    if( !date_Get( &p_sys->end_date ) && (*pp_block)->i_pts <= VLC_TS_INVALID)
+    {
+        /* We've just started the stream, wait for the first PTS. */
+        block_Release( *pp_block );
+        *pp_block = NULL;
+        return NULL;
+    }
+
+    block_BytestreamPush( &p_sys->bytestream, *pp_block );
+
+    while( 1 )
+    {
+        switch( p_sys->i_state )
+        {
+        case STATE_NOSYNC:
+            while( block_PeekBytes( &p_sys->bytestream, p_header, 2 )
+                   == VLC_SUCCESS )
+            {
+                if( p_header[0] == 0x0b && p_header[1] == 0x77 )
+                {
+                    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 A/52 frame header (VLC_A52_HEADER_SIZE bytes) */
+            if( block_PeekBytes( &p_sys->bytestream, p_header,
+                                 VLC_A52_HEADER_SIZE ) != VLC_SUCCESS )
+            {
+                /* Need more data */
+                return NULL;
+            }
+
+            /* Check if frame is valid and get frame info */
+            if( vlc_a52_header_Parse( &p_sys->frame, p_header, VLC_A52_HEADER_SIZE ) )
+            {
+                msg_Dbg( p_dec, "emulated sync word" );
+                block_SkipByte( &p_sys->bytestream );
+                p_sys->i_state = STATE_NOSYNC;
+                break;
+            }
+
+            p_sys->i_state = STATE_NEXT_SYNC;
+
+        case STATE_NEXT_SYNC:
+            /* TODO: If pp_block == NULL, flush the buffer without checking the
+             * next sync word */
+
+            /* Check if next expected frame contains the sync word */
+            if( block_PeekOffsetBytes( &p_sys->bytestream,
+                                       p_sys->frame.i_size, p_header, 2 )
+                != VLC_SUCCESS )
+            {
+                /* Need more data */
+                return NULL;
+            }
+
+            if( 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;
+            }
+
+            if( p_header[0] != 0x0b || p_header[1] != 0x77 )
+            {
+                msg_Dbg( p_dec, "emulated sync word "
+                         "(no sync on following frame)" );
+                p_sys->i_state = STATE_NOSYNC;
+                block_SkipByte( &p_sys->bytestream );
+                break;
+            }
+            p_sys->i_state = STATE_SEND_DATA;
+            break;
+
+        case STATE_GET_DATA:
+            /* Make sure we have enough data.
+             * (Not useful if we went through NEXT_SYNC) */
+            if( block_WaitBytes( &p_sys->bytestream,
+                                 p_sys->frame.i_size ) != VLC_SUCCESS )
+            {
+                /* Need more data */
+                return NULL;
+            }
+            p_sys->i_state = STATE_SEND_DATA;
+
+        case STATE_SEND_DATA:
+            if( !(p_out_buffer = GetOutBuffer( p_dec )) )
+            {
+                //p_dec->b_error = true;
+                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,
+                            __MIN( p_sys->frame.i_size, p_out_buffer->i_buffer ) );
+
+            /* 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;
+
+            /* So p_block doesn't get re-added several times */
+            *pp_block = block_BytestreamPop( &p_sys->bytestream );
+
+            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_A52:
+        break;
+    case VLC_CODEC_EAC3:
+        break;
+    default:
+        return VLC_EGENERIC;
+    }
+
+    /* Allocate the memory needed to store the decoder's structure */
+    if( ( p_dec->p_sys = p_sys =
+          (decoder_sys_t *)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;
+
+    block_BytestreamInit( &p_sys->bytestream );
+
+    /* Set output properties */
+    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;
+
+    p_sys->frame.i_rate = p_dec->fmt_out.audio.i_rate;
+    p_sys->frame.i_channels = p_dec->fmt_out.audio.i_channels;
+    p_sys->frame.i_size = p_dec->fmt_out.audio.i_bytes_per_frame;
+    p_sys->frame.i_samples = p_dec->fmt_out.audio.i_frame_length;
+    p_sys->frame.i_channels_conf = p_dec->fmt_out.audio.i_original_channels;
+    p_sys->frame.i_bitrate = p_dec->fmt_out.i_bitrate;
+
+    /* Set callback */
+    p_dec->pf_packetize = PacketizeBlock;
+    p_dec->pf_flush     = Flush;
+    fprintf(stderr, "A52: Open!\n");
+    return VLC_SUCCESS;
+}
diff --git a/modules/packetizer/a52.h b/modules/packetizer/a52.h
new file mode 100644
index 0000000..3b7315c
--- /dev/null
+++ b/modules/packetizer/a52.h
@@ -0,0 +1,216 @@
+/*****************************************************************************
+ * a52.h
+ *****************************************************************************
+ * Copyright (C) 2001-2009 Laurent Aimar
+ * $Id$
+ *
+ * Authors: Stéphane Borel <stef at via.ecp.fr>
+ *          Christophe Massiot <massiot at via.ecp.fr>
+ *          Gildas Bazin <gbazin at videolan.org>
+ *          Laurent Aimar <fenrir at via.ecp.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.
+ *****************************************************************************/
+
+#ifndef VLC_A52_H_
+#define VLC_A52_H_
+
+#include <vlc_bits.h>
+
+/**
+ * Minimum AC3 header size that vlc_a52_header_Parse needs.
+ */
+#define VLC_A52_HEADER_SIZE (8)
+
+/**
+ * AC3 header information.
+ */
+typedef struct
+{
+    bool b_eac3;
+
+    unsigned int i_channels;
+    unsigned int i_channels_conf;
+    unsigned int i_rate;
+    unsigned int i_bitrate;
+
+    unsigned int i_size;
+    unsigned int i_samples;
+
+} vlc_a52_header_t;
+
+/**
+ * It parse AC3 sync info.
+ *
+ * This code is borrowed from liba52 by Aaron Holtzman & Michel Lespinasse,
+ * since we don't want to oblige S/PDIF people to use liba52 just to get
+ * their SyncInfo...
+ */
+static inline int vlc_a52_header_ParseAc3( vlc_a52_header_t *p_header,
+                                           const uint8_t *p_buf,
+                                           const uint32_t *p_acmod )
+{
+    static const uint8_t pi_halfrate[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 };
+    static const unsigned int pi_bitrate[] = { 32,  40,  48,  56,  64,  80,  96, 112,
+                                128, 160, 192, 224, 256, 320, 384, 448,
+                                512, 576, 640 };
+    static const uint8_t pi_lfeon[8] = { 0x10, 0x10, 0x04, 0x04,
+                                      0x04, 0x01, 0x04, 0x01 };
+
+    /* */
+    const unsigned i_rate_shift = pi_halfrate[p_buf[5] >> 3];
+
+    /* acmod, dsurmod and lfeon */
+    const unsigned i_acmod = p_buf[6] >> 5;
+    if( (p_buf[6] & 0xf8) == 0x50 )
+        /* Dolby surround = stereo + Dolby */
+        p_header->i_channels_conf = AOUT_CHANS_STEREO | AOUT_CHAN_DOLBYSTEREO;
+    else
+        p_header->i_channels_conf = p_acmod[i_acmod];
+    if( p_buf[6] & pi_lfeon[i_acmod] )
+        p_header->i_channels_conf |= AOUT_CHAN_LFE;
+
+    p_header->i_channels = popcount(p_header->i_channels_conf
+                                                         & AOUT_CHAN_PHYSMASK);
+
+    const unsigned i_frmsizecod = p_buf[4] & 63;
+    if( i_frmsizecod >= 38 )
+        return VLC_EGENERIC;
+    const unsigned i_bitrate_base = pi_bitrate[i_frmsizecod >> 1];
+    p_header->i_bitrate = (i_bitrate_base * 1000) >> i_rate_shift;
+
+    switch( p_buf[4] & 0xc0 )
+    {
+    case 0:
+        p_header->i_rate = 48000 >> i_rate_shift;
+        p_header->i_size = 4 * i_bitrate_base;
+        break;
+    case 0x40:
+        p_header->i_rate = 44100 >> i_rate_shift;
+        p_header->i_size = 2 * (320 * i_bitrate_base / 147 + (i_frmsizecod & 1));
+        break;
+    case 0x80:
+        p_header->i_rate = 32000 >> i_rate_shift;
+        p_header->i_size = 6 * i_bitrate_base;
+        break;
+    default:
+        return VLC_EGENERIC;
+    }
+    p_header->i_samples = 6*256;
+
+    p_header->b_eac3 = false;
+    return VLC_SUCCESS;
+}
+
+/**
+ * It parse E-AC3 sync info
+ */
+static inline int vlc_a52_header_ParseEac3( vlc_a52_header_t *p_header,
+                                            const uint8_t *p_buf,
+                                            const uint32_t *p_acmod )
+{
+    static const unsigned pi_samplerate[3] = { 48000, 44100, 32000 };
+    unsigned i_numblkscod;
+    bs_t s;
+
+
+    bs_init( &s, (void*)p_buf, VLC_A52_HEADER_SIZE );
+    bs_skip( &s, 16 +   /* start code */
+                 2 +    /* stream type */
+                 3 );   /* substream id */
+    const unsigned i_frame_size = bs_read( &s, 11 );
+    if( i_frame_size < 2 )
+        return VLC_EGENERIC;
+    p_header->i_size = 2 * ( i_frame_size + 1 );
+
+    const unsigned i_fscod = bs_read( &s, 2 );
+    if( i_fscod == 0x03 )
+    {
+        const unsigned i_fscod2 = bs_read( &s, 2 );
+        if( i_fscod2 == 0X03 )
+            return VLC_EGENERIC;
+        p_header->i_rate = pi_samplerate[i_fscod2] / 2;
+        i_numblkscod = 6;
+    }
+    else
+    {
+        static const int pi_blocks[4] = { 1, 2, 3, 6 };
+
+        p_header->i_rate = pi_samplerate[i_fscod];
+        i_numblkscod = pi_blocks[bs_read( &s, 2 )];
+    }
+
+    const unsigned i_acmod = bs_read( &s, 3 );
+    const unsigned i_lfeon = bs_read1( &s );
+
+    p_header->i_channels_conf = p_acmod[i_acmod];
+    if( i_lfeon )
+        p_header->i_channels_conf |= AOUT_CHAN_LFE;
+    p_header->i_channels = popcount(p_header->i_channels_conf
+                                                         & AOUT_CHAN_PHYSMASK);
+    p_header->i_bitrate = 8 * p_header->i_size * (p_header->i_rate)
+                                               / (i_numblkscod * 256);
+    p_header->i_samples = i_numblkscod * 256;
+
+    p_header->b_eac3 = true;
+    return VLC_SUCCESS;
+}
+
+/**
+ * It will parse the header AC3 frame and fill vlc_a52_header_t* if
+ * it is valid or return VLC_EGENERIC.
+ *
+ * XXX It will only recognize big endian bitstream ie starting with 0x0b, 0x77
+ */
+static inline int vlc_a52_header_Parse( vlc_a52_header_t *p_header,
+                                        const uint8_t *p_buffer, int i_buffer )
+{
+    static const uint32_t p_acmod[8] = {
+        AOUT_CHANS_2_0 | AOUT_CHAN_DUALMONO,
+        AOUT_CHAN_CENTER,
+        AOUT_CHANS_2_0,
+        AOUT_CHANS_3_0,
+        AOUT_CHANS_FRONT | AOUT_CHAN_REARCENTER, /* 2F1R */
+        AOUT_CHANS_FRONT | AOUT_CHANS_CENTER,    /* 3F1R */
+        AOUT_CHANS_4_0,
+        AOUT_CHANS_5_0,
+    };
+
+    if( i_buffer < VLC_A52_HEADER_SIZE )
+        return VLC_EGENERIC;
+
+    /* Check synword */
+    if( p_buffer[0] != 0x0b || p_buffer[1] != 0x77 )
+        return VLC_EGENERIC;
+
+    /* Check bsid */
+    const int bsid = p_buffer[5] >> 3;
+    if( bsid > 16 )
+        return VLC_EGENERIC;
+
+    if( bsid <= 10 )
+    {
+        if( vlc_a52_header_ParseAc3( p_header, p_buffer, p_acmod ) )
+            return VLC_EGENERIC;
+    }
+    else
+    {
+        if( vlc_a52_header_ParseEac3( p_header, p_buffer, p_acmod ) )
+            return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3e74e42..9610c4d 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -333,7 +333,6 @@ modules/audio_output/volume.h
 modules/audio_output/wasapi.c
 modules/audio_output/waveout.c
 modules/audio_output/windows_audio_common.h
-modules/codec/a52.c
 modules/codec/adpcm.c
 modules/codec/aes3.c
 modules/codec/araw.c
@@ -396,6 +395,7 @@ modules/codec/scte18.c
 modules/codec/scte18.h
 modules/codec/sdl_image.c
 modules/codec/shine.c
+modules/codec/spdif.c
 modules/codec/speex.c
 modules/codec/spudec/parse.c
 modules/codec/spudec/spudec.c
@@ -1001,6 +1001,7 @@ modules/mux/ogg.c
 modules/mux/wav.c
 modules/notify/osx_notifications.m
 modules/notify/notify.c
+modules/packetizer/a52.c
 modules/packetizer/avparser.h
 modules/packetizer/copy.c
 modules/packetizer/dirac.c
-- 
2.8.1



More information about the vlc-devel mailing list