diff options
Diffstat (limited to 'libhb/encavcodecaudio.c')
-rw-r--r-- | libhb/encavcodecaudio.c | 213 |
1 files changed, 132 insertions, 81 deletions
diff --git a/libhb/encavcodecaudio.c b/libhb/encavcodecaudio.c index 14df3f317..001000b9a 100644 --- a/libhb/encavcodecaudio.c +++ b/libhb/encavcodecaudio.c @@ -9,8 +9,7 @@ #include "hb.h" #include "hbffmpeg.h" -#include "downmix.h" -#include "libavcodec/audioconvert.h" +#include "audio_remap.h" struct hb_work_private_s { @@ -23,6 +22,9 @@ struct hb_work_private_s unsigned long output_bytes; hb_list_t * list; uint8_t * buf; + + AVAudioResampleContext *avresample; + int *remap_table; }; static int encavcodecaInit( hb_work_object_t *, hb_job_t * ); @@ -49,8 +51,6 @@ static int encavcodecaInit( hb_work_object_t * w, hb_job_t * job ) pv->job = job; - pv->out_discrete_channels = hb_mixdown_get_discrete_channel_count( audio->config.out.mixdown ); - codec = avcodec_find_encoder( w->codec_param ); if( !codec ) { @@ -60,40 +60,30 @@ static int encavcodecaInit( hb_work_object_t * w, hb_job_t * job ) } context = avcodec_alloc_context3(codec); - AVDictionary *av_opts = NULL; - if ( w->codec_param == CODEC_ID_AAC ) + int mode; + context->channel_layout = hb_ff_mixdown_xlat(audio->config.out.mixdown, &mode); + pv->out_discrete_channels = hb_mixdown_get_discrete_channel_count(audio->config.out.mixdown); + + if (pv->out_discrete_channels > 2 && + audio->config.in.channel_map != &hb_libav_chan_map) { - av_dict_set( &av_opts, "stereo_mode", "ms_off", 0 ); + pv->remap_table = hb_audio_remap_build_table(context->channel_layout, + audio->config.in.channel_map, + &hb_libav_chan_map); } - if ( w->codec_param == CODEC_ID_AC3 ) + else { - if( audio->config.out.mixdown == HB_AMIXDOWN_DOLBY || - audio->config.out.mixdown == HB_AMIXDOWN_DOLBYPLII ) - { - av_dict_set( &av_opts, "dsur_mode", "on", 0 ); - } + pv->remap_table = NULL; } - switch (audio->config.out.mixdown) + AVDictionary *av_opts = NULL; + if (w->codec_param == CODEC_ID_AAC) + { + av_dict_set(&av_opts, "stereo_mode", "ms_off", 0); + } + else if (w->codec_param == CODEC_ID_AC3 && mode != AV_MATRIX_ENCODING_NONE) { - 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: - context->channel_layout = AV_CH_LAYOUT_STEREO; - hb_log("encavcodecaInit: bad mixdown"); - break; + av_dict_set(&av_opts, "dsur_mode", "on", 0); } if( audio->config.out.bitrate > 0 ) @@ -146,6 +136,31 @@ static int encavcodecaInit( hb_work_object_t * w, hb_job_t * job ) w->config->extradata.length = context->extradata_size; } + // Check if sample format conversion is necessary + if (AV_SAMPLE_FMT_FLT != pv->context->sample_fmt) + { + // Set up avresample to do conversion + pv->avresample = avresample_alloc_context(); + if (pv->avresample == NULL) + { + hb_error("Failed to initialize avresample"); + return 1; + } + + uint64_t layout; + layout = hb_ff_layout_xlat(context->channel_layout, context->channels); + av_opt_set_int(pv->avresample, "in_channel_layout", layout, 0); + av_opt_set_int(pv->avresample, "out_channel_layout", layout, 0); + av_opt_set_int(pv->avresample, "in_sample_fmt", AV_SAMPLE_FMT_FLT, 0); + av_opt_set_int(pv->avresample, "out_sample_fmt", context->sample_fmt, 0); + + if (avresample_open(pv->avresample) < 0) + { + hb_error("Failed to open avresample"); + avresample_free(&pv->avresample); + return 1; + } + } return 0; } @@ -159,11 +174,20 @@ static int encavcodecaInit( hb_work_object_t * w, hb_job_t * job ) static void Finalize( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; - hb_buffer_t * buf = hb_buffer_init( pv->output_bytes ); + hb_buffer_t * buf; // Finalize with NULL input needed by FLAC to generate md5sum // in context extradata - avcodec_encode_audio( pv->context, buf->data, buf->alloc, NULL ); + + // Prepare output packet + AVPacket pkt; + int got_packet; + buf = hb_buffer_init( pv->output_bytes ); + av_init_packet(&pkt); + pkt.data = buf->data; + pkt.size = buf->alloc; + + avcodec_encode_audio2( pv->context, &pkt, NULL, &got_packet); hb_buffer_close( &buf ); // Then we need to recopy the header since it was modified @@ -198,11 +222,36 @@ static void encavcodecaClose( hb_work_object_t * w ) if ( pv->list ) hb_list_empty( &pv->list ); + if (pv->avresample != NULL) + { + avresample_free(&pv->avresample); + } + free( pv ); w->private_data = NULL; } } +static void convertAudioFormat( hb_work_private_t *pv, AVFrame *frame ) +{ + if (pv->avresample != NULL) + { + int out_samples, out_linesize; + + av_samples_get_buffer_size(&out_linesize, pv->context->channels, + frame->nb_samples, pv->context->sample_fmt, 0); + + out_samples = avresample_convert(pv->avresample, + (void **)frame->data, out_linesize, frame->nb_samples, + (void **)frame->data, frame->linesize[0], frame->nb_samples); + + if (out_samples < 0) + { + hb_error("avresample_convert() failed"); + } + } +} + static hb_buffer_t * Encode( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; @@ -217,65 +266,67 @@ static hb_buffer_t * Encode( hb_work_object_t * w ) hb_list_getbytes( pv->list, pv->buf, pv->input_samples * sizeof( float ), &pts, &pos); - - // XXX: ffaac fails to remap from the internal libav* channel map (SMPTE) to the native AAC channel map - // do it here - this hack should be removed if Libav fixes the bug - hb_chan_map_t * out_map = ( w->codec_param == CODEC_ID_AAC ) ? &hb_qt_chan_map : &hb_smpte_chan_map; - - if (audio->config.in.channel_map != out_map) + if (pv->remap_table != NULL) { - hb_layout_remap(audio->config.in.channel_map, out_map, - pv->context->channel_layout, (float*)pv->buf, - pv->samples_per_frame); + hb_audio_remap(pv->out_discrete_channels, pv->samples_per_frame, + (hb_sample_t*)pv->buf, pv->remap_table); } + // Prepare input frame + AVFrame frame; + frame.nb_samples= pv->samples_per_frame; + int size = av_samples_get_buffer_size(NULL, pv->context->channels, + frame.nb_samples, pv->context->sample_fmt, 1); + avcodec_fill_audio_frame(&frame, pv->context->channels, + pv->context->sample_fmt, pv->buf, size, 1); + frame.pts = pts + 90000 * pos / pv->out_discrete_channels / sizeof( float ) / audio->config.out.samplerate; + + // libav requires that timebase of audio input frames to be + // in sample_rate units. + frame.pts = av_rescale( frame.pts, pv->context->sample_rate, 90000); + // Do we need to convert our internal float format? - if ( pv->context->sample_fmt != AV_SAMPLE_FMT_FLT ) + convertAudioFormat(pv, &frame); + + // Prepare output packet + AVPacket pkt; + int got_packet; + buf = hb_buffer_init( pv->output_bytes ); + av_init_packet(&pkt); + pkt.data = buf->data; + pkt.size = buf->alloc; + + // Encode + int ret = avcodec_encode_audio2( pv->context, &pkt, &frame, &got_packet); + if ( ret < 0 ) { - int isamp, osamp; - AVAudioConvert *ctx; - - isamp = av_get_bytes_per_sample( AV_SAMPLE_FMT_FLT ); - osamp = av_get_bytes_per_sample( pv->context->sample_fmt ); - 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 ); + hb_log( "encavcodeca: avcodec_encode_audio failed" ); + hb_buffer_close( &buf ); + return NULL; } - - buf = hb_buffer_init( pv->output_bytes ); - buf->size = avcodec_encode_audio( pv->context, buf->data, buf->alloc, - (short*)pv->buf ); - buf->s.start = pts + 90000 * pos / pv->out_discrete_channels / sizeof( float ) / audio->config.out.samplerate; - buf->s.stop = buf->s.start + 90000 * pv->samples_per_frame / audio->config.out.samplerate; + if ( got_packet && pkt.size ) + { + buf->size = pkt.size; - buf->s.type = AUDIO_BUF; - buf->s.frametype = HB_FRAME_AUDIO; + // The output pts from libav is in context->time_base. Convert + // it back to our timebase. + // + // Also account for the "delay" factor that libav seems to arbitrarily + // subtract from the packet. Not sure WTH they think they are doing + // by offseting the value in a negative direction. + buf->s.start = av_rescale_q( pkt.pts + pv->context->delay, + pv->context->time_base, (AVRational){ 1, 90000 }); - if ( !buf->size ) - { - hb_buffer_close( &buf ); - return Encode( w ); + buf->s.stop = buf->s.start + 90000 * pv->samples_per_frame / audio->config.out.samplerate; + + buf->s.type = AUDIO_BUF; + buf->s.frametype = HB_FRAME_AUDIO; } - else if (buf->size < 0) + else { - hb_log( "encavcodeca: avcodec_encode_audio failed" ); hb_buffer_close( &buf ); - return NULL; + return Encode( w ); } return buf; |