405 changes: 288 additions & 117 deletions mythtv/libs/libmyth/audio/audiooutputbase.cpp

Large diffs are not rendered by default.

59 changes: 44 additions & 15 deletions mythtv/libs/libmyth/audio/audiooutputbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,17 @@ class AsyncLooseLock
int tail;
};

// Forward declaration of SPDIF encoder
class SPDIFEncoder;

class AudioOutputBase : public AudioOutput, public QThread
{
public:
AudioOutputBase(const AudioSettings &settings);
virtual ~AudioOutputBase();

AudioOutputSettings* GetOutputSettingsCleaned(void);
AudioOutputSettings* GetOutputSettingsUsers(void);
AudioOutputSettings* GetOutputSettingsCleaned(bool digital = true);
AudioOutputSettings* GetOutputSettingsUsers(bool digital = false);

// reconfigure sound out for new params
virtual void Reconfigure(const AudioSettings &settings);
Expand All @@ -65,7 +68,7 @@ class AudioOutputBase : public AudioOutput, public QThread
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 CanPassthrough(int samplerate, int channels, int codec) const;
virtual bool ToggleUpmix(void);

virtual void Reset(void);
Expand All @@ -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 @@ -111,15 +116,15 @@ class AudioOutputBase : public AudioOutput, public QThread
virtual bool OpenDevice(void) = 0;
virtual void CloseDevice(void) = 0;
virtual void WriteAudio(uchar *aubuf, int size) = 0;
/*
/**
* Return the size in bytes of frames currently in the audio buffer adjusted
* with the audio playback latency
*/
virtual int GetBufferedOnSoundcard(void) const = 0;
// Default implementation only supports 2ch s16le at 48kHz
virtual AudioOutputSettings* GetOutputSettings(void)
virtual AudioOutputSettings* GetOutputSettings(bool digital = false)
{ return new AudioOutputSettings; }
/// You need to call this from any implementation in the dtor.
// You need to call this from any implementation in the dtor.
void KillAudio(void);

// The following functions may be overridden, but don't need to be
Expand Down Expand Up @@ -153,11 +158,13 @@ class AudioOutputBase : public AudioOutput, public QThread
AudioFormat format;
AudioFormat output_format;
int samplerate;
int bitrate;
int effdsp; // from the recorded stream (NuppelVideo)
int fragment_size;
long soundcard_buffer_size;

QString main_device, passthru_device;
bool m_discretedigital;

bool passthru, enc, reenc;

Expand All @@ -176,10 +183,15 @@ class AudioOutputBase : public AudioOutput, public QThread
int src_quality;

private:
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;
AudioOutputSettings *output_settings;
AudioOutputSettings *output_settingsdigitalraw;
AudioOutputSettings *output_settingsdigital;
bool need_resampler;
SRC_STATE *src_ctx;
soundtouch::SoundTouch *pSoundStretch;
Expand All @@ -203,20 +215,30 @@ class AudioOutputBase : public AudioOutput, public QThread

bool audio_thread_exists;

/* Writes to the audiobuffer, reconfigures and audiobuffer resets can only
take place while holding this lock */
/**
* Writes to the audiobuffer, reconfigures and audiobuffer resets can only
* take place while holding this lock
*/
QMutex audio_buflock;

/** must hold avsync_lock to read or write 'audiotime' and
'audiotime_updated' */
/**
* must hold avsync_lock to read or write 'audiotime' and
* 'audiotime_updated'
*/
QMutex avsync_lock;

// timecode of audio leaving the soundcard (same units as timecodes)
/**
* timecode of audio leaving the soundcard (same units as timecodes)
*/
int64_t audiotime;

/* Audio circular buffer */
int raud, waud; /* read and write positions */
// timecode of audio most recently placed into buffer
/**
* Audio circular buffer
*/
int raud, waud; // read and write positions
/**
* timecode of audio most recently placed into buffer
*/
int64_t audbuf_timecode;
AsyncLooseLock reset_active;

Expand All @@ -235,9 +257,16 @@ class AudioOutputBase : public AudioOutput, public QThread
float *src_out;
int kAudioSRCOutputSize;
uint memory_corruption_test2;
/** main audio buffer */
/**
* main audio buffer
*/
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
7 changes: 4 additions & 3 deletions mythtv/libs/libmyth/audio/audiooutputdigitalencoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,12 @@ 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();
delete m_spdifenc;
VERBOSE(VB_IMPORTANT, LOC_ERR +
"Could not create spdif muxer");
return false;
}

Expand Down Expand Up @@ -143,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
16 changes: 9 additions & 7 deletions mythtv/libs/libmyth/audio/audiooutputdx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class AudioOutputDXPrivate
FreeLibrary(dsound_dll);
}

int InitDirectSound(void);
int InitDirectSound(bool passthrough = false);
void ResetDirectSound(void);
void DestroyDSBuffer(void);
void FillBuffer(unsigned char *buffer, int size);
Expand Down Expand Up @@ -110,6 +110,8 @@ AudioOutputDX::AudioOutputDX(const AudioSettings &settings) :
InitSettings(settings);
if (passthru_device == "auto" || passthru_device.toLower() == "default")
passthru_device = main_device;
else
m_discretedigital = true;
if (settings.init)
Reconfigure(settings);
}
Expand Down Expand Up @@ -179,7 +181,7 @@ void AudioOutputDXPrivate::ResetDirectSound(void)
device_list.clear();
}

int AudioOutputDXPrivate::InitDirectSound(void)
int AudioOutputDXPrivate::InitDirectSound(bool passthrough)
{
LPFNDSC OurDirectSoundCreate;
LPFNDSE OurDirectSoundEnumerate;
Expand All @@ -195,7 +197,7 @@ int AudioOutputDXPrivate::InitDirectSound(void)
}

if (parent) // parent can be NULL only when called from GetDXDevices()
device_name = parent->m_UseSPDIF ?
device_name = passthrough ?
parent->passthru_device : parent->main_device;
device_name = device_name.section(':', 1);
device_num = device_name.toInt(&ok, 10);
Expand Down Expand Up @@ -378,13 +380,13 @@ bool AudioOutputDXPrivate::StartPlayback(void)
return true;
}

