X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;ds=sidebyside;f=modules%2Fcodec%2Fffmpeg%2Faudio.c;h=1a3c3b5edc59eeeb64d46736251044d6a141e557;hb=70ee5fbf988a94c0c2e394f3346a3dc6eeb18d72;hp=b2d3f845f8290b87945a6f3d67fc83a44167963b;hpb=968cc6bf0391d06c628efcfb972ba3b6c31d99c2;p=vlc diff --git a/modules/codec/ffmpeg/audio.c b/modules/codec/ffmpeg/audio.c index b2d3f845f8..1a3c3b5edc 100644 --- a/modules/codec/ffmpeg/audio.c +++ b/modules/codec/ffmpeg/audio.c @@ -1,16 +1,17 @@ /***************************************************************************** * audio.c: audio decoder using ffmpeg library ***************************************************************************** - * Copyright (C) 1999-2001 VideoLAN - * $Id: audio.c,v 1.4 2002/11/27 12:41:45 fenrir Exp $ + * Copyright (C) 1999-2003 VideoLAN + * $Id$ * * Authors: Laurent Aimar + * 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 * 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 @@ -24,296 +25,260 @@ /***************************************************************************** * Preamble *****************************************************************************/ -#include /* malloc(), free() */ - #include -#include -#include #include -#include - -#ifdef HAVE_UNISTD_H -#include /* getpid() */ -#endif -#include -#include - -#ifdef HAVE_SYS_TIMES_H -# include +/* ffmpeg header */ +#ifdef HAVE_FFMPEG_AVCODEC_H +# include +#else +# include #endif -#include "avcodec.h" /* ffmpeg */ - -#include "postprocessing/postprocessing.h" - #include "ffmpeg.h" -#include "audio.h" - -/* - * Local prototypes - */ -int E_( InitThread_Audio ) ( adec_thread_t * ); -void E_( EndThread_Audio ) ( adec_thread_t * ); -void E_( DecodeThread_Audio ) ( adec_thread_t * ); -static int pi_channels_maps[6] = +static unsigned int pi_channels_maps[7] = { 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_REARLEFT + | AOUT_CHAN_REARRIGHT, AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER - | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT -}; + | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT, + AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER + | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE +}; /***************************************************************************** - * locales Functions + * decoder_sys_t : decoder descriptor *****************************************************************************/ - -static void ffmpeg_GetWaveFormatEx( waveformatex_t *p_wh, - u8 *p_data ) +struct decoder_sys_t { - p_wh->i_formattag = GetWLE( p_data ); - p_wh->i_nb_channels = GetWLE( p_data + 2 ); - p_wh->i_samplespersec = GetDWLE( p_data + 4 ); - p_wh->i_avgbytespersec= GetDWLE( p_data + 8 ); - p_wh->i_blockalign = GetWLE( p_data + 12 ); - p_wh->i_bitspersample = GetWLE( p_data + 14 ); - p_wh->i_size = GetWLE( p_data + 16 ); - - if( p_wh->i_size ) - { - p_wh->p_data = malloc( p_wh->i_size ); - memcpy( p_wh->p_data, p_data + 18, p_wh->i_size ); - } -} - + /* Common part between video and audio decoder */ + int i_cat; + int i_codec_id; + char *psz_namecodec; + AVCodecContext *p_context; + AVCodec *p_codec; + + /* Temporary buffer for libavcodec */ + uint8_t *p_output; + + /* + * Output properties + */ + audio_sample_format_t aout_format; + audio_date_t end_date; + + /* + * + */ + uint8_t *p_samples; + int i_samples; +}; /***************************************************************************** - * - * Functions that initialize, decode and end the decoding process - * - * Functions exported for ffmpeg.c - * * E_( InitThread_Audio ) - * * E_( DecodeThread_Audio ) - * * E_( EndThread_Video_Audio ) - *****************************************************************************/ - -/***************************************************************************** - * InitThread: initialize vdec output thread + * InitAudioDec: initialize audio decoder ***************************************************************************** - * This function is called from decoder_Run and performs the second step - * of the initialization. It returns 0 on success. Note that the thread's - * flag are not modified inside this function. - * - * ffmpeg codec will be open, some memory allocated. + * The ffmpeg codec will be opened, some memory allocated. *****************************************************************************/ -int E_( InitThread_Audio )( adec_thread_t *p_adec ) +int E_(InitAudioDec)( decoder_t *p_dec, AVCodecContext *p_context, + AVCodec *p_codec, int i_codec_id, char *psz_namecodec ) { - if( p_adec->p_fifo->p_demux_data ) - { - ffmpeg_GetWaveFormatEx( &p_adec->format, - (u8*)p_adec->p_fifo->p_demux_data ); - } - else + decoder_sys_t *p_sys; + vlc_value_t lockval; + + var_Get( p_dec->p_libvlc, "avcodec", &lockval ); + + /* 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_Warn( p_adec->p_fifo, "audio informations missing" ); + msg_Err( p_dec, "out of memory" ); + return VLC_EGENERIC; } + p_sys->p_context = p_context; + p_sys->p_codec = p_codec; + p_sys->i_codec_id = i_codec_id; + p_sys->psz_namecodec = psz_namecodec; + /* ***** Fill p_context with init values ***** */ - p_adec->p_context->sample_rate = p_adec->format.i_samplespersec; - p_adec->p_context->channels = p_adec->format.i_nb_channels; -#if LIBAVCODEC_BUILD >= 4618 - p_adec->p_context->block_align = p_adec->format.i_blockalign; -#endif - p_adec->p_context->bit_rate = p_adec->format.i_avgbytespersec * 8; - - if( ( p_adec->p_context->extradata_size = p_adec->format.i_size ) > 0 ) - { - p_adec->p_context->extradata = - malloc( p_adec->format.i_size ); + p_sys->p_context->sample_rate = p_dec->fmt_in.audio.i_rate; + p_sys->p_context->channels = p_dec->fmt_in.audio.i_channels; + p_sys->p_context->block_align = p_dec->fmt_in.audio.i_blockalign; + p_sys->p_context->bit_rate = p_dec->fmt_in.i_bitrate; - memcpy( p_adec->p_context->extradata, - p_adec->format.p_data, - p_adec->format.i_size ); + if( ( p_sys->p_context->extradata_size = p_dec->fmt_in.i_extra ) > 0 ) + { + p_sys->p_context->extradata = + malloc( p_dec->fmt_in.i_extra + FF_INPUT_BUFFER_PADDING_SIZE ); + memcpy( p_sys->p_context->extradata, + p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra ); + memset( (char*)p_sys->p_context->extradata + p_dec->fmt_in.i_extra, 0, + FF_INPUT_BUFFER_PADDING_SIZE ); } - - /* ***** Open the codec ***** */ - if (avcodec_open(p_adec->p_context, p_adec->p_codec) < 0) + + /* ***** Open the codec ***** */ + vlc_mutex_lock( lockval.p_address ); + if (avcodec_open( p_sys->p_context, p_sys->p_codec ) < 0) { - msg_Err( p_adec->p_fifo, - "cannot open codec (%s)", - p_adec->psz_namecodec ); - return( -1 ); + vlc_mutex_unlock( lockval.p_address ); + msg_Err( p_dec, "cannot open codec (%s)", p_sys->psz_namecodec ); + free( p_sys ); + return VLC_EGENERIC; } - else + vlc_mutex_unlock( lockval.p_address ); + + msg_Dbg( p_dec, "ffmpeg codec (%s) started", p_sys->psz_namecodec ); + + p_sys->p_output = malloc( 3 * AVCODEC_MAX_AUDIO_FRAME_SIZE ); + p_sys->p_samples = NULL; + p_sys->i_samples = 0; + + if( p_dec->fmt_in.audio.i_rate ) { - msg_Dbg( p_adec->p_fifo, - "ffmpeg codec (%s) started", - p_adec->psz_namecodec ); + aout_DateInit( &p_sys->end_date, p_dec->fmt_in.audio.i_rate ); + aout_DateSet( &p_sys->end_date, 0 ); } - p_adec->p_output = malloc( AVCODEC_MAX_AUDIO_FRAME_SIZE ); - - - p_adec->output_format.i_format = AOUT_FMT_S16_NE; - p_adec->output_format.i_rate = p_adec->format.i_samplespersec; - p_adec->output_format.i_physical_channels - = p_adec->output_format.i_original_channels - = p_adec->format.i_nb_channels; - - p_adec->p_aout = NULL; - p_adec->p_aout_input = NULL; - - return( 0 ); -} + /* Set output properties */ + p_dec->fmt_out.i_cat = AUDIO_ES; + p_dec->fmt_out.i_codec = AOUT_FMT_S16_NE; + p_dec->fmt_out.audio.i_bitspersample = 16; + return VLC_SUCCESS; +} /***************************************************************************** - * DecodeThread: Called for decode one frame + * SplitBuffer: Needed because aout really doesn't like big audio chunk and + * wma produces easily > 30000 samples... *****************************************************************************/ -void E_( DecodeThread_Audio )( adec_thread_t *p_adec ) +aout_buffer_t *SplitBuffer( decoder_t *p_dec ) { - pes_packet_t *p_pes; - aout_buffer_t *p_aout_buffer; + decoder_sys_t *p_sys = p_dec->p_sys; + int i_samples = __MIN( p_sys->i_samples, 4096 ); + aout_buffer_t *p_buffer; - int i_samplesperchannel; - int i_output_size; - int i_frame_size; - int i_status; + if( i_samples == 0 ) return NULL; - do - { - input_ExtractPES( p_adec->p_fifo, &p_pes ); - if( !p_pes ) - { - p_adec->p_fifo->b_error = 1; - return; - } - p_adec->pts = p_pes->i_pts; - i_frame_size = p_pes->i_pes_size; - - if( i_frame_size > 0 ) - { - if( p_adec->i_buffer_size < i_frame_size + 16 ) - { - FREE( p_adec->p_buffer ); - p_adec->p_buffer = malloc( i_frame_size + 16 ); - p_adec->i_buffer_size = i_frame_size + 16; - } - - E_( GetPESData )( p_adec->p_buffer, p_adec->i_buffer_size, p_pes ); - } - input_DeletePES( p_adec->p_fifo->p_packets_mgt, p_pes ); - } while( i_frame_size <= 0 ); - - - i_status = avcodec_decode_audio( p_adec->p_context, - (s16*)p_adec->p_output, - &i_output_size, - p_adec->p_buffer, - i_frame_size ); - if( i_status < 0 ) + if( ( p_buffer = p_dec->pf_aout_buffer_new( p_dec, i_samples ) ) == NULL ) { - msg_Warn( p_adec->p_fifo, - "cannot decode one frame (%d bytes)", - i_frame_size ); - return; + msg_Err( p_dec, "cannot get aout buffer" ); + return NULL; } - if( i_output_size <= 0 ) + p_buffer->start_date = aout_DateGet( &p_sys->end_date ); + p_buffer->end_date = aout_DateIncrement( &p_sys->end_date, i_samples ); + + memcpy( p_buffer->p_buffer, p_sys->p_samples, p_buffer->i_nb_bytes ); + + p_sys->p_samples += p_buffer->i_nb_bytes; + p_sys->i_samples -= i_samples; + + return p_buffer; +} + +/***************************************************************************** + * DecodeAudio: Called to decode one frame + *****************************************************************************/ +aout_buffer_t *E_( DecodeAudio )( decoder_t *p_dec, block_t **pp_block ) +{ + decoder_sys_t *p_sys = p_dec->p_sys; + int i_used, i_output; + aout_buffer_t *p_buffer; + block_t *p_block; + + if( !pp_block || !*pp_block ) return NULL; + + p_block = *pp_block; + + if( p_block->i_buffer <= 0 && p_sys->i_samples > 0 ) { - msg_Warn( p_adec->p_fifo, - "decoded %d samples bytes", - i_output_size ); + /* More data */ + p_buffer = SplitBuffer( p_dec ); + if( !p_buffer ) block_Release( p_block ); + return p_buffer; } - if( p_adec->p_context->channels <= 0 || - p_adec->p_context->channels > 5 ) + if( !aout_DateGet( &p_sys->end_date ) && !p_block->i_pts ) { - msg_Warn( p_adec->p_fifo, - "invalid channels count", - p_adec->p_context->channels ); + /* We've just started the stream, wait for the first PTS. */ + block_Release( p_block ); + return NULL; } - /* **** Now we can output these samples **** */ - i_samplesperchannel = i_output_size / 2 - / aout_FormatNbChannels( &p_adec->output_format ); - /* **** First check if we have a valid output **** */ - if( ( p_adec->p_aout_input == NULL )|| - ( p_adec->output_format.i_original_channels != - pi_channels_maps[p_adec->p_context->channels] ) ) + if( p_block->i_buffer <= 0 || ( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) ) { - if( p_adec->p_aout_input != NULL ) - { - /* **** Delete the old **** */ - aout_DecDelete( p_adec->p_aout, p_adec->p_aout_input ); - } - - /* **** Create a new audio output **** */ - p_adec->output_format.i_physical_channels = - p_adec->output_format.i_original_channels = - pi_channels_maps[p_adec->p_context->channels]; - - aout_DateInit( &p_adec->date, p_adec->output_format.i_rate ); - p_adec->p_aout_input = aout_DecNew( p_adec->p_fifo, - &p_adec->p_aout, - &p_adec->output_format ); + block_Release( p_block ); + return NULL; } - if( !p_adec->p_aout_input ) + i_used = avcodec_decode_audio( p_sys->p_context, + (int16_t*)p_sys->p_output, &i_output, + p_block->p_buffer, p_block->i_buffer ); + + if( i_used < 0 || i_output < 0 ) { - msg_Err( p_adec->p_fifo, "cannot create aout" ); - return; + if( i_used < 0 ) + msg_Warn( p_dec, "cannot decode one frame (%d bytes)", + p_block->i_buffer ); + + block_Release( p_block ); + return NULL; } - - if( p_adec->pts != 0 && p_adec->pts != aout_DateGet( &p_adec->date ) ) + else if( i_used > p_block->i_buffer ) { - aout_DateSet( &p_adec->date, p_adec->pts ); + i_used = p_block->i_buffer; } - else if( !aout_DateGet( &p_adec->date ) ) + + p_block->i_buffer -= i_used; + p_block->p_buffer += i_used; + + if( p_sys->p_context->channels <= 0 || p_sys->p_context->channels > 6 ) { - return; + msg_Warn( p_dec, "invalid channels count %d", + p_sys->p_context->channels ); + block_Release( p_block ); + return NULL; } - p_aout_buffer = aout_DecNewBuffer( p_adec->p_aout, - p_adec->p_aout_input, - i_samplesperchannel ); - if( !p_aout_buffer ) + if( p_dec->fmt_out.audio.i_rate != p_sys->p_context->sample_rate ) { - msg_Err( p_adec->p_fifo, "cannot get aout buffer" ); - p_adec->p_fifo->b_error = 1; - return; + aout_DateInit( &p_sys->end_date, p_sys->p_context->sample_rate ); + aout_DateSet( &p_sys->end_date, p_block->i_pts ); } - p_aout_buffer->start_date = aout_DateGet( &p_adec->date ); - p_aout_buffer->end_date = aout_DateIncrement( &p_adec->date, - i_samplesperchannel ); - memcpy( p_aout_buffer->p_buffer, - p_adec->p_output, - p_aout_buffer->i_nb_bytes ); + /* **** Set audio output parameters **** */ + p_dec->fmt_out.audio.i_rate = p_sys->p_context->sample_rate; + p_dec->fmt_out.audio.i_channels = p_sys->p_context->channels; + p_dec->fmt_out.audio.i_original_channels = + p_dec->fmt_out.audio.i_physical_channels = + pi_channels_maps[p_sys->p_context->channels]; - aout_DecPlay( p_adec->p_aout, p_adec->p_aout_input, p_aout_buffer ); - - return; -} + if( p_block->i_pts != 0 && + p_block->i_pts != aout_DateGet( &p_sys->end_date ) ) + { + aout_DateSet( &p_sys->end_date, p_block->i_pts ); + } + p_block->i_pts = 0; + /* **** Now we can output these samples **** */ + p_sys->i_samples = i_output / 2 / p_sys->p_context->channels; + p_sys->p_samples = p_sys->p_output; + + p_buffer = SplitBuffer( p_dec ); + if( !p_buffer ) block_Release( p_block ); + return p_buffer; +} /***************************************************************************** - * EndThread: thread destruction - ***************************************************************************** - * This function is called when the thread ends after a sucessful - * initialization. + * EndAudioDec: audio decoder destruction *****************************************************************************/ -void E_( EndThread_Audio )( adec_thread_t *p_adec ) +void E_(EndAudioDec)( decoder_t *p_dec ) { - FREE( p_adec->format.p_data ); + decoder_sys_t *p_sys = p_dec->p_sys; - if( p_adec->p_aout_input ) - { - aout_DecDelete( p_adec->p_aout, p_adec->p_aout_input ); - } - + if( p_sys->p_output ) free( p_sys->p_output ); } -