Skip to content

Commit

Permalink
Merge pull request xbmc#23650 from thexai/sink-names
Browse files Browse the repository at this point in the history
[ActiveAESink] Improves sinks enumeration names and fallback logic when exact sink name not exists
  • Loading branch information
thexai authored Sep 3, 2023
2 parents d3f1d4b + ba77cb9 commit 320c7c2
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 56 deletions.
41 changes: 29 additions & 12 deletions xbmc/cores/AudioEngine/AESinkFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,41 +38,58 @@ bool CAESinkFactory::HasSinks()
return !m_AESinkRegEntry.empty();
}

void CAESinkFactory::ParseDevice(std::string &device, std::string &driver)
AESinkDevice CAESinkFactory::ParseDevice(const std::string& device)
{
int pos = device.find_first_of(':');
AESinkDevice dev{};
dev.name = device;

size_t pos = dev.name.find_first_of(':');
bool found = false;
if (pos > 0)

if (pos != std::string::npos)
{
driver = device.substr(0, pos);
dev.driver = device.substr(0, pos);

for (const auto& reg : m_AESinkRegEntry)
{
if (!StringUtils::EqualsNoCase(driver, reg.second.sinkName))
if (!StringUtils::EqualsNoCase(dev.driver, reg.second.sinkName))
continue;

device = device.substr(pos + 1, device.length() - pos - 1);
dev.name = dev.name.substr(pos + 1, dev.name.length() - pos - 1);
found = true;
}
}

if (!found)
driver.clear();
dev.driver.clear();

pos = dev.name.find_first_of(':');

if (pos != std::string::npos)
{
// if no known driver found considers the string starts
// with the device name and discarts the rest
if (found)
dev.friendlyName = dev.name.substr(pos + 1);
dev.name = dev.name.substr(0, pos);
}

return dev;
}

