diff options
author | jstebbins <[email protected]> | 2011-06-07 21:27:02 +0000 |
---|---|---|
committer | jstebbins <[email protected]> | 2011-06-07 21:27:02 +0000 |
commit | d40287132815adc74a0d1444a1e7b76e89879f69 (patch) | |
tree | 00c95f36cbe47104a7ad40233c93bc588702251f /libhb/encavcodecaudio.c | |
parent | e07dec1ec259a52a893642da14856e16085f3c36 (diff) |
libhb: add support for libav aac encoder (libhb only)
This generalizes the ac3 encoder to make it easy to add support
for any audio encoder that libav supports. Since ffaac is not quite
ready, the cli and gui does not expose ffaac yet.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@4029 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb/encavcodecaudio.c')
-rw-r--r-- | libhb/encavcodecaudio.c | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/libhb/encavcodecaudio.c b/libhb/encavcodecaudio.c new file mode 100644 index 000000000..a295dc368 --- /dev/null +++ b/libhb/encavcodecaudio.c @@ -0,0 +1,293 @@ +/* $Id: encavcodeca.c,v 1.23 2005/10/13 23:47:06 titer Exp $ + + This file is part of the HandBrake source code. + Homepage: <http://handbrake.fr/>. + It may be used under the terms of the GNU General Public License. */ + +#include "hb.h" +#include "hbffmpeg.h" +#include "downmix.h" +#include "libavcodec/audioconvert.h" + +struct hb_work_private_s +{ + hb_job_t * job; + AVCodecContext * context; + + int out_discrete_channels; + int samples_per_frame; + unsigned long input_samples; + unsigned long output_bytes; + hb_list_t * list; + uint8_t * buf; +}; + +static int encavcodecaInit( hb_work_object_t *, hb_job_t * ); +static int encavcodecaWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +static void encavcodecaClose( hb_work_object_t * ); + +hb_work_object_t hb_encavcodeca = +{ + WORK_ENCAVCODEC_AUDIO, + "AVCodec Audio encoder (libavcodec)", + encavcodecaInit, + encavcodecaWork, + encavcodecaClose +}; + +static int encavcodecaInit( hb_work_object_t * w, hb_job_t * job ) +{ + AVCodec * codec; + AVCodecContext * context; + hb_audio_t * audio = w->audio; + + hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); + w->private_data = pv; + + pv->job = job; + + pv->out_discrete_channels = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown); + + codec = avcodec_find_encoder( w->codec_param ); + if( !codec ) + { + hb_log( "encavcodecaInit: avcodec_find_encoder " + "failed" ); + return 1; + } + context = avcodec_alloc_context(); + avcodec_get_context_defaults3(context, codec); + + int ret = av_set_string3( context, "stereo_mode", "ms_off", 1, NULL ); + /* Let avutil sanity check the options for us*/ + if( ret == AVERROR(ENOENT) ) + hb_log( "avcodec options: Unknown option %s", "stereo_mode" ); + if( ret == AVERROR(EINVAL) ) + hb_log( "avcodec options: Bad argument %s=%s", "stereo_mode", "ms_off" ? "ms_off" : "(null)" ); + + context->channel_layout = AV_CH_LAYOUT_STEREO; + switch( audio->config.out.mixdown ) + { + case HB_AMIXDOWN_MONO: + context->channel_layout = AV_CH_LAYOUT_MONO; + break; + + case HB_AMIXDOWN_STEREO: + case HB_AMIXDOWN_DOLBY: + case HB_AMIXDOWN_DOLBYPLII: + context->channel_layout = AV_CH_LAYOUT_STEREO; + break; + + case HB_AMIXDOWN_6CH: + context->channel_layout = AV_CH_LAYOUT_5POINT1; + break; + + default: + hb_log(" encavcodecaInit: bad mixdown" ); + break; + } + + context->bit_rate = audio->config.out.bitrate * 1000; + context->sample_rate = audio->config.out.samplerate; + context->channels = pv->out_discrete_channels; + // Try to set format to float. Fallback to whatever is supported. + hb_ff_set_sample_fmt( context, codec ); + + if( hb_avcodec_open( context, codec ) ) + { + hb_log( "encavcodecaInit: avcodec_open failed" ); + return 1; + } + pv->context = context; + + pv->samples_per_frame = context->frame_size; + pv->input_samples = pv->samples_per_frame * pv->out_discrete_channels; + + // Set a reasonable maximum output size + pv->output_bytes = context->frame_size * + (av_get_bits_per_sample_fmt(context->sample_fmt) / 8) * + context->channels; + + pv->buf = malloc( pv->input_samples * sizeof( float ) ); + + pv->list = hb_list_init(); + + if ( w->codec_param == CODEC_ID_AAC && context->extradata ) + { + memcpy( w->config->aac.bytes, context->extradata, context->extradata_size ); + w->config->aac.length = context->extradata_size; + } + + return 0; +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +static void encavcodecaClose( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + + if ( pv ) + { + if( pv->context ) + { + hb_deep_log( 2, "encavcodeca: closing libavcodec" ); + if ( pv->context->codec ) + avcodec_flush_buffers( pv->context ); + hb_avcodec_close( pv->context ); + } + + if ( pv->buf ) + { + free( pv->buf ); + pv->buf = NULL; + } + + if ( pv->list ) + hb_list_empty( &pv->list ); + + free( pv ); + w->private_data = NULL; + } +} + +static hb_buffer_t * Encode( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + uint64_t pts, pos; + hb_audio_t * audio = w->audio; + hb_buffer_t * buf; + + if( hb_list_bytes( pv->list ) < pv->input_samples * sizeof( float ) ) + { + return NULL; + } + + hb_list_getbytes( pv->list, pv->buf, pv->input_samples * sizeof( float ), + &pts, &pos); + + hb_chan_map_t *map = NULL; + if ( audio->config.in.codec == HB_ACODEC_AC3 ) + { + map = &hb_ac3_chan_map; + } + else if ( audio->config.in.codec == HB_ACODEC_DCA ) + { + map = &hb_qt_chan_map; + } + if ( map ) + { + int layout; + switch (audio->config.out.mixdown) + { + case HB_AMIXDOWN_MONO: + layout = HB_INPUT_CH_LAYOUT_MONO; + break; + case HB_AMIXDOWN_STEREO: + case HB_AMIXDOWN_DOLBY: + case HB_AMIXDOWN_DOLBYPLII: + layout = HB_INPUT_CH_LAYOUT_STEREO; + break; + case HB_AMIXDOWN_6CH: + default: + layout = HB_INPUT_CH_LAYOUT_3F2R | HB_INPUT_CH_LAYOUT_HAS_LFE; + break; + } + hb_layout_remap( map, &hb_smpte_chan_map, layout, + (float*)pv->buf, pv->samples_per_frame); + } + + // Do we need to convert our internal float format? + if ( pv->context->sample_fmt != AV_SAMPLE_FMT_FLT ) + { + int isamp, osamp; + AVAudioConvert *ctx; + + isamp = av_get_bits_per_sample_fmt( AV_SAMPLE_FMT_FLT ) / 8; + osamp = av_get_bits_per_sample_fmt( pv->context->sample_fmt ) / 8; + ctx = av_audio_convert_alloc( pv->context->sample_fmt, 1, + AV_SAMPLE_FMT_FLT, 1, + NULL, 0 ); + + // get output buffer size then malloc a buffer + //nsamples = out_size / isamp; + //buffer = av_malloc( nsamples * sizeof(hb_sample_t) ); + + // we're doing straight sample format conversion which + // behaves as if there were only one channel. + const void * const ibuf[6] = { pv->buf }; + void * const obuf[6] = { pv->buf }; + const int istride[6] = { isamp }; + const int ostride[6] = { osamp }; + + av_audio_convert( ctx, obuf, ostride, ibuf, istride, pv->input_samples ); + av_audio_convert_free( ctx ); + } + + buf = hb_buffer_init( pv->output_bytes ); + buf->size = avcodec_encode_audio( pv->context, buf->data, buf->alloc, + (short*)pv->buf ); + + buf->start = pts + 90000 * pos / pv->out_discrete_channels / sizeof( float ) / audio->config.out.samplerate; + buf->stop = buf->start + 90000 * pv->samples_per_frame / audio->config.out.samplerate; + + buf->frametype = HB_FRAME_AUDIO; + + if ( !buf->size ) + { + hb_buffer_close( &buf ); + return Encode( w ); + } + else if (buf->size < 0) + { + hb_log( "encavcodeca: avcodec_encode_audio failed" ); + hb_buffer_close( &buf ); + return NULL; + } + + return buf; +} + +/*********************************************************************** + * Work + *********************************************************************** + * + **********************************************************************/ +static int encavcodecaWork( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * in = *buf_in, * buf; + + if ( in->size <= 0 ) + { + /* EOF on input - send it downstream & say we're done */ + *buf_out = in; + *buf_in = NULL; + return HB_WORK_DONE; + } + + if ( pv->context == NULL || pv->context->codec == NULL ) + { + // No encoder context. Nothing we can do. + return HB_WORK_OK; + } + + hb_list_add( pv->list, in ); + *buf_in = NULL; + + *buf_out = buf = Encode( w ); + + while ( buf ) + { + buf->next = Encode( w ); + buf = buf->next; + } + + return HB_WORK_OK; +} + + |