From: Gildas Bazin Date: Fri, 21 Nov 2003 01:45:48 +0000 (+0000) Subject: * modules/codec/flac.c: complete rewrite of the flac decoder + new packetizer. X-Git-Tag: 0.7.0~455 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=40c4b64095e00df941c5681c9f2b70f1dc671873;p=vlc * modules/codec/flac.c: complete rewrite of the flac decoder + new packetizer. * modules/demux/flac.c: rewrite of the flac demuxer which makes use of the flac packetizer. --- diff --git a/modules/codec/Modules.am b/modules/codec/Modules.am index 515e7a4d8c..131b93da17 100644 --- a/modules/codec/Modules.am +++ b/modules/codec/Modules.am @@ -1,7 +1,7 @@ SOURCES_a52 = a52.c SOURCES_cinepak = cinepak.c SOURCES_dts = dts.c -SOURCES_flacdec = flacdec.c +SOURCES_flacdec = flac.c SOURCES_lpcm = lpcm.c SOURCES_araw = araw.c SOURCES_vorbis = vorbis.c diff --git a/modules/codec/flac.c b/modules/codec/flac.c new file mode 100644 index 0000000000..05ea8aa720 --- /dev/null +++ b/modules/codec/flac.c @@ -0,0 +1,1001 @@ +/***************************************************************************** + * flac.c: flac decoder/packetizer module making use of libflac + ***************************************************************************** + * Copyright (C) 1999-2001 VideoLAN + * $Id: flac.c,v 1.1 2003/11/21 01:45:48 gbazin Exp $ + * + * Authors: Gildas Bazin + * Sigmund Augdal + * + * 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 + * GNU General Public License for more details. + * + * 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. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#include +#include +#include + +#include + +#include "vlc_block_helper.h" + +#define MAX_FLAC_HEADER_SIZE 16 + +/***************************************************************************** + * decoder_sys_t : FLAC decoder descriptor + *****************************************************************************/ +struct decoder_sys_t +{ + /* Module mode */ + vlc_bool_t b_packetizer; + + /* + * Input properties + */ + int i_state; + + block_bytestream_t bytestream; + + /* + * Input/Output properties + */ + block_t *p_block; + aout_buffer_t *p_aout_buffer; + + /* + * FLAC properties + */ + FLAC__StreamDecoder *p_flac; + + vlc_bool_t b_stream_info; + FLAC__StreamMetadata_StreamInfo stream_info; + + /* + * Common properties + */ + audio_date_t end_date; + mtime_t i_pts; + + int i_frame_size, i_frame_length, i_bits_per_sample; + unsigned int i_rate, i_channels, i_channels_conf; +}; + +enum { + + STATE_NOSYNC, + STATE_SYNC, + STATE_HEADER, + STATE_NEXT_SYNC, + STATE_GET_DATA, + STATE_SEND_DATA +}; + +static int pi_channels_maps[6] = +{ + 0, + AOUT_CHAN_CENTER, + AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT, + AOUT_CHAN_CENTER | AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT, + AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT + | AOUT_CHAN_REARRIGHT, + AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER + | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT +}; + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int OpenDecoder ( vlc_object_t * ); +static int OpenPacketizer( vlc_object_t * ); +static void CloseDecoder ( vlc_object_t * ); + +static aout_buffer_t *DecodeBlock( decoder_t *, block_t ** ); +static block_t *PacketizeBlock( decoder_t *, block_t ** ); + +static int SyncInfo( decoder_t *, uint8_t *, int *, int *, int *,int * ); + + +static FLAC__StreamDecoderReadStatus +DecoderReadCallback( const FLAC__StreamDecoder *decoder, + FLAC__byte buffer[], unsigned *bytes, void *client_data ); + +static FLAC__StreamDecoderWriteStatus +DecoderWriteCallback( const FLAC__StreamDecoder *decoder, + const FLAC__Frame *frame, + const FLAC__int32 *const buffer[], void *client_data ); + +static void DecoderMetadataCallback( const FLAC__StreamDecoder *decoder, + const FLAC__StreamMetadata *metadata, + void *client_data ); +static void DecoderErrorCallback( const FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderErrorStatus status, + void *client_data); + +static void Interleave32( int32_t *p_out, const int32_t * const *pp_in, + int i_nb_channels, int i_samples ); +static void Interleave16( int16_t *p_out, const int32_t * const *pp_in, + int i_nb_channels, int i_samples ); + +static void decoder_state_error( decoder_t *p_dec, + FLAC__StreamDecoderState state ); + +static uint64_t read_utf8( const uint8_t *p_buf, int *pi_read ); +static uint8_t flac_crc8( const uint8_t *data, unsigned len ); + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +vlc_module_begin(); + + set_description( _("Flac audio decoder") ); + set_capability( "decoder", 100 ); + set_callbacks( OpenDecoder, CloseDecoder ); + + add_submodule(); + set_description( _("Flac audio packetizer") ); + set_capability( "packetizer", 100 ); + set_callbacks( OpenPacketizer, CloseDecoder ); + +vlc_module_end(); + +/***************************************************************************** + * OpenDecoder: probe the decoder and return score + *****************************************************************************/ +static int OpenDecoder( vlc_object_t *p_this ) +{ + decoder_t *p_dec = (decoder_t*)p_this; + decoder_sys_t *p_sys; + + if( p_dec->fmt_in.i_codec != VLC_FOURCC('f','l','a','c') ) + { + 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; + } + + /* Misc init */ + aout_DateSet( &p_sys->end_date, 0 ); + p_sys->b_packetizer = VLC_FALSE; + p_sys->i_state = STATE_NOSYNC; + + p_sys->bytestream = block_BytestreamInit( p_dec ); + + /* Take care of flac init */ + if( !(p_sys->p_flac = FLAC__stream_decoder_new()) ) + { + msg_Err( p_dec, "FLAC__stream_decoder_new() failed" ); + free( p_sys ); + return VLC_EGENERIC; + } + + FLAC__stream_decoder_set_read_callback( p_sys->p_flac, + DecoderReadCallback ); + FLAC__stream_decoder_set_write_callback( p_sys->p_flac, + DecoderWriteCallback ); + FLAC__stream_decoder_set_metadata_callback( p_sys->p_flac, + DecoderMetadataCallback ); + FLAC__stream_decoder_set_error_callback( p_sys->p_flac, + DecoderErrorCallback ); + FLAC__stream_decoder_set_client_data( p_sys->p_flac, p_dec ); + + FLAC__stream_decoder_init( p_sys->p_flac ); + + /* Set output properties */ + p_dec->fmt_out.i_cat = AUDIO_ES; + p_dec->fmt_out.i_codec = VLC_FOURCC('f','l','3','2'); + + /* Set callbacks */ + p_dec->pf_decode_audio = DecodeBlock; + p_dec->pf_packetize = PacketizeBlock; + + + /* Decode STREAMINFO */ + msg_Dbg( p_dec, "decode STREAMINFO" ); + p_sys->p_block = block_New( p_dec, p_dec->fmt_in.i_extra + 4 ); + memcpy( p_sys->p_block->p_buffer + 4, p_dec->fmt_in.p_extra, + p_dec->fmt_in.i_extra ); + memcpy( p_sys->p_block->p_buffer, "fLaC", 4 ); + FLAC__stream_decoder_process_until_end_of_metadata( p_sys->p_flac ); + msg_Dbg( p_dec, "STREAMINFO decoded" ); + + return VLC_SUCCESS; +} + +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 ) + { + p_dec->p_sys->b_packetizer = VLC_TRUE; + p_dec->fmt_out.i_codec = VLC_FOURCC('f','l','a','c'); + } + + return i_ret; +} + +/**************************************************************************** + * PacketizeBlock: the whole thing + **************************************************************************** + * This function is called just after the thread is launched. + ****************************************************************************/ +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[MAX_FLAC_HEADER_SIZE]; + block_t *p_sout_block; + + if( !pp_block || !*pp_block ) return NULL; + +#if 0 + if( (*pp_block)->b_discontinuity ) + { + p_sys->i_state = STATE_NOSYNC; + } +#endif + + 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] == 0xFF && p_header[1] == 0xF8 ) + { + 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 != 0 && + p_sys->i_pts != aout_DateGet( &p_sys->end_date ) ) + { + aout_DateSet( &p_sys->end_date, p_sys->i_pts ); + } + p_sys->i_state = STATE_HEADER; + + case STATE_HEADER: + /* Get FLAC frame header (MAX_FLAC_HEADER_SIZE bytes) */ + if( block_PeekBytes( &p_sys->bytestream, p_header, + MAX_FLAC_HEADER_SIZE ) != VLC_SUCCESS ) + { + /* Need more data */ + return NULL; + } + + /* Check if frame is valid and get frame info */ + p_sys->i_frame_length = SyncInfo( p_dec, p_header, + &p_sys->i_channels, + &p_sys->i_channels_conf, + &p_sys->i_rate, + &p_sys->i_bits_per_sample ); + if( !p_sys->i_frame_length ) + { + 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; + p_sys->i_frame_size = 1; + + 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 */ + while( block_PeekOffsetBytes( &p_sys->bytestream, + p_sys->i_frame_size, p_header, + MAX_FLAC_HEADER_SIZE ) + == VLC_SUCCESS ) + { + if( p_header[0] == 0xFF && p_header[1] == 0xF8 ) + { + /* Check if frame is valid and get frame info */ + p_sys->i_frame_length = + SyncInfo( p_dec, p_header, + &p_sys->i_channels, + &p_sys->i_channels_conf, + &p_sys->i_rate, + &p_sys->i_bits_per_sample ); + + if( p_sys->i_frame_length ) + { + p_sys->i_state = STATE_SEND_DATA; + break; + } + } + p_sys->i_frame_size++; + } + + if( p_sys->i_state != STATE_SEND_DATA ) + { + /* Need more data */ + return NULL; + } + break; + + case STATE_SEND_DATA: + p_sout_block = block_New( p_dec, p_sys->i_frame_size ); + + /* 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_sout_block->p_buffer, + p_sys->i_frame_size ); + + /* 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 = 0; + + /* So p_block doesn't get re-added several times */ + *pp_block = block_BytestreamPop( &p_sys->bytestream ); + + p_sys->i_state = STATE_NOSYNC; + + /* Date management */ + p_sout_block->i_pts = aout_DateGet( &p_sys->end_date ); + p_sout_block->i_length = + aout_DateIncrement( &p_sys->end_date, p_sys->i_frame_length ); + + return p_sout_block; + } + } + + return NULL; +} + +/**************************************************************************** + * DecodeBlock: the whole thing + ****************************************************************************/ +static aout_buffer_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) +{ + decoder_sys_t *p_sys = p_dec->p_sys; + + if( !pp_block || !*pp_block ) return NULL; + + if( !aout_DateGet( &p_sys->end_date ) && !(*pp_block)->i_pts ) + { + /* We've just started the stream, wait for the first PTS. */ + block_Release( *pp_block ); + return NULL; + } + + p_sys->p_aout_buffer = 0; + if( ( p_sys->p_block = PacketizeBlock( p_dec, pp_block ) ) ) + { + if( !FLAC__stream_decoder_process_single( p_sys->p_flac ) ) + { + decoder_state_error( p_dec, + FLAC__stream_decoder_get_state( p_sys->p_flac ) ); + } + block_Release( p_sys->p_block ); + } + + return p_sys->p_aout_buffer; +} + +/***************************************************************************** + * CloseDecoder: flac decoder destruction + *****************************************************************************/ +static void CloseDecoder( vlc_object_t *p_this ) +{ + decoder_t *p_dec = (decoder_t *)p_this; + decoder_sys_t *p_sys = p_dec->p_sys; + + FLAC__stream_decoder_finish( p_sys->p_flac ); + FLAC__stream_decoder_delete( p_sys->p_flac ); + free( p_sys ); +} + +/***************************************************************************** + * DecoderReadCallback: called by libflac when it needs more data + *****************************************************************************/ +static FLAC__StreamDecoderReadStatus +DecoderReadCallback( const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], + unsigned *bytes, void *client_data ) +{ + decoder_t *p_dec = (decoder_t *)client_data; + decoder_sys_t *p_sys = p_dec->p_sys; + + msg_Err( p_dec, "buffer: %i", p_sys->p_block->i_buffer ); + if( p_sys->p_block && p_sys->p_block->i_buffer ) + { + *bytes = __MIN(*bytes, (unsigned)p_sys->p_block->i_buffer); + memcpy( buffer, p_sys->p_block->p_buffer, *bytes ); + p_sys->p_block->i_buffer -= *bytes; + p_sys->p_block->p_buffer += *bytes; + } + else + { + *bytes = 0; + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } + + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; +} + +/***************************************************************************** + * DecoderWriteCallback: called by libflac to output decoded samples + *****************************************************************************/ +static FLAC__StreamDecoderWriteStatus +DecoderWriteCallback( const FLAC__StreamDecoder *decoder, + const FLAC__Frame *frame, + const FLAC__int32 *const buffer[], void *client_data ) +{ + decoder_t *p_dec = (decoder_t *)client_data; + decoder_sys_t *p_sys = p_dec->p_sys; + + if( p_sys->p_block && p_sys->p_block->i_pts != 0 && + p_sys->p_block->i_pts != aout_DateGet( &p_sys->end_date ) ) + { + aout_DateSet( &p_sys->end_date, p_sys->p_block->i_pts ); + } + else if( !aout_DateGet( &p_sys->end_date ) ) + { + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } + + if( p_sys->p_block ) p_sys->p_block->i_pts = 0; + + p_sys->p_aout_buffer = + p_dec->pf_aout_buffer_new( p_dec, frame->header.blocksize ); + + if( p_sys->p_aout_buffer == NULL ) + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + + switch( frame->header.bits_per_sample ) + { + case 16: + Interleave16( (int16_t *)p_sys->p_aout_buffer->p_buffer, buffer, + frame->header.channels, frame->header.blocksize ); + break; + default: + Interleave32( (int32_t *)p_sys->p_aout_buffer->p_buffer, buffer, + frame->header.channels, frame->header.blocksize ); + } + + /* Date management */ + p_sys->p_aout_buffer->start_date = aout_DateGet( &p_sys->end_date ); + p_sys->p_aout_buffer->end_date = + aout_DateIncrement( &p_sys->end_date, frame->header.blocksize ); + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +/***************************************************************************** + * DecoderMetadataCallback: called by libflac to when it encounters metadata + *****************************************************************************/ +static void DecoderMetadataCallback( const FLAC__StreamDecoder *decoder, + const FLAC__StreamMetadata *metadata, + void *client_data ) +{ + decoder_t *p_dec = (decoder_t *)client_data; + decoder_sys_t *p_sys = p_dec->p_sys; + + switch( metadata->data.stream_info.bits_per_sample ) + { + case 8: + p_dec->fmt_out.i_codec = VLC_FOURCC('s','8',' ',' '); + break; + case 16: + p_dec->fmt_out.i_codec = AOUT_FMT_S16_NE; + break; + default: + msg_Dbg( p_dec, "strange bps %d", + metadata->data.stream_info.bits_per_sample ); + p_dec->fmt_out.i_codec = VLC_FOURCC('f','i','3','2'); + break; + } + + /* Setup the format */ + p_dec->fmt_out.audio.i_rate = metadata->data.stream_info.sample_rate; + p_dec->fmt_out.audio.i_channels = metadata->data.stream_info.channels; + p_dec->fmt_out.audio.i_physical_channels = + p_dec->fmt_out.audio.i_original_channels = + pi_channels_maps[metadata->data.stream_info.channels]; + p_dec->fmt_out.audio.i_bitspersample = + metadata->data.stream_info.bits_per_sample; + + aout_DateInit( &p_sys->end_date, p_dec->fmt_out.audio.i_rate ); + + msg_Dbg( p_dec, "channels:%d samplerate:%d bitspersamples:%d", + p_dec->fmt_out.audio.i_channels, p_dec->fmt_out.audio.i_rate, + p_dec->fmt_out.audio.i_bitspersample ); + + p_sys->b_stream_info = VLC_TRUE; + p_sys->stream_info = metadata->data.stream_info; + + return; +} + +/***************************************************************************** + * DecoderErrorCallback: called when the libflac decoder encounters an error + *****************************************************************************/ +static void DecoderErrorCallback( const FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderErrorStatus status, + void *client_data ) +{ + decoder_t *p_dec = (decoder_t *)client_data; + + switch( status ) + { + case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC: + msg_Err( p_dec, "An error in the stream caused the decoder to " + "loose synchronization." ); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER: + msg_Err( p_dec, "The decoder encountered a corrupted frame header." ); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH: + msg_Err( p_dec, "The frame's data did not match the CRC in the " + "footer." ); + break; + default: + msg_Err( p_dec, "got decoder error: %d", status ); + } + return; +} + +/***************************************************************************** + * Interleave: helper function to interleave channels + *****************************************************************************/ +static void Interleave32( int32_t *p_out, const int32_t * const *pp_in, + int i_nb_channels, int i_samples ) +{ + int i, j; + for ( j = 0; j < i_samples; j++ ) + { + for ( i = 0; i < i_nb_channels; i++ ) + { + p_out[j * i_nb_channels + i] = pp_in[i][j]; + } + } +} +static void Interleave16( int16_t *p_out, const int32_t * const *pp_in, + int i_nb_channels, int i_samples ) +{ + int i, j; + for ( j = 0; j < i_samples; j++ ) + { + for ( i = 0; i < i_nb_channels; i++ ) + { + p_out[j * i_nb_channels + i] = (int32_t)(pp_in[i][j]); + } + } +} + +/***************************************************************************** + * decoder_state_error: print meaningful error messages + *****************************************************************************/ +static void decoder_state_error( decoder_t *p_dec, + FLAC__StreamDecoderState state ) +{ + switch ( state ) + { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + msg_Err( p_dec, "The decoder is ready to search for metadata." ); + break; + case FLAC__STREAM_DECODER_READ_METADATA: + msg_Err( p_dec, "The decoder is ready to or is in the process of " + "reading metadata." ); + break; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + msg_Err( p_dec, "The decoder is ready to or is in the process of " + "searching for the frame sync code." ); + break; + case FLAC__STREAM_DECODER_READ_FRAME: + msg_Err( p_dec, "The decoder is ready to or is in the process of " + "reading a frame." ); + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + msg_Err( p_dec, "The decoder has reached the end of the stream." ); + break; + case FLAC__STREAM_DECODER_ABORTED: + msg_Err( p_dec, "The decoder was aborted by the read callback." ); + break; + case FLAC__STREAM_DECODER_UNPARSEABLE_STREAM: + msg_Err( p_dec, "The decoder encountered reserved fields in use " + "in the stream." ); + FLAC__stream_decoder_flush( p_dec->p_sys->p_flac ); + break; + case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR: + msg_Err( p_dec, "An error occurred allocating memory." ); + break; + case FLAC__STREAM_DECODER_ALREADY_INITIALIZED: + msg_Err( p_dec, "FLAC__stream_decoder_init() was called when the " + "decoder was already initialized, usually because " + "FLAC__stream_decoder_finish() was not called." ); + break; + case FLAC__STREAM_DECODER_INVALID_CALLBACK: + msg_Err( p_dec, "FLAC__stream_decoder_init() was called without " + "all callbacks being set." ); + break; + case FLAC__STREAM_DECODER_UNINITIALIZED: + msg_Err( p_dec, "The decoder is in the uninitialized state." ); + break; + default: + msg_Err(p_dec, "unknown error" ); + } +} + +/***************************************************************************** + * SyncInfo: parse FLAC sync info + *****************************************************************************/ +static int SyncInfo( decoder_t *p_dec, uint8_t *p_buf, + int * pi_channels, int * pi_channels_conf, + int * pi_sample_rate, int * pi_bits_per_sample ) +{ + decoder_sys_t *p_sys = p_dec->p_sys; + int i_header, i_temp, i_read; + int i_blocksize = 0, i_blocksize_hint = 0, i_sample_rate_hint = 0; + uint64_t i_sample_number = 0; + + vlc_bool_t b_variable_blocksize = ( p_sys->b_stream_info && + p_sys->stream_info.min_blocksize != p_sys->stream_info.max_blocksize ); + vlc_bool_t b_fixed_blocksize = ( p_sys->b_stream_info && + p_sys->stream_info.min_blocksize == p_sys->stream_info.max_blocksize ); + + /* Check syncword */ + if( p_buf[0] != 0xFF || p_buf[1] != 0xF8 ) return 0; + + /* Check there is no emulated sync code in the rest of the header */ + if( p_buf[2] == 0xff || p_buf[3] == 0xFF ) return 0; + + /* Find blocksize (framelength) */ + switch( i_temp = p_buf[2] >> 4 ) + { + case 0: + if( b_fixed_blocksize ) + i_blocksize = p_sys->stream_info.min_blocksize; + else return 0; /* We can't do anything with this */ + break; + + case 1: + i_blocksize = 192; + break; + + case 2: + case 3: + case 4: + case 5: + i_blocksize = 576 << (i_temp - 2); + break; + + case 6: + case 7: + i_blocksize_hint = i_temp; + break; + + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + i_blocksize = 256 << (i_temp - 8); + break; + } + + /* Find samplerate */ + switch( i_temp = p_buf[2] & 0x0f ) + { + case 0: + if( p_sys->b_stream_info ) + *pi_sample_rate = p_sys->stream_info.sample_rate; + else return 0; /* We can't do anything with this */ + break; + + case 1: + case 2: + case 3: + return 0; + break; + + case 4: + *pi_sample_rate = 8000; + break; + + case 5: + *pi_sample_rate = 16000; + break; + + case 6: + *pi_sample_rate = 22050; + break; + + case 7: + *pi_sample_rate = 24000; + break; + + case 8: + *pi_sample_rate = 32000; + break; + + case 9: + *pi_sample_rate = 44100; + break; + + case 10: + *pi_sample_rate = 48000; + break; + + case 11: + *pi_sample_rate = 96000; + break; + + case 12: + case 13: + case 14: + i_sample_rate_hint = i_temp; + break; + + case 15: + return 0; + } + + /* Find channels */ + i_temp = (unsigned)(p_buf[3] >> 4); + if( i_temp & 8 ) + { + int i_channel_assignment; /* ??? */ + + *pi_channels = 2; + switch( i_temp & 7 ) + { + case 0: + i_channel_assignment = FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE; + break; + case 1: + i_channel_assignment = FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE; + break; + case 2: + i_channel_assignment = FLAC__CHANNEL_ASSIGNMENT_MID_SIDE; + break; + default: + return 0; + break; + } + } + else + { + *pi_channels = i_temp + 1; + *pi_channels_conf = pi_channels_maps[ *pi_channels ]; + } + + /* Find bits per sample */ + switch( i_temp = (unsigned)(p_buf[3] & 0x0e) >> 1 ) + { + case 0: + if( p_sys->b_stream_info ) + *pi_bits_per_sample = p_sys->stream_info.bits_per_sample; + else + return 0; + break; + + case 1: + *pi_bits_per_sample = 8; + break; + + case 2: + *pi_bits_per_sample = 12; + break; + + case 4: + *pi_bits_per_sample = 16; + break; + + case 5: + *pi_bits_per_sample = 20; + break; + + case 6: + *pi_bits_per_sample = 24; + break; + + case 3: + case 7: + return 0; + break; + } + + /* Zero padding bit */ + if( p_buf[3] & 0x01 ) return 0; + + /* End of fixed size header */ + i_header = 4; + + /* Find Sample/Frame number */ + if( i_blocksize_hint && b_variable_blocksize ) + { + i_sample_number = read_utf8( &p_buf[i_header++], &i_read ); + if( i_sample_number == I64C(0xffffffffffffffff) ) return 0; + } + else + { + i_sample_number = read_utf8( &p_buf[i_header++], &i_read ); + if( i_sample_number == I64C(0xffffffffffffffff) ) return 0; + + if( p_sys->b_stream_info ) + i_sample_number *= p_sys->stream_info.min_blocksize; + } + + i_header += i_read; + + /* Read blocksize */ + if( i_blocksize_hint ) + { + int i_val1 = p_buf[i_header++]; + if( i_blocksize_hint == 7 ) + { + int i_val2 = p_buf[i_header++]; + i_val1 = (i_val1 << 8) | i_val2; + } + i_blocksize = i_val1 + 1; + } + + /* Read sample rate */ + if( i_sample_rate_hint ) + { + int i_val1 = p_buf[i_header++]; + if( i_sample_rate_hint != 12 ) + { + int i_val2 = p_buf[i_header++]; + i_val1 = (i_val1 << 8) | i_val2; + } + if( i_sample_rate_hint == 12 ) *pi_sample_rate = i_val1 * 1000; + else if( i_sample_rate_hint == 13 ) *pi_sample_rate = i_val1; + else *pi_sample_rate = i_val1 * 10; + } + + /* Check the CRC-8 byte */ + if( flac_crc8( p_buf, i_header ) != p_buf[i_header] ) + { + return 0; + } + + return i_blocksize; +} + +/* Will return 0xffffffffffffffff for an invalid utf-8 sequence */ +static uint64_t read_utf8( const uint8_t *p_buf, int *pi_read ) +{ + uint64_t i_result = 0; + unsigned i, j; + + if( !(p_buf[0] & 0x80) ) /* 0xxxxxxx */ + { + i_result = p_buf[0]; + i = 0; + } + else if( p_buf[0] & 0xC0 && !(p_buf[0] & 0x20) ) /* 110xxxxx */ + { + i_result = p_buf[0] & 0x1F; + i = 1; + } + else if( p_buf[0] & 0xE0 && !(p_buf[0] & 0x10) ) /* 1110xxxx */ + { + i_result = p_buf[0] & 0x0F; + i = 2; + } + else if( p_buf[0] & 0xF0 && !(p_buf[0] & 0x08) ) /* 11110xxx */ + { + i_result = p_buf[0] & 0x07; + i = 3; + } + else if( p_buf[0] & 0xF8 && !(p_buf[0] & 0x04) ) /* 111110xx */ + { + i_result = p_buf[0] & 0x03; + i = 4; + } + else if( p_buf[0] & 0xFC && !(p_buf[0] & 0x02) ) /* 1111110x */ + { + i_result = p_buf[0] & 0x01; + i = 5; + } + else if( p_buf[0] & 0xFE && !(p_buf[0] & 0x01) ) /* 11111110 */ + { + i_result = 0; + i = 6; + } + else { + return I64C(0xffffffffffffffff); + } + + for( j = 1; j <= i; j++ ) + { + if( !(p_buf[j] & 0x80) || (p_buf[j] & 0x40) ) /* 10xxxxxx */ + { + return I64C(0xffffffffffffffff); + } + i_result <<= 6; + i_result |= (p_buf[j] & 0x3F); + } + + *pi_read = i; + return i_result; +} + +/* CRC-8, poly = x^8 + x^2 + x^1 + x^0, init = 0 */ +static uint8_t const flac_crc8_table[256] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, + 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, + 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, + 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, + 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, + 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, + 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, + 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, + 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, + 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, + 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, + 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, + 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, + 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, + 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, + 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +static uint8_t flac_crc8( const uint8_t *data, unsigned len ) +{ + uint8_t crc = 0; + + while(len--) + crc = flac_crc8_table[crc ^ *data++]; + + return crc; +} diff --git a/modules/codec/flacdec.c b/modules/codec/flacdec.c deleted file mode 100644 index 3b0cd72da7..0000000000 --- a/modules/codec/flacdec.c +++ /dev/null @@ -1,457 +0,0 @@ -/***************************************************************************** - * flac.c: flac decoder module making use of libflac - ***************************************************************************** - * Copyright (C) 1999-2001 VideoLAN - * $Id: flacdec.c,v 1.5 2003/11/16 21:07:30 gbazin Exp $ - * - * Authors: Sigmund Augdal - * - * 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 - * GNU General Public License for more details. - * - * 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. - *****************************************************************************/ - -/***************************************************************************** - * Preamble - *****************************************************************************/ -#include /* malloc(), free() */ -#include /* memcpy(), memset() */ -#include - -#include -#include -#include -#include - -#include - -#include - -/***************************************************************************** - * dec_thread_t : flac decoder thread descriptor - *****************************************************************************/ -typedef struct dec_thread_t -{ - /* - * Thread properties - */ - vlc_thread_t thread_id; /* id for thread functions */ - - /* - * Input properties - */ - decoder_fifo_t *p_fifo; /* stores the PES stream data */ - pes_packet_t *p_pes; /* current PES we are decoding */ - int i_last_pes_pos; /* possition into pes*/ - - int i_tot; - /* - * libflac decoder struct - */ - FLAC__StreamDecoder *p_decoder; - - /* - * Output properties - */ - aout_instance_t *p_aout; - aout_input_t *p_aout_input; - audio_sample_format_t output_format; - audio_date_t end_date; - mtime_t pts; - -} dec_thread_t; - -static int pi_channels_maps[6] = -{ - 0, - AOUT_CHAN_CENTER, AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT, - AOUT_CHAN_CENTER | AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT, - AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT - | AOUT_CHAN_REARRIGHT, - AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER - | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT -}; - -/***************************************************************************** - * Local prototypes - *****************************************************************************/ -static int OpenDecoder ( vlc_object_t * ); -static int RunDecoder ( decoder_fifo_t * ); -static void CloseDecoder ( dec_thread_t * ); - -static FLAC__StreamDecoderReadStatus DecoderReadCallback (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data); - -static FLAC__StreamDecoderWriteStatus DecoderWriteCallback (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data); - -static void DecoderMetadataCallback (const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); -static void DecoderErrorCallback (const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); -static void Interleave32( int32_t *p_out, const int32_t * const *pp_in, - int i_nb_channels, int i_samples ); -static void Interleave16( int16_t *p_out, const int32_t * const *pp_in, - int i_nb_channels, int i_samples ); -static void decoder_state_error( dec_thread_t *p_dec, FLAC__StreamDecoderState state ); -/***************************************************************************** - * Module descriptor - *****************************************************************************/ -vlc_module_begin(); - set_description( _("flac audio decoder") ); - set_capability( "decoder", 100 ); - set_callbacks( OpenDecoder, NULL ); -vlc_module_end(); - -/***************************************************************************** - * OpenDecoder: probe the decoder and return score - *****************************************************************************/ -static int OpenDecoder( vlc_object_t *p_this ) -{ - decoder_t *p_dec = (decoder_t*)p_this; - - if( p_dec->fmt_in.i_codec != VLC_FOURCC('f','l','a','c') ) - { - return VLC_EGENERIC; - } - - p_dec->pf_run = RunDecoder; - return VLC_SUCCESS; -} - -/***************************************************************************** - * RunDecoder: the vorbis decoder - *****************************************************************************/ -static int RunDecoder( decoder_fifo_t * p_fifo ) -{ - dec_thread_t *p_dec; - FLAC__StreamDecoderState state; - /* Allocate the memory needed to store the thread's structure */ - if( (p_dec = (dec_thread_t *)malloc (sizeof(dec_thread_t)) ) - == NULL) - { - msg_Err( p_fifo, "out of memory" ); - goto error; - } - - /* Initialize the thread properties */ - memset( p_dec, 0, sizeof(dec_thread_t) ); - p_dec->p_fifo = p_fifo; - p_dec->p_pes = NULL; - p_dec->p_decoder = FLAC__stream_decoder_new(); - if( p_dec->p_decoder == NULL ) - { - msg_Err( p_fifo, "FLAC__stream_decoder_new() failed" ); - goto error; - } - FLAC__stream_decoder_set_read_callback( p_dec->p_decoder, - DecoderReadCallback ); - FLAC__stream_decoder_set_write_callback( p_dec->p_decoder, - DecoderWriteCallback ); - FLAC__stream_decoder_set_metadata_callback( p_dec->p_decoder, - DecoderMetadataCallback ); - FLAC__stream_decoder_set_error_callback( p_dec->p_decoder, - DecoderErrorCallback ); - FLAC__stream_decoder_set_client_data( p_dec->p_decoder, - p_dec ); - - - FLAC__stream_decoder_init( p_dec->p_decoder ); - if ( !FLAC__stream_decoder_process_until_end_of_metadata( p_dec->p_decoder ) ) - { - state = FLAC__stream_decoder_get_state( p_dec->p_decoder ); - decoder_state_error( p_dec, state ); - goto error; - } - - aout_DateInit( &p_dec->end_date, p_dec->output_format.i_rate ); - p_dec->p_aout = NULL; - 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 ) - { - msg_Err( p_dec->p_fifo, "failed to create aout fifo" ); - goto error; - } - - /* flac decoder thread's main loop */ - while( (!p_dec->p_fifo->b_die) && (!p_dec->p_fifo->b_error) ) - { - if ( !FLAC__stream_decoder_process_single( p_dec->p_decoder ) ) - { - state = FLAC__stream_decoder_get_state( p_dec->p_decoder ); - decoder_state_error( p_dec, state ); - } - } - - /* If b_error is set, the vorbis decoder thread enters the error loop */ - if( p_dec->p_fifo->b_error ) - { - DecoderError( p_dec->p_fifo ); - } - - /* End of the vorbis decoder thread */ - CloseDecoder( p_dec ); - - return 0; - - error: - DecoderError( p_fifo ); - if( p_dec ) - { - if( p_dec->p_fifo ) - p_dec->p_fifo->b_error = 1; - - /* End of the vorbis decoder thread */ - CloseDecoder( p_dec ); - } - - return -1; -} - -/***************************************************************************** - * CloseDecoder: closes the decoder - *****************************************************************************/ -static void CloseDecoder ( dec_thread_t *p_dec ) -{ - if( p_dec->p_aout_input != NULL ) - { - aout_DecDelete( p_dec->p_aout, p_dec->p_aout_input ); - } - - if( p_dec ) - { - if( p_dec->p_pes ) - input_DeletePES( p_dec->p_fifo->p_packets_mgt, p_dec->p_pes ); - FLAC__stream_decoder_finish( p_dec->p_decoder ); - FLAC__stream_decoder_delete( p_dec->p_decoder ); - free( p_dec ); - } - -} - - - -/***************************************************************************** - * DecoderReadCallback: called by libflac when it needs more data - *****************************************************************************/ -static FLAC__StreamDecoderReadStatus DecoderReadCallback (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data) -{ - dec_thread_t *p_dec = (dec_thread_t *)client_data; - if( !p_dec->i_last_pes_pos ) - { - input_DeletePES( p_dec->p_fifo->p_packets_mgt, - p_dec->p_pes ); - input_ExtractPES( p_dec->p_fifo, &p_dec->p_pes ); - if( !p_dec->p_pes ) - { - return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; - } - } - p_dec->pts = p_dec->p_pes->i_pts; - if( ( p_dec->p_pes->i_pes_size - p_dec->i_last_pes_pos ) > *bytes ) - { - p_dec->p_fifo->p_vlc->pf_memcpy( buffer, - p_dec->p_pes->p_first->p_payload_start - + p_dec->i_last_pes_pos, - *bytes ); - p_dec->i_last_pes_pos += *bytes; - } - else - { - p_dec->p_fifo->p_vlc->pf_memcpy( buffer, - p_dec->p_pes->p_first->p_payload_start - + p_dec->i_last_pes_pos, - p_dec->p_pes->i_pes_size - - p_dec->i_last_pes_pos ); - *bytes = p_dec->p_pes->i_pes_size - p_dec->i_last_pes_pos ; - p_dec->i_last_pes_pos = 0; - } - p_dec->i_tot += *bytes; - - return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; -} - -/***************************************************************************** - * DecoderWriteCallback: called by libflac to output decoded samples - *****************************************************************************/ -static FLAC__StreamDecoderWriteStatus DecoderWriteCallback ( - const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, - const FLAC__int32 *const buffer[], void *client_data ) -{ - dec_thread_t *p_dec = (dec_thread_t *)client_data; - int i_samples = frame->header.blocksize; - aout_buffer_t *p_aout_buffer; - p_aout_buffer = aout_DecNewBuffer( p_dec->p_aout, p_dec->p_aout_input, - i_samples ); - if( !p_aout_buffer ) - { - msg_Err( p_dec->p_fifo, "cannot get aout buffer" ); - p_dec->p_fifo->b_error = 1; - return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; - } - switch ( frame->header.bits_per_sample ) - { - case 16: - Interleave16( (int16_t *)p_aout_buffer->p_buffer, buffer, - frame->header.channels, i_samples ); - break; - default: - Interleave32( (int32_t *)p_aout_buffer->p_buffer, buffer, - frame->header.channels, i_samples ); - } - - if( p_dec->pts != 0 && p_dec->pts != aout_DateGet( &p_dec->end_date ) ) - { - aout_DateSet( &p_dec->end_date, p_dec->pts ); - p_dec->pts = 0; - } - else if( !aout_DateGet( &p_dec->end_date ) ) - { - return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; - } - - /* Date management */ - p_aout_buffer->start_date = aout_DateGet( &p_dec->end_date ); - p_aout_buffer->end_date = aout_DateIncrement( &p_dec->end_date, - i_samples ); - aout_DecPlay( p_dec->p_aout, p_dec->p_aout_input, p_aout_buffer ); - return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; -} - -/***************************************************************************** -' * DecoderMetadataCallback: called by libflac to when it encounters metadata - *****************************************************************************/ -static void DecoderMetadataCallback (const FLAC__StreamDecoder *decoder, - const FLAC__StreamMetadata *metadata, - void *client_data) -{ - dec_thread_t *p_dec = (dec_thread_t *)client_data; - switch ( metadata->data.stream_info.bits_per_sample ) - { - case 8: - p_dec->output_format.i_format = VLC_FOURCC('s','8',' ',' '); - break; - case 16: - p_dec->output_format.i_format = AOUT_FMT_S16_NE; - break; - default: - msg_Dbg( p_dec->p_fifo, "strange bps %d", - metadata->data.stream_info.bits_per_sample ); - p_dec->output_format.i_format = VLC_FOURCC('f','i','3','2'); - break; - } - p_dec->output_format.i_physical_channels = - p_dec->output_format.i_original_channels = - pi_channels_maps[metadata->data.stream_info.channels]; - p_dec->output_format.i_rate = metadata->data.stream_info.sample_rate; - - return; -} - -/***************************************************************************** - * DecoderErrorCallback: called when the libflac decoder encounters an error - *****************************************************************************/ -static void DecoderErrorCallback (const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) -{ - dec_thread_t *p_dec = (dec_thread_t *)client_data; - switch ( status ) - { - case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC : - msg_Err( p_dec->p_fifo, "An error in the stream caused the decoder to lose synchronization."); - break; - - case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER : - msg_Err( p_dec->p_fifo, "The decoder encountered a corrupted frame header."); - break; - - case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH : - msg_Err( p_dec->p_fifo, "The frame's data did not match the CRC in the footer."); - break; - default: - msg_Err( p_dec->p_fifo, "got decoder error: %d", status ); - } - return; -} - -/***************************************************************************** - * Interleave: helper function to interleave channels - *****************************************************************************/ -static void Interleave32( int32_t *p_out, const int32_t * const *pp_in, - int i_nb_channels, int i_samples ) -{ - int i, j; - - for ( j = 0; j < i_samples; j++ ) - { - for ( i = 0; i < i_nb_channels; i++ ) - { - p_out[j * i_nb_channels + i] = pp_in[i][j]; - } - } -} -static void Interleave16( int16_t *p_out, const int32_t * const *pp_in, - int i_nb_channels, int i_samples ) -{ - int i, j; - - for ( j = 0; j < i_samples; j++ ) - { - for ( i = 0; i < i_nb_channels; i++ ) - { - p_out[j * i_nb_channels + i] = (int32_t)(pp_in[i][j]); - } - } -} - - -static void decoder_state_error( dec_thread_t *p_dec, FLAC__StreamDecoderState state ) -{ - switch ( state ) - { - case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA : - msg_Err( p_dec->p_fifo, "The decoder is ready to search for metadata."); - break; - case FLAC__STREAM_DECODER_READ_METADATA : - msg_Err( p_dec->p_fifo, "The decoder is ready to or is in the process of reading metadata."); - break; - case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC : - msg_Err( p_dec->p_fifo, "The decoder is ready to or is in the process of searching for the frame sync code."); - break; - case FLAC__STREAM_DECODER_READ_FRAME : - msg_Err( p_dec->p_fifo, "The decoder is ready to or is in the process of reading a frame."); - break; - case FLAC__STREAM_DECODER_END_OF_STREAM : - msg_Err( p_dec->p_fifo, "The decoder has reached the end of the stream."); - break; - case FLAC__STREAM_DECODER_ABORTED : - msg_Err( p_dec->p_fifo, "The decoder was aborted by the read callback."); - break; - case FLAC__STREAM_DECODER_UNPARSEABLE_STREAM : - msg_Err( p_dec->p_fifo, "The decoder encountered reserved fields in use in the stream."); - FLAC__stream_decoder_flush( p_dec->p_decoder ); - break; - case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR : - msg_Err( p_dec->p_fifo, "An error occurred allocating memory."); - break; - case FLAC__STREAM_DECODER_ALREADY_INITIALIZED : - msg_Err( p_dec->p_fifo, "FLAC__stream_decoder_init() was called when the decoder was already initialized, usually because FLAC__stream_decoder_finish() was not called."); - break; - case FLAC__STREAM_DECODER_INVALID_CALLBACK : - msg_Err( p_dec->p_fifo, "FLAC__stream_decoder_init() was called without all callbacks being set."); - break; - case FLAC__STREAM_DECODER_UNINITIALIZED : - msg_Err( p_dec->p_fifo, "The decoder is in the uninitialized state."); - break; - default: - msg_Err(p_dec->p_fifo, "unknown error" ); - } -} diff --git a/modules/demux/flac.c b/modules/demux/flac.c index 2674122a76..af232fdff3 100644 --- a/modules/demux/flac.c +++ b/modules/demux/flac.c @@ -1,10 +1,10 @@ /***************************************************************************** - * flac.c : FLAC demuc module for vlc + * flac.c : FLAC demux module for vlc ***************************************************************************** * Copyright (C) 2001 VideoLAN - * $Id: flac.c,v 1.5 2003/09/07 22:48:29 fenrir Exp $ + * $Id: flac.c,v 1.6 2003/11/21 01:45:48 gbazin Exp $ * - * Authors: Sigmund Augdal + * Authors: Gildas Bazin * * 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 @@ -24,52 +24,49 @@ /***************************************************************************** * Preamble *****************************************************************************/ -#include /* malloc(), free() */ -#include /* strdup() */ -#include - #include #include +#include -#include - -/***************************************************************************** - * Constants - *****************************************************************************/ +#define STREAMINFO_SIZE 38 #define FLAC_PACKET_SIZE 16384 -#define MAX_PACKETS_IN_FIFO 1 /***************************************************************************** * Local prototypes *****************************************************************************/ -static int Init ( vlc_object_t * ); +static int Open ( vlc_object_t * ); +static void Close ( vlc_object_t * ); static int Demux ( input_thread_t * ); +struct demux_sys_t +{ + vlc_bool_t b_start; + es_out_id_t *p_es; + + /* Packetizer */ + decoder_t *p_packetizer; +}; + /***************************************************************************** * Module descriptor *****************************************************************************/ vlc_module_begin(); set_description( _("flac demuxer") ); set_capability( "demux", 155 ); - set_callbacks( Init, NULL ); + set_callbacks( Open, Close ); add_shortcut( "flac" ); vlc_module_end(); /***************************************************************************** - * Init: initializes ES structures + * Open: initializes ES structures *****************************************************************************/ -static int Init( vlc_object_t * p_this ) +static int Open( vlc_object_t * p_this ) { - input_thread_t * p_input = (input_thread_t *)p_this; - es_descriptor_t * p_es; - byte_t * p_peek; - - /* Initialize access plug-in structures. */ - if( p_input->i_mtu == 0 ) - { - /* Improve speed. */ - p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE; - } + input_thread_t *p_input = (input_thread_t *)p_this; + demux_sys_t *p_sys; + int i_peek; + byte_t * p_peek; + es_format_t fmt; p_input->pf_demux = Demux; p_input->pf_demux_control = demux_vaControlDefault; @@ -80,41 +77,117 @@ static int Init( vlc_object_t * p_this ) { /* Stream shorter than 4 bytes... */ msg_Err( p_input, "cannot peek()" ); - return( -1 ); + return VLC_EGENERIC; } - if( *p_peek != 'f' || *(p_peek + 1) != 'L' || *(p_peek +2) != 'a' - || *(p_peek+3) != 'C') + if( p_peek[0]!='f' || p_peek[1]!='L' || p_peek[2]!='a' || p_peek[3]!='C' ) { - if( *p_input->psz_demux && !strncmp( p_input->psz_demux, "flac", 4 ) ) + if( p_input->psz_demux && !strncmp( p_input->psz_demux, "flac", 4 ) ) { /* User forced */ - msg_Err( p_input, "this doesn't look like an flac stream, " + msg_Err( p_input, "this doesn't look like a flac stream, " "continuing anyway" ); } else { msg_Warn( p_input, "flac module discarded (no startcode)" ); - return( -1 ); + return VLC_EGENERIC; } } - if( input_InitStream( p_input, 0 ) == -1 ) + p_input->p_demux_data = p_sys = malloc( sizeof( demux_sys_t ) ); + es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'f', 'l', 'a', 'c' ) ); + p_sys->b_start = VLC_TRUE; + + /* Skip stream marker */ + stream_Read( p_input->s, NULL, 4 ); + + /* We need to read and store the STREAMINFO metadata */ + i_peek = stream_Peek( p_input->s, &p_peek, 4 ); + if( p_peek[0] & 0x7F ) + { + msg_Err( p_input, "This isn't a STREAMINFO metadata block" ); + return VLC_EGENERIC; + } + + if( ((p_peek[1]<<16)+(p_peek[2]<<8)+p_peek[3]) != (STREAMINFO_SIZE - 4) ) { - return( -1 ); + msg_Err( p_input, "Invalid size for a STREAMINFO metadata block" ); + return VLC_EGENERIC; } - input_AddProgram( p_input, 0, 0 ); - p_input->stream.p_selected_program = p_input->stream.pp_programs[0]; + + /* + * Load the FLAC packetizer + */ + p_sys->p_packetizer = vlc_object_create( p_input, VLC_OBJECT_DECODER ); + p_sys->p_packetizer->pf_decode = 0; + p_sys->p_packetizer->pf_decode_audio = 0; + p_sys->p_packetizer->pf_decode_video = 0; + p_sys->p_packetizer->pf_decode_sub = 0; + p_sys->p_packetizer->pf_packetize = 0; + p_sys->p_packetizer->pf_run = 0; + + /* Initialization of decoder structure */ + es_format_Init( &p_sys->p_packetizer->fmt_in, AUDIO_ES, + VLC_FOURCC( 'f', 'l', 'a', 'c' ) ); + + /* Store STREAMINFO for the decoder an packetizer */ + p_sys->p_packetizer->fmt_in.i_extra = fmt.i_extra = STREAMINFO_SIZE; + p_sys->p_packetizer->fmt_in.p_extra = malloc( STREAMINFO_SIZE ); + stream_Read( p_input->s, p_sys->p_packetizer->fmt_in.p_extra, + STREAMINFO_SIZE ); + + /* Fake this a the last metadata block */ + ((uint8_t*)p_sys->p_packetizer->fmt_in.p_extra)[0] |= 0x80; + fmt.p_extra = malloc( STREAMINFO_SIZE ); + memcpy( fmt.p_extra, p_sys->p_packetizer->fmt_in.p_extra, STREAMINFO_SIZE); + + p_sys->p_packetizer->p_module = + module_Need( p_sys->p_packetizer, "packetizer", NULL ); + if( !p_sys->p_packetizer->p_module ) + { + if( p_sys->p_packetizer->fmt_in.p_extra ) + free( p_sys->p_packetizer->fmt_in.p_extra ); + + vlc_object_destroy( p_sys->p_packetizer ); + msg_Err( p_input, "cannot find flac packetizer" ); + return VLC_EGENERIC; + } + + /* Create one program */ vlc_mutex_lock( &p_input->stream.stream_lock ); - p_es = input_AddES( p_input, p_input->stream.p_selected_program, 1, - AUDIO_ES, NULL, 0 ); - p_es->i_stream_id = 1; - p_es->i_fourcc = VLC_FOURCC('f','l','a','c'); - input_SelectES( p_input, p_es ); - p_input->stream.p_selected_program->b_is_ok = 1; + if( input_InitStream( p_input, 0 ) == -1 ) + { + vlc_mutex_unlock( &p_input->stream.stream_lock ); + msg_Err( p_input, "cannot init stream" ); + return VLC_EGENERIC; + } + p_input->stream.i_mux_rate = 0; vlc_mutex_unlock( &p_input->stream.stream_lock ); - return( 0 ); + p_sys->p_es = es_out_Add( p_input->p_es_out, &fmt ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Close: frees unused data + *****************************************************************************/ +static void Close( vlc_object_t * p_this ) +{ + input_thread_t *p_input = (input_thread_t*)p_this; + demux_sys_t *p_sys = p_input->p_demux_data; + + /* Unneed module */ + module_Unneed( p_input, p_sys->p_packetizer->p_module ); + + if( p_sys->p_packetizer->fmt_in.p_extra ) + free( p_sys->p_packetizer->fmt_in.p_extra ); + + /* Delete the decoder */ + vlc_object_destroy( p_sys->p_packetizer ); + + free( p_sys ); } /***************************************************************************** @@ -124,58 +197,45 @@ static int Init( vlc_object_t * p_this ) *****************************************************************************/ static int Demux( input_thread_t * p_input ) { - ssize_t i_read; - decoder_fifo_t * p_fifo = - p_input->stream.p_selected_program->pp_es[0]->p_decoder_fifo; - pes_packet_t * p_pes; - data_packet_t * p_data; + demux_sys_t *p_sys = p_input->p_demux_data; + block_t *p_block_in, *p_block_out; - if( p_fifo == NULL ) + if( !( p_block_in = stream_Block( p_input->s, FLAC_PACKET_SIZE ) ) ) { - return -1; + return 0; } - i_read = input_SplitBuffer( p_input, &p_data, FLAC_PACKET_SIZE ); - - if ( i_read <= 0 ) + if( p_sys->b_start ) { - return i_read; + p_block_in->i_pts = p_block_in->i_dts = 1; + p_sys->b_start = VLC_FALSE; } - - p_pes = input_NewPES( p_input->p_method_data ); - - if( p_pes == NULL ) + else { - msg_Err( p_input, "out of memory" ); - input_DeletePacket( p_input->p_method_data, p_data ); - return( -1 ); + p_block_in->i_pts = p_block_in->i_dts = 0; } - p_pes->i_rate = p_input->stream.control.i_rate; - p_pes->p_first = p_pes->p_last = p_data; - p_pes->i_pes_size = i_read; - p_pes->i_nb_data = 1; - - vlc_mutex_lock( &p_fifo->data_lock ); - if( p_fifo->i_depth >= MAX_PACKETS_IN_FIFO ) + while( (p_block_out = p_sys->p_packetizer->pf_packetize( + p_sys->p_packetizer, &p_block_in )) ) { - /* Wait for the decoder. */ - vlc_cond_wait( &p_fifo->data_wait, &p_fifo->data_lock ); - } - vlc_mutex_unlock( &p_fifo->data_lock ); + while( p_block_out ) + { + block_t *p_next = p_block_out->p_next; - if( (p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT) - |(p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_START) - | (input_ClockManageControl( p_input, - p_input->stream.p_selected_program, - (mtime_t)0 ) == PAUSE_S) ) - { - msg_Warn( p_input, "synchro reinit" ); - p_pes->i_pts = mdate() + DEFAULT_PTS_DELAY; - p_input->stream.p_selected_program->i_synchro_state = SYNCHRO_OK; - } + input_ClockManageRef( p_input, + p_input->stream.p_selected_program, + p_block_out->i_pts * 9 / 100 ); + + p_block_in->b_discontinuity = 0; + p_block_out->i_dts = p_block_out->i_pts = + input_ClockGetTS( p_input, p_input->stream.p_selected_program, + p_block_out->i_pts * 9 / 100 ); + + es_out_Send( p_input->p_es_out, p_sys->p_es, p_block_out ); - input_DecodePES( p_fifo, p_pes ); + p_block_out = p_next; + } + } return 1; }