AudioOutputSettings* AudioOutputDX::GetOutputSettings(void)
AudioOutputSettings* AudioOutputDX::GetOutputSettings(bool passthrough)
{
AudioOutputSettings *settings = new AudioOutputSettings();
DSCAPS devcaps;
devcaps.dwSize = sizeof(DSCAPS);

m_priv->InitDirectSound();
m_priv->InitDirectSound(passthrough);
if ((!m_priv->dsobject || !m_priv->dsound_dll) ||
FAILED(IDirectSound_GetCaps(m_priv->dsobject, &devcaps)) )
{
Expand Down Expand Up @@ -431,7 +433,7 @@ bool AudioOutputDX::OpenDevice(void)
CloseDevice();

m_UseSPDIF = passthru || enc;
m_priv->InitDirectSound();
m_priv->InitDirectSound(m_UseSPDIF);
if (!m_priv->dsobject || !m_priv->dsound_dll)
{
Error("DirectSound initialization failed");
Expand Down Expand Up @@ -607,7 +609,7 @@ void AudioOutputDX::SetVolumeChannel(int channel, int volume)
QMap<int, QString> *AudioOutputDX::GetDXDevices(void)
{
AudioOutputDXPrivate *tmp_priv = new AudioOutputDXPrivate(NULL);
tmp_priv->InitDirectSound();
tmp_priv->InitDirectSound(false);
QMap<int, QString> *dxdevs = new QMap<int, QString>(tmp_priv->device_list);
delete tmp_priv;
return dxdevs;
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmyth/audio/audiooutputdx.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class AudioOutputDX : public AudioOutputBase
virtual void CloseDevice(void);
virtual void WriteAudio(unsigned char *buffer, int size);
virtual int GetBufferedOnSoundcard(void) const;
AudioOutputSettings* GetOutputSettings(void);
AudioOutputSettings* GetOutputSettings(bool passthrough = false);

protected:
AudioOutputDXPrivate *m_priv;
Expand Down
138 changes: 105 additions & 33 deletions mythtv/libs/libmyth/audio/audiooutputsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ using namespace std;
#include "mythcorecontext.h"

extern "C" {
#include "libavutil/avutil.h" // to check version of libavformat
#include "libavutil/avutil.h" // to check version of libavformat
}

#define LOC QString("AO: ")

AudioOutputSettings::AudioOutputSettings(bool invalid) :
m_passthrough(-1), m_AC3(false), m_DTS(false), m_LPCM(false),
m_HD(false) , m_HDLL(false), m_invalid(invalid)
m_passthrough(-1), m_features(FEATURE_NONE),
m_invalid(invalid), m_has_eld(false)
{
m_sr.assign(srs, srs +
sizeof(srs) / sizeof(int));
Expand Down Expand Up @@ -52,11 +52,8 @@ AudioOutputSettings& AudioOutputSettings::operator=(
m_formats = rhs.m_formats;
m_channels = rhs.m_channels;
m_passthrough = rhs.m_passthrough;
m_AC3 = rhs.m_AC3;
m_DTS = rhs.m_DTS;
m_LPCM = rhs.m_LPCM;
m_HD = rhs.m_HD;
m_HDLL = rhs.m_HDLL;
m_features = rhs.m_features;
m_has_eld = rhs.m_has_eld;
m_invalid = rhs.m_invalid;
m_sr_it = m_sr.begin() + (rhs.m_sr_it - rhs.m_sr.begin());
m_sf_it = m_sf.begin() + (rhs.m_sf_it - rhs.m_sf.begin());
Expand Down Expand Up @@ -251,6 +248,14 @@ void AudioOutputSettings::SetBestSupportedChannels(int channels)
m_channels.push_back(channels);
}

void AudioOutputSettings::setFeature(bool val, DigitalFeature arg)
{
if (val)
m_features |= arg;
else
m_features &= ~arg;
};

/**
* Returns capabilities supported by the audio device
* amended to take into account the digital audio
Expand All @@ -274,20 +279,30 @@ AudioOutputSettings* AudioOutputSettings::GetCleaned(bool newcopy)

int mchannels = BestSupportedChannels();

aosettings->m_LPCM = (mchannels > 2);
// E-AC3 is transferred as sterep PCM at 4 times the rates
// assume all amplifier supporting E-AC3 also supports 7.1 LPCM
// as it's required under the bluray standard
#if LIBAVFORMAT_VERSION_INT > AV_VERSION_INT( 52, 83, 0 )
aosettings->m_HD = (mchannels == 8 && BestSupportedRate() == 192000);
aosettings->m_HDLL = (mchannels == 8 && BestSupportedRate() == 192000);
#endif
if (mchannels == 2 && m_passthrough >= 0)
if (mchannels > 2)
{
VERBOSE(VB_AUDIO, LOC + QString("may be AC3 or DTS capable"));
aosettings->AddSupportedChannels(6);
aosettings->setFeature(FEATURE_LPCM);
}

if (IsSupportedFormat(FORMAT_S16))
{
// E-AC3 is transferred as stereo PCM at 4 times the rates
// assume all amplifier supporting E-AC3 also supports 7.1 LPCM
// as it's mandatory under the bluray standard
//#if LIBAVFORMAT_VERSION_INT > AV_VERSION_INT( 52, 83, 0 )
if (IsSupportedChannels(8) && IsSupportedRate(192000))
aosettings->setFeature(FEATURE_TRUEHD | FEATURE_DTSHD | FEATURE_EAC3);
//#endif
if (m_passthrough >= 0)
{
if (mchannels == 2)
{
VERBOSE(VB_AUDIO, LOC + QString("may be AC3 or DTS capable"));
aosettings->AddSupportedChannels(6);
}
aosettings->setFeature(FEATURE_AC3 | FEATURE_DTS);
}
}
aosettings->m_DTS = aosettings->m_AC3 = (m_passthrough >= 0);

return aosettings;
}
Expand All @@ -312,19 +327,28 @@ AudioOutputSettings* AudioOutputSettings::GetUsers(bool newcopy)

int cur_channels = gCoreContext->GetNumSetting("MaxChannels", 2);
int max_channels = aosettings->BestSupportedChannels();
bool bForceDigital = gCoreContext->GetNumSetting(
"PassThruDeviceOverride", false);
bool bAC3 = (aosettings->m_AC3 || bForceDigital) &&

bool bAC3 = aosettings->canFeature(FEATURE_AC3) &&
gCoreContext->GetNumSetting("AC3PassThru", false);
bool bDTS = (aosettings->m_DTS || bForceDigital) &&

bool bDTS = aosettings->canFeature(FEATURE_DTS) &&
gCoreContext->GetNumSetting("DTSPassThru", false);
bool bLPCM = aosettings->m_LPCM &&

bool bLPCM = aosettings->canFeature(FEATURE_LPCM) &&
!gCoreContext->GetNumSetting("StereoPCM", false);
bool bHD = bLPCM && aosettings->m_HD &&

bool bEAC3 = aosettings->canFeature(FEATURE_EAC3) &&
gCoreContext->GetNumSetting("EAC3PassThru", false) &&
!gCoreContext->GetNumSetting("Audio48kOverride", false);
bool bHDLL = bLPCM && aosettings->m_HD &&

// TrueHD requires HBR support.
bool bTRUEHD = aosettings->canFeature(FEATURE_TRUEHD) &&
gCoreContext->GetNumSetting("TrueHDPassThru", false) &&
!gCoreContext->GetNumSetting("Audio48kOverride", false) &&
gCoreContext->GetNumSetting("HBRPassthru", true);

bool bDTSHD = aosettings->canFeature(FEATURE_DTSHD) &&
gCoreContext->GetNumSetting("DTSHDPassThru", false) &&
!gCoreContext->GetNumSetting("Audio48kOverride", false);

if (max_channels > 2 && !bLPCM)
Expand All @@ -336,12 +360,60 @@ AudioOutputSettings* AudioOutputSettings::GetUsers(bool newcopy)
cur_channels = max_channels;

aosettings->SetBestSupportedChannels(cur_channels);
aosettings->m_AC3 = bAC3;
aosettings->m_DTS = bDTS;
aosettings->m_HD = bHD;
aosettings->m_HDLL = bHDLL;
aosettings->m_LPCM = bLPCM;
aosettings->setFeature(bAC3, FEATURE_AC3);
aosettings->setFeature(bDTS, FEATURE_DTS);
aosettings->setFeature(bLPCM, FEATURE_LPCM);
aosettings->setFeature(bEAC3, FEATURE_EAC3);
aosettings->setFeature(bTRUEHD, FEATURE_TRUEHD);
aosettings->setFeature(bDTSHD, FEATURE_DTSHD);

return aosettings;
}

int AudioOutputSettings::GetMaxHDRate()
{
if (!canFeature(FEATURE_DTSHD))
return 0;

// If no HBR or no LPCM, limit bitrate to 6.144Mbit/s
if (!gCoreContext->GetNumSetting("HBRPassthru", true) ||
!canFeature(FEATURE_LPCM))
{
return 192000; // E-AC3/DTS-HD High Res: 192k, 16 bits, 2 ch
}
return 768000; // TrueHD or DTS-HD MA: 192k, 16 bits, 8 ch
}

#define ARG(x) ((tmp.isEmpty() ? "" : ",") + QString(x))

QString AudioOutputSettings::FeaturesToString(DigitalFeature arg)
{
QString tmp;
DigitalFeature feature[] = {
FEATURE_AC3,
FEATURE_DTS,
FEATURE_LPCM,
FEATURE_EAC3,
FEATURE_TRUEHD,
FEATURE_DTSHD,
FEATURE_AAC,
(DigitalFeature)-1
};
const char *feature_str[] = {
"AC3",
"DTS",
"LPCM",
"EAC3",
"TRUEHD",
"DTSHD",
"AAC",
NULL
};

for (unsigned int i = 0; feature[i] != (DigitalFeature)-1; i++)
{
if (arg & feature[i])
tmp += ARG(feature_str[i]);
}
return tmp;
}
102 changes: 79 additions & 23 deletions mythtv/libs/libmyth/audio/audiooutputsettings.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* -*- Mode: c++ -*-
*
* Copyright (C) foobum@gmail.com 2010
* Copyright (C) Jean-Yves Avenard 2010
*
* Licensed under the GPL v2 or a later version at your choosing.
*/
Expand All @@ -11,6 +12,11 @@
#include <vector>

#include "mythexp.h"
#include <QString>

extern "C" {
#include "libavcodec/avcodec.h" // to get codec id
}

using namespace std;

Expand All @@ -24,6 +30,17 @@ typedef enum {
FORMAT_FLT
} AudioFormat;

typedef enum {
FEATURE_NONE = 0,
FEATURE_AC3 = 1 << 0,
FEATURE_DTS = 1 << 1,
FEATURE_LPCM = 1 << 2,
FEATURE_EAC3 = 1 << 3,
FEATURE_TRUEHD = 1 << 4,
FEATURE_DTSHD = 1 << 5,
FEATURE_AAC = 1 << 6,
} DigitalFeature;

static const int srs[] = { 8000, 11025, 16000, 22050, 32000, 44100,
48000, 64000, 88200, 96000, 176400, 192000 };

Expand Down Expand Up @@ -57,62 +74,101 @@ class MPUBLIC AudioOutputSettings

void setPassthrough(int val) { m_passthrough = val; };
int canPassthrough() { return m_passthrough; };
/**
* return DigitalFeature mask.
* possible values are:
* - FEATURE_AC3
* - FEATURE_DTS
* - FEATURE_LPCM
* - FEATURE_EAC3
* - FEATURE_TRUEHD
* - FEATURE_DTSHD
*/
unsigned int canFeature(DigitalFeature arg)
{ return m_features & arg; };
unsigned int canFeature(unsigned int arg)
{ return m_features & arg; };

/**
* return true if device can or may support AC3
* (deprecated, see canFeature())
*/
bool canAC3() { return m_AC3; };
bool canAC3() { return m_features & FEATURE_AC3; };
/**
* return true if device can or may support DTS
* (deprecated, see canFeature())
*/
bool canDTS() { return m_DTS; };
bool canDTS() { return m_features & FEATURE_DTS; };
/**
* return true if device supports multichannels PCM
* (deprecated, see canFeature())
*/
bool canLPCM() { return m_LPCM; };
/**
* return true if device supports E-AC3 or DTS-HD passthrough
*/
bool canHD() { return m_HD; };
/**
* return true if device supports TrueHD or DTS-HD passthrough
*/
bool canHDLL() { return m_HDLL; };
bool canLPCM() { return m_features & FEATURE_LPCM; };
/**
* return true if class instance is marked invalid.
* if true, you can not assume any of the other method returned
* values are valid
*/
bool IsInvalid() { return m_invalid; };

/**
* return true if device supports TrueHD or DTS-HD passthrough
* set the provided digital feature
* possible values are:
* - FEATURE_AC3
* - FEATURE_DTS
* - FEATURE_LPCM
* - FEATURE_EAC3
* - FEATURE_TRUEHD
* - FEATURE_DTSHD
*/
void setAC3(bool b) { m_AC3 = b; };
void setDTS(bool b) { m_DTS = b; };
void setLPCM(bool b) { m_LPCM = b; };
void setHD(bool b) { m_HD = b; };
void setHDLL(bool b) { m_HDLL = b; };
void setFeature(DigitalFeature arg) { m_features |= arg; };
void setFeature(unsigned int arg) { m_features |= arg; };

/**
* clear or set digital feature internal mask
*/
void setFeature(bool val, DigitalFeature arg);

/**
* Force set the greatest number of channels supported by the audio
* device
*/
void SetBestSupportedChannels(int channels);

/**
* return the highest iec958 rate supported.
* return 0 if no HD rate are supported
*/
int GetMaxHDRate();

/**
* Display in human readable form the digital features
* supported by the output device
*/
QString FeaturesToString(DigitalFeature arg);
QString FeaturesToString(void)
{ return FeaturesToString((DigitalFeature)m_features); };

private:
void SortSupportedChannels();

/* passthrough status
/** passthrough status
* -1 : no
* 0: unknown
* 1: yes
*/
int m_passthrough;
bool m_AC3;
bool m_DTS;
bool m_LPCM;
bool m_HD;
bool m_HDLL;

unsigned int m_features;

bool m_invalid;
/**
* will be set to true if we were able to retrieve the device ELD
* (EDID like Data). ELD contains information about the audio
* processing capabilities of the device connected to the audio card
* ELD is usually retrieve from EDID CEA-861-E extension.
*/
bool m_has_eld;

vector<int> m_sr, m_rates, m_channels;
vector<AudioFormat> m_sf, m_formats;
Expand Down
4 changes: 2 additions & 2 deletions mythtv/libs/libmyth/audio/audiooutpututil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -650,8 +650,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/audio/audiooutpututil.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,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
8 changes: 7 additions & 1 deletion mythtv/libs/libmyth/audio/audiosettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ AudioSettings::AudioSettings() :
format(FORMAT_NONE),
channels(-1),
codec(0),
codec_profile(-1),
samplerate(-1),
set_initial_vol(false),
use_passthru(false),
Expand All @@ -30,6 +31,7 @@ AudioSettings::AudioSettings(const AudioSettings &other) :
format(other.format),
channels(other.channels),
codec(other.codec),
codec_profile(other.codec_profile),
samplerate(other.samplerate),
set_initial_vol(other.set_initial_vol),
use_passthru(other.use_passthru),
Expand Down Expand Up @@ -64,6 +66,7 @@ AudioSettings::AudioSettings(
format(format),
channels(channels),
codec(codec),
codec_profile(-1),
samplerate(samplerate),
set_initial_vol(set_initial_vol),
use_passthru(use_passthru),
Expand All @@ -87,12 +90,14 @@ AudioSettings::AudioSettings(
int codec,
int samplerate,
bool use_passthru,
int upmixer_startup) :
int upmixer_startup,
int codec_profile) :
main_device(QString::null),
passthru_device(QString::null),
format(format),
channels(channels),
codec(codec),
codec_profile(codec_profile),
samplerate(samplerate),
set_initial_vol(false),
use_passthru(use_passthru),
Expand All @@ -111,6 +116,7 @@ AudioSettings::AudioSettings(
format(FORMAT_NONE),
channels(-1),
codec(0),
codec_profile(-1),
samplerate(-1),
set_initial_vol(false),
use_passthru(false),
Expand Down
15 changes: 14 additions & 1 deletion mythtv/libs/libmyth/audio/audiosettings.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* -*- Mode: c++ -*-
*
* Copyright (C) Daniel Kristjansson 2008
* Copyright (C) Jean-Yves Avenard 2010
*
* Licensed under the GPL v2 or a later version at your choosing.
*/
Expand Down Expand Up @@ -43,7 +44,8 @@ class MPUBLIC AudioSettings
int codec,
int samplerate,
bool use_passthru,
int upmixer_startup = 0);
int upmixer_startup = 0,
int codec_profile = 0);

AudioSettings(const QString &main_device,
const QString &passthru_device = QString::null);
Expand All @@ -61,12 +63,23 @@ class MPUBLIC AudioSettings
AudioFormat format;
int channels;
int codec;
int codec_profile;
int samplerate;
bool set_initial_vol;
bool use_passthru;
AudioOutputSource source;
int upmixer;
/**
* If set to false, AudioOutput instance will not try to initially open
* the audio device
*/
bool init;
/**
* custom contains a pointer to the audio device capabilities
* if defined, AudioOutput will not try to automatically discover them.
* This is used by the AudioTest setting screen where the user can
* manually override and immediately use them.
*/
AudioOutputSettings *custom;
};

Expand Down
62 changes: 35 additions & 27 deletions mythtv/libs/libmyth/audio/spdifencoder.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#include "config.h"

#include "mythcorecontext.h"

#include "compat.h"
#include "spdifencoder.h"
#include "mythverbose.h"

#define LOC QString("SPDIFEncoder: ")
#define LOC_ERR QString("SPDIFEncoder, Error: ")

/*
/**
* SPDIFEncoder constructor
* Args:
* QString muxer : name of the muxer.
Expand All @@ -16,15 +18,13 @@
* 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();
AVOutputFormat *fmt =
av_guess_format(dev_ba.constData(), NULL, NULL);
AVOutputFormat *fmt;

if (!(av_guess_format && avformat_alloc_context &&
if (!(av_register_all && av_guess_format && avformat_alloc_context &&
av_new_stream && av_write_header && av_write_frame &&
av_write_trailer && av_set_parameters))
{
Expand All @@ -44,6 +44,11 @@ SPDIFEncoder::SPDIFEncoder(QString muxer, AVCodecContext *ctx)
return;
}

avcodeclock->lock();
av_register_all();
avcodeclock->unlock();

fmt = av_guess_format(dev_ba.constData(), NULL, NULL);
if (!fmt)
{
VERBOSE(VB_AUDIO, LOC_ERR + "av_guess_format");
Expand Down Expand Up @@ -97,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 All @@ -121,12 +116,11 @@ SPDIFEncoder::~SPDIFEncoder(void)
Destroy();
}

/*
/**
* Encode data through created muxer
* unsigned char data: pointer to data to encode
* int size: size of data to encode
*/

void SPDIFEncoder::WriteFrame(unsigned char *data, int size)
{
AVPacket packet;
Expand All @@ -141,7 +135,7 @@ void SPDIFEncoder::WriteFrame(unsigned char *data, int size)
}
}

/*
/**
* Retrieve encoded data and copy it in the provided buffer.
* Return -1 if there is no data to retrieve.
* On return, dest_size will contain the length of the data copied
Expand All @@ -159,18 +153,33 @@ int SPDIFEncoder::GetData(unsigned char *buffer, int &dest_size)
return -1;
}

/*
/**
* Reset the internal encoder buffer
*/
void SPDIFEncoder::Reset()
{
m_size = 0;
}

/*
* funcIO: Internal callback function that will receive encoded frames
/**
* Set the maximum HD rate.
* If playing DTS-HD content, setting a HD rate of 0 will only use the DTS-Core
* and the HD stream be stripped out before encoding
* Input: rate = maximum HD rate in Hz
*/
bool SPDIFEncoder::SetMaxHDRate(int rate)
{
if (!m_oc)
{
return false;
}
av_set_int(m_oc->priv_data, "dtshd_rate", rate);
return true;
}

/**
* funcIO: Internal callback function that will receive encoded frames
*/
int SPDIFEncoder::funcIO(void *opaque, unsigned char *buf, int size)
{
SPDIFEncoder *enc = (SPDIFEncoder *)opaque;
Expand All @@ -180,10 +189,9 @@ int SPDIFEncoder::funcIO(void *opaque, unsigned char *buf, int size)
return size;
}

/*
/**
* Destroy and free all allocated memory
*/

void SPDIFEncoder::Destroy()
{
Reset();
Expand Down
9 changes: 6 additions & 3 deletions mythtv/libs/libmyth/audio/spdifencoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,22 @@ extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavcodec/audioconvert.h"
#include "libavcodec/opt.h"
}

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);

private:
static int funcIO(void *opaque, unsigned char *buf, int size);
void Destroy();
Expand Down
307 changes: 161 additions & 146 deletions mythtv/libs/libmythtv/audioplayer.cpp

Large diffs are not rendered by default.

26 changes: 16 additions & 10 deletions mythtv/libs/libmythtv/audioplayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ class MTV_PUBLIC AudioPlayer
void SetAudioOutput(AudioOutput *ao);
void SetAudioInfo(const QString &main_device,
const QString &passthru_device,
uint samplerate);
uint samplerate,
int bitrate = -1);
void SetAudioParams(AudioFormat format, int orig_channels, int channels,
int codec, int samplerate, bool passthru);
int codec, int samplerate, bool passthru,
int bitrate = -1);
void SetEffDsp(int dsprate);

