X-Git-Url: https://git.sesse.net/?p=vlc;a=blobdiff_plain;f=modules%2Fcodec%2Flpcm.c;h=8e62b152d41ac8bfefbc15afed91464ab9fb135e;hp=1478e99ed16068efcfebef78ef5e6029d9c0b234;hb=3118165aefce584687fd67953e477fbbdf7e1bcb;hpb=db7c69ed67fc0df5a21e3b303b574ae8453dee5f diff --git a/modules/codec/lpcm.c b/modules/codec/lpcm.c index 1478e99ed1..8e62b152d4 100644 --- a/modules/codec/lpcm.c +++ b/modules/codec/lpcm.c @@ -1,18 +1,20 @@ /***************************************************************************** - * lpcm.c: lpcm decoder module + * lpcm.c: lpcm decoder/packetizer module ***************************************************************************** - * Copyright (C) 1999-2001 VideoLAN - * $Id: lpcm.c,v 1.14 2003/03/18 01:22:13 sam Exp $ + * Copyright (C) 1999-2008 the VideoLAN team + * $Id$ * * Authors: Samuel Hocevar * Henri Fallon * Christophe Massiot + * Gildas Bazin + * Lauren Aimar * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 @@ -20,47 +22,92 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include /* malloc(), free() */ -#include /* memcpy(), memset() */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif -#include -#include -#include -#include +#include +#include +#include +#include +#include + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +static int OpenDecoder ( vlc_object_t * ); +static int OpenPacketizer( vlc_object_t * ); +static void CloseCommon ( vlc_object_t * ); + +#ifdef ENABLE_SOUT +static int OpenEncoder ( vlc_object_t * ); +static void CloseEncoder ( vlc_object_t * ); +static block_t *EncodeFrames( encoder_t *, aout_buffer_t * ); +#endif -#ifdef HAVE_UNISTD_H -# include /* getpid() */ +vlc_module_begin () + + set_category( CAT_INPUT ) + set_subcategory( SUBCAT_INPUT_ACODEC ) + set_description( N_("Linear PCM audio decoder") ) + set_capability( "decoder", 100 ) + set_callbacks( OpenDecoder, CloseCommon ) + + add_submodule () + set_description( N_("Linear PCM audio packetizer") ) + set_capability( "packetizer", 100 ) + set_callbacks( OpenPacketizer, CloseCommon ) + +#ifdef ENABLE_SOUT + add_submodule () + set_description( N_("Linear PCM audio encoder") ) + set_capability( "encoder", 100 ) + set_callbacks( OpenEncoder, CloseEncoder ) + add_shortcut( "lpcm" ) #endif +vlc_module_end () + + /***************************************************************************** - * dec_thread_t : lpcm decoder thread descriptor + * decoder_sys_t : lpcm decoder descriptor *****************************************************************************/ -typedef struct dec_thread_t +struct decoder_sys_t { - /* - * Input properties - */ - decoder_fifo_t * p_fifo; /* stores the PES stream data */ + /* Module mode */ + bool b_packetizer; /* * Output properties */ - aout_instance_t *p_aout; - aout_input_t *p_aout_input; - audio_sample_format_t output_format; - audio_date_t end_date; -} dec_thread_t; + date_t end_date; + + /* */ + unsigned i_header_size; + int i_type; +}; + +#ifdef ENABLE_SOUT +struct encoder_sys_t +{ + int i_channels; + int i_rate; + + int i_frame_samples; + uint8_t *p_buffer; + int i_buffer_used; + int i_frame_num; +}; +#endif /* - * LPCM header : - * - PES header - * - private stream ID (16 bits) == 0xA0 -> not in the bitstream + * LPCM DVD header : * - frame number (8 bits) * - unknown (16 bits) == 0x0003 ? * - unknown (4 bits) @@ -70,292 +117,894 @@ typedef struct dec_thread_t * - unknown (1 bit) * - number of channels - 1 (3 bits) 1 == 2 channels * - start code (8 bits) == 0x80 + * + * LPCM DVD-A header (http://dvd-audio.sourceforge.net/spec/aob.shtml) + * - continuity counter (8 bits, clipped to 0x00-0x1f) + * - header size (16 bits) + * - byte pointer to start of first audio frame. + * - unknown (8bits, 0x10 for stereo, 0x00 for surround) + * - sample size (4+4 bits) + * - samplerate (4+4 bits) + * - unknown (8 bits) + * - group assignment (8 bits) + * - unknown (8 bits) + * - padding(variable) + * + * LPCM BD header : + * - unkown (16 bits) + * - number of channels (4 bits) + * - frequency (4 bits) + * - bits per sample (2 bits) + * - unknown (6 bits) */ -#define LPCM_HEADER_LEN 6 +#define LPCM_VOB_HEADER_LEN (6) +#define LPCM_AOB_HEADER_LEN (11) +#define LPCM_BD_HEADER_LEN (4) -/***************************************************************************** - * Local prototypes - *****************************************************************************/ -static int OpenDecoder ( vlc_object_t * ); -static int RunDecoder ( decoder_fifo_t * ); +enum +{ + LPCM_VOB, + LPCM_AOB, + LPCM_BD, +}; -static void DecodeFrame ( dec_thread_t * ); -static void EndThread ( dec_thread_t * ); +typedef struct +{ + unsigned i_channels; + bool b_used; + unsigned pi_position[6]; +} aob_group_t; /***************************************************************************** - * Module descriptor + * Local prototypes *****************************************************************************/ -vlc_module_begin(); - set_description( _("linear PCM audio parser") ); - set_capability( "decoder", 100 ); - set_callbacks( OpenDecoder, NULL ); -vlc_module_end(); +static void *DecodeFrame ( decoder_t *, block_t ** ); + +/* */ +static int VobHeader( unsigned *pi_rate, + unsigned *pi_channels, unsigned *pi_original_channels, + unsigned *pi_bits, + const uint8_t *p_header ); +static void VobExtract( aout_buffer_t *, block_t *, unsigned i_bits ); +/* */ +static int AobHeader( unsigned *pi_rate, + unsigned *pi_channels, unsigned *pi_layout, + unsigned *pi_bits, + unsigned *pi_padding, + aob_group_t g[2], + const uint8_t *p_header ); +static void AobExtract( aout_buffer_t *, block_t *, unsigned i_bits, aob_group_t p_group[2] ); +/* */ +static int BdHeader( unsigned *pi_rate, + unsigned *pi_channels, unsigned *pi_original_channels, + unsigned *pi_bits, + const uint8_t *p_header ); +static void BdExtract( aout_buffer_t *, block_t * ); -/***************************************************************************** - * OpenDecoder: probe the decoder and return score - *****************************************************************************/ -static int OpenDecoder( vlc_object_t *p_this ) -{ - decoder_fifo_t *p_fifo = (decoder_fifo_t*) p_this; - - if( p_fifo->i_fourcc != VLC_FOURCC('l','p','c','m') - && p_fifo->i_fourcc != VLC_FOURCC('l','p','c','b') ) - { - return VLC_EGENERIC; - } - - p_fifo->pf_run = RunDecoder; - return VLC_SUCCESS; -} /***************************************************************************** - * RunDecoder: the lpcm decoder + * OpenCommon: *****************************************************************************/ -static int RunDecoder( decoder_fifo_t * p_fifo ) +static int OpenCommon( vlc_object_t *p_this, bool b_packetizer ) { - dec_thread_t * p_dec; + decoder_t *p_dec = (decoder_t*)p_this; + decoder_sys_t *p_sys; + int i_type; + int i_header_size; - /* Allocate the memory needed to store the thread's structure */ - if( (p_dec = (dec_thread_t *)malloc( sizeof(dec_thread_t)) ) - == NULL) + switch( p_dec->fmt_in.i_codec ) { - msg_Err( p_fifo, "out of memory" ); - DecoderError( p_fifo ); - return -1; + /* DVD LPCM */ + case VLC_CODEC_DVD_LPCM: + i_type = LPCM_VOB; + i_header_size = LPCM_VOB_HEADER_LEN; + break; + /* DVD-Audio LPCM */ + case VLC_CODEC_DVDA_LPCM: + i_type = LPCM_AOB; + i_header_size = LPCM_AOB_HEADER_LEN; + break; + /* BD LPCM */ + case VLC_CODEC_BD_LPCM: + i_type = LPCM_BD; + i_header_size = LPCM_BD_HEADER_LEN; + break; + default: + return VLC_EGENERIC; } - /* Initialize the thread properties */ - p_dec->p_fifo = p_fifo; + /* 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->b_packetizer = b_packetizer; + date_Set( &p_sys->end_date, 0 ); + p_sys->i_type = i_type; + p_sys->i_header_size = i_header_size; - p_dec->output_format.i_format = VLC_FOURCC('s','1','6','b'); - p_dec->p_aout = NULL; - p_dec->p_aout_input = NULL; + /* Set output properties */ + p_dec->fmt_out.i_cat = AUDIO_ES; - /* LPCM decoder thread's main loop */ - while ( (!p_dec->p_fifo->b_die) && (!p_dec->p_fifo->b_error) ) + if( b_packetizer ) { - DecodeFrame(p_dec); + switch( i_type ) + { + case LPCM_VOB: + p_dec->fmt_out.i_codec = VLC_CODEC_DVD_LPCM; + break; + case LPCM_AOB: + p_dec->fmt_out.i_codec = VLC_CODEC_DVDA_LPCM; + break; + default: + assert(0); + case LPCM_BD: + p_dec->fmt_out.i_codec = VLC_CODEC_BD_LPCM; + break; + } } - - /* If b_error is set, the lpcm decoder thread enters the error loop */ - if ( p_dec->p_fifo->b_error ) + else { - DecoderError( p_dec->p_fifo ); + switch( p_dec->fmt_out.audio.i_bitspersample ) + { + case 24: + case 20: + p_dec->fmt_out.i_codec = VLC_CODEC_S24B; + p_dec->fmt_out.audio.i_bitspersample = 24; + break; + default: + p_dec->fmt_out.i_codec = VLC_CODEC_S16B; + p_dec->fmt_out.audio.i_bitspersample = 16; + break; + } } - /* End of the lpcm decoder thread */ - EndThread( p_dec ); + /* Set callback */ + p_dec->pf_decode_audio = (aout_buffer_t *(*)(decoder_t *, block_t **)) + DecodeFrame; + p_dec->pf_packetize = (block_t *(*)(decoder_t *, block_t **)) + DecodeFrame; - return 0; + return VLC_SUCCESS; +} +static int OpenDecoder( vlc_object_t *p_this ) +{ + return OpenCommon( p_this, false ); +} +static int OpenPacketizer( vlc_object_t *p_this ) +{ + return OpenCommon( p_this, true ); } /***************************************************************************** - * DecodeFrame: decodes a frame. + * DecodeFrame: decodes an lpcm frame. + **************************************************************************** + * Beware, this function must be fed with complete frames (PES packet). *****************************************************************************/ -static void DecodeFrame( dec_thread_t * p_dec ) +static void *DecodeFrame( decoder_t *p_dec, block_t **pp_block ) { - pes_packet_t * p_pes; - data_packet_t * p_data; - aout_buffer_t * p_buffer; - void * p_dest; - mtime_t i_pts; - uint8_t i_header; - unsigned int i_rate = 0, i_original_channels = 0, i_size; - int i; - - input_ExtractPES( p_dec->p_fifo, &p_pes ); - if ( !p_pes ) + decoder_sys_t *p_sys = p_dec->p_sys; + block_t *p_block; + unsigned int i_rate = 0, i_original_channels = 0, i_channels = 0, i_bits = 0; + int i_frame_length; + + if( !pp_block || !*pp_block ) return NULL; + + p_block = *pp_block; + *pp_block = NULL; /* So the packet doesn't get re-sent */ + + /* Date management */ + if( p_block->i_pts > VLC_TS_INVALID && + p_block->i_pts != date_Get( &p_sys->end_date ) ) + { + date_Set( &p_sys->end_date, p_block->i_pts ); + } + + if( !date_Get( &p_sys->end_date ) ) + { + /* We've just started the stream, wait for the first PTS. */ + block_Release( p_block ); + return NULL; + } + + if( p_block->i_buffer <= p_sys->i_header_size ) { - p_dec->p_fifo->b_error = 1; - return; + msg_Err(p_dec, "frame is too short"); + block_Release( p_block ); + return NULL; } - /* Compute the size of the PES - i_pes_size includes the PES header. */ - p_data = p_pes->p_first; - i_size = 0; - while ( p_data != NULL ) + int i_ret; + unsigned i_padding = 0; + aob_group_t p_aob_group[2]; + switch( p_sys->i_type ) { - i_size += p_data->p_payload_end - p_data->p_payload_start; - p_data = p_data->p_next; + case LPCM_VOB: + i_ret = VobHeader( &i_rate, &i_channels, &i_original_channels, &i_bits, + p_block->p_buffer ); + break; + case LPCM_AOB: + i_ret = AobHeader( &i_rate, &i_channels, &i_original_channels, &i_bits, &i_padding, + p_aob_group, + p_block->p_buffer ); + break; + case LPCM_BD: + i_ret = BdHeader( &i_rate, &i_channels, &i_original_channels, &i_bits, + p_block->p_buffer ); + break; + default: + abort(); } - if ( i_size < LPCM_HEADER_LEN ) + + if( i_ret || p_block->i_buffer <= p_sys->i_header_size + i_padding ) { - msg_Err(p_dec->p_fifo, "PES packet is too short"); - input_DeletePES( p_dec->p_fifo->p_packets_mgt, p_pes ); - return; + msg_Warn( p_dec, "no frame sync or too small frame" ); + block_Release( p_block ); + return NULL; } - i_pts = p_pes->i_pts; - if( i_pts != 0 && i_pts != aout_DateGet( &p_dec->end_date ) ) + /* Set output properties */ + if( p_dec->fmt_out.audio.i_rate != i_rate ) { - aout_DateSet( &p_dec->end_date, i_pts ); + date_Init( &p_sys->end_date, i_rate, 1 ); + date_Set( &p_sys->end_date, p_block->i_pts ); } + p_dec->fmt_out.audio.i_rate = i_rate; + p_dec->fmt_out.audio.i_channels = i_channels; + p_dec->fmt_out.audio.i_original_channels = i_original_channels; + p_dec->fmt_out.audio.i_physical_channels = i_original_channels & AOUT_CHAN_PHYSMASK; - p_data = p_pes->p_first; - /* It necessarily contains one byte. */ - /* Get LPCM header. */ + i_frame_length = (p_block->i_buffer - p_sys->i_header_size - i_padding) / i_channels * 8 / i_bits; - /* Drop the first four bytes. */ - for ( i = 0; i < 4; i++ ) + if( p_sys->b_packetizer ) { - if ( p_data->p_payload_end == p_data->p_payload_start ) + p_block->i_pts = p_block->i_dts = date_Get( &p_sys->end_date ); + p_block->i_length = + date_Increment( &p_sys->end_date, i_frame_length ) - + p_block->i_pts; + + /* Just pass on the incoming frame */ + return p_block; + } + else + { + /* */ + if( i_bits == 16 ) + { + p_dec->fmt_out.i_codec = VLC_CODEC_S16B; + p_dec->fmt_out.audio.i_bitspersample = 16; + } + else { - p_data = p_data->p_next; + p_dec->fmt_out.i_codec = VLC_CODEC_S24B; + p_dec->fmt_out.audio.i_bitspersample = 24; } - p_data->p_payload_start++; + + /* */ + aout_buffer_t *p_aout_buffer; + p_aout_buffer = decoder_NewAudioBuffer( p_dec, i_frame_length ); + if( !p_aout_buffer ) + return NULL; + + p_aout_buffer->i_pts = date_Get( &p_sys->end_date ); + p_aout_buffer->i_length = + date_Increment( &p_sys->end_date, i_frame_length ) + - p_aout_buffer->i_pts; + + p_block->p_buffer += p_sys->i_header_size + i_padding; + p_block->i_buffer -= p_sys->i_header_size + i_padding; + + switch( p_sys->i_type ) + { + case LPCM_VOB: + VobExtract( p_aout_buffer, p_block, i_bits ); + break; + case LPCM_AOB: + AobExtract( p_aout_buffer, p_block, i_bits, p_aob_group ); + break; + default: + assert(0); + case LPCM_BD: + BdExtract( p_aout_buffer, p_block ); + break; + } + + block_Release( p_block ); + return p_aout_buffer; + } +} + +/***************************************************************************** + * CloseCommon : lpcm decoder destruction + *****************************************************************************/ +static void CloseCommon( vlc_object_t *p_this ) +{ + decoder_t *p_dec = (decoder_t*)p_this; + free( p_dec->p_sys ); +} + +#ifdef ENABLE_SOUT +/***************************************************************************** + * OpenEncoder: lpcm encoder construction + *****************************************************************************/ +static int OpenEncoder( vlc_object_t *p_this ) +{ + encoder_t *p_enc = (encoder_t *)p_this; + encoder_sys_t *p_sys; + + /* We only support DVD LPCM yet. */ + if( p_enc->fmt_out.i_codec != VLC_CODEC_DVD_LPCM || + ( p_enc->fmt_in.audio.i_rate != 48000 && + p_enc->fmt_in.audio.i_rate != 96000 && + p_enc->fmt_in.audio.i_rate != 44100 && + p_enc->fmt_in.audio.i_rate != 32000 ) || + p_enc->fmt_in.audio.i_channels > 8 ) + return VLC_EGENERIC; + + /* Allocate the memory needed to store the encoder's structure */ + if( ( p_enc->p_sys = p_sys = + (encoder_sys_t *)malloc(sizeof(encoder_sys_t)) ) == NULL ) + return VLC_ENOMEM; + + /* In DVD LCPM, a frame is always 150 PTS ticks. */ + p_sys->i_frame_samples = p_enc->fmt_in.audio.i_rate * 150 / 90000; + p_sys->p_buffer = (uint8_t *)malloc( + p_sys->i_frame_samples * + p_enc->fmt_in.audio.i_channels * + p_enc->fmt_in.audio.i_bitspersample); + p_sys->i_buffer_used = 0; + p_sys->i_frame_num = 0; + + p_sys->i_channels = p_enc->fmt_in.audio.i_channels; + p_sys->i_rate = p_enc->fmt_in.audio.i_rate; + + p_enc->pf_encode_audio = EncodeFrames; + p_enc->fmt_in.i_codec = p_enc->fmt_out.i_codec; + + p_enc->fmt_in.audio.i_bitspersample = 16; + p_enc->fmt_in.i_codec = VLC_CODEC_S16B; + + p_enc->fmt_out.i_bitrate = + p_enc->fmt_in.audio.i_channels * + p_enc->fmt_in.audio.i_rate * + p_enc->fmt_in.audio.i_bitspersample * + (p_sys->i_frame_samples + LPCM_VOB_HEADER_LEN) / + p_sys->i_frame_samples; + + return VLC_SUCCESS; +} + +/***************************************************************************** + * CloseEncoder: lpcm encoder destruction + *****************************************************************************/ +static void CloseEncoder ( vlc_object_t *p_this ) +{ + VLC_UNUSED(p_this); +} + +/***************************************************************************** + * EncodeFrames: encode zero or more LCPM audio packets + *****************************************************************************/ +static block_t *EncodeFrames( encoder_t *p_enc, aout_buffer_t *p_aout_buf ) +{ + encoder_sys_t *p_sys = p_enc->p_sys; + block_t *p_first_block = NULL, *p_last_block = NULL; + + if( !p_aout_buf || !p_aout_buf->i_buffer ) return NULL; + + const int i_num_frames = ( p_sys->i_buffer_used + p_aout_buf->i_nb_samples ) / + p_sys->i_frame_samples; + const int i_leftover_samples = ( p_sys->i_buffer_used + p_aout_buf->i_nb_samples ) % + p_sys->i_frame_samples; + const int i_frame_size = p_sys->i_frame_samples * p_sys->i_channels * 2 + LPCM_VOB_HEADER_LEN; + const int i_start_offset = -p_sys->i_buffer_used; + + uint8_t i_freq_code = 0; + + switch( p_sys->i_rate ) { + case 48000: + i_freq_code = 0; + break; + case 96000: + i_freq_code = 1; + break; + case 44100: + i_freq_code = 2; + break; + case 32000: + i_freq_code = 3; + break; } - i_header = p_data->p_payload_start[0]; - p_data->p_payload_start++; + int i_bytes_consumed = 0; - switch ( (i_header >> 4) & 0x3 ) + for ( int i = 0; i < i_num_frames; ++i ) + { + block_t *p_block = block_New( p_enc, i_frame_size ); + if( !p_block ) + return NULL; + + uint8_t *frame = (uint8_t *)p_block->p_buffer; + frame[0] = 1; /* one frame in packet */ + frame[1] = 0; + frame[2] = 0; /* no first access unit */ + frame[3] = (p_sys->i_frame_num + i) & 0x1f; /* no emphasis, no mute */ + frame[4] = (i_freq_code << 4) | (p_sys->i_channels - 1); + frame[5] = 0x80; /* neutral dynamic range */ + + const int i_consume_samples = p_sys->i_frame_samples - p_sys->i_buffer_used; + const int i_kept_bytes = p_sys->i_buffer_used * p_sys->i_channels * 2; + const int i_consume_bytes = i_consume_samples * p_sys->i_channels * 2; + + memcpy( frame + 6, p_sys->p_buffer, i_kept_bytes ); + memcpy( frame + 6 + i_kept_bytes, p_aout_buf->p_buffer + i_bytes_consumed, i_consume_bytes ); + + p_sys->i_frame_num++; + p_sys->i_buffer_used = 0; + i_bytes_consumed += i_consume_bytes; + + p_block->i_dts = p_block->i_pts = p_aout_buf->i_pts + + (i * p_sys->i_frame_samples + i_start_offset) * CLOCK_FREQ / p_sys->i_rate; + p_block->i_length = p_sys->i_frame_samples * CLOCK_FREQ / p_sys->i_rate; + + if( !p_first_block ) + p_first_block = p_last_block = p_block; + else + p_last_block = p_last_block->p_next = p_block; + } + + memcpy( p_sys->p_buffer, + p_aout_buf->p_buffer + i_bytes_consumed, + i_leftover_samples * p_sys->i_channels * 2 ); + p_sys->i_buffer_used = i_leftover_samples; + + return p_first_block; +} +#endif + +/***************************************************************************** + * + *****************************************************************************/ +static int VobHeader( unsigned *pi_rate, + unsigned *pi_channels, unsigned *pi_original_channels, + unsigned *pi_bits, + const uint8_t *p_header ) +{ + const uint8_t i_header = p_header[4]; + + switch( (i_header >> 4) & 0x3 ) { case 0: - i_rate = 48000; + *pi_rate = 48000; break; case 1: - i_rate = 96000; + *pi_rate = 96000; break; case 2: - i_rate = 44100; + *pi_rate = 44100; break; case 3: - i_rate = 32000; + *pi_rate = 32000; break; } - switch ( i_header & 0x7 ) + *pi_channels = (i_header & 0x7) + 1; + switch( *pi_channels - 1 ) { case 0: - i_original_channels = AOUT_CHAN_CENTER; + *pi_original_channels = AOUT_CHAN_CENTER; break; case 1: - i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; break; case 2: /* This is unsure. */ - i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_LFE; + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_LFE; break; case 3: - i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; break; case 4: /* This is unsure. */ - i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE; break; case 5: - i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_LFE; break; case 6: - i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT; break; case 7: - i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE; break; } - /* Check frame sync and drop it. */ - if ( p_data->p_payload_end == p_data->p_payload_start ) + switch( (i_header >> 6) & 0x3 ) { - p_data = p_data->p_next; + case 2: + *pi_bits = 24; + break; + case 1: + *pi_bits = 20; + break; + case 0: + default: + *pi_bits = 16; + break; } - if ( p_data->p_payload_start[0] != 0x80 ) + + /* Check frame sync and drop it. */ + if( p_header[5] != 0x80 ) + return -1; + return 0; +} + +static const unsigned p_aob_group1[21][6] = { + { AOUT_CHAN_CENTER, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 }, + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 }, +}; +static const unsigned p_aob_group2[21][6] = { + { 0 }, + { 0 }, + { AOUT_CHAN_REARCENTER, 0 }, + { AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 }, + { AOUT_CHAN_LFE, 0 }, + { AOUT_CHAN_LFE, AOUT_CHAN_REARCENTER, 0 }, + { AOUT_CHAN_LFE, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 }, + { AOUT_CHAN_CENTER, 0 }, + { AOUT_CHAN_CENTER, AOUT_CHAN_REARCENTER, 0 }, + { AOUT_CHAN_CENTER, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 }, + { AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 }, + { AOUT_CHAN_CENTER, AOUT_CHAN_LFE, AOUT_CHAN_REARCENTER, 0 }, + { AOUT_CHAN_CENTER, AOUT_CHAN_LFE, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 }, + { AOUT_CHAN_REARCENTER, 0 }, + { AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 }, + { AOUT_CHAN_LFE, 0 }, + { AOUT_CHAN_LFE, AOUT_CHAN_REARCENTER, 0 }, + { AOUT_CHAN_LFE, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 }, + { AOUT_CHAN_LFE, 0 }, + { AOUT_CHAN_CENTER, 0 }, + { AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 }, +}; + +static int AobHeader( unsigned *pi_rate, + unsigned *pi_channels, unsigned *pi_layout, + unsigned *pi_bits, + unsigned *pi_padding, + aob_group_t g[2], + const uint8_t *p_header ) +{ + const unsigned i_header_size = GetWBE( &p_header[1] ); + if( i_header_size + 3 < LPCM_AOB_HEADER_LEN ) + return VLC_EGENERIC; + + *pi_padding = 3+i_header_size - LPCM_AOB_HEADER_LEN; + + const int i_index_size_g1 = (p_header[6] >> 4) & 0x0f; + const int i_index_size_g2 = (p_header[6] ) & 0x0f; + const int i_index_rate_g1 = (p_header[7] >> 4) & 0x0f; + const int i_index_rate_g2 = (p_header[7] ) & 0x0f; + const int i_assignment = p_header[9]; + + /* Validate */ + if( i_index_size_g1 > 0x02 || + ( i_index_size_g2 != 0x0f && i_index_size_g2 > 0x02 ) ) + return VLC_EGENERIC; + if( (i_index_rate_g1 & 0x07) > 0x02 || + ( i_index_rate_g2 != 0x0f && (i_index_rate_g1 & 0x07) > 0x02 ) ) + return VLC_EGENERIC; + if( i_assignment > 20 ) + return VLC_EGENERIC; + + /* */ + *pi_bits = 16 + 4 * i_index_size_g1; + if( i_index_rate_g1 & 0x08 ) + *pi_rate = 44100 << (i_index_rate_g1 & 0x07); + else + *pi_rate = 48000 << (i_index_rate_g1 & 0x07); + + + /* Group1 */ + unsigned i_channels1 = 0; + unsigned i_layout1 = 0; + for( int i = 0; p_aob_group1[i_assignment][i] != 0; i++ ) { - msg_Warn(p_dec->p_fifo, "no frame sync"); - input_DeletePES( p_dec->p_fifo->p_packets_mgt, p_pes ); - return; + i_channels1++; + i_layout1 |= p_aob_group1[i_assignment][i]; } - p_data->p_payload_start++; - - if( (p_dec->p_aout_input != NULL) && - ( (p_dec->output_format.i_rate != i_rate) - || (p_dec->output_format.i_original_channels - != i_original_channels) ) ) + /* Group2 */ + unsigned i_channels2 = 0; + unsigned i_layout2 = 0; + if( i_index_size_g2 != 0x0f && i_index_rate_g2 != 0x0f ) { - /* Parameters changed - this should not happen. */ - aout_DecDelete( p_dec->p_aout, p_dec->p_aout_input ); - p_dec->p_aout_input = NULL; + for( int i = 0; p_aob_group2[i_assignment][i] != 0; i++ ) + { + i_channels2++; + i_layout2 |= p_aob_group2[i_assignment][i]; + } + assert( (i_layout1 & i_layout2) == 0 ); } + /* It is enabled only when presents and compatible wih group1 */ + const bool b_group2_used = i_index_size_g1 == i_index_size_g2 && + i_index_rate_g1 == i_index_rate_g2; - /* Creating the audio input if not created yet. */ - if( p_dec->p_aout_input == NULL ) + /* */ + *pi_channels = i_channels1 + ( b_group2_used ? i_channels2 : 0 ); + *pi_layout = i_layout1 | ( b_group2_used ? i_layout2 : 0 ); + + /* */ + for( unsigned i = 0; i < 2; i++ ) { - p_dec->output_format.i_rate = i_rate; - p_dec->output_format.i_original_channels = i_original_channels; - p_dec->output_format.i_physical_channels - = i_original_channels & AOUT_CHAN_PHYSMASK; - aout_DateInit( &p_dec->end_date, i_rate ); - p_dec->p_aout_input = aout_DecNew( p_dec->p_fifo, - &p_dec->p_aout, - &p_dec->output_format ); - - if ( p_dec->p_aout_input == NULL ) + const unsigned *p_aob = i == 0 ? p_aob_group1[i_assignment] : + p_aob_group2[i_assignment]; + g[i].i_channels = i == 0 ? i_channels1 : + i_channels2; + + g[i].b_used = i == 0 || b_group2_used; + if( !g[i].b_used ) + continue; + for( unsigned j = 0; j < g[i].i_channels; j++ ) { - p_dec->p_fifo->b_error = 1; - input_DeletePES( p_dec->p_fifo->p_packets_mgt, p_pes ); - return; + g[i].pi_position[j] = 0; + for( int k = 0; pi_vlc_chan_order_wg4[k] != 0; k++ ) + { + const unsigned i_channel = pi_vlc_chan_order_wg4[k]; + if( i_channel == p_aob[j] ) + break; + if( (*pi_layout) & i_channel ) + g[i].pi_position[j]++; + } } } + return VLC_SUCCESS; +} - if ( !aout_DateGet( &p_dec->end_date ) ) +static int BdHeader( unsigned *pi_rate, + unsigned *pi_channels, unsigned *pi_original_channels, + unsigned *pi_bits, + const uint8_t *p_header ) +{ + const uint32_t h = GetDWBE( p_header ); + switch( ( h & 0xf000) >> 12 ) { - /* We've just started the stream, wait for the first PTS. */ - input_DeletePES( p_dec->p_fifo->p_packets_mgt, p_pes ); - return; - } + case 1: + *pi_channels = 1; + *pi_original_channels = AOUT_CHAN_CENTER; + break; + case 3: + *pi_channels = 2; + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; + break; + case 4: + *pi_channels = 3; + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER; + break; + case 5: + *pi_channels = 3; + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARCENTER; + break; + case 6: + *pi_channels = 4; + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | + AOUT_CHAN_REARCENTER; + break; + case 7: + *pi_channels = 4; + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | + AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; + break; + case 8: + *pi_channels = 5; + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | + AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; + break; + case 9: + *pi_channels = 6; + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | + AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | + AOUT_CHAN_LFE; + break; + case 10: + *pi_channels = 7; + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | + AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | + AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT; + break; + case 11: + *pi_channels = 8; + *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | + AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | + AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT | + AOUT_CHAN_LFE; + break; - p_buffer = aout_DecNewBuffer( p_dec->p_aout, p_dec->p_aout_input, - (i_size - LPCM_HEADER_LEN) - / p_dec->output_format.i_bytes_per_frame ); - - if( p_buffer == NULL ) + default: + return -1; + } + switch( (h >> 6) & 0x03 ) { - msg_Err( p_dec->p_fifo, "cannot get aout buffer" ); - p_dec->p_fifo->b_error = 1; - input_DeletePES( p_dec->p_fifo->p_packets_mgt, p_pes ); - return; + case 1: + *pi_bits = 16; + break; + case 2: /* 20 bits but samples are stored on 24 bits */ + case 3: /* 24 bits */ + *pi_bits = 24; + break; + default: + return -1; } - p_buffer->start_date = aout_DateGet( &p_dec->end_date ); - p_buffer->end_date = aout_DateIncrement( &p_dec->end_date, - (i_size - LPCM_HEADER_LEN) - / p_dec->output_format.i_bytes_per_frame ); - - /* Get the whole frame. */ - p_dest = p_buffer->p_buffer; - while ( p_data != NULL ) + switch( (h >> 8) & 0x0f ) { - p_dec->p_fifo->p_vlc->pf_memcpy( p_dest, p_data->p_payload_start, - p_data->p_payload_end - p_data->p_payload_start ); - p_dest += p_data->p_payload_end - p_data->p_payload_start; - p_data = p_data->p_next; + case 1: + *pi_rate = 48000; + break; + case 4: + *pi_rate = 96000; + break; + case 5: + *pi_rate = 192000; + break; + default: + return -1; } - input_DeletePES( p_dec->p_fifo->p_packets_mgt, p_pes ); - - /* Send the buffer to the aout core. */ - aout_DecPlay( p_dec->p_aout, p_dec->p_aout_input, p_buffer ); + return 0; } -/***************************************************************************** - * EndThread : lpcm decoder thread destruction - *****************************************************************************/ -static void EndThread( dec_thread_t * p_dec ) +static void VobExtract( aout_buffer_t *p_aout_buffer, block_t *p_block, + unsigned i_bits ) { - if( p_dec->p_aout_input != NULL ) + uint8_t *p_out = p_aout_buffer->p_buffer; + + /* 20/24 bits LPCM use special packing */ + if( i_bits == 24 ) + { + while( p_block->i_buffer / 12 ) + { + /* Sample 1 */ + p_out[0] = p_block->p_buffer[0]; + p_out[1] = p_block->p_buffer[1]; + p_out[2] = p_block->p_buffer[8]; + /* Sample 2 */ + p_out[3] = p_block->p_buffer[2]; + p_out[4] = p_block->p_buffer[3]; + p_out[5] = p_block->p_buffer[9]; + /* Sample 3 */ + p_out[6] = p_block->p_buffer[4]; + p_out[7] = p_block->p_buffer[5]; + p_out[8] = p_block->p_buffer[10]; + /* Sample 4 */ + p_out[9] = p_block->p_buffer[6]; + p_out[10] = p_block->p_buffer[7]; + p_out[11] = p_block->p_buffer[11]; + + p_block->i_buffer -= 12; + p_block->p_buffer += 12; + p_out += 12; + } + } + else if( i_bits == 20 ) + { + while( p_block->i_buffer / 10 ) + { + /* Sample 1 */ + p_out[0] = p_block->p_buffer[0]; + p_out[1] = p_block->p_buffer[1]; + p_out[2] = p_block->p_buffer[8] & 0xF0; + /* Sample 2 */ + p_out[3] = p_block->p_buffer[2]; + p_out[4] = p_block->p_buffer[3]; + p_out[5] = p_block->p_buffer[8] << 4; + /* Sample 3 */ + p_out[6] = p_block->p_buffer[4]; + p_out[7] = p_block->p_buffer[5]; + p_out[8] = p_block->p_buffer[9] & 0xF0; + /* Sample 4 */ + p_out[9] = p_block->p_buffer[6]; + p_out[10] = p_block->p_buffer[7]; + p_out[11] = p_block->p_buffer[9] << 4; + + p_block->i_buffer -= 10; + p_block->p_buffer += 10; + p_out += 12; + } + } + else { - aout_DecDelete( p_dec->p_aout, p_dec->p_aout_input ); + assert( i_bits == 16 ); + memcpy( p_out, p_block->p_buffer, p_block->i_buffer ); } +} +static void AobExtract( aout_buffer_t *p_aout_buffer, + block_t *p_block, unsigned i_bits, aob_group_t p_group[2] ) +{ + const unsigned i_channels = p_group[0].i_channels + + ( p_group[1].b_used ? p_group[1].i_channels : 0 ); + uint8_t *p_out = p_aout_buffer->p_buffer; - free( p_dec ); + while( p_block->i_buffer > 0 ) + { + for( int i = 0; i < 2; i++ ) + { + const aob_group_t *g = &p_group[1-i]; + const unsigned int i_group_size = 2 * g->i_channels * i_bits / 8; + + if( p_block->i_buffer < i_group_size ) + { + p_block->i_buffer = 0; + break; + } + for( unsigned n = 0; n < 2; n++ ) + { + for( unsigned j = 0; j < g->i_channels && g->b_used; j++ ) + { + const int i_src = n * g->i_channels + j; + const int i_dst = n * i_channels + g->pi_position[j]; + + if( i_bits == 24 ) + { + p_out[3*i_dst+0] = p_block->p_buffer[2*i_src+0]; + p_out[3*i_dst+1] = p_block->p_buffer[2*i_src+1]; + p_out[3*i_dst+2] = p_block->p_buffer[4*g->i_channels+i_src]; + } + else if( i_bits == 20 ) + { + p_out[3*i_dst+0] = p_block->p_buffer[2*i_src+0]; + p_out[3*i_dst+1] = p_block->p_buffer[2*i_src+1]; + if( n == 0 ) + p_out[3*i_dst+2] = (p_block->p_buffer[4*g->i_channels+i_src] ) & 0xf0; + else + p_out[3*i_dst+2] = (p_block->p_buffer[4*g->i_channels+i_src] << 4) & 0xf0; + } + else + { + assert( i_bits == 16 ); + p_out[2*i_dst+0] = p_block->p_buffer[2*i_src+0]; + p_out[2*i_dst+1] = p_block->p_buffer[2*i_src+1]; + } + } + } + + p_block->i_buffer -= i_group_size; + p_block->p_buffer += i_group_size; + } + /* */ + p_out += (i_bits == 16 ? 2 : 3) * i_channels * 2; + + } } +static void BdExtract( aout_buffer_t *p_aout_buffer, block_t *p_block ) +{ + memcpy( p_aout_buffer->p_buffer, p_block->p_buffer, p_block->i_buffer ); +} +