From 21892e9bc10ccfe0b7be15981256588248c8d3f7 Mon Sep 17 00:00:00 2001 From: CrystalP Date: Sat, 11 May 2024 11:09:53 +0200 Subject: [PATCH] [XAudio2] share Xbox audio device enumeration with desktop and Windows 8.1 compatibility --- xbmc/cores/AudioEngine/CMakeLists.txt | 3 +- xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp | 99 ++++++++++++---- xbmc/cores/AudioEngine/Sinks/AESinkXAudio.h | 4 - .../AudioEngine/Sinks/windows/AESinkFactoryWin.h | 7 +- .../Sinks/windows/AESinkFactoryWin10.cpp | 98 +--------------- .../Sinks/windows/AESinkFactoryWin32.cpp | 1 - .../Sinks/windows/AESinkFactoryWinRT.cpp | 124 +++++++++++++++++++++ xbmc/platform/win10/AsyncHelpers.h | 4 + 8 files changed, 213 insertions(+), 127 deletions(-) create mode 100644 xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWinRT.cpp diff --git a/xbmc/cores/AudioEngine/CMakeLists.txt b/xbmc/cores/AudioEngine/CMakeLists.txt index 4a2531753e..d3a5acc973 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 1c6e3a2f36..84e5e601c7 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp @@ -13,27 +13,62 @@ #include "cores/AudioEngine/Sinks/windows/AESinkFactoryWin.h" #include "cores/AudioEngine/Utils/AEDeviceInfo.h" #include "cores/AudioEngine/Utils/AEUtil.h" +#include "utils/SystemInfo.h" #include "utils/log.h" -#ifdef TARGET_WINDOWS_STORE -#include "platform/win10/AsyncHelpers.h" -#endif #include "platform/win32/CharsetConverter.h" #include #include #include -#include -#include -#include -#include using namespace Microsoft::WRL; namespace { constexpr int XAUDIO_BUFFERS_IN_QUEUE = 2; + +HRESULT KXAudio2Create(IXAudio2** ppXAudio2, + UINT32 Flags X2DEFAULT(0), + XAUDIO2_PROCESSOR XAudio2Processor X2DEFAULT(XAUDIO2_DEFAULT_PROCESSOR)) +{ + typedef HRESULT(__stdcall * XAudio2CreateInfoFunc)(_Outptr_ IXAudio2**, UINT32, + XAUDIO2_PROCESSOR); + static HMODULE dll = NULL; + static XAudio2CreateInfoFunc XAudio2CreateFn = nullptr; + + if (dll == NULL) + { + dll = LoadLibraryEx(L"xaudio2_9redist.dll", NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + + if (dll == NULL) + { + dll = LoadLibraryEx(L"xaudio2_9.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + + if (dll == NULL) + { + // Windows 8 compatibility + dll = LoadLibraryEx(L"xaudio2_8.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + + if (dll == NULL) + return HRESULT_FROM_WIN32(GetLastError()); + } + } + + XAudio2CreateFn = (XAudio2CreateInfoFunc)(void*)GetProcAddress(dll, "XAudio2Create"); + if (!XAudio2CreateFn) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + } + + if (XAudio2CreateFn) + return (*XAudio2CreateFn)(ppXAudio2, Flags, XAudio2Processor); + else + return E_FAIL; +} + } // namespace extern const char* WASAPIErrToStr(HRESULT err); @@ -50,7 +85,7 @@ inline void SafeDestroyVoice(TVoice **ppVoice) CAESinkXAudio::CAESinkXAudio() { - HRESULT hr = XAudio2Create(m_xAudio2.ReleaseAndGetAddressOf(), 0); + HRESULT hr = KXAudio2Create(m_xAudio2.ReleaseAndGetAddressOf(), 0); if (FAILED(hr)) { CLog::LogF(LOGERROR, "XAudio initialization failed."); @@ -283,7 +318,13 @@ void CAESinkXAudio::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo IXAudio2SourceVoice* mSourceVoice = nullptr; Microsoft::WRL::ComPtr xaudio2; - hr = XAudio2Create(xaudio2.ReleaseAndGetAddressOf(), eflags); + // ForegroundOnlyMedia/BackgroundCapableMedia replaced in Windows 10 by Movie/Media + const AUDIO_STREAM_CATEGORY streamCategory{ + CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin10) + ? AudioCategory_Media + : AudioCategory_ForegroundOnlyMedia}; + + hr = KXAudio2Create(xaudio2.ReleaseAndGetAddressOf(), eflags); if (FAILED(hr)) { CLog::LogF(LOGERROR, "failed to activate XAudio for capability testing ({})", @@ -291,7 +332,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(); @@ -304,7 +345,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); @@ -314,9 +355,15 @@ void CAESinkXAudio::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - xaudio2->CreateMasteringVoice(&mMasterVoice, wfxex.Format.nChannels, - wfxex.Format.nSamplesPerSec, 0, deviceId.c_str(), nullptr, - AudioCategory_Media); + hr = xaudio2->CreateMasteringVoice(&mMasterVoice, wfxex.Format.nChannels, + wfxex.Format.nSamplesPerSec, 0, deviceId.c_str(), nullptr, + streamCategory); + + if (FAILED(hr)) + { + CLog::LogF(LOGERROR, "failed to create mastering voice (:X)", hr); + return; + } for (int p = AE_FMT_FLOAT; p > AE_FMT_INVALID; p--) { @@ -367,7 +414,7 @@ void CAESinkXAudio::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo xaudio2->CreateMasteringVoice(&mMasterVoice, wfxex.Format.nChannels, wfxex.Format.nSamplesPerSec, 0, deviceId.c_str(), nullptr, - AudioCategory_Media); + streamCategory); hr = xaudio2->CreateSourceVoice(&mSourceVoice, &wfxex.Format); if (SUCCEEDED(hr)) @@ -377,7 +424,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; @@ -442,11 +489,17 @@ bool CAESinkXAudio::InitializeInternal(std::string deviceId, AEAudioFormat &form HRESULT hr; IXAudio2MasteringVoice* pMasterVoice = nullptr; + // ForegroundOnlyMedia/BackgroundCapableMedia replaced in Windows 10 by Movie/Media + const AUDIO_STREAM_CATEGORY streamCategory{ + CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin10) + ? AudioCategory_Media + : AudioCategory_ForegroundOnlyMedia}; if (!bdefault) { - hr = m_xAudio2->CreateMasteringVoice(&pMasterVoice, wfxex.Format.nChannels, wfxex.Format.nSamplesPerSec, - 0, device.c_str(), nullptr, AudioCategory_Media); + hr = m_xAudio2->CreateMasteringVoice(&pMasterVoice, wfxex.Format.nChannels, + wfxex.Format.nSamplesPerSec, 0, device.c_str(), nullptr, + streamCategory); } if (!pMasterVoice) @@ -461,8 +514,9 @@ bool CAESinkXAudio::InitializeInternal(std::string deviceId, AEAudioFormat &form // smartphone issue: providing device ID (even default ID) causes E_NOINTERFACE result // workaround: device = nullptr will initialize default audio endpoint - hr = m_xAudio2->CreateMasteringVoice(&pMasterVoice, wfxex.Format.nChannels, wfxex.Format.nSamplesPerSec, - 0, 0, nullptr, AudioCategory_Media); + hr = m_xAudio2->CreateMasteringVoice(&pMasterVoice, wfxex.Format.nChannels, + wfxex.Format.nSamplesPerSec, 0, nullptr, nullptr, + streamCategory); if (FAILED(hr) || !pMasterVoice) { CLog::LogF(LOGINFO, "Could not retrieve the default XAudio audio endpoint ({}).", @@ -528,8 +582,9 @@ bool CAESinkXAudio::InitializeInternal(std::string deviceId, AEAudioFormat &form wfxex.Format.nSamplesPerSec = WASAPISampleRates[i]; wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; - hr = m_xAudio2->CreateMasteringVoice(&m_masterVoice, wfxex.Format.nChannels, wfxex.Format.nSamplesPerSec, - 0, device.c_str(), nullptr, AudioCategory_Media); + hr = m_xAudio2->CreateMasteringVoice(&m_masterVoice, wfxex.Format.nChannels, + wfxex.Format.nSamplesPerSec, 0, device.c_str(), + nullptr, streamCategory); if (SUCCEEDED(hr)) { hr = m_xAudio2->CreateSourceVoice(&m_sourceVoice, &wfxex.Format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &m_voiceCallback); diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.h b/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.h index f398ca9b0e..1335714d09 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.h +++ b/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.h @@ -13,14 +13,10 @@ #include -#include -#include -#include #include #include #include #include -#pragma comment(lib,"xaudio2.lib") class CAESinkXAudio : public IAESink { diff --git a/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin.h b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin.h index d308673b91..68a8bd4a5c 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; @@ -198,10 +197,14 @@ class CAESinkFactoryWin { public: /* - Gets list of audio renderers available on platform + Gets list of available audio renderers - using MMDevice */ static std::vector GetRendererDetails(); /* + Gets list of available audio renderers - using WinRT + */ + static std::vector GetRendererDetailsWinRT(); + /* Gets default device id */ static std::string GetDefaultDeviceId(); diff --git a/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin10.cpp b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin10.cpp index 2492f02279..ea3eccf56f 100644 --- a/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin10.cpp +++ b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin10.cpp @@ -6,117 +6,21 @@ * 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 #include #include -#include #include 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 CAESinkFactoryWin::GetRendererDetails() { - std::vector 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().GetUInt32()].winEndpointType; - details.eDeviceType = winEndpoints[propObj.as().GetUInt32()].aeDeviceType; - - unsigned long ulChannelMask = 0; - unsigned int nChannels = 0; - - propObj = devInfo.Properties().Lookup(PKEY_AudioEngine_DeviceFormat); - if (propObj) - { - winrt::com_array com_arr; - propObj.as().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().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 diff --git a/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin32.cpp b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin32.cpp index e7d81282ae..d5869c5e2e 100644 --- a/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin32.cpp +++ b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin32.cpp @@ -147,7 +147,6 @@ std::vector CAESinkFactoryWin::GetRendererDetails() if (wstrDDID.compare(pwszID) == 0) details.bDefault = true; - details.strDevicePath = KODI::PLATFORM::WINDOWS::FromW(pwszID); CoTaskMemFree(pwszID); } diff --git a/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWinRT.cpp b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWinRT.cpp new file mode 100644 index 0000000000..d69c64f69a --- /dev/null +++ b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWinRT.cpp @@ -0,0 +1,124 @@ +/* + * 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 "utils/log.h" + +#include "platform/win10/AsyncHelpers.h" +#include "platform/win32/CharsetConverter.h" + +#include +#include +#include +#include +#include + +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; + +// clang-format off +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"; +// clang-format on + +std::vector CAESinkFactoryWin::GetRendererDetailsWinRT() +{ + std::vector 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 (const DeviceInformation& devInfo : devInfoCollection) + { + RendererDetail details; + + 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().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 com_arr; + propObj.as().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().GetUInt32() + : static_cast(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 #include +#ifndef TARGET_WINDOWS_STORE +#include +#endif + namespace winrt { using namespace Windows::Foundation; -- 2.11.4.GIT