Permalink
Browse files

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.
  • Loading branch information...
1 parent ea24712 commit de0c5fa4d184c075de31c8786b89972a1440e610 @jyavenard jyavenard committed May 2, 2011
@@ -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;
@@ -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;
}
@@ -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;
};

0 comments on commit de0c5fa

Please sign in to comment.