Permalink
Browse files

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

A big thank you to Anssi Hannulafor his changes to FFmpeg and working with me to get this up and running.
Thanks to foobum for his code review

Details on some of the changes included in this commit are:
- When creating the AudioOutput instance, only open the audio device and set all parameters when we have valid configuration information. This seriously reduces the amount of noise in the logs
- Simplify locking code in audioplayer, in particular do not create more locking than necessary
- E-AC3 , DTS-HD hi-res can be enabled on any platform supporting stereo 192kHz, so change the test to see if hardware supports that mode
- Add ability to play audio-only files with default player. Set -O AudioOnlyPlayback=1
- Add support for 6.144Mbit/s bitrate passthrough. This allows the use of DTS-HD passthrough for card not supporting HBR (ION, 9400M or nvidia GT2xx).
- Make audiooutputsettings digital features more generic and future-proof
- Change digital passthrough auto-configuration:
  Open device with settings:
  AES0=6 AES1=0x82 AES2=0x00 AES3=0x01.

  AES0 = NON_AUDIO | PRO_MODE
  AES1 = original stream, original PCM coder
  AES2 = source and channel unspecified
  AES3 = sample rate unspecified

  by default, ALSA uses AES3=0x02 which force the sampling rate to 48kHz. This break 44.1kHz passthrough with some audio cards.
  On the other hand, with some cards, you need to set 48kHz sample rate to get 44.1kHz passthrough. As the later case is obviously a bug, we do the right thing and set it as it should by default.
  For people finding that they no longer get multi-channel AC3/DTS audio, they need to go into the Advanced Option and check the SPDIF 48k override option
-Up to now, if the user had overriden the passthrough device, we assumed he knew what he was doing and it would disable all consistency tests.
  We now handle errors should the passthrough device not be valid. In the settings screen, an error message will now be shown.
  We also now handle two different AudioOutputSettings type one for PCM output, the other for passthrough. Previously capabilities would only be checked on the main audio device.
-Fix some segfault should the upmixer failed to be initialised properly. Fix timestretch.
-Mythmusic seems to happily supply audio frames to AudioOutput even when audio initialisation fail. As some fields weren't initialised, this could result with some division by zero error
-Audio framework: Main function member will now return an error if called without audio framework properly initialised
-Add an OutputSettings(bool digital) method in AudioOutputBase, get rid of alternation in Reconfigure()
-Check for FORMAT_S16 support for passthru support, use canFeature instead of canAC3 / canLPCM in AO
-Rewrite support for the various DTS streams.
  Can now properly distinguish between the various DTS types:
  - DTS Core
  - DTS-ES
  - DTS 96/24
  - DTS-HD High-Res
  - DTS-HD MA
 Note that if not using-passthrough, only the DTS Core stream can be decoded
-Change in the audio stream can now be properly monitored such as changing DTS audio type.
-Move SPDIF/IEC958 encapsulation within the audio class so avfd doesn't depends directly on any of the audio code.
-Fixes timestamp estimation when timestamps are missing or frames are dropped
  • Loading branch information...