void CheckFormat(void);
void SetNoAudio(void) { no_audio_out = true; }
bool HasAudioIn(void) const { return !no_audio_in; }
bool HasAudioOut(void) const { return !no_audio_out; }
void SetNoAudio(void) { m_no_audio_out = true; }
bool HasAudioIn(void) const { return !m_no_audio_in; }
bool HasAudioOut(void) const { return !m_no_audio_out; }

bool Pause(bool pause);
bool IsPaused(void);
Expand All @@ -39,12 +41,14 @@ class MTV_PUBLIC AudioPlayer
float GetStretchFactor(void) { return m_stretchfactor; }
void SetStretchFactor(float factor);
bool ToggleUpmix(void);
bool CanPassthrough(int samplerate, int channels);
bool CanPassthrough(int samplerate, int channels, int codec = 0);
bool CanAC3(void);
bool CanDTS(void);
bool CanHD(void);
bool CanHDLL(void);
bool CanEAC3(void);
bool CanTrueHD(void);
bool CanDTSHD(void);
uint GetMaxChannels(void);
int GetMaxHDRate(void);
int64_t GetAudioTime(void);

bool IsMuted(void) { return GetMuteState() == kMuteAll; }
Expand All @@ -54,6 +58,7 @@ class MTV_PUBLIC 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 All @@ -65,14 +70,15 @@ class MTV_PUBLIC AudioPlayer
int m_codec;
AudioFormat m_format;
int m_samplerate;
int m_codec_profile;
float m_stretchfactor;
bool m_passthru;
QMutex m_lock;
bool m_muted_on_creation;
QString m_main_device;
QString m_passthru_device;
bool no_audio_in;
bool no_audio_out;
bool m_no_audio_in;
bool m_no_audio_out;
};

