Skip to content

Commit

Permalink
Rewrite of AC3 encoder.
Browse files Browse the repository at this point in the history
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
jyavenard committed May 2, 2011
1 parent ea24712 commit de0c5fa
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 86 deletions.
64 changes: 21 additions & 43 deletions mythtv/libs/libmyth/audio/audiooutputbase.cpp
Expand Up @@ -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 =
Expand Down Expand Up @@ -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;
Expand Down
145 changes: 116 additions & 29 deletions mythtv/libs/libmyth/audio/audiooutputdigitalencoder.cpp
Expand Up @@ -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()
Expand All @@ -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)
{
Expand All @@ -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)
{
Expand All @@ -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");
Expand All @@ -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)
{
Expand All @@ -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");
Expand All @@ -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;
}
31 changes: 17 additions & 14 deletions mythtv/libs/libmyth/audio/audiooutputdigitalencoder.h
Expand Up @@ -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;
};

Expand Down

0 comments on commit de0c5fa

Please sign in to comment.