Skip to content

Instantly share code, notes, and snippets.

@CrystalP
Created May 12, 2024 06:34
Show Gist options
  • Save CrystalP/63326c10f0af04079fc968baab0466c0 to your computer and use it in GitHub Desktop.
Save CrystalP/63326c10f0af04079fc968baab0466c0 to your computer and use it in GitHub Desktop.
[Windows] Share Xbox audio device enumeration with desktop and XAudio2
diff --git a/xbmc/cores/AudioEngine/CMakeLists.txt b/xbmc/cores/AudioEngine/CMakeLists.txt
index 646c29c6e5..b5a0d3abbd 100644
--- a/xbmc/cores/AudioEngine/CMakeLists.txt
+++ b/xbmc/cores/AudioEngine/CMakeLists.txt
@@ -106,7 +106,8 @@ endif()
if(CORE_SYSTEM_NAME MATCHES windows)
list(APPEND SOURCES Sinks/AESinkWASAPI.cpp
Sinks/AESinkXAudio.cpp
- Sinks/windows/AESinkFactoryWin.cpp)
+ Sinks/windows/AESinkFactoryWin.cpp
+ Sinks/windows/AESinkFactoryWinRT.cpp)
list(APPEND HEADERS Sinks/AESinkWASAPI.h
Sinks/AESinkXAudio.h
Sinks/windows/AESinkFactoryWin.h)
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp
index 449602f323..32ba5784ba 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp
@@ -16,19 +16,12 @@
#include "utils/SystemInfo.h"
#include "utils/log.h"
-#ifdef TARGET_WINDOWS_STORE
-#include "platform/win10/AsyncHelpers.h"
-#endif
#include "platform/win32/CharsetConverter.h"
#include <algorithm>
#include <stdint.h>
#include <ksmedia.h>
-#include <mfapi.h>
-#include <mmdeviceapi.h>
-#include <mmreg.h>
-#include <wrl/implements.h>
using namespace Microsoft::WRL;
@@ -334,7 +327,7 @@ void CAESinkXAudio::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo
return;
}
- for(RendererDetail& details : CAESinkFactoryWin::GetRendererDetails())
+ for (RendererDetail& details : CAESinkFactoryWin::GetRendererDetailsWinRT())
{
deviceInfo.m_channels.Reset();
deviceInfo.m_dataFormats.clear();
@@ -347,7 +340,7 @@ void CAESinkXAudio::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo
deviceChannels += AEChannelNames[c];
}
- const std::wstring deviceId = KODI::PLATFORM::WINDOWS::ToW(details.strDevicePath);
+ const std::wstring deviceId = KODI::PLATFORM::WINDOWS::ToW(details.strDeviceId);
/* Test format for PCM format iteration */
wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
@@ -429,7 +422,7 @@ void CAESinkXAudio::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo
SafeDestroyVoice(&mSourceVoice);
SafeDestroyVoice(&mMasterVoice);
- deviceInfo.m_deviceName = details.strDevicePath;
+ deviceInfo.m_deviceName = details.strDeviceId;
deviceInfo.m_displayName = details.strWinDevType.append(details.strDescription);
deviceInfo.m_displayNameExtra = std::string("XAudio: ").append(details.strDescription);
deviceInfo.m_deviceType = details.eDeviceType;
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.h b/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.h
index 838fbef9d8..c879d75194 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.h
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.h
@@ -13,9 +13,6 @@
#include <stdint.h>
-#include <mmdeviceapi.h>
-#include <ppltasks.h>
-#include <wrl/implements.h>
#include <x3daudio.h>
#include <xapofx.h>
#include <xaudio2.h>
diff --git a/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin.h b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin.h
index 51cca733d1..f636d8ed56 100644
--- a/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin.h
+++ b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin.h
@@ -175,7 +175,6 @@ static const sampleFormat testFormats[] = { {KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 32
struct RendererDetail
{
- std::string strDevicePath;
std::string strDeviceId;
std::string strDescription;
std::string strWinDevType;
@@ -197,9 +196,13 @@ class CAESinkFactoryWin
{
public:
/*
- Gets list of audio renderers available on platform
+ Gets list of audio renderers available on platform - using MMDevice
*/
static std::vector<RendererDetail> GetRendererDetails();
+ /*
+ Gets list of audio renderers available on platform - using WinRT
+ */
+ static std::vector<RendererDetail> GetRendererDetailsWinRT();
/*
Gets default device id
*/
diff --git a/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin10.cpp b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin10.cpp
index 2492f02279..fcc5e4cd0c 100644
--- a/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin10.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin10.cpp
@@ -6,117 +6,20 @@
* See LICENSES/README.md for more information.
*/
#include "AESinkFactoryWin.h"
-#include "utils/log.h"
-
-#include "platform/win10/AsyncHelpers.h"
#include "platform/win32/CharsetConverter.h"
#include <mmdeviceapi.h>
#include <mmreg.h>
#include <winrt/Windows.Devices.Enumeration.h>
-#include <winrt/Windows.Media.Devices.Core.h>
#include <winrt/Windows.Media.Devices.h>
using namespace winrt::Windows::Devices::Enumeration;
using namespace winrt::Windows::Media::Devices;
-using namespace winrt::Windows::Media::Devices::Core;
using namespace Microsoft::WRL;
-static winrt::hstring PKEY_Device_FriendlyName = L"System.ItemNameDisplay";
-static winrt::hstring PKEY_AudioEndpoint_FormFactor = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 0";
-static winrt::hstring PKEY_AudioEndpoint_ControlPanelPageProvider = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 1";
-static winrt::hstring PKEY_AudioEndpoint_Association = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 2";
-static winrt::hstring PKEY_AudioEndpoint_PhysicalSpeakers = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 3";
-static winrt::hstring PKEY_AudioEndpoint_GUID = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 4";
-static winrt::hstring PKEY_AudioEndpoint_Disable_SysFx = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 5";
-static winrt::hstring PKEY_AudioEndpoint_FullRangeSpeakers = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 6";
-static winrt::hstring PKEY_AudioEndpoint_Supports_EventDriven_Mode = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 7";
-static winrt::hstring PKEY_AudioEndpoint_JackSubType = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 8";
-static winrt::hstring PKEY_AudioEndpoint_Default_VolumeInDb = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 9";
-static winrt::hstring PKEY_AudioEngine_DeviceFormat = L"{f19f064d-082c-4e27-bc73-6882a1bb8e4c} 0";
-static winrt::hstring PKEY_Device_EnumeratorName = L"{a45c254e-df1c-4efd-8020-67d146a850e0} 24";
-
std::vector<RendererDetail> CAESinkFactoryWin::GetRendererDetails()
{
- std::vector<RendererDetail> list;
- try
- {
- // Get the string identifier of the audio renderer
- auto defaultId = MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default);
- auto audioSelector = MediaDevice::GetAudioRenderSelector();
-
- // Add custom properties to the query
- DeviceInformationCollection devInfocollection = Wait(DeviceInformation::FindAllAsync(audioSelector,
- {
- PKEY_AudioEndpoint_FormFactor,
- PKEY_AudioEndpoint_GUID,
- PKEY_AudioEndpoint_PhysicalSpeakers,
- PKEY_AudioEngine_DeviceFormat,
- PKEY_Device_EnumeratorName
- }));
- if (devInfocollection == nullptr || devInfocollection.Size() == 0)
- goto failed;
-
- for (unsigned int i = 0; i < devInfocollection.Size(); i++)
- {
- RendererDetail details;
-
- DeviceInformation devInfo = devInfocollection.GetAt(i);
- if (devInfo.Properties().Size() == 0)
- goto failed;
-
- winrt::IInspectable propObj = nullptr;
-
- propObj = devInfo.Properties().Lookup(PKEY_AudioEndpoint_FormFactor);
- if (!propObj)
- goto failed;
-
- details.strWinDevType = winEndpoints[propObj.as<winrt::IPropertyValue>().GetUInt32()].winEndpointType;
- details.eDeviceType = winEndpoints[propObj.as<winrt::IPropertyValue>().GetUInt32()].aeDeviceType;
-
- unsigned long ulChannelMask = 0;
- unsigned int nChannels = 0;
-
- propObj = devInfo.Properties().Lookup(PKEY_AudioEngine_DeviceFormat);
- if (propObj)
- {
- winrt::com_array<uint8_t> com_arr;
- propObj.as<winrt::IPropertyValue>().GetUInt8Array(com_arr);
-
- WAVEFORMATEXTENSIBLE* smpwfxex = (WAVEFORMATEXTENSIBLE*)com_arr.data();
- nChannels = std::max(std::min(smpwfxex->Format.nChannels, (WORD)8), (WORD)2);
- ulChannelMask = smpwfxex->dwChannelMask;
- }
- else
- {
- // suppose stereo
- nChannels = 2;
- ulChannelMask = 3;
- }
-
- propObj = devInfo.Properties().Lookup(PKEY_AudioEndpoint_PhysicalSpeakers);
- details.uiChannelMask = propObj ? propObj.as<winrt::IPropertyValue>().GetUInt32() : ulChannelMask;
- details.nChannels = nChannels;
-
- details.strDescription = KODI::PLATFORM::WINDOWS::FromW(devInfo.Name().c_str());
- details.strDeviceId = KODI::PLATFORM::WINDOWS::FromW(devInfo.Id().c_str());
-
- // on Windows UWP device Id is same as Path
- details.strDevicePath = details.strDeviceId;
-
- details.bDefault = (devInfo.Id() == defaultId);
-
- list.push_back(details);
- }
- return list;
- }
- catch (...)
- {
- }
-
-failed:
- CLog::Log(LOGERROR, __FUNCTION__": Failed to enumerate audio renderer devices.");
- return list;
+ return GetRendererDetailsWinRT();
}
class CAudioInterfaceActivator : public winrt::implements<CAudioInterfaceActivator, IActivateAudioInterfaceCompletionHandler>
diff --git a/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin32.cpp b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin32.cpp
index 637fb6c773..c728155c56 100644
--- a/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin32.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin32.cpp
@@ -22,8 +22,6 @@ const IID IID_IAudioClient = __uuidof(IAudioClient);
DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);
DEFINE_PROPERTYKEY(PKEY_Device_EnumeratorName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 24);
-const PROPERTYKEY PKEY_AudioEndpoint_Path{
- {0x9c119480, 0xddc2, 0x4954, {0xa1, 0x50, 0x5b, 0xd2, 0x40, 0xd4, 0x54, 0xad}}, 1};
extern const char *WASAPIErrToStr(HRESULT err);
#define EXIT_ON_FAILURE(hr, reason) \
@@ -131,16 +129,6 @@ std::vector<RendererDetail> CAESinkFactoryWin::GetRendererDetails()
details.uiChannelMask = std::max(varName.uintVal, (unsigned int)KSAUDIO_SPEAKER_STEREO);
PropVariantClear(&varName);
- hr = pProperty->GetValue(PKEY_AudioEndpoint_Path, &varName);
- if (FAILED(hr))
- {
- CLog::LogF(LOGERROR, "Retrieval of endpoint path failed.");
- goto failed;
- }
-
- details.strDevicePath = KODI::PLATFORM::WINDOWS::FromW(varName.pwszVal);
- PropVariantClear(&varName);
-
if (pDevice->GetId(&pwszID) == S_OK)
{
if (wstrDDID.compare(pwszID) == 0)
diff --git a/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWinRT.cpp b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWinRT.cpp
new file mode 100644
index 0000000000..7ed5da438c
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWinRT.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2024 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "AESinkFactoryWin.h"
+#include "platform/win10/AsyncHelpers.h"
+#include "platform/win32/CharsetConverter.h"
+#include "utils/log.h"
+
+#include <winrt/windows.devices.enumeration.h>
+#include <winrt/windows.foundation.collections.h>
+#include <winrt/windows.foundation.h>
+#include <winrt/windows.media.devices.core.h>
+#include <winrt/windows.media.devices.h>
+
+namespace winrt
+{
+ using namespace Windows::Foundation;
+}
+
+using namespace winrt::Windows::Devices::Enumeration;
+using namespace winrt::Windows::Media::Devices;
+using namespace winrt::Windows::Media::Devices::Core;
+
+static winrt::hstring PKEY_Device_FriendlyName = L"System.ItemNameDisplay";
+static winrt::hstring PKEY_AudioEndpoint_FormFactor = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 0";
+static winrt::hstring PKEY_AudioEndpoint_ControlPanelPageProvider = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 1";
+static winrt::hstring PKEY_AudioEndpoint_Association = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 2";
+static winrt::hstring PKEY_AudioEndpoint_PhysicalSpeakers = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 3";
+static winrt::hstring PKEY_AudioEndpoint_GUID = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 4";
+static winrt::hstring PKEY_AudioEndpoint_Disable_SysFx = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 5";
+static winrt::hstring PKEY_AudioEndpoint_FullRangeSpeakers = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 6";
+static winrt::hstring PKEY_AudioEndpoint_Supports_EventDriven_Mode = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 7";
+static winrt::hstring PKEY_AudioEndpoint_JackSubType = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 8";
+static winrt::hstring PKEY_AudioEndpoint_Default_VolumeInDb = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 9";
+static winrt::hstring PKEY_AudioEngine_DeviceFormat = L"{f19f064d-082c-4e27-bc73-6882a1bb8e4c} 0";
+static winrt::hstring PKEY_Device_EnumeratorName = L"{a45c254e-df1c-4efd-8020-67d146a850e0} 24";
+
+std::vector<RendererDetail> CAESinkFactoryWin::GetRendererDetailsWinRT()
+{
+ std::vector<RendererDetail> list;
+ try
+ {
+ // Get the string identifier of the audio renderer
+ auto defaultId = MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default);
+ auto audioSelector = MediaDevice::GetAudioRenderSelector();
+
+ // Add custom properties to the query
+ DeviceInformationCollection devInfocollection = Wait(DeviceInformation::FindAllAsync(
+ audioSelector, {PKEY_AudioEndpoint_FormFactor, PKEY_AudioEndpoint_GUID,
+ PKEY_AudioEndpoint_PhysicalSpeakers, PKEY_AudioEngine_DeviceFormat,
+ PKEY_Device_EnumeratorName}));
+
+ if (devInfocollection == nullptr || devInfocollection.Size() == 0)
+ goto failed;
+
+ for (unsigned int i = 0; i < devInfocollection.Size(); i++)
+ {
+ RendererDetail details;
+
+ DeviceInformation devInfo = devInfocollection.GetAt(i);
+ if (devInfo.Properties().Size() == 0)
+ goto failed;
+
+ winrt::IInspectable propObj = nullptr;
+
+ propObj = devInfo.Properties().Lookup(PKEY_AudioEndpoint_FormFactor);
+ if (!propObj)
+ goto failed;
+
+ const uint32_t indexFF{propObj.as<winrt::IPropertyValue>().GetUInt32()};
+ details.strWinDevType = winEndpoints[indexFF].winEndpointType;
+ details.eDeviceType = winEndpoints[indexFF].aeDeviceType;
+
+ DWORD ulChannelMask = 0;
+ unsigned int nChannels = 0;
+
+ propObj = devInfo.Properties().Lookup(PKEY_AudioEngine_DeviceFormat);
+ if (propObj)
+ {
+ winrt::com_array<uint8_t> com_arr;
+ propObj.as<winrt::IPropertyValue>().GetUInt8Array(com_arr);
+
+ WAVEFORMATEXTENSIBLE* smpwfxex = (WAVEFORMATEXTENSIBLE*)com_arr.data();
+ nChannels = std::max(std::min(smpwfxex->Format.nChannels, (WORD)8), (WORD)2);
+ ulChannelMask = smpwfxex->dwChannelMask;
+ }
+ else
+ {
+ // suppose stereo
+ nChannels = 2;
+ ulChannelMask = 3;
+ }
+
+ propObj = devInfo.Properties().Lookup(PKEY_AudioEndpoint_PhysicalSpeakers);
+
+ details.uiChannelMask = propObj ? propObj.as<winrt::IPropertyValue>().GetUInt32()
+ : static_cast<unsigned int>(ulChannelMask);
+
+ details.nChannels = nChannels;
+
+ details.strDescription = KODI::PLATFORM::WINDOWS::FromW(devInfo.Name().c_str());
+ details.strDeviceId = KODI::PLATFORM::WINDOWS::FromW(devInfo.Id().c_str());
+
+ details.bDefault = (devInfo.Id() == defaultId);
+
+ list.push_back(details);
+ }
+ return list;
+ }
+ catch (...)
+ {
+ }
+
+failed:
+ CLog::LogF(LOGERROR, "Failed to enumerate audio renderer devices.");
+ return list;
+}
diff --git a/xbmc/platform/win10/AsyncHelpers.h b/xbmc/platform/win10/AsyncHelpers.h
index dd2a290b9f..7ad9320dd5 100644
--- a/xbmc/platform/win10/AsyncHelpers.h
+++ b/xbmc/platform/win10/AsyncHelpers.h
@@ -12,6 +12,10 @@
#include <ppltasks.h>
#include <sdkddkver.h>
+#ifndef TARGET_WINDOWS_STORE
+#include <winrt/windows.foundation.h>
+#endif
+
namespace winrt
{
using namespace Windows::Foundation;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment