Permalink
Browse files

Add support for E-AC3, TrueHD and DTS-HD MA passthrough.

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...
1 parent 7531972 commit c1cf68b9c251d15ed36ae66c1690c588d64d3a0f @jyavenard jyavenard committed Dec 13, 2010
@@ -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; }
@@ -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
{
@@ -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
{
@@ -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");
}
}
}
@@ -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;
@@ -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);
@@ -483,7 +486,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,
@@ -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)
{
@@ -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)
{
@@ -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;
}
@@ -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
@@ -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;
@@ -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 ||
@@ -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;
@@ -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);
@@ -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;
@@ -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)
@@ -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
@@ -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);
@@ -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);
}
@@ -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!");
}
@@ -3625,7 +3625,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;
@@ -3736,6 +3736,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
{
@@ -3772,8 +3777,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;
}
@@ -4282,6 +4286,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;
@@ -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;

0 comments on commit c1cf68b

Please sign in to comment.