#endif // AUDIOPLAYER_H
272 changes: 83 additions & 189 deletions mythtv/libs/libmythtv/avformatdecoder.cpp

Large diffs are not rendered by default.

31 changes: 17 additions & 14 deletions mythtv/libs/libmythtv/avformatdecoder.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

#ifndef AVFORMATDECODER_H_
#define AVFORMATDECODER_H_

Expand Down Expand Up @@ -45,38 +46,38 @@ class AudioInfo
public:
AudioInfo() :
codec_id(CODEC_ID_NONE), format(FORMAT_NONE), sample_size(-2),
sample_rate(-1), channels(-1), do_passthru(false),
original_channels(-1)
sample_rate(-1), channels(-1), codec_profile(0),
do_passthru(false), original_channels(-1)
{;}

AudioInfo(CodecID id, AudioFormat fmt, int sr, int ch, bool passthru,
int original_ch) :
int original_ch, int profile = 0) :
codec_id(id), format(fmt),
sample_size(ch * AudioOutputSettings::SampleSize(fmt)),
sample_rate(sr), channels(ch), do_passthru(passthru),
original_channels(original_ch)
sample_rate(sr), channels(ch), codec_profile(profile),
do_passthru(passthru), original_channels(original_ch)
{
}

CodecID codec_id;
AudioFormat format;
int sample_size, sample_rate, channels;
int sample_size, sample_rate, channels, codec_profile;
bool do_passthru;
int original_channels;

bool operator==(const AudioInfo &o) const
{
return (codec_id==o.codec_id && channels==o.channels &&
sample_size==o.sample_size && sample_rate==o.sample_rate &&
format==o.format && do_passthru==o.do_passthru &&
original_channels==o.original_channels);
original_channels==o.original_channels &&
codec_profile == o.codec_profile);
}
QString toString() const
{
return QString("id(%1) %2Hz %3ch %4bps %5")
return QString("id(%1) %2Hz %3ch %4bps %5 (profile %6)")
.arg(ff_codec_id_string(codec_id),4).arg(sample_rate,6)
.arg(channels,2).arg(AudioOutputSettings::FormatToBits(format),2)
.arg((do_passthru) ? "pt":"",3);
.arg((do_passthru) ? "pt":"",3).arg(codec_profile);
}
};

Expand Down Expand Up @@ -185,6 +186,11 @@ class AvFormatDecoder : public DecoderBase
void ScanRawTextCaptions(int av_stream_index);
void ScanDSMCCStreams(void);
int AutoSelectAudioTrack(void);
int filter_max_ch(const AVFormatContext *ic,
const sinfo_vec_t &tracks,
const vector<int> &fs,
enum CodecID codecId = CODEC_ID_NONE,
int profile = -1);

friend int get_avf_buffer(struct AVCodecContext *c, AVFrame *pic);
friend void release_avf_buffer(struct AVCodecContext *c, AVFrame *pic);
Expand Down Expand Up @@ -281,7 +287,7 @@ class AvFormatDecoder : public DecoderBase
uint32_t start_code_state;

long long lastvpts;
long long lastapts;
long long lastapts;
long long lastccptsu;

int64_t faulty_pts;
Expand Down Expand Up @@ -340,9 +346,6 @@ class AvFormatDecoder : public DecoderBase

float m_fps;
bool codec_is_mpeg;

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

#endif
Expand Down
13 changes: 7 additions & 6 deletions mythtv/libs/libmythtv/mythplayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,8 @@ int MythPlayer::OpenFile(uint retries, bool allow_libmpeg2)
decoder->setWatchingRecording(watchingrecording);
decoder->setTranscoding(transcoding);
CheckExtraAudioDecode();
noVideoTracks = !decoder->GetTrackCount(kTrackTypeVideo);
if (gCoreContext->GetNumSetting("AudioOnlyPlayback", false))
noVideoTracks = !decoder->GetTrackCount(kTrackTypeVideo);

