Skip to content

Commit

Permalink
mythtranscode code re-org
Browse files Browse the repository at this point in the history
Move the following classes out of transcode.cpp to make things a little
cleaner and allow later reuse of the audio and video classes in a
HTTP Live Streaming encoder which will not be contained within transcode.cpp.

  AudioBuffer
  AudioReencodeBuffer
  Cutter
  TranscodeFrameQueue (renamed to VideoDecodeBuffer)

There is no new functionality in this patch, only a code re-org.
  • Loading branch information
cpinkham committed Oct 12, 2012
1 parent 58e1ad3 commit 807b46b
Show file tree
Hide file tree
Showing 8 changed files with 711 additions and 684 deletions.
228 changes: 228 additions & 0 deletions mythtv/programs/mythtranscode/audioreencodebuffer.cpp
@@ -0,0 +1,228 @@

#include "audioreencodebuffer.h"

extern "C" {
#include "libavutil/mem.h"
}


AudioBuffer::AudioBuffer()
: m_frames(0), m_time(-1)
{
m_size = 0;
m_buffer = (uint8_t *)av_malloc(ABLOCK_SIZE);
if (m_buffer == NULL)
{
throw std::bad_alloc();
}
m_realsize = ABLOCK_SIZE;
}

AudioBuffer::AudioBuffer(const AudioBuffer &old)
: m_size(old.m_size), m_realsize(old.m_realsize),
m_frames(old.m_frames), m_time(old.m_time)
{
m_buffer = (uint8_t *)av_malloc(m_realsize);
if (m_buffer == NULL)
{
throw std::bad_alloc();
}
memcpy(m_buffer, old.m_buffer, m_size);
}

AudioBuffer::~AudioBuffer()
{
av_free(m_buffer);
}

void AudioBuffer::appendData(unsigned char *buffer, int len, int frames,
long long time)
{
if ((m_size + len) > m_realsize)
{
// buffer is too small to fit all
// can't use av_realloc as it doesn't guarantee reallocated memory
// to be 16 bytes aligned
m_realsize = ((m_size + len) / ABLOCK_SIZE + 1 ) * ABLOCK_SIZE;
uint8_t *tmp = (uint8_t *)av_malloc(m_realsize);
if (tmp == NULL)
{
throw std::bad_alloc();
}
memcpy(tmp, m_buffer, m_size);
av_free(m_buffer);
m_buffer = tmp;
}

memcpy(m_buffer + m_size, buffer, len);
m_size += len;
m_frames += frames;
m_time = time;
}

//////////////////////////////////////////////////////////////////////////////

AudioReencodeBuffer::AudioReencodeBuffer(AudioFormat audio_format,
int audio_channels, bool passthru)
: m_saveBuffer(NULL)
{
Reset();
const AudioSettings settings(audio_format, audio_channels, 0, 0, false);
Reconfigure(settings);
m_initpassthru = passthru;
m_audioFrameSize = 0;
}

AudioReencodeBuffer::~AudioReencodeBuffer()
{
Reset();
if (m_saveBuffer)
delete m_saveBuffer;
}

// reconfigure sound out for new params
void AudioReencodeBuffer::Reconfigure(const AudioSettings &settings)
{
ClearError();

m_passthru = settings.use_passthru;
m_channels = settings.channels;
m_bytes_per_frame = m_channels *
AudioOutputSettings::SampleSize(settings.format);
m_eff_audiorate = settings.samplerate;
}

// dsprate is in 100 * frames/second
void AudioReencodeBuffer::SetEffDsp(int dsprate)
{
m_eff_audiorate = (dsprate / 100);
}

void AudioReencodeBuffer::Reset(void)
{
QMutexLocker locker(&m_bufferMutex);
foreach (AudioBuffer *ab, m_bufferList)
{
delete ab;
}
m_bufferList.clear();
}

// timecode is in milliseconds.
bool AudioReencodeBuffer::AddFrames(void *buffer, int frames, int64_t timecode)
{
return AddData(buffer, frames * m_bytes_per_frame, timecode, frames);
}

// timecode is in milliseconds.
bool AudioReencodeBuffer::AddData(void *buffer, int len, int64_t timecode,
int frames)
{
unsigned char *buf = (unsigned char *)buffer;

// Test if target is using a fixed buffer size.
if (m_audioFrameSize)
{
int index = 0;

// Target has a fixed buffer size, which may not match len.
// Redistribute the bytes into appropriately sized buffers.
while (index < len)
{
// See if we have some saved from last iteration in
// m_saveBuffer. If not create a new empty buffer.
if (!m_saveBuffer)
m_saveBuffer = new AudioBuffer();

// Use as many of the remaining frames as will fit in the space
// left in the buffer.
int bufsize = m_saveBuffer->size();
int part = min(len - index, m_audioFrameSize - bufsize);
int out_frames = part / m_bytes_per_frame;
timecode += out_frames * 1000 / m_eff_audiorate;

// Store frames in buffer, basing frame count on number of
// bytes, which works only for uncompressed data.
m_saveBuffer->appendData(&buf[index], part,
out_frames, timecode);

// If we have filled the buffer...
if (m_saveBuffer->size() == m_audioFrameSize)
{
QMutexLocker locker(&m_bufferMutex);

// store the buffer
m_bufferList.append(m_saveBuffer);
// mark m_saveBuffer as emtpy.
m_saveBuffer = NULL;
// m_last_audiotime is updated iff we store a buffer.
m_last_audiotime = timecode;
}

index += part;
}
}
else
{
// Target has no fixed buffer size. We can use a simpler algorithm
// and use 'frames' directly rather than 'len / m_bytes_per_frame',
// thus also covering the passthrough case.
m_saveBuffer = new AudioBuffer();
timecode += frames * 1000 / m_eff_audiorate;
m_saveBuffer->appendData(buf, len, frames, timecode);

QMutexLocker locker(&m_bufferMutex);
m_bufferList.append(m_saveBuffer);
m_saveBuffer = NULL;
m_last_audiotime = timecode;
}

return true;
}

