diff options
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; +} + + |