std::unique_ptr<IAESink> CAESinkFactory::Create(std::string& device, AEAudioFormat& desiredFormat)
std::unique_ptr<IAESink> CAESinkFactory::Create(const std::string& device,
AEAudioFormat& desiredFormat)
{
// extract the driver from the device string if it exists
std::string driver;
ParseDevice(device, driver);
const AESinkDevice dev = ParseDevice(device);

AEAudioFormat tmpFormat = desiredFormat;
std::unique_ptr<IAESink> sink;
std::string tmpDevice = device;
std::string tmpDevice = dev.name;

for (const auto& reg : m_AESinkRegEntry)
{
if (driver != reg.second.sinkName)
if (dev.driver != reg.second.sinkName)
continue;

sink = reg.second.createFunc(tmpDevice, tmpFormat);
Expand Down
16 changes: 14 additions & 2 deletions xbmc/cores/AudioEngine/AESinkFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,27 @@ struct AESinkRegEntry
Cleanup cleanupFunc;
};

struct AESinkDevice
{
std::string driver;
std::string name;
std::string friendlyName;

bool IsSameDeviceAs(const AESinkDevice& d) const
{
return driver == d.driver && (name == d.name || friendlyName == d.friendlyName);
}
};

class CAESinkFactory
{
public:
static void RegisterSink(const AESinkRegEntry& regEntry);
static void ClearSinks();
static bool HasSinks();

static void ParseDevice(std::string &device, std::string &driver);
static std::unique_ptr<IAESink> Create(std::string& device, AEAudioFormat& desiredFormat);
static AESinkDevice ParseDevice(const std::string& device);
static std::unique_ptr<IAESink> Create(const std::string& device, AEAudioFormat& desiredFormat);
static void EnumerateEx(std::vector<AESinkInfo>& list, bool force, const std::string& driver);
static void Cleanup();

Expand Down
80 changes: 68 additions & 12 deletions xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,7 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
m_extError = false;
m_sink.EnumerateSinkList(false, "");
LoadSettings();
ValidateOutputDevices(true);
Configure();
if (!m_isWinSysReg)
{
Expand Down Expand Up @@ -647,6 +648,7 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
m_controlPort.PurgeOut(CActiveAEControlProtocol::DEVICECHANGE);
m_sink.EnumerateSinkList(true, "");
LoadSettings();
ValidateOutputDevices(false);
m_extError = false;
Configure();
if (!m_extError)
Expand All @@ -669,6 +671,7 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
{
UnconfigureSink();
LoadSettings();
ValidateOutputDevices(false);
m_extError = false;
Configure();
if (!m_extError)
Expand Down Expand Up @@ -890,6 +893,7 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
m_controlPort.PurgeOut(CActiveAEControlProtocol::DEVICECOUNTCHANGE);
m_sink.EnumerateSinkList(true, "");
LoadSettings();
ValidateOutputDevices(false);
}
Configure();
if (!displayReset)
Expand Down Expand Up @@ -1191,17 +1195,18 @@ void CActiveAE::Configure(AEAudioFormat *desiredFmt)
m_extKeepConfig = 0ms;

std::string device = (m_sinkRequestFormat.m_dataFormat == AE_FMT_RAW) ? m_settings.passthroughdevice : m_settings.device;
std::string driver;
CAESinkFactory::ParseDevice(device, driver);
if ((!CompareFormat(m_sinkRequestFormat, m_sinkFormat) && !CompareFormat(m_sinkRequestFormat, oldSinkRequestFormat)) ||
m_currDevice.compare(device) != 0 ||
m_settings.driver.compare(driver) != 0)

const AESinkDevice dev = CAESinkFactory::ParseDevice(device);

if ((!CompareFormat(m_sinkRequestFormat, m_sinkFormat) &&
!CompareFormat(m_sinkRequestFormat, oldSinkRequestFormat)) ||
m_currDevice.compare(dev.name) != 0 || m_settings.driver.compare(dev.driver) != 0)
{
FlushEngine();
if (!InitSink())
return;
m_settings.driver = driver;
m_currDevice = device;
m_settings.driver = dev.driver;
m_currDevice = dev.name;
initSink = true;
m_stats.Reset(m_sinkFormat.m_sampleRate, m_mode == MODE_PCM);
m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::VOLUME, &m_volume, sizeof(float));
Expand Down Expand Up @@ -1781,12 +1786,11 @@ bool CActiveAE::NeedReconfigureSink()
ApplySettingsToFormat(newFormat, m_settings);

std::string device = (newFormat.m_dataFormat == AE_FMT_RAW) ? m_settings.passthroughdevice : m_settings.device;
std::string driver;
CAESinkFactory::ParseDevice(device, driver);

return !CompareFormat(newFormat, m_sinkFormat) ||
m_currDevice.compare(device) != 0 ||
m_settings.driver.compare(driver) != 0;
const AESinkDevice dev = CAESinkFactory::ParseDevice(device);

return !CompareFormat(newFormat, m_sinkFormat) || m_currDevice.compare(dev.name) != 0 ||
m_settings.driver.compare(dev.driver) != 0;
}

bool CActiveAE::InitSink()
Expand Down Expand Up @@ -2670,6 +2674,58 @@ void CActiveAE::LoadSettings()
m_settings.silenceTimeoutMinutes = settings->GetInt(CSettings::SETTING_AUDIOOUTPUT_STREAMSILENCE);
}

void CActiveAE::ValidateOutputDevices(bool saveChanges)
{
std::string device = m_sink.ValidateOuputDevice(m_settings.device, false);

if (!device.empty() && device != m_settings.device)
{
CLog::LogF(LOGWARNING, "audio output device setting has been updated from '{}' to '{}'",
m_settings.device, device);

const AESinkDevice oldDevice = CAESinkFactory::ParseDevice(m_settings.device);
const AESinkDevice newDevice = CAESinkFactory::ParseDevice(device);

m_settings.device = device;

if (saveChanges && newDevice.IsSameDeviceAs(oldDevice))
{
const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
if (settings)
{
settings->SetString(CSettings::SETTING_AUDIOOUTPUT_AUDIODEVICE, m_settings.device);
settings->Save();
CLog::LogF(LOGDEBUG, "the change of the audio output device setting has been saved");
}
}
}

device = m_sink.ValidateOuputDevice(m_settings.passthroughdevice, true);

if (!device.empty() && device != m_settings.passthroughdevice)
{
CLog::LogF(LOGWARNING, "passthrough output device setting has been updated from '{}' to '{}'",
m_settings.passthroughdevice, device);

const AESinkDevice oldDevice = CAESinkFactory::ParseDevice(m_settings.passthroughdevice);
const AESinkDevice newDevice = CAESinkFactory::ParseDevice(device);

m_settings.passthroughdevice = device;

if (saveChanges && newDevice.IsSameDeviceAs(oldDevice))
{
const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
if (settings)
{
settings->SetString(CSettings::SETTING_AUDIOOUTPUT_PASSTHROUGHDEVICE,
m_settings.passthroughdevice);
settings->Save();
CLog::LogF(LOGDEBUG, "the change of the passthrough output device setting has been saved");
}
}
}
}

void CActiveAE::Start()
{
Create();
Expand Down
1 change: 1 addition & 0 deletions xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ class CActiveAE : public IAE, public IDispResource, private CThread
void UnconfigureSink();
void Dispose();
void LoadSettings();
void ValidateOutputDevices(bool saveChanges);
bool NeedReconfigureBuffers();
bool NeedReconfigureSink();
void ApplySettingsToFormat(AEAudioFormat& format,
Expand Down
Loading

0 comments on commit 320c7c2

Please sign in to comment.