X-Git-Url: https://git.sesse.net/?p=vlc;a=blobdiff_plain;f=modules%2Fcodec%2Flpcm.c;h=8e62b152d41ac8bfefbc15afed91464ab9fb135e;hp=77dddf90de7f5c0708f16dd4eaa9878e8e770571;hb=3118165aefce584687fd67953e477fbbdf7e1bcb;hpb=99fab9089e9e1709d9c3a4bc5ced0c137ac59134 diff --git a/modules/codec/lpcm.c b/modules/codec/lpcm.c index 77dddf90de..8e62b152d4 100644 --- a/modules/codec/lpcm.c +++ b/modules/codec/lpcm.c @@ -1,13 +1,14 @@ /***************************************************************************** * lpcm.c: lpcm decoder/packetizer module ***************************************************************************** - * Copyright (C) 1999-2005 the VideoLAN team + * 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 @@ -31,9 +32,48 @@ # include "config.h" #endif -#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 + +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 () + /***************************************************************************** * decoder_sys_t : lpcm decoder descriptor @@ -41,20 +81,33 @@ struct decoder_sys_t { /* Module mode */ - vlc_bool_t b_packetizer; + bool b_packetizer; /* * Output properties */ - audio_date_t end_date; + 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) @@ -64,74 +117,147 @@ struct decoder_sys_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 OpenPacketizer( vlc_object_t * ); -static void CloseDecoder ( vlc_object_t * ); +enum +{ + LPCM_VOB, + LPCM_AOB, + LPCM_BD, +}; -static void *DecodeFrame ( decoder_t *, block_t ** ); +typedef struct +{ + unsigned i_channels; + bool b_used; + unsigned pi_position[6]; +} aob_group_t; /***************************************************************************** - * Module descriptor + * Local prototypes *****************************************************************************/ -vlc_module_begin(); - - set_category( CAT_INPUT ); - set_subcategory( SUBCAT_INPUT_ACODEC ); - set_description( _("Linear PCM audio decoder") ); - set_capability( "decoder", 100 ); - set_callbacks( OpenDecoder, CloseDecoder ); +static void *DecodeFrame ( decoder_t *, block_t ** ); - add_submodule(); - set_description( _("Linear PCM audio packetizer") ); - set_capability( "packetizer", 100 ); - set_callbacks( OpenPacketizer, CloseDecoder ); +/* */ +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 * ); -vlc_module_end(); /***************************************************************************** - * OpenDecoder: probe the decoder and return score + * OpenCommon: *****************************************************************************/ -static int OpenDecoder( vlc_object_t *p_this ) +static int OpenCommon( vlc_object_t *p_this, bool b_packetizer ) { decoder_t *p_dec = (decoder_t*)p_this; decoder_sys_t *p_sys; + int i_type; + int i_header_size; - if( p_dec->fmt_in.i_codec != VLC_FOURCC('l','p','c','m') - && p_dec->fmt_in.i_codec != VLC_FOURCC('l','p','c','b') ) + switch( p_dec->fmt_in.i_codec ) { + /* 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; } /* 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 ) - { - msg_Err( p_dec, "out of memory" ); - return VLC_EGENERIC; - } + if( ( p_dec->p_sys = p_sys = malloc(sizeof(decoder_sys_t)) ) == NULL ) + return VLC_ENOMEM; /* Misc init */ - p_sys->b_packetizer = VLC_FALSE; - aout_DateSet( &p_sys->end_date, 0 ); + 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; /* Set output properties */ p_dec->fmt_out.i_cat = AUDIO_ES; - if( p_dec->fmt_out.audio.i_bitspersample == 24 ) + if( b_packetizer ) { - p_dec->fmt_out.i_codec = VLC_FOURCC('s','2','4','b'); + 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; + } } else { - p_dec->fmt_out.i_codec = VLC_FOURCC('s','1','6','b'); - p_dec->fmt_out.audio.i_bitspersample = 16; + 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; + } } /* Set callback */ @@ -142,20 +268,13 @@ static int OpenDecoder( vlc_object_t *p_this ) return VLC_SUCCESS; } - +static int OpenDecoder( vlc_object_t *p_this ) +{ + return OpenCommon( p_this, false ); +} static int OpenPacketizer( vlc_object_t *p_this ) { - decoder_t *p_dec = (decoder_t*)p_this; - - int i_ret = OpenDecoder( p_this ); - - if( i_ret != VLC_SUCCESS ) return i_ret; - - p_dec->p_sys->b_packetizer = VLC_TRUE; - - p_dec->fmt_out.i_codec = VLC_FOURCC('l','p','c','m'); - - return i_ret; + return OpenCommon( p_this, true ); } /***************************************************************************** @@ -167,9 +286,8 @@ static void *DecodeFrame( decoder_t *p_dec, block_t **pp_block ) { 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; - int i_frame_length, i_bitspersample; - uint8_t i_header; + 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; @@ -177,225 +295,716 @@ static void *DecodeFrame( decoder_t *p_dec, block_t **pp_block ) *pp_block = NULL; /* So the packet doesn't get re-sent */ /* Date management */ - if( p_block->i_pts > 0 && - p_block->i_pts != aout_DateGet( &p_sys->end_date ) ) + if( p_block->i_pts > VLC_TS_INVALID && + p_block->i_pts != date_Get( &p_sys->end_date ) ) { - aout_DateSet( &p_sys->end_date, p_block->i_pts ); + date_Set( &p_sys->end_date, p_block->i_pts ); } - if( !aout_DateGet( &p_sys->end_date ) ) + 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 <= LPCM_HEADER_LEN ) + if( p_block->i_buffer <= p_sys->i_header_size ) { msg_Err(p_dec, "frame is too short"); block_Release( p_block ); return NULL; } - i_header = p_block->p_buffer[4]; - switch ( (i_header >> 4) & 0x3 ) + int i_ret; + unsigned i_padding = 0; + aob_group_t p_aob_group[2]; + switch( p_sys->i_type ) + { + 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_ret || p_block->i_buffer <= p_sys->i_header_size + i_padding ) + { + msg_Warn( p_dec, "no frame sync or too small frame" ); + block_Release( p_block ); + return NULL; + } + + /* Set output properties */ + if( p_dec->fmt_out.audio.i_rate != i_rate ) + { + 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; + + i_frame_length = (p_block->i_buffer - p_sys->i_header_size - i_padding) / i_channels * 8 / i_bits; + + if( p_sys->b_packetizer ) + { + 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_dec->fmt_out.i_codec = VLC_CODEC_S24B; + p_dec->fmt_out.audio.i_bitspersample = 24; + } + + /* */ + 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; + } + + int i_bytes_consumed = 0; + + 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; } - i_channels = (i_header & 0x7); - switch ( i_channels ) + *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; } - switch ( (i_header >> 6) & 0x3 ) + switch( (i_header >> 6) & 0x3 ) { case 2: - p_dec->fmt_out.i_codec = VLC_FOURCC('s','2','4','b'); - p_dec->fmt_out.audio.i_bitspersample = 24; - i_bitspersample = 24; + *pi_bits = 24; break; case 1: - p_dec->fmt_out.i_codec = VLC_FOURCC('s','2','4','b'); - p_dec->fmt_out.audio.i_bitspersample = 24; - i_bitspersample = 20; + *pi_bits = 20; break; case 0: default: - p_dec->fmt_out.i_codec = VLC_FOURCC('s','1','6','b'); - p_dec->fmt_out.audio.i_bitspersample = 16; - i_bitspersample = 16; + *pi_bits = 16; break; } /* Check frame sync and drop it. */ - if( p_block->p_buffer[5] != 0x80 ) + 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, "no frame sync" ); - block_Release( p_block ); - return NULL; + i_channels1++; + i_layout1 |= p_aob_group1[i_assignment][i]; } - - /* Set output properties */ - if( p_dec->fmt_out.audio.i_rate != i_rate ) + /* Group2 */ + unsigned i_channels2 = 0; + unsigned i_layout2 = 0; + if( i_index_size_g2 != 0x0f && i_index_rate_g2 != 0x0f ) { - aout_DateInit( &p_sys->end_date, i_rate ); - aout_DateSet( &p_sys->end_date, p_block->i_pts ); + 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 ); } - p_dec->fmt_out.audio.i_rate = i_rate; - p_dec->fmt_out.audio.i_channels = i_channels + 1; - 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; + /* 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; - i_frame_length = (p_block->i_buffer - LPCM_HEADER_LEN) / - p_dec->fmt_out.audio.i_channels * 8 / i_bitspersample; + /* */ + *pi_channels = i_channels1 + ( b_group2_used ? i_channels2 : 0 ); + *pi_layout = i_layout1 | ( b_group2_used ? i_layout2 : 0 ); - if( p_sys->b_packetizer ) + /* */ + for( unsigned i = 0; i < 2; i++ ) { - p_dec->fmt_out.i_codec = VLC_FOURCC('l','p','c','m'); - p_block->i_pts = p_block->i_dts = aout_DateGet( &p_sys->end_date ); - p_block->i_length = - aout_DateIncrement( &p_sys->end_date, i_frame_length ) - - p_block->i_pts; + 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++ ) + { + 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; +} - /* Just pass on the incoming frame */ - return p_block; +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 ) + { + 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; + + default: + return -1; } - else + switch( (h >> 6) & 0x03 ) { - aout_buffer_t *p_aout_buffer; - p_aout_buffer = p_dec->pf_aout_buffer_new( p_dec, i_frame_length ); - if( p_aout_buffer == NULL ) return NULL; + 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; + } + switch( (h >> 8) & 0x0f ) + { + case 1: + *pi_rate = 48000; + break; + case 4: + *pi_rate = 96000; + break; + case 5: + *pi_rate = 192000; + break; + default: + return -1; + } + return 0; +} - p_aout_buffer->start_date = aout_DateGet( &p_sys->end_date ); - p_aout_buffer->end_date = - aout_DateIncrement( &p_sys->end_date, i_frame_length ); +static void VobExtract( aout_buffer_t *p_aout_buffer, block_t *p_block, + unsigned i_bits ) +{ + uint8_t *p_out = p_aout_buffer->p_buffer; - p_block->p_buffer += LPCM_HEADER_LEN; - p_block->i_buffer -= LPCM_HEADER_LEN; + /* 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 + { + 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; - /* 20/24 bits LPCM use special packing */ - if( i_bitspersample == 24 ) + while( p_block->i_buffer > 0 ) + { + for( int i = 0; i < 2; i++ ) { - uint8_t *p_out = p_aout_buffer->p_buffer; + const aob_group_t *g = &p_group[1-i]; + const unsigned int i_group_size = 2 * g->i_channels * i_bits / 8; - while( p_block->i_buffer / 12 ) + if( p_block->i_buffer < i_group_size ) { - /* 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; + p_block->i_buffer = 0; + break; } - } - else if( i_bitspersample == 20 ) - { - uint8_t *p_out = p_aout_buffer->p_buffer; - - while( p_block->i_buffer / 10 ) + for( unsigned n = 0; n < 2; n++ ) { - /* 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; + 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; } - else - { - memcpy( p_aout_buffer->p_buffer, - p_block->p_buffer, p_block->i_buffer ); - } + /* */ + p_out += (i_bits == 16 ? 2 : 3) * i_channels * 2; - block_Release( p_block ); - return p_aout_buffer; } } - -/***************************************************************************** - * CloseDecoder : lpcm decoder destruction - *****************************************************************************/ -static void CloseDecoder( vlc_object_t *p_this ) +static void BdExtract( aout_buffer_t *p_aout_buffer, block_t *p_block ) { - decoder_t *p_dec = (decoder_t*)p_this; - free( p_dec->p_sys ); + memcpy( p_aout_buffer->p_buffer, p_block->p_buffer, p_block->i_buffer ); } +