Skip to content

Commit

Permalink
Move SPDIF/IEC958 encapsulation within the audio class so avfd doesn'…
Browse files Browse the repository at this point in the history
…t depends directly on any of the audio code.

Fixes timestamp estimation when timestamps are missing or frames are dropped
  • Loading branch information
jyavenard committed Feb 26, 2011
1 parent d9b9408 commit aebfb4d
Show file tree
Hide file tree
Showing 14 changed files with 157 additions and 123 deletions.
4 changes: 3 additions & 1 deletion mythtv/libs/libmyth/audiooutput.h
Expand Up @@ -76,7 +76,9 @@ class MPUBLIC AudioOutput : public VolumeBase, public OutputListeners

virtual void Reset(void) = 0;

virtual bool AddFrames(void *buffer, int samples, int64_t timecode) = 0;
virtual bool AddFrames(void *buffer, int frames, int64_t timecode) = 0;
virtual bool AddData(void *buffer, int len, int64_t timecode) = 0;
virtual int64_t LengthLastData(void) { return 0; }

virtual void SetTimecode(int64_t timecode) = 0;
virtual bool IsPaused(void) const = 0;
Expand Down
111 changes: 89 additions & 22 deletions mythtv/libs/libmyth/audiooutputbase.cpp
Expand Up @@ -17,6 +17,7 @@
#include "audiooutputdownmix.h"
#include "SoundTouch.h"
#include "freesurround.h"
#include "spdifencoder.h"

