Skip to content

Commit

Permalink
Add support for E-AC3, TrueHD and DTS-HD MA passthrough.
Browse files Browse the repository at this point in the history
Only TrueHD tested so far. There are strong requirements to get HD AUDIO passthrough to work. Please refer to this wiki page http://www.mythtv.org/wiki/User_Manual:HDAudioPassthrough for more information.

A big thank you to Anssi Hannula for adding HBR support to Alsa and ffmpeg, and
for his patience in getting me up to scratch on the matter
  • Loading branch information
jyavenard committed Dec 13, 2010
1 parent 554f667 commit 6cb7956
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 31 deletions.
3 changes: 3 additions & 0 deletions mythtv/libs/libmyth/audiooutput.h
Expand Up @@ -60,6 +60,9 @@ class MPUBLIC AudioOutput : public VolumeBase, public OutputListeners

virtual void SetStretchFactor(float factor);
virtual float GetStretchFactor(void) const { return 1.0f; }
virtual int GetChannels(void) const { return 2; }
virtual AudioFormat GetFormat(void) const { return FORMAT_S16; };
virtual int GetBytesPerFrame(void) const { return 4; };

virtual AudioOutputSettings* GetOutputSettingsCleaned(void)
{ return new AudioOutputSettings; }
Expand Down
16 changes: 11 additions & 5 deletions mythtv/libs/libmyth/audiooutputalsa.cpp
Expand Up @@ -55,7 +55,7 @@ AudioOutputALSA::AudioOutputALSA(const AudioSettings &settings) :
if (args < 0)
{
/* no existing parameters: add it behind device name */
passthru_device += ":AES0=6";
passthru_device += ":AES0=6,AES1=0x82,AES2=0x00,AES3=0x01";
}
else
{
Expand All @@ -66,12 +66,12 @@ AudioOutputALSA::AudioOutputALSA(const AudioSettings &settings) :
if (args == passthru_device.length())
{
/* ":" but no parameters */
passthru_device += "AES0=6";
passthru_device += "AES0=6,AES1=0x82,AES2=0x00,AES3=0x01";
}
else if (passthru_device[args] != '{')
{
/* a simple list of parameters: add it at the end of the list */
passthru_device += ",AES0=6";
passthru_device += ",AES0=6,AES1=0x82,AES2=0x00,AES3=0x01";
}
else
{
Expand All @@ -80,7 +80,8 @@ AudioOutputALSA::AudioOutputALSA(const AudioSettings &settings) :
--len;
while (len > 0 && passthru_device[len].isSpace());
if (passthru_device[len] == '}')
passthru_device = passthru_device.insert(len, " AES0=6");
passthru_device = passthru_device.insert(
len, " AES0=6 AES1 0x82 AES2=0x00 AES3=0x01");
}
}
}
Expand All @@ -106,6 +107,7 @@ int AudioOutputALSA::TryOpenDevice(int open_mode, int try_ac3)
if (try_ac3)
{
dev_ba = passthru_device.toAscii();
VBAUDIO(QString("OpenDevice %1 for passthrough").arg(passthru_device));
err = snd_pcm_open(&pcm_handle, dev_ba.constData(),
SND_PCM_STREAM_PLAYBACK, open_mode);
m_lastdevice = passthru_device;
Expand All @@ -120,6 +122,7 @@ int AudioOutputALSA::TryOpenDevice(int open_mode, int try_ac3)
if (!try_ac3 || err < 0)
{
// passthru open failed, retry default device
VBAUDIO(QString("OpenDevice %1").arg(main_device));
dev_ba = main_device.toAscii();
err = snd_pcm_open(&pcm_handle, dev_ba.constData(),
SND_PCM_STREAM_PLAYBACK, open_mode);
Expand Down Expand Up @@ -484,7 +487,10 @@ void AudioOutputALSA::WriteAudio(uchar *aubuf, int size)
return;
}

if ((!passthru && channels == 6) || channels == 8)
/* Audio received is using SMPTE channel ordering
* ALSA uses its own channel order.
* Do not re-order passthu audio */
if (!passthru && (channels == 6 || channels == 8))
ReorderSmpteToAlsa(aubuf, frames, output_format, channels - 6);

VERBOSE(VB_AUDIO+VB_TIMESTAMP,
Expand Down
90 changes: 72 additions & 18 deletions mythtv/libs/libmyth/audiooutputbase.cpp
Expand Up @@ -173,7 +173,7 @@ void AudioOutputBase::InitSettings(const AudioSettings &settings)
/**
* Returns capabilities supported by the audio device
* amended to take into account the digital audio
* options (AC3 and DTS)
* options (AC3, DTS, E-AC3 and TrueHD)
*/
AudioOutputSettings* AudioOutputBase::GetOutputSettingsCleaned(void)
{
Expand All @@ -195,7 +195,7 @@ AudioOutputSettings* AudioOutputBase::GetOutputSettingsCleaned(void)
/**
* Returns capabilities supported by the audio device
* amended to take into account the digital audio
* options (AC3 and DTS) as well as the user settings
* options (AC3, DTS, E-AC3 and TrueHD) as well as the user settings
*/
AudioOutputSettings* AudioOutputBase::GetOutputSettingsUsers(void)
{
Expand All @@ -221,10 +221,12 @@ bool AudioOutputBase::CanPassthrough(int samplerate, int channels) const
// Don't know any cards that support spdif clocked at < 44100
// Some US cable transmissions have 2ch 32k AC-3 streams
ret &= samplerate >= 44100;
// Will downmix if we can't support the amount of channels
ret &= channels <= max_channels;
// Stereo content will always be decoded so it can later be upmixed
ret &= channels != 2;
// Will passthrough if surround audio was defined. Amplifier will
// do the downmix if required
ret &= max_channels >= 6;
// Stereo content will always be decoded so it can later be upmixed
// unless audio is configured for stereoo
ret |= channels == 2 && max_channels == 2;

return ret;
}
Expand Down Expand Up @@ -429,7 +431,8 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
VBAUDIO(QString("Original codec was %1, %2, %3 kHz, %4 channels")
.arg(ff_codec_id_string((CodecID)codec))
.arg(output_settings->FormatToString(format))
.arg(samplerate/1000).arg(source_channels));
.arg(samplerate/1000)
.arg(source_channels));

/* Encode to AC-3 if we're allowed to passthru but aren't currently
and we have more than 2 channels but multichannel PCM is not supported
Expand All @@ -439,15 +442,19 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
output_settings->canAC3() &&
((!output_settings->canLPCM() && configured_channels > 2) ||
!output_settings->IsSupportedChannels(channels)));
VBAUDIO(QString("enc(%1), passthru(%2), canAC3(%3), canDTS(%4), canLPCM(%5)"
", configured_channels(%6), %7 channels supported(%8)")
VBAUDIO(QString("enc(%1), passthru(%2), canAC3(%3), canDTS(%4), canHD(%5), "
"canHDLL(%6), canLPCM(%7), "
"configured_channels(%8), %9 channels supported(%10)")
.arg(enc)
.arg(passthru)
.arg(output_settings->canAC3())
.arg(output_settings->canDTS())
.arg(output_settings->canHD())
.arg(output_settings->canHDLL())
.arg(output_settings->canLPCM())
.arg(configured_channels)
.arg(channels).arg(output_settings->IsSupportedChannels(channels)));
.arg(channels)
.arg(output_settings->IsSupportedChannels(channels)));

int dest_rate = 0;

Expand Down Expand Up @@ -520,8 +527,46 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
}
}

source_bytes_per_frame = source_channels *
output_settings->SampleSize(format);
if (passthru)
{
switch (settings.codec)
{
case CODEC_ID_EAC3:
samplerate *= 4;
case CODEC_ID_AC3:
case CODEC_ID_DTS:
channels = 2;
break;
case CODEC_ID_TRUEHD:
channels = 8;
switch(samplerate)
{
case 48000:
case 96000:
case 192000:
samplerate = 192000;
break;
case 44100:
case 88200:
case 176400:
samplerate = 176400;
break;
default:
VBAUDIO("TrueHD: Unsupported samplerate");
break;
}
break;
}
//AC3, DTS, DTS-HD MA and TrueHD use 16 bits samples
format = output_format = FORMAT_S16;
source_bytes_per_frame = channels *
output_settings->SampleSize(format);
}
else
{
source_bytes_per_frame = source_channels *
output_settings->SampleSize(format);
}

// Turn on float conversion?
if (need_resampler || needs_upmix || needs_downmix ||
Expand All @@ -534,12 +579,17 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
if (enc)
output_format = FORMAT_S16; // Output s16le for AC-3 encoder
else
output_format = output_settings->BestSupportedFormat();
{
// re-encode audio using same format as input if upmixing
// to minimize the memory sound buffer usage. There should be
// no siginificant quality loss
if (needs_upmix)
output_format = format;
else
output_settings->BestSupportedFormat();
}
}

if (passthru)
channels = 2; // IEC958 bitstream - 2 ch

bytes_per_frame = processing ? 4 : output_settings->SampleSize(format);
bytes_per_frame *= channels;

Expand Down Expand Up @@ -976,6 +1026,7 @@ int AudioOutputBase::CopyWithUpmix(char *buffer, int frames, int &org_waud)
if (!needs_upmix)
{
int num = len;

if (bdiff <= num)
{
memcpy(WPOS, buffer, bdiff);
Expand Down Expand Up @@ -1054,7 +1105,8 @@ bool AudioOutputBase::AddFrames(void *in_buffer, int in_frames,
int org_waud = waud, afree = audiofree();
int frames = in_frames;
void *buffer = in_buffer;
int bpf = bytes_per_frame, len = frames * source_bytes_per_frame;
int bpf = bytes_per_frame;
int len = frames * source_bytes_per_frame;
int used = kAudioRingBufferSize - afree;
bool music = false;
int bdiff;
Expand Down Expand Up @@ -1119,7 +1171,8 @@ bool AudioOutputBase::AddFrames(void *in_buffer, int in_frames,

int frames_remaining = in_frames;
int frames_final = 0;
int maxframes = (kAudioSRCInputSize / source_channels) & ~0xf;
int maxframes = (kAudioSRCInputSize /
(passthru ? channels : source_channels)) & ~0xf;
int offset = 0;

while(frames_remaining > 0)
Expand All @@ -1140,6 +1193,7 @@ bool AudioOutputBase::AddFrames(void *in_buffer, int in_frames,
// Convert to floats
len = AudioOutputUtil::toFloat(format, src_in, buffer, len);
}

frames_remaining -= frames;

// Perform downmix if necessary
Expand Down
3 changes: 3 additions & 0 deletions mythtv/libs/libmyth/audiooutputbase.h
Expand Up @@ -61,6 +61,9 @@ class AudioOutputBase : public AudioOutput, public QThread
// timestretch
virtual void SetStretchFactor(float factor);
virtual float GetStretchFactor(void) const;
virtual int GetChannels(void) const { return channels; }
virtual AudioFormat GetFormat(void) const { return format; };
virtual int GetBytesPerFrame(void) const { return source_bytes_per_frame; };

virtual bool CanPassthrough(int samplerate, int channels) const;
virtual bool ToggleUpmix(void);
Expand Down
8 changes: 4 additions & 4 deletions mythtv/libs/libmythtv/audioplayer.cpp
Expand Up @@ -106,8 +106,6 @@ QString AudioPlayer::ReinitAudio(void)
const AudioSettings settings(m_format, m_channels, m_codec,
m_samplerate, m_passthru);
m_audioOutput->Reconfigure(settings);
if (m_passthru)
m_channels = 2;
errMsg = m_audioOutput->GetError();
SetStretchFactor(m_stretchfactor);
}
Expand Down Expand Up @@ -350,12 +348,14 @@ void AudioPlayer::AddAudioData(char *buffer, int len, int64_t timecode)
if (m_parent->PrepareAudioSample(timecode) && m_audioOutput &&
!no_audio_out)
m_audioOutput->Drain();
int samplesize = m_channels * AudioOutputSettings::SampleSize(m_format);
int samplesize = m_audioOutput->GetBytesPerFrame();

if ((samplesize <= 0) || !m_audioOutput)
return;
int frames = len / samplesize;

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

Expand Down
14 changes: 11 additions & 3 deletions mythtv/libs/libmythtv/avformatdecoder.cpp
Expand Up @@ -3919,7 +3919,7 @@ bool AvFormatDecoder::ProcessAudioPacket(AVStream *curstream, AVPacket *pkt,
long long pts = 0;
int ret = 0;
int data_size = 0;
bool firstloop = true, dts = false;
bool firstloop = true;

avcodeclock->lock();
int audIdx = selectedTrack[kTrackTypeAudio].av_stream_index;
Expand Down Expand Up @@ -4030,6 +4030,11 @@ bool AvFormatDecoder::ProcessAudioPacket(AVStream *curstream, AVPacket *pkt,
m_spdifenc->WriteFrame(tmp_pkt.data, tmp_pkt.size);
data_size = tmp_pkt.size;
ret = m_spdifenc->GetData((unsigned char *)audioSamples, data_size);
if (ret < 0)
{
avcodeclock->unlock();
return true;
}
}
else
{
Expand Down Expand Up @@ -4066,8 +4071,7 @@ bool AvFormatDecoder::ProcessAudioPacket(AVStream *curstream, AVPacket *pkt,

if (ret < 0)
{
if (!dts)
VERBOSE(VB_IMPORTANT, LOC_ERR + "Unknown audio decoding error");
VERBOSE(VB_IMPORTANT, LOC_ERR + "Unknown audio decoding error");
return false;
}

Expand Down Expand Up @@ -4606,6 +4610,10 @@ bool AvFormatDecoder::DoPassThrough(const AVCodecContext *ctx)
passthru = m_audio->CanAC3();
else if (ctx->codec_id == CODEC_ID_DTS)
passthru = m_audio->CanDTS();
else if (ctx->codec_id == CODEC_ID_EAC3)
passthru = m_audio->CanHD();
else if (ctx->codec_id == CODEC_ID_TRUEHD)
passthru = m_audio->CanHDLL();
passthru &= m_audio->CanPassthrough(ctx->sample_rate, ctx->channels);
passthru &= !internal_vol;
passthru &= !transcoding && !disable_passthru;
Expand Down
1 change: 0 additions & 1 deletion mythtv/programs/mythtranscode/transcode.cpp
Expand Up @@ -74,7 +74,6 @@ class AudioReencodeBuffer : public AudioOutput
eff_audiorate = (dsprate / 100);
}

virtual void SetBlocking(bool block) { (void)block; }
virtual void Reset(void)
{
audiobuffer_len = 0;
Expand Down

0 comments on commit 6cb7956

Please sign in to comment.