AudioBuffer *AudioReencodeBuffer::GetData(long long time)
{
QMutexLocker locker(&m_bufferMutex);

if (m_bufferList.isEmpty())
return NULL;

AudioBuffer *ab = m_bufferList.front();

if (ab->m_time <= time)
{
m_bufferList.pop_front();
return ab;
}

return NULL;
}

long long AudioReencodeBuffer::GetSamples(long long time)
{
QMutexLocker locker(&m_bufferMutex);

if (m_bufferList.isEmpty())
return 0;

long long samples = 0;
for (QList<AudioBuffer *>::iterator it = m_bufferList.begin();
it != m_bufferList.end(); ++it)
{
AudioBuffer *ab = *it;

if (ab->m_time <= time)
samples += ab->m_frames;
else
break;
}
return samples;
}

void AudioReencodeBuffer::SetTimecode(int64_t timecode)
{
m_last_audiotime = timecode;
}

/* vim: set expandtab tabstop=4 shiftwidth=4: */

89 changes: 89 additions & 0 deletions mythtv/programs/mythtranscode/audioreencodebuffer.h
@@ -0,0 +1,89 @@
#ifndef AUDIOREENCODEBUFFER_H
#define AUDIOREENCODEBUFFER_H

#include "mythconfig.h"
#include "audiooutput.h"

#define ABLOCK_SIZE 8192

class AudioBuffer
{
public:
AudioBuffer();
AudioBuffer(const AudioBuffer &old);
~AudioBuffer();

void appendData(unsigned char *buffer, int len, int frames, long long time);
char *data(void) { return (char *)m_buffer; }
int size(void) { return m_size; }

uint8_t *m_buffer;
int m_size;
int m_realsize;
int m_frames;
long long m_time;
};

// This class is to act as a fake audio output device to store the data
// for reencoding.
class AudioReencodeBuffer : public AudioOutput
{
public:
AudioReencodeBuffer(AudioFormat audio_format, int audio_channels,
bool passthru);
~AudioReencodeBuffer();

virtual void Reconfigure(const AudioSettings &settings);
virtual void SetEffDsp(int dsprate);
virtual void Reset(void);
virtual bool AddFrames(void *buffer, int frames, int64_t timecode);
virtual bool AddData(void *buffer, int len, int64_t timecode,
int frames);
AudioBuffer *GetData(long long time);
long long GetSamples(long long time);
virtual void SetTimecode(int64_t timecode);
virtual bool IsPaused(void) const { return false; }
virtual void Pause(bool paused) { (void)paused; }
virtual void PauseUntilBuffered(void) { }
virtual void Drain(void) { }
virtual int64_t GetAudiotime(void) { return m_last_audiotime; }
virtual int GetVolumeChannel(int) const { return 100; }
virtual void SetVolumeChannel(int, int) { }
virtual void SetVolumeAll(int) { }
virtual uint GetCurrentVolume(void) const { return 100; }
virtual void SetCurrentVolume(int) { }
virtual void AdjustCurrentVolume(int) { }
virtual void SetMute(bool) { }
virtual void ToggleMute(void) { }
virtual MuteState GetMuteState(void) const { return kMuteOff; }
virtual MuteState IterateMutedChannels(void) { return kMuteOff; }
virtual bool IsUpmixing(void) { return false; }
virtual bool ToggleUpmix(void) { return false; }
virtual bool CanUpmix(void) { return false; }
virtual void SetSWVolume(int new_volume, bool save) { return; }
virtual int GetSWVolume(void) { return 100; }
virtual bool CanPassthrough(int, int, int, int) const
{ return m_initpassthru; }

// These are pure virtual in AudioOutput, but we don't need them here
virtual void bufferOutputData(bool) { return; }
virtual int readOutputData(unsigned char*, int ) { return 0; }

int m_channels;
int m_bits;
int m_bytes_per_frame;
int m_eff_audiorate;
long long m_last_audiotime;
bool m_passthru;
int m_audioFrameSize;

private:
bool m_initpassthru;
QMutex m_bufferMutex;
QList<AudioBuffer *> m_bufferList;
AudioBuffer *m_saveBuffer;
};

#endif
/* vim: set expandtab tabstop=4 shiftwidth=4: */

0 comments on commit 807b46b

Please sign in to comment.