#define LOC QString("AO: ")
#define LOC_ERR QString("AO, ERROR: ")
Expand Down Expand Up @@ -97,7 +98,8 @@ AudioOutputBase::AudioOutputBase(const AudioSettings &settings) :
src_out(NULL), kAudioSRCOutputSize(0),
memory_corruption_test2(0xdeadbeef),
memory_corruption_test3(0xdeadbeef),
m_configure_succeeded(true)
m_configure_succeeded(true),m_length_last_data(0),
m_spdifenc(NULL)
{
src_in = (float *)AOALIGN(src_in_buf);
// The following are not bzero() because MS Windows doesn't like it.
Expand Down Expand Up @@ -343,25 +345,25 @@ bool AudioOutputBase::ToggleUpmix(void)

/*
* Setup samplerate and number of channels for passthrough
* Create SPDIF encoder and true if successful
*/
void AudioOutputBase::SetupPassthrough(AudioSettings &settings,
bool AudioOutputBase::SetupPassthrough(int codec, int codec_profile,
int &samplerate_tmp, int &channels_tmp)
{
samplerate_tmp = settings.samplerate;
channels_tmp = 2;
QString log;

switch (settings.codec)
switch (codec)
{
case CODEC_ID_AC3:
log = "AC3";
break;
case CODEC_ID_EAC3:
samplerate_tmp = settings.samplerate * 4;
samplerate_tmp = samplerate_tmp * 4;
log = "Dolby Digital Plus (E-AC3)";
break;
case CODEC_ID_DTS:
switch(settings.codec_profile)
switch(codec_profile)
{
case FF_PROFILE_DTS_ES:
log = "DTS-ES";
Expand Down Expand Up @@ -391,7 +393,7 @@ void AudioOutputBase::SetupPassthrough(AudioSettings &settings,
case CODEC_ID_TRUEHD:
channels_tmp = 8;
log = "TrueHD";
switch(settings.samplerate)
switch(samplerate_tmp)
{
case 48000:
case 96000:
Expand All @@ -412,6 +414,39 @@ void AudioOutputBase::SetupPassthrough(AudioSettings &settings,
break;
}
VBAUDIO("Setting " + log + " passthrough");

if (m_spdifenc)
{
delete m_spdifenc;
}

m_spdifenc = new SPDIFEncoder("spdif", codec);
if (m_spdifenc->Succeeded() && codec == CODEC_ID_DTS)
{
switch(codec_profile)
{
case FF_PROFILE_DTS:
case FF_PROFILE_DTS_ES:
case FF_PROFILE_DTS_96_24:
m_spdifenc->SetMaxHDRate(0);
break;
case FF_PROFILE_DTS_HD_HRA:
m_spdifenc->SetMaxHDRate(
OutputSettings(true)->canFeature(FEATURE_DTSHD) ?
192000 : 0);
break;
case FF_PROFILE_DTS_HD_MA:
m_spdifenc->SetMaxHDRate(OutputSettings(true)->GetMaxHDRate());
}
}

if (!m_spdifenc->Succeeded())
{
delete m_spdifenc;
m_spdifenc = NULL;
return false;
}
return true;
}

AudioOutputSettings *AudioOutputBase::OutputSettings(bool digital)
Expand Down Expand Up @@ -495,7 +530,9 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
int samplerate_tmp, channels_tmp;
if (settings.use_passthru)
{
SetupPassthrough(settings, samplerate_tmp, channels_tmp);
samplerate_tmp = settings.samplerate;
SetupPassthrough(settings.codec, settings.codec_profile,
samplerate_tmp, channels_tmp);
general_deps = samplerate == samplerate_tmp && channels == channels_tmp;
}

Expand Down Expand Up @@ -859,7 +896,7 @@ void AudioOutputBase::Reset()
* Set the timecode of the samples most recently added to the audiobuffer
*
* Used by mythmusic for seeking since it doesn't provide timecodes to
* AddFrames()
* AddData()
*/
void AudioOutputBase::SetTimecode(int64_t timecode)
{
Expand Down Expand Up @@ -953,7 +990,7 @@ int64_t AudioOutputBase::GetAudiotime(void)

/* timecode is the stretch adjusted version
of major post-stretched buffer contents
processing latencies are catered for in AddFrames/SetAudiotime
processing latencies are catered for in AddData/SetAudiotime
to eliminate race */
audiotime = audbuf_timecode - (
((int64_t)(main_buffer + soundcard_buffer) * eff_stretchfactor) /
Expand Down Expand Up @@ -983,7 +1020,7 @@ int64_t AudioOutputBase::GetAudiotime(void)
/**
* Set the timecode of the top of the ringbuffer
* Exclude all other processing elements as they dont vary
* between AddFrames calls
* between AddData calls
*/
void AudioOutputBase::SetAudiotime(int frames, int64_t timecode)
{
Expand Down Expand Up @@ -1193,27 +1230,35 @@ int AudioOutputBase::CopyWithUpmix(char *buffer, int frames, int &org_waud)
bool AudioOutputBase::AddFrames(void *in_buffer, int in_frames,
int64_t timecode)
{
int org_waud = waud, afree = audiofree();
int frames = in_frames;
return AddData(in_buffer, in_frames * source_bytes_per_frame, timecode);
}

/**
* Add data to the audiobuffer and perform any required processing
*
* Returns false if there's not enough space right now
*/
bool AudioOutputBase::AddData(void *in_buffer, int in_len,
int64_t timecode)
{
int org_waud = waud;
int afree = audiofree();
int frames = in_len / source_bytes_per_frame;
void *buffer = in_buffer;
int bpf = bytes_per_frame;
int len = frames * source_bytes_per_frame;
int len = in_len;
int used = kAudioRingBufferSize - afree;
bool music = false;
int bdiff;

if (!m_configure_succeeded)
{
VERBOSE(VB_IMPORTANT, LOC + "AddFrames called with audio framework not "
VERBOSE(VB_IMPORTANT, LOC + "AddData called with audio framework not "
"initialised");
m_length_last_data = 0;
return false;
}

VBAUDIOTS(QString("AddFrames frames=%1, bytes=%2, used=%3, free=%4, "
"timecode=%5 needsupmix=%6")
.arg(frames).arg(len).arg(used).arg(afree).arg(timecode)
.arg(needs_upmix));

/* See if we're waiting for new samples to be buffered before we unpause
post channel change, seek, etc. Wait for 4 fragments to be buffered */
if (unpause_when_ready && pauseaudio && audioready() > fragment_size << 2)
Expand All @@ -1222,6 +1267,28 @@ bool AudioOutputBase::AddFrames(void *in_buffer, int in_frames,
Pause(false);
}

if (passthru && m_spdifenc)
{
// mux into an IEC958 packet
m_spdifenc->WriteFrame((unsigned char *)in_buffer, len);
len = m_spdifenc->GetProcessedSize();
if (len > 0)
{
buffer = in_buffer = m_spdifenc->GetProcessedBuffer();
m_spdifenc->Reset();
frames = len / source_bytes_per_frame;
}
else
frames = 0;
}
m_length_last_data = (int64_t)
((double)(len * 1000) / (source_samplerate * source_bytes_per_frame));

VBAUDIOTS(QString("AddData frames=%1, bytes=%2, used=%3, free=%4, "
"timecode=%5 needsupmix=%6")
.arg(frames).arg(len).arg(used).arg(afree).arg(timecode)
.arg(needs_upmix));

// Don't write new samples if we're resetting the buffer or reconfiguring
QMutexLocker lock(&audio_buflock);

Expand Down Expand Up @@ -1263,11 +1330,11 @@ bool AudioOutputBase::AddFrames(void *in_buffer, int in_frames,

if (len > afree)
{
VBAUDIOTS("Buffer is full, AddFrames returning false");
VBAUDIOTS("Buffer is full, AddData returning false");
return false; // would overflow
}

int frames_remaining = in_frames;
int frames_remaining = frames;
int frames_final = 0;
int maxframes = (kAudioSRCInputSize /
(passthru ? channels : source_channels)) & ~0xf;
Expand Down
15 changes: 12 additions & 3 deletions mythtv/libs/libmyth/audiooutputbase.h
Expand Up @@ -43,6 +43,9 @@ class AsyncLooseLock
int tail;
};

// Forward declaration of SPDIF encoder
class SPDIFEncoder;

class AudioOutputBase : public AudioOutput, public QThread
{
public:
Expand Down Expand Up @@ -75,6 +78,8 @@ class AudioOutputBase : public AudioOutput, public QThread

// timecode is in milliseconds.
virtual bool AddFrames(void *buffer, int frames, int64_t timecode);
virtual bool AddData(void *buffer, int len, int64_t timecode);
virtual int64_t LengthLastData(void) { return m_length_last_data; }

virtual void SetTimecode(int64_t timecode);
virtual bool IsPaused(void) const { return actually_paused; }
Expand Down Expand Up @@ -178,9 +183,9 @@ class AudioOutputBase : public AudioOutput, public QThread
int src_quality;

private:
void SetupPassthrough(AudioSettings &settings, int &samplerate_tmp,
int &channels_tmp);
AudioOutputSettings* OutputSettings(bool digital);
bool SetupPassthrough(int codec, int codec_profile,
int &samplerate_tmp, int &channels_tmp);
AudioOutputSettings* OutputSettings(bool digital = true);
int CopyWithUpmix(char *buffer, int frames, int &org_waud);
void SetAudiotime(int frames, int64_t timecode);
AudioOutputSettings *output_settingsraw;
Expand Down Expand Up @@ -258,6 +263,10 @@ class AudioOutputBase : public AudioOutput, public QThread
uchar audiobuffer[kAudioRingBufferSize];
uint memory_corruption_test3;
uint m_configure_succeeded;
int64_t m_length_last_data;

// SPDIF Encoder for digital passthrough
SPDIFEncoder *m_spdifenc;
};

#endif
4 changes: 2 additions & 2 deletions mythtv/libs/libmyth/audiooutputdigitalencoder.cpp
Expand Up @@ -94,7 +94,7 @@ bool AudioOutputDigitalEncoder::Init(
delete m_spdifenc;
}

m_spdifenc = new SPDIFEncoder("spdif", av_context);
m_spdifenc = new SPDIFEncoder("spdif", CODEC_ID_AC3);
if (!m_spdifenc->Succeeded())
{
Dispose();
Expand Down Expand Up @@ -144,7 +144,7 @@ size_t AudioOutputDigitalEncoder::Encode(void *buf, int len, bool isFloat)

if (!m_spdifenc)
{
m_spdifenc = new SPDIFEncoder("spdif", av_context);
m_spdifenc = new SPDIFEncoder("spdif", CODEC_ID_AC3);
}
m_spdifenc->WriteFrame(m_encodebuffer, outsize);
m_spdifenc->GetData(out + outlen, data_size);
Expand Down
4 changes: 2 additions & 2 deletions mythtv/libs/libmyth/audiooutpututil.cpp
Expand Up @@ -640,8 +640,8 @@ void AudioOutputUtil::MuteChannel(int obits, int channels, int ch,
#define LE_INT(v) (v)
#endif

char *AudioOutputUtil::GeneratePinkSamples(char *frames, int channels,
int channel, int count, int bits)
char *AudioOutputUtil::GeneratePinkFrames(char *frames, int channels,
int channel, int count, int bits)
{
pink_noise_t pink;

Expand Down
4 changes: 2 additions & 2 deletions mythtv/libs/libmyth/audiooutpututil.h
Expand Up @@ -19,8 +19,8 @@ class MPUBLIC AudioOutputUtil
bool music, bool upmix);
static void MuteChannel(int obits, int channels, int ch,
void *buffer, int bytes);
static char *GeneratePinkSamples(char *frames, int channels,
int channel, int count, int bits = 16);
static char *GeneratePinkFrames(char *frames, int channels,
int channel, int count, int bits = 16);
};

#endif
18 changes: 4 additions & 14 deletions mythtv/libs/libmyth/spdifencoder.cpp
Expand Up @@ -18,7 +18,7 @@
* Use "adts" for ADTS encpsulation (AAC)
* AVCodecContext *ctx : CodecContext to be encaspulated
*/
SPDIFEncoder::SPDIFEncoder(QString muxer, AVCodecContext *ctx)
SPDIFEncoder::SPDIFEncoder(QString muxer, int codec_id)
: m_complete(false), m_oc(NULL), m_stream(NULL), m_size(0)
{
QByteArray dev_ba = muxer.toAscii();
Expand Down Expand Up @@ -102,21 +102,11 @@ SPDIFEncoder::SPDIFEncoder(QString muxer, AVCodecContext *ctx)

AVCodecContext *codec = m_stream->codec;

codec->codec_type = ctx->codec_type;
codec->codec_id = ctx->codec_id;
codec->sample_rate = ctx->sample_rate;
codec->sample_fmt = ctx->sample_fmt;
codec->channels = ctx->channels;
codec->bit_rate = ctx->bit_rate;
codec->extradata = new uint8_t[ctx->extradata_size];
codec->extradata_size = ctx->extradata_size;
memcpy(codec->extradata, ctx->extradata, ctx->extradata_size);

codec->codec_id = (CodecID)codec_id;
av_write_header(m_oc);

VERBOSE(VB_AUDIO, LOC + QString("Creating %1 encoder (%2, %3Hz)")
.arg(muxer).arg(ff_codec_id_string((CodecID)codec->codec_type))
.arg(codec->sample_rate));
VERBOSE(VB_AUDIO, LOC + QString("Creating %1 encoder (for %2)")
.arg(muxer).arg(ff_codec_id_string((CodecID)codec_id)));

m_complete = true;
}
Expand Down
4 changes: 3 additions & 1 deletion mythtv/libs/libmyth/spdifencoder.h
Expand Up @@ -15,10 +15,12 @@ extern "C" {
class MPUBLIC SPDIFEncoder
{
public:
SPDIFEncoder(QString muxer, AVCodecContext *ctx);
SPDIFEncoder(QString muxer, int codec_id);
~SPDIFEncoder();
void WriteFrame(unsigned char *data, int size);
int GetData(unsigned char *buffer, int &dest_size);
int GetProcessedSize() { return m_size; };
unsigned char *GetProcessedBuffer() { return m_buffer; };
void Reset();
bool Succeeded() { return m_complete; };
bool SetMaxHDRate(int rate);
Expand Down
10 changes: 9 additions & 1 deletion mythtv/libs/libmythtv/audioplayer.cpp
Expand Up @@ -363,11 +363,19 @@ void AudioPlayer::AddAudioData(char *buffer, int len, int64_t timecode)
if (samplesize <= 0)
return;

if (!m_audioOutput->AddFrames(buffer, len / samplesize, timecode))
if (!m_audioOutput->AddData(buffer, len, timecode))
VERBOSE(VB_PLAYBACK, LOC + "AddAudioData(): "
"Audio buffer overflow, audio data lost!");
}

int64_t AudioPlayer::LengthLastData(void)
{
if (!m_audioOutput)
return 0;
else
return m_audioOutput->LengthLastData();
}

bool AudioPlayer::GetBufferStatus(uint &fill, uint &total)
{
fill = total = 0;
Expand Down
1 change: 1 addition & 0 deletions mythtv/libs/libmythtv/audioplayer.h
Expand Up @@ -58,6 +58,7 @@ class MPUBLIC AudioPlayer
MuteState IncrMuteState(void);

void AddAudioData(char *buffer, int len, int64_t timecode);
int64_t LengthLastData(void);
bool GetBufferStatus(uint &fill, uint &total);
bool IsBufferAlmostFull(void);

Expand Down

0 comments on commit aebfb4d

Please sign in to comment.