From de0c5fa4d184c075de31c8786b89972a1440e610 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 2 May 2011 21:18:21 +1000 Subject: [PATCH] Rewrite of AC3 encoder. Memory allocation is now dynamic. Allow easy migration to the new floating point AC3 encoder found in the newer ffmpeg. That the memory allocation is dynamic allow to remove the loop in aobase ; possibly removing the audio corruption that could be seen when the AC3 re-encoder was in use. --- mythtv/libs/libmyth/audio/audiooutputbase.cpp | 64 +++----- .../audio/audiooutputdigitalencoder.cpp | 145 ++++++++++++++---- .../libmyth/audio/audiooutputdigitalencoder.h | 31 ++-- 3 files changed, 154 insertions(+), 86 deletions(-) diff --git a/mythtv/libs/libmyth/audio/audiooutputbase.cpp b/mythtv/libs/libmyth/audio/audiooutputbase.cpp index 79cdd0b11b3..6481675fb05 100644 --- a/mythtv/libs/libmyth/audio/audiooutputbase.cpp +++ b/mythtv/libs/libmyth/audio/audiooutputbase.cpp @@ -1019,9 +1019,7 @@ void AudioOutputBase::SetAudiotime(int frames, int64_t timecode) if (encoder) { - // the input buffered data is still in audio_bytes_per_sample format - processframes_stretched -= - encoder->Buffered() / output_bytes_per_frame; + processframes_stretched -= encoder->Buffered(); } audbuf_timecode = @@ -1443,50 +1441,30 @@ bool AudioOutputBase::AddData(void *in_buffer, int in_len, if (encoder) { org_waud = waud; - int org_waud2 = waud; - int remaining = len; int to_get = 0; - // The AC3 encoder can only work on 128kB of data at a time - int maxlength = - ((ENCODER_INBUFSIZE / encoder->FrameSize() - 1) * - encoder->FrameSize()) & ~0xf; - do + if (bdiff < len) { - len = remaining; - if (len > maxlength) - { - len = maxlength; - } - remaining -= len; - - bdiff = kAudioRingBufferSize - org_waud; - if (bdiff < len) - { - encoder->Encode(WPOS, bdiff, processing); - to_get = encoder->Encode(ABUF, len - bdiff, processing); - org_waud = len - bdiff; - } - else - { - to_get = encoder->Encode(WPOS, len, processing); - org_waud += len; - } - - bdiff = kAudioRingBufferSize - org_waud2; - if (bdiff <= to_get) - { - encoder->GetFrames(audiobuffer + org_waud2, bdiff); - to_get -= bdiff ; - org_waud2 = 0; - } - if (to_get > 0) - encoder->GetFrames(audiobuffer + org_waud2, to_get); - - org_waud2 += to_get; + encoder->Encode(WPOS, bdiff, processing ? FORMAT_FLT : format); + to_get = encoder->Encode(ABUF, len - bdiff, + processing ? FORMAT_FLT : format); } - while (remaining > 0); - org_waud = org_waud2; + else + { + to_get = encoder->Encode(WPOS, len, + processing ? FORMAT_FLT : format); + } + + if (bdiff <= to_get) + { + encoder->GetFrames(WPOS, bdiff); + to_get -= bdiff ; + org_waud = 0; + } + if (to_get > 0) + encoder->GetFrames(WPOS, to_get); + + org_waud += to_get; } waud = org_waud; diff --git a/mythtv/libs/libmyth/audio/audiooutputdigitalencoder.cpp b/mythtv/libs/libmyth/audio/audiooutputdigitalencoder.cpp index e279b7d402d..4823d28d15a 100644 --- a/mythtv/libs/libmyth/audio/audiooutputdigitalencoder.cpp +++ b/mythtv/libs/libmyth/audio/audiooutputdigitalencoder.cpp @@ -21,14 +21,23 @@ extern "C" { #define LOC_ERR QString("DEnc, Error: ") AudioOutputDigitalEncoder::AudioOutputDigitalEncoder(void) : - bytes_per_sample(0), av_context(NULL), - outlen(0), - inlen(0), - one_frame_bytes(0), + out(NULL), out_size(0), + in(NULL), in_size(0), + outlen(0), inlen(0), + samples_per_frame(0), m_spdifenc(NULL) { - in = (unsigned char *)(((long)&inbuf + 15) & ~15); + out = (outbuf_t *)av_malloc(OUTBUFSIZE); + if (out) + { + out_size = OUTBUFSIZE; + } + in = (inbuf_t *)av_malloc(INBUFSIZE); + if (in) + { + in_size = INBUFSIZE; + } } AudioOutputDigitalEncoder::~AudioOutputDigitalEncoder() @@ -41,8 +50,17 @@ void AudioOutputDigitalEncoder::Dispose() if (av_context) { avcodec_close(av_context); - av_free(av_context); - av_context = NULL; + av_freep(&av_context); + } + if (out) + { + av_freep(&out); + out_size = 0; + } + if (in) + { + av_freep(&in); + in_size = 0; } if (m_spdifenc) { @@ -51,6 +69,24 @@ void AudioOutputDigitalEncoder::Dispose() } } +void *AudioOutputDigitalEncoder::realloc(void *ptr, + size_t old_size, size_t new_size) +{ + if (!ptr) + return ptr; + + // av_realloc doesn't maintain 16 bytes alignment + void *new_ptr = av_malloc(new_size); + if (!new_ptr) + { + av_free(ptr); + return new_ptr; + } + memcpy(new_ptr, ptr, old_size); + av_free(ptr); + return new_ptr; +} + bool AudioOutputDigitalEncoder::Init( CodecID codec_id, int bitrate, int samplerate, int channels) { @@ -66,8 +102,11 @@ bool AudioOutputDigitalEncoder::Init( // We need to do this when called from mythmusic avcodec_init(); avcodec_register_all(); +#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT( 52, 113, 0 ) codec = avcodec_find_encoder_by_name("ac3_fixed"); - //codec = avcodec_find_encoder(CODEC_ID_AC3); +#else + codec = avcodec_find_encoder(CODEC_ID_AC3); +#endif if (!codec) { VERBOSE(VB_IMPORTANT, LOC_ERR + "Could not find codec"); @@ -80,10 +119,15 @@ bool AudioOutputDigitalEncoder::Init( av_context->bit_rate = bitrate; av_context->sample_rate = samplerate; av_context->channels = channels; - av_context->channel_layout = CH_LAYOUT_5POINT1; +#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT( 52, 113, 0 ) + av_context->channel_layout = AV_CH_LAYOUT_5POINT1; av_context->sample_fmt = AV_SAMPLE_FMT_S16; +#else + av_context->channel_layout = CH_LAYOUT_5POINT1; + av_context->sample_fmt = SAMPLE_FMT_S16; +#endif - // open it +// open it ret = avcodec_open(av_context, codec); if (ret < 0) { @@ -108,39 +152,59 @@ bool AudioOutputDigitalEncoder::Init( return false; } - bytes_per_sample = av_context->channels * sizeof(short); - one_frame_bytes = bytes_per_sample * av_context->frame_size; + samples_per_frame = av_context->frame_size * av_context->channels; - VERBOSE(VB_AUDIO, QString("DigitalEncoder::Init fs=%1, bpf=%2 ofb=%3") + VERBOSE(VB_AUDIO, QString("DigitalEncoder::Init fs=%1, spf=%2") .arg(av_context->frame_size) - .arg(bytes_per_sample) - .arg(one_frame_bytes)); + .arg(samples_per_frame)); return true; } -size_t AudioOutputDigitalEncoder::Encode(void *buf, int len, bool isFloat) +size_t AudioOutputDigitalEncoder::Encode(void *buf, int len, AudioFormat format) { size_t outsize = 0; int data_size; - if (isFloat) - inlen += AudioOutputUtil::fromFloat(FORMAT_S16, in + inlen, buf, len); + // Check if there is enough space in incoming buffer + int required_len = inlen + len / AudioOutputSettings::SampleSize(format) * + AudioOutputSettings::SampleSize(FORMAT_S16); + if (required_len > (int)in_size) + { + required_len = ((required_len / INBUFSIZE) + 1) * INBUFSIZE; + VERBOSE(VB_AUDIO, LOC + + QString("low mem, reallocating in buffer from %1 to %2") + .arg(in_size) + .arg(required_len)); + if (!(in = (inbuf_t *)realloc(in, in_size, required_len))) + { + in_size = 0; + VERBOSE(VB_AUDIO, LOC_ERR + + "AC-3 encode error, insufficient memory"); + return outlen; + } + in_size = required_len; + } + if (format != FORMAT_S16) + { + inlen += AudioOutputUtil::fromFloat(FORMAT_S16, (char *)in + inlen, + buf, len); + } else { - memcpy(in + inlen, buf, len); + memcpy((char *)in + inlen, buf, len); inlen += len; } - int frames = inlen / one_frame_bytes; + int frames = inlen / sizeof(inbuf_t) / samples_per_frame; int i = 0; while (i < frames) { outsize = avcodec_encode_audio(av_context, - m_encodebuffer, - FF_MIN_BUFFER_SIZE, - (short *)(in + i * one_frame_bytes)); + (uint8_t *)m_encodebuffer, + sizeof(m_encodebuffer), + (short *)(in + i * samples_per_frame)); if (outsize < 0) { VERBOSE(VB_AUDIO, LOC_ERR + "AC-3 encode error"); @@ -151,21 +215,44 @@ size_t AudioOutputDigitalEncoder::Encode(void *buf, int len, bool isFloat) { m_spdifenc = new SPDIFEncoder("spdif", CODEC_ID_AC3); } - m_spdifenc->WriteFrame(m_encodebuffer, outsize); - m_spdifenc->GetData(out + outlen, data_size); + m_spdifenc->WriteFrame((uint8_t *)m_encodebuffer, outsize); + // Check if output buffer is big enough + required_len = outlen + m_spdifenc->GetProcessedSize(); + if (required_len > (int)out_size) + { + required_len = ((required_len / OUTBUFSIZE) + 1) * OUTBUFSIZE; + VERBOSE(VB_AUDIO, LOC + + QString("low mem, reallocating out buffer from %1 to %2") + .arg(out_size) + .arg(required_len)); + if (!(out = (outbuf_t *)realloc(out, out_size, required_len))) + { + out_size = 0; + VERBOSE(VB_AUDIO, LOC_ERR + + "AC-3 encode error, insufficient memory"); + return outlen; + } + out_size = required_len; + } + m_spdifenc->GetData((uint8_t *)out + outlen, data_size); outlen += data_size; - inlen -= one_frame_bytes; + inlen -= samples_per_frame * sizeof(inbuf_t); i++; } - memmove(in, in + i * one_frame_bytes, inlen); + memmove(in, in + i * samples_per_frame, inlen); return outlen; } -void AudioOutputDigitalEncoder::GetFrames(void *ptr, int maxlen) +size_t AudioOutputDigitalEncoder::GetFrames(void *ptr, int maxlen) { int len = std::min(maxlen, outlen); + if (len != maxlen) + { + VERBOSE(VB_AUDIO, LOC + QString("GetFrames: getting less than requested")); + } memcpy(ptr, out, len); outlen -= len; - memmove(out, out + len, outlen); + memmove(out, (char *)out + len, outlen); + return len; } diff --git a/mythtv/libs/libmyth/audio/audiooutputdigitalencoder.h b/mythtv/libs/libmyth/audio/audiooutputdigitalencoder.h index 44b72bbab2b..151f3136349 100644 --- a/mythtv/libs/libmyth/audio/audiooutputdigitalencoder.h +++ b/mythtv/libs/libmyth/audio/audiooutputdigitalencoder.h @@ -6,36 +6,39 @@ extern "C" { }; #include "spdifencoder.h" +#include "audiooutputsettings.h" #define INBUFSIZE 131072 -#define OUTBUFSIZE 98304 -#define ENCODER_INBUFSIZE INBUFSIZE +#define OUTBUFSIZE INBUFSIZE class AudioOutputDigitalEncoder { + typedef int16_t inbuf_t; + typedef int16_t outbuf_t; + public: AudioOutputDigitalEncoder(void); ~AudioOutputDigitalEncoder(); bool Init(CodecID codec_id, int bitrate, int samplerate, int channels); void Dispose(void); - size_t Encode(void *buf, int len, bool isFloat); - void GetFrames(void *ptr, int maxlen); - size_t FrameSize(void) const { return one_frame_bytes; } - int Buffered(void) const { return inlen; } - - public: - size_t bytes_per_sample; + size_t Encode(void *buf, int len, AudioFormat format); + size_t GetFrames(void *ptr, int maxlen); + int Buffered(void) const + { return inlen / sizeof(inbuf_t) / av_context->channels; } private: + void *realloc(void *ptr, size_t old_size, size_t new_size); + AVCodecContext *av_context; - unsigned char out[OUTBUFSIZE]; - unsigned char inbuf[INBUFSIZE+16]; - unsigned char *in; + outbuf_t *out; + size_t out_size; + inbuf_t *in; + size_t in_size; int outlen; int inlen; - size_t one_frame_bytes; - uint8_t m_encodebuffer[FF_MIN_BUFFER_SIZE]; + size_t samples_per_frame; + int16_t m_encodebuffer[FF_MIN_BUFFER_SIZE]; SPDIFEncoder *m_spdifenc; };