-
-
Save CrystalP/63326c10f0af04079fc968baab0466c0 to your computer and use it in GitHub Desktop.
[Windows] Share Xbox audio device enumeration with desktop and XAudio2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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