// Set 'no_video_decode' to true for audio only decodeing
bool no_video_decode = false;
Expand Down Expand Up @@ -2139,7 +2140,7 @@ void MythPlayer::VideoStart(void)

bool MythPlayer::VideoLoop(void)
{
if (videoPaused || isDummy /*|| noVideoTracks*/)
if (videoPaused || isDummy || noVideoTracks)
{
usleep(frame_interval);
DisplayPauseFrame();
Expand Down Expand Up @@ -2562,7 +2563,7 @@ void MythPlayer::EventLoop(void)

// Disable rewind if we are too close to the beginning of the buffer
if (CalcRWTime(-ffrew_skip) > 0 &&
(/*!noVideoTracks && */(framesPlayed <= keyframedist)))
(!noVideoTracks && (framesPlayed <= keyframedist)))
{
VERBOSE(VB_PLAYBACK, LOC + "Near start, stopping rewind.");
float stretch = (ffrew_skip > 0) ? 1.0f : audio.GetStretchFactor();
Expand Down Expand Up @@ -2814,7 +2815,7 @@ void MythPlayer::DecoderLoop(bool pause)
DecoderPauseCheck();

decoder_change_lock.lock();
if (decoder)
if (gCoreContext->GetNumSetting("AudioOnlyPlayback", false) && decoder)
noVideoTracks = !decoder->GetTrackCount(kTrackTypeVideo);
decoder_change_lock.unlock();

Expand Down Expand Up @@ -2856,8 +2857,8 @@ void MythPlayer::DecoderLoop(bool pause)

DecodeType dt = (audio.HasAudioOut() && normal_speed) ?
kDecodeAV : kDecodeVideo;
//if (noVideoTracks && audio.HasAudioOut())
// dt = kDecodeAudio;
if (noVideoTracks && audio.HasAudioOut())
dt = kDecodeAudio;
DecoderGetFrame(dt);
decodeOneFrame = false;
}
Expand Down
169 changes: 115 additions & 54 deletions mythtv/programs/mythfrontend/audiogeneralsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ AudioConfigSettings::AudioConfigSettings(ConfigurationWizard *parent) :
m_OutputDevice(NULL), m_MaxAudioChannels(NULL),
m_AudioUpmix(NULL), m_AudioUpmixType(NULL),
m_AC3PassThrough(NULL), m_DTSPassThrough(NULL),
m_EAC3PassThrough(NULL),m_TrueHDPassThrough(NULL),
m_passthrough8(false), m_parent(parent)
m_EAC3PassThrough(NULL),m_TrueHDPassThrough(NULL), m_DTSHDPassThrough(NULL),
m_parent(parent)
{
setLabel(QObject::tr("Audio System"));
setUseLabel(false);
Expand Down Expand Up @@ -137,7 +137,6 @@ AudioConfigSettings::AudioConfigSettings(ConfigurationWizard *parent) :
devices.append(*adc);

delete adc;
CheckPassthrough();

ConfigurationGroup *maingroup = new VerticalConfigurationGroup(false,
false);
Expand All @@ -148,13 +147,15 @@ AudioConfigSettings::AudioConfigSettings(ConfigurationWizard *parent) :
m_DTSPassThrough = DTSPassThrough();
m_EAC3PassThrough = EAC3PassThrough();
m_TrueHDPassThrough = TrueHDPassThrough();
m_DTSHDPassThrough = DTSHDPassThrough();

m_cgsettings = new HorizontalConfigurationGroup();
m_cgsettings->setLabel(QObject::tr("Digital Audio Capabilities"));
m_cgsettings->addChild(m_AC3PassThrough);
m_cgsettings->addChild(m_DTSPassThrough);
m_cgsettings->addChild(m_EAC3PassThrough);
m_cgsettings->addChild(m_TrueHDPassThrough);
m_cgsettings->addChild(m_DTSHDPassThrough);

TriggeredItem *sub1 = new TriggeredItem(m_triggerDigital, m_cgsettings);

Expand All @@ -174,7 +175,7 @@ AudioConfigSettings::AudioConfigSettings(ConfigurationWizard *parent) :
TransButtonSetting *advanced = new TransButtonSetting("advanced");
advanced->setLabel(QObject::tr("Advanced Audio Settings"));
advanced->setHelpText(QObject::tr("Enable extra audio settings. Under most "
"usage all options should be unchecked"));
"usage all options should be left alone"));
connect(advanced, SIGNAL(pressed()), this, SLOT(AudioAdvanced()));
addChild(advanced);

Expand All @@ -191,6 +192,8 @@ AudioConfigSettings::AudioConfigSettings(ConfigurationWizard *parent) :
this, SLOT(UpdateCapabilities(const QString&)));
connect(m_TrueHDPassThrough, SIGNAL(valueChanged(const QString&)),
this, SLOT(UpdateCapabilities(const QString&)));
connect(m_DTSHDPassThrough, SIGNAL(valueChanged(const QString&)),
this, SLOT(UpdateCapabilities(const QString&)));
AudioRescan();
}

Expand Down Expand Up @@ -229,6 +232,16 @@ void AudioConfigSettings::AudioRescan()
delete adc;
}
m_OutputDevice->AudioRescan();
if (!CheckPassthrough())
{
QString msg =QObject::tr("Passthrough device is invalid or not useable."
" Check configuration in Advanced Settings:") +
gCoreContext->GetSetting("PassThruOutputDevice");
MythPopupBox::showOkPopup(
GetMythMainWindow(), QObject::tr("Warning"), msg);
VERBOSE(VB_IMPORTANT, QString("Audio device %1 isn't usable ")
.arg(name));
}
slotlock.unlock();
UpdateCapabilities(QString::null);
}
Expand All @@ -250,24 +263,23 @@ AudioOutputSettings AudioConfigSettings::UpdateCapabilities(
int realmax_speakers = 8;

bool invalid = false;
AudioOutputSettings settings;
AudioOutputSettings settings, settingsdigital;

// Test if everything is set yet
if (!m_OutputDevice || !m_MaxAudioChannels ||
!m_AC3PassThrough || !m_DTSPassThrough ||
!m_EAC3PassThrough || !m_TrueHDPassThrough)
!m_EAC3PassThrough || !m_TrueHDPassThrough || !m_DTSHDPassThrough)
return settings;

if (!slotlock.tryLock()) // Doing a rescan of channels
return settings;

bool bForceDigital = gCoreContext->GetNumSetting("PassThruDeviceOverride",
false);
bool bAC3 = true;
bool bDTS = true;
bool bLPCM = true;
bool bHD = true;
bool bHDLL = true;
bool bEAC3 = true;
bool bTRUEHD = true;
bool bDTSHD = true;

QString out = m_OutputDevice->getValue();
if (!audiodevs.contains(out))
Expand All @@ -277,33 +289,42 @@ AudioOutputSettings AudioConfigSettings::UpdateCapabilities(
}
else
{
bool bForceDigital =
gCoreContext->GetNumSetting("PassThruDeviceOverride", false);

settings = audiodevs.value(out).settings;
settingsdigital = bForceDigital ?
audiodevs.value(gCoreContext->GetSetting("PassThruOutputDevice"))
.settings : settings;

realmax_speakers = max_speakers = settings.BestSupportedChannels();

bAC3 = (settings.canAC3() || bForceDigital) &&
bAC3 = settingsdigital.canFeature(FEATURE_AC3) &&
m_AC3PassThrough->boolValue();
bDTS = (settings.canDTS() || bForceDigital) &&
bDTS = settingsdigital.canFeature(FEATURE_DTS) &&
m_DTSPassThrough->boolValue();
bLPCM = settings.canLPCM() &&
bLPCM = settings.canFeature(FEATURE_LPCM) &&
!gCoreContext->GetNumSetting("StereoPCM", false);
bHD = ((bLPCM && settings.canHD()) || bForceDigital) &&
m_EAC3PassThrough->boolValue() &&
bEAC3 = settingsdigital.canFeature(FEATURE_EAC3) &&
!gCoreContext->GetNumSetting("Audio48kOverride", false);
bHDLL = ((bLPCM && settings.canHDLL()) || bForceDigital) &&
m_TrueHDPassThrough->boolValue() &&
bTRUEHD = settingsdigital.canFeature(FEATURE_TRUEHD) &&
!gCoreContext->GetNumSetting("Audio48kOverride", false) &&
gCoreContext->GetNumSetting("HBRPassthru", true);
bDTSHD = settingsdigital.canFeature(FEATURE_DTSHD) &&
!gCoreContext->GetNumSetting("Audio48kOverride", false);

if (max_speakers > 2 && !bLPCM)
max_speakers = 2;
if (max_speakers == 2 && (bAC3 || bDTS))
max_speakers = 6;
}

m_triggerDigital->setValue(invalid || bForceDigital ||
settings.canAC3() || settings.canDTS());
m_EAC3PassThrough->setEnabled(settings.canHD() && bLPCM);
m_TrueHDPassThrough->setEnabled(settings.canHDLL() & bLPCM);
m_triggerDigital->setValue(invalid || settingsdigital.canFeature(
FEATURE_AC3 | FEATURE_DTS | FEATURE_EAC3 |
FEATURE_TRUEHD | FEATURE_DTSHD));
m_EAC3PassThrough->setEnabled(bEAC3);
m_TrueHDPassThrough->setEnabled(bTRUEHD);
m_DTSHDPassThrough->setEnabled(bDTSHD);

int cur_speakers = m_MaxAudioChannels->getValue().toInt();

Expand All @@ -318,8 +339,7 @@ AudioOutputSettings AudioConfigSettings::UpdateCapabilities(
m_MaxAudioChannels->resetMaxCount(3);
for (int i = 1; i <= max_speakers; i++)
{
if (invalid || settings.IsSupportedChannels(i) ||
(bForceDigital && i >= 6))
if (invalid || settings.IsSupportedChannels(i))
{
QString txt;

Expand All @@ -341,10 +361,12 @@ AudioOutputSettings AudioConfigSettings::UpdateCapabilities(
i == cur_speakers);
}
}
// Return values is used by audio test
// where we mainly are interested by the number of channels
// if we support AC3 and/or LPCM
settings.SetBestSupportedChannels(cur_speakers);
settings.setAC3(bAC3);
settings.setDTS(bDTS);
settings.setLPCM(bLPCM && (realmax_speakers > 2));
settings.setFeature(bAC3, FEATURE_AC3);
settings.setFeature(bLPCM && realmax_speakers > 2, FEATURE_LPCM);

slotlock.unlock();
return settings;
Expand All @@ -371,7 +393,7 @@ void AudioConfigSettings::AudioAdvanced()

if (audiosettings.exec() == kDialogCodeAccepted)
{
CheckPassthrough();
AudioRescan();
UpdateCapabilities(QString::null);
}
}
Expand Down Expand Up @@ -434,27 +456,37 @@ HostCheckBox *AudioConfigSettings::DTSPassThrough()
HostCheckBox *AudioConfigSettings::EAC3PassThrough()
{
HostCheckBox *gc = new HostCheckBox("EAC3PassThru");
gc->setLabel(QObject::tr("E-AC3/DTS-HD"));
gc->setLabel(QObject::tr("E-AC3"));
gc->setValue(false);
gc->setHelpText(QObject::tr("Enable if your amplifier or sound decoder "
"supports E-AC3 (DD+) or DTS-HD. You must use a hdmi "
"connection."));
"supports E-AC3 (DD+). You must use a hdmi connection."));
return gc;
}

HostCheckBox *AudioConfigSettings::TrueHDPassThrough()
{
HostCheckBox *gc = new HostCheckBox("TrueHDPassThru");
gc->setLabel(QObject::tr("TrueHD/DTS-HD MA"));
gc->setLabel(QObject::tr("TrueHD"));
gc->setValue(false);
gc->setHelpText(QObject::tr("Enable if your amplifier or sound decoder "
"supports Dolby TrueHD. You must use a hdmi connection."));
return gc;
}

HostCheckBox *AudioConfigSettings::DTSHDPassThrough()
{
HostCheckBox *gc = new HostCheckBox("DTSHDPassThru");
gc->setLabel(QObject::tr("DTS-HD"));
gc->setValue(false);
gc->setHelpText(QObject::tr("Enable if your amplifier or sound decoder "
"supports DTS-HD. You must use a hdmi connection."));
return gc;
}

bool AudioConfigSettings::CheckPassthrough()
{
m_passthrough8 = false;
bool ok = true;

if (gCoreContext->GetNumSetting("PassThruDeviceOverride", false))
{
QString name = gCoreContext->GetSetting("PassThruOutputDevice");
Expand All @@ -464,17 +496,14 @@ bool AudioConfigSettings::CheckPassthrough()
{
VERBOSE(VB_IMPORTANT, QString("Passthru device %1 isn't usable "
"Check audio configuration").arg(name));
ok = false;
}
else
{
if (adc->settings.BestSupportedChannels() >= 8)
{
m_passthrough8 = true;
}
}
// add it to list of known devices
audiodevs.insert(name, *adc);
devices.append(*adc);
delete adc;
}
return m_passthrough8;
return ok;
}

void AudioConfigSettings::StartAudioTest()
Expand All @@ -501,9 +530,6 @@ AudioTestThread::AudioTestThread(QObject *parent,
m_parent(parent), m_channels(channels), m_device(main),
m_passthrough(passthrough), m_interrupted(false), m_channel(-1), m_hd(hd)
{
/* initialize libavcodec, and register all codecs and formats */
av_register_all();

m_format = hd ? settings.BestSupportedFormat() : FORMAT_S16;
m_samplerate = hd ? settings.BestSupportedRate() : 48000;

Expand Down Expand Up @@ -630,12 +656,12 @@ void AudioTestThread::run()
int top = m_samplerate / 1000 * 3;
for (int j = 0; j < top && !m_interrupted; j++)
{
AudioOutputUtil::GeneratePinkSamples(frames, m_channels,
current, 1000,
m_hd ? 32 : 16);
if (!m_audioOutput->AddFrames(frames, 1000, -1))
AudioOutputUtil::GeneratePinkFrames(frames, m_channels,
current, 1000,
m_hd ? 32 : 16);
if (!m_audioOutput->AddFrames(frames, 1000 , -1))
{
VERBOSE(VB_AUDIO, "AddAudioData() "
VERBOSE(VB_AUDIO, "AddData() "
"Audio buffer overflow, audio data lost!");
}
// a tad less than 1/48th of a second to avoid underruns
Expand Down Expand Up @@ -1073,7 +1099,8 @@ HostCheckBox *AudioAdvancedSettings::PassThroughOverride()
gc->setLabel(QObject::tr("Separate digital output device"));
gc->setValue(false);
gc->setHelpText(QObject::tr("Use a distinct digital output device from "
"default."));
"default."
" (default is not checked)"));
return gc;
}

Expand All @@ -1098,6 +1125,32 @@ HostComboBox *AudioAdvancedSettings::PassThroughOutputDevice()
return gc;
}

HostCheckBox *AudioAdvancedSettings::SPDIFRateOverride()
{
HostCheckBox *gc = new HostCheckBox("SPDIFRateOverride");
gc->setLabel(QObject::tr("SPDIF 48k rate override"));
gc->setValue(false);
gc->setHelpText(QObject::tr("ALSA only. By default, let ALSA determine "
"the passthrough sampling rate. If checked "
"set the sampling rate to 48kHz for passthrough."
" (default is not checked)"));
return gc;
}

HostCheckBox *AudioAdvancedSettings::HBRPassthrough()
{
HostCheckBox *gc = new HostCheckBox("HBRPassthru");
gc->setLabel(QObject::tr("HBR passthrough support"));
gc->setValue(true);
gc->setHelpText(QObject::tr("HBR support is required for TrueHD and DTS-HD "
"passthrough. If unchecked, Myth will limit the "
"passthrough bitrate to 6.144Mbit/s."
"This will disable True-HD passthrough, however will "
"allow DTS-HD content to be sent as DTS-HD Hi-Res."
" (default is checked)"));
return gc;
}

AudioAdvancedSettings::AudioAdvancedSettings(bool mpcm)
{
ConfigurationGroup *settings3 =
Expand All @@ -1120,17 +1173,25 @@ AudioAdvancedSettings::AudioAdvancedSettings(bool mpcm)
ConfigurationGroup *settings5 =
new HorizontalConfigurationGroup(false, false);
settings5->addChild(Audio48kOverride());
#if USING_ALSA
settings5->addChild(SPDIFRateOverride());
#endif

ConfigurationGroup *settings6 =
new HorizontalConfigurationGroup(false, false);
settings6->addChild(HBRPassthrough());

addChild(settings4);
addChild(settings5);
addChild(settings3);
addChild(settings6);

if (mpcm)
{
ConfigurationGroup *settings6;
settings6 = new HorizontalConfigurationGroup(false, false);
settings6->addChild(MPCM());
addChild(settings6);
ConfigurationGroup *settings7;
settings7 = new HorizontalConfigurationGroup(false, false);
settings7->addChild(MPCM());
addChild(settings7);
}
}

Expand Down
5 changes: 4 additions & 1 deletion mythtv/programs/mythfrontend/audiogeneralsettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class AudioConfigSettings : public VerticalConfigurationGroup
HostCheckBox *DTSPassThrough();
HostCheckBox *EAC3PassThrough();
HostCheckBox *TrueHDPassThrough();
HostCheckBox *DTSHDPassThrough();
bool CheckPassthrough();

ConfigurationGroup *m_cgsettings;
Expand All @@ -60,10 +61,10 @@ class AudioConfigSettings : public VerticalConfigurationGroup
HostCheckBox *m_DTSPassThrough;
HostCheckBox *m_EAC3PassThrough;
HostCheckBox *m_TrueHDPassThrough;
HostCheckBox *m_DTSHDPassThrough;
ADCMap audiodevs;
AudioOutput::ADCVect devices;
QMutex slotlock;
bool m_passthrough8;
ConfigurationWizard *m_parent;
};

Expand Down Expand Up @@ -111,6 +112,8 @@ class AudioAdvancedSettings : public VerticalConfigurationGroup
HostCheckBox *Audio48kOverride();
HostCheckBox *PassThroughOverride();
HostComboBox *PassThroughOutputDevice();
HostCheckBox *SPDIFRateOverride();
HostCheckBox *HBRPassthrough();

CheckBoxSetting *m_triggerMPCM;
HostCheckBox *m_MPCM;
Expand Down
23 changes: 15 additions & 8 deletions mythtv/programs/mythtranscode/transcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class AudioReencodeBuffer : public AudioOutput
Error(QString("Invalid channel count %1").arg(channels));
}

// dsprate is in 100 * samples/second
// dsprate is in 100 * frames/second
virtual void SetEffDsp(int dsprate)
{
eff_audiorate = (dsprate / 100);
Expand All @@ -82,27 +82,34 @@ class AudioReencodeBuffer : public AudioOutput

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

// timecode is in milliseconds.
virtual bool AddData(void *buffer, int len, int64_t timecode)
{
int freebuf = bufsize - audiobuffer_len;

if (frames * bytes_per_frame > freebuf)
if (len > freebuf)
{
bufsize += frames * bytes_per_frame - freebuf;
bufsize += len - freebuf;
unsigned char *tmpbuf = new unsigned char[bufsize];
memcpy(tmpbuf, audiobuffer, audiobuffer_len);
delete [] audiobuffer;
audiobuffer = tmpbuf;
}

ab_len[ab_count] = frames * bytes_per_frame;
ab_len[ab_count] = len;
ab_offset[ab_count] = audiobuffer_len;

memcpy(audiobuffer + audiobuffer_len, buffer,
frames * bytes_per_frame);
audiobuffer_len += frames * bytes_per_frame;
len);
audiobuffer_len += len;

// last_audiotime is at the end of the sample
last_audiotime = timecode + frames * 1000 / eff_audiorate;
// last_audiotime is at the end of the frame
last_audiotime = timecode + (len / bytes_per_frame) * 1000 /
eff_audiorate;

ab_time[ab_count] = last_audiotime;
ab_count++;
Expand Down