1 parent 8b8b128 commit 649fa85cb41f128606038a9789693657a11cd178 @jyavenard jyavenard committed Feb 27, 2011
@@ -303,11 +303,8 @@ void MusicPlayer::openOutputDevice(void)
else
adevice = gCoreContext->GetSetting("MusicAudioDevice");
- pdevice = gCoreContext->GetNumSetting("AdvancedAudioSettings",
- false) &&
- gCoreContext->GetNumSetting("PassThruDeviceOverride",
- false) ?
- gCoreContext->GetSetting("PassThruOutputDevice") : QString::null;
+ pdevice = gCoreContext->GetNumSetting("PassThruDeviceOverride", false) ?
+ gCoreContext->GetSetting("PassThruOutputDevice") : "auto";
// TODO: Error checking that device is opened correctly!
m_output = AudioOutput::OpenAudio(
@@ -64,18 +64,21 @@ class MPUBLIC AudioOutput : public VolumeBase, public OutputListeners
virtual AudioFormat GetFormat(void) const { return FORMAT_S16; };
virtual int GetBytesPerFrame(void) const { return 4; };
- virtual AudioOutputSettings* GetOutputSettingsCleaned(void)
+ virtual AudioOutputSettings* GetOutputSettingsCleaned(bool digital = true)
{ return new AudioOutputSettings; }
- virtual AudioOutputSettings* GetOutputSettingsUsers(void)
+ virtual AudioOutputSettings* GetOutputSettingsUsers(bool digital = true)
{ return new AudioOutputSettings; }
- virtual bool CanPassthrough(int samplerate, int channels) const = 0;
+ virtual bool CanPassthrough(int samplerate, int channels, int codec) const
+ { return false; }
// dsprate is in 100 * samples/second
virtual void SetEffDsp(int dsprate) = 0;
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;
@@ -36,26 +36,37 @@ AudioOutputALSA::AudioOutputALSA(const AudioSettings &settings) :
pbufsize(-1),
m_card(-1),
m_device(-1),
- m_subdevice(-1),
- m_autopassthrough(false)
+ m_subdevice(-1)
{
m_mixer.handle = NULL;
m_mixer.elem = NULL;
// Set everything up
if (passthru_device == "auto")
{
- m_autopassthrough = true;
passthru_device = main_device;
- /* to set the non-audio bit, use AES0=6 */
int len = passthru_device.length();
int args = passthru_device.indexOf(":");
+ /*
+ * AES description:
+ * AES0=6 AES1=0x82 AES2=0x00 AES3=0x01.
+ * AES0 = NON_AUDIO | PRO_MODE
+ * AES1 = original stream, original PCM coder
+ * AES2 = source and channel unspecified
+ * AES3 = sample rate unspecified
+ */
+ bool s48k = gCoreContext->GetNumSetting("SPDIFRateOverride", false);
+ QString iecarg = QString("AES0=6,AES1=0x82,AES2=0x00") +
+ (s48k ? QString() : QString(",AES3=0x01"));
+ QString iecarg2 = QString("AES0=6 AES1=0x82 AES2=0x00") +
+ (s48k ? QString() : QString(" AES3=0x01"));
+
if (args < 0)
{
/* no existing parameters: add it behind device name */
- passthru_device += ":AES0=6"; //,AES1=0x82,AES2=0x00,AES3=0x01";
+ passthru_device += ":" + iecarg;
}
else
{
@@ -66,12 +77,12 @@ AudioOutputALSA::AudioOutputALSA(const AudioSettings &settings) :
if (args == passthru_device.length())
{
/* ":" but no parameters */
- passthru_device += "AES0=6"; //,AES1=0x82,AES2=0x00,AES3=0x01";
+ passthru_device += iecarg;
}
else if (passthru_device[args] != '{')
{
/* a simple list of parameters: add it at the end of the list */
- passthru_device += ",AES0=6"; //,AES1=0x82,AES2=0x00,AES3=0x01";
+ passthru_device += "," + iecarg;
}
else
{
@@ -80,13 +91,15 @@ 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"); // AES1=0x82 AES2=0x00 AES3=0x01");
+ passthru_device =
+ passthru_device.insert(len, " " + iecarg2);
}
}
}
else if (passthru_device.toLower() == "default")
passthru_device = main_device;
+ else
+ m_discretedigital = true;
InitSettings(settings);
if (settings.init)
@@ -110,9 +123,12 @@ int AudioOutputALSA::TryOpenDevice(int open_mode, int try_ac3)
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;
- if (!m_autopassthrough)
+
+ if (m_discretedigital)
return err;
+
if (err < 0)
{
VBAUDIO(QString("Auto setting passthrough failed (%1), defaulting "
@@ -266,7 +282,7 @@ bool AudioOutputALSA::IncPreallocBufferSize(int requested, int buffer_time)
return ret;
}
-AudioOutputSettings* AudioOutputALSA::GetOutputSettings()
+AudioOutputSettings* AudioOutputALSA::GetOutputSettings(bool passthrough)
{
snd_pcm_hw_params_t *params;
snd_pcm_format_t afmt = SND_PCM_FORMAT_UNKNOWN;
@@ -282,7 +298,7 @@ AudioOutputSettings* AudioOutputALSA::GetOutputSettings()
pcm_handle = NULL;
}
- if((err = TryOpenDevice(OPEN_FLAGS, passthru || enc)) < 0)
+ if ((err = TryOpenDevice(OPEN_FLAGS, passthrough)) < 0)
{
AERROR(QString("snd_pcm_open(\"%1\")").arg(m_lastdevice));
delete settings;
@@ -294,7 +310,7 @@ AudioOutputSettings* AudioOutputALSA::GetOutputSettings()
if ((err = snd_pcm_hw_params_any(pcm_handle, params)) < 0)
{
snd_pcm_close(pcm_handle);
- if((err = TryOpenDevice(OPEN_FLAGS&FILTER_FLAGS, passthru || enc)) < 0)
+ if ((err = TryOpenDevice(OPEN_FLAGS&FILTER_FLAGS, passthrough)) < 0)
{
AERROR(QString("snd_pcm_open(\"%1\")").arg(m_lastdevice));
delete settings;
@@ -337,12 +353,13 @@ AudioOutputSettings* AudioOutputALSA::GetOutputSettings()
snd_pcm_close(pcm_handle);
pcm_handle = NULL;
- // Check if name or description contains information
- // to know if device can accept passthrough or not
+
+ /* Check if name or description contains information
+ to know if device can accept passthrough or not */
QMap<QString, QString> *alsadevs = GetALSADevices("pcm");
while(1)
{
- QString real_device = (((passthru || enc) && !m_autopassthrough) ?
+ QString real_device = (((passthru || enc) && m_discretedigital) ?
passthru_device : main_device);
QString desc = alsadevs->value(real_device);
@@ -386,7 +403,7 @@ bool AudioOutputALSA::OpenDevice()
if (pcm_handle != NULL)
CloseDevice();
- if ((err = TryOpenDevice(0, passthru || enc)) < 0)
+ if ((err = TryOpenDevice(0, passthru || enc)) < 0)
{
AERROR(QString("snd_pcm_open(\"%1\")").arg(m_lastdevice));
if (pcm_handle)
@@ -448,7 +465,7 @@ void AudioOutputALSA::CloseDevice()
template <class AudioDataType>
static inline void _ReorderSmpteToAlsa(AudioDataType *buf, uint frames,
- uint extrach)
+ uint extrach)
{
AudioDataType tmpC, tmpLFE, *buf2;
@@ -467,7 +484,7 @@ static inline void _ReorderSmpteToAlsa(AudioDataType *buf, uint frames,
}
static inline void ReorderSmpteToAlsa(void *buf, uint frames,
- AudioFormat format, uint extrach)
+ AudioFormat format, uint extrach)
{
switch(AudioOutputSettings::FormatToBits(format))
{
@@ -489,11 +506,11 @@ void AudioOutputALSA::WriteAudio(uchar *aubuf, int size)
return;
}
- /* Audio received is using SMPTE channel ordering
- * ALSA uses its own channel order.
- * Do not re-order passthu audio */
+ //Audio received is in SMPTE channel order, reorder to ALSA unless passthru
if (!passthru && (channels == 6 || channels == 8))
+ {
ReorderSmpteToAlsa(aubuf, frames, output_format, channels - 6);
+ }
VERBOSE(VB_AUDIO+VB_TIMESTAMP,
QString("WriteAudio: Preparing %1 bytes (%2 frames)")
@@ -589,7 +606,7 @@ int AudioOutputALSA::GetBufferedOnSoundcard(void) const
return delay;
}
-/*
+/**
* Set the various ALSA audio parameters.
* Returns:
* < 0 : an error occurred
@@ -667,9 +684,8 @@ int AudioOutputALSA::SetParameters(snd_pcm_t *handle, snd_pcm_format_t format,
&buffer_time, &dir);
CHECKERR(QString("Unable to set buffer time %1").arg(buffer_time));
- // See if we need to increase the prealloc'd buffer size
- // If buffer_time is too small we could underrun
- // Make 10% leeway okay
+ /* See if we need to increase the prealloc'd buffer size
+ If buffer_time is too small we could underrun - make 10% difference ok */
if ((buffer_time * 1.10f < (float)original_buffer_time) && pbufsize < 0)
{
VBAUDIO(QString("Requested %1us got %2 buffer time")
@@ -28,7 +28,7 @@ class AudioOutputALSA : public AudioOutputBase
virtual void CloseDevice(void);
virtual void WriteAudio(uchar *aubuf, int size);
virtual int GetBufferedOnSoundcard(void) const;
- AudioOutputSettings* GetOutputSettings(void);
+ AudioOutputSettings* GetOutputSettings(bool passthrough = false);
private:
int TryOpenDevice(int open_mode, int try_ac3);
@@ -46,7 +46,6 @@ class AudioOutputALSA : public AudioOutputBase
int pbufsize;
int m_card, m_device, m_subdevice;
QMutex killAudioLock;
- bool m_autopassthrough;
QString m_lastdevice;
struct {
Oops, something went wrong.

0 comments on commit 649fa85

Please sign in to comment.