diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkPULSE.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkPULSE.cpp index c6763bae13877..5ec64fe9f0bb8 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkPULSE.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkPULSE.cpp @@ -19,6 +19,9 @@ #include #include +#include +#include + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -27,7 +30,7 @@ class CDriverMonitor public: CDriverMonitor() = default; virtual ~CDriverMonitor(); - bool Start(); + bool Start(bool allowPipeWireCompatServer); bool IsInitialized(); CCriticalSection m_sec; @@ -315,6 +318,21 @@ static void SinkChangedCallback(pa_context *c, pa_subscription_event_type_t t, u } } +struct ServerInfoStruct +{ + pa_threaded_mainloop* m_mainloop; + bool m_isInfoSet{false}; + std::string m_serverName; +}; + +static void ServerInfoCallback(pa_context* c, const pa_server_info* i, void* userdata) +{ + auto serverInfo = reinterpret_cast(userdata); + serverInfo->m_serverName = i->server_name; + serverInfo->m_isInfoSet = true; + pa_threaded_mainloop_signal(serverInfo->m_mainloop, 0); +} + struct SinkInfoStruct { AEDeviceInfoList *list; @@ -656,7 +674,7 @@ bool CDriverMonitor::IsInitialized() return m_isInit; } -bool CDriverMonitor::Start() +bool CDriverMonitor::Start(bool allowPipeWireCompatServer) { if (!SetupContext(nullptr, "KodiDriver", &m_pContext, &m_pMainLoop)) { @@ -666,6 +684,30 @@ bool CDriverMonitor::Start() pa_threaded_mainloop_lock(m_pMainLoop); + ServerInfoStruct serverInfo; + serverInfo.m_mainloop = m_pMainLoop; + pa_context_get_server_info(m_pContext, ServerInfoCallback, &serverInfo); + while (!serverInfo.m_isInfoSet) + pa_threaded_mainloop_wait(m_pMainLoop); + + CLog::Log(LOGINFO, "PulseAudio: Server name: {}", serverInfo.m_serverName); + +#ifdef HAS_PIPEWIRE + // If Kodi is build with PipeWire support we prefer native PipeWire over the + // PulseAudio compatibility layer IF NOT PulseAudio is explicitly selected + if (!allowPipeWireCompatServer) + { + // Check the PulseAudio server name. If it contains PipeWire in any form + // it is the compatibility layer provided by PipeWire + if (StringUtils::Contains(serverInfo.m_serverName, "pipewire", true)) + { + CLog::Log(LOGINFO, "PulseAudio: Server is PipeWire, bail and use native PipeWire interface"); + pa_threaded_mainloop_unlock(m_pMainLoop); + return false; + } + } +#endif + m_isInit = true; std::unique_lock lock(m_sec); @@ -687,7 +729,7 @@ bool CDriverMonitor::Start() std::unique_ptr CAESinkPULSE::m_pMonitor; -bool CAESinkPULSE::Register() +bool CAESinkPULSE::Register(bool allowPipeWireCompatServer) { // check if pulseaudio is actually available pa_simple *s; @@ -708,7 +750,11 @@ bool CAESinkPULSE::Register() } m_pMonitor = std::make_unique(); - m_pMonitor->Start(); + if (!m_pMonitor->Start(allowPipeWireCompatServer)) + { + m_pMonitor.reset(); + return false; + } AE::AESinkRegEntry entry; entry.sinkName = "PULSE"; diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkPULSE.h b/xbmc/cores/AudioEngine/Sinks/AESinkPULSE.h index 3606a0b4f55bb..48f73f9a10af1 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkPULSE.h +++ b/xbmc/cores/AudioEngine/Sinks/AESinkPULSE.h @@ -31,7 +31,7 @@ class CAESinkPULSE : public IAESink CAESinkPULSE(); ~CAESinkPULSE() override; - static bool Register(); + static bool Register(bool allowPipeWireCompatServer); static std::unique_ptr Create(std::string& device, AEAudioFormat& desiredFormat); static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false); static void Cleanup(); diff --git a/xbmc/platform/freebsd/PlatformFreebsd.cpp b/xbmc/platform/freebsd/PlatformFreebsd.cpp index c949a339cbebc..6ab3f46ed2564 100644 --- a/xbmc/platform/freebsd/PlatformFreebsd.cpp +++ b/xbmc/platform/freebsd/PlatformFreebsd.cpp @@ -89,7 +89,7 @@ bool CPlatformFreebsd::InitStageOne() } else if (sink == "pulseaudio") { - OPTIONALS::PulseAudioRegister(); + OPTIONALS::PulseAudioRegister(true); } else if (sink == "oss") { @@ -102,11 +102,11 @@ bool CPlatformFreebsd::InitStageOne() else if (sink == "alsa+pulseaudio") { OPTIONALS::ALSARegister(); - OPTIONALS::PulseAudioRegister(); + OPTIONALS::PulseAudioRegister(true); } else { - if (!OPTIONALS::PulseAudioRegister()) + if (!OPTIONALS::PulseAudioRegister(false)) { if (!OPTIONALS::ALSARegister()) { diff --git a/xbmc/platform/linux/OptionalsReg.cpp b/xbmc/platform/linux/OptionalsReg.cpp index 82fb176d1d038..3992f2a0d05b0 100644 --- a/xbmc/platform/linux/OptionalsReg.cpp +++ b/xbmc/platform/linux/OptionalsReg.cpp @@ -33,13 +33,13 @@ bool OPTIONALS::ALSARegister() #ifdef HAS_PULSEAUDIO #include "cores/AudioEngine/Sinks/AESinkPULSE.h" -bool OPTIONALS::PulseAudioRegister() +bool OPTIONALS::PulseAudioRegister(bool allowPipeWireCompatServer) { - bool ret = CAESinkPULSE::Register(); + bool ret = CAESinkPULSE::Register(allowPipeWireCompatServer); return ret; } #else -bool OPTIONALS::PulseAudioRegister() +bool OPTIONALS::PulseAudioRegister(bool) { return false; } diff --git a/xbmc/platform/linux/OptionalsReg.h b/xbmc/platform/linux/OptionalsReg.h index 75fdb6ae62c7a..3a777b70036e7 100644 --- a/xbmc/platform/linux/OptionalsReg.h +++ b/xbmc/platform/linux/OptionalsReg.h @@ -23,7 +23,7 @@ bool ALSARegister(); namespace OPTIONALS { -bool PulseAudioRegister(); +bool PulseAudioRegister(bool allowPipeWireCompatServer); } //----------------------------------------------------------------------------- diff --git a/xbmc/platform/linux/PlatformLinux.cpp b/xbmc/platform/linux/PlatformLinux.cpp index 6be5163883223..e01f8216bf0d8 100644 --- a/xbmc/platform/linux/PlatformLinux.cpp +++ b/xbmc/platform/linux/PlatformLinux.cpp @@ -101,7 +101,7 @@ bool CPlatformLinux::InitStageOne() } else if (sink == "pulseaudio") { - OPTIONALS::PulseAudioRegister(); + OPTIONALS::PulseAudioRegister(true); } else if (sink == "pipewire") { @@ -114,13 +114,13 @@ bool CPlatformLinux::InitStageOne() else if (sink == "alsa+pulseaudio") { OPTIONALS::ALSARegister(); - OPTIONALS::PulseAudioRegister(); + OPTIONALS::PulseAudioRegister(true); } else { - if (!OPTIONALS::PipewireRegister()) + if (!OPTIONALS::PulseAudioRegister(false)) { - if (!OPTIONALS::PulseAudioRegister()) + if (!OPTIONALS::PipewireRegister()) { if (!OPTIONALS::ALSARegister()) {