diff options
-rw-r--r-- | xbmc/cores/AudioEngine/CMakeLists.txt | 3 | ||||
-rw-r--r-- | xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp | 146 | ||||
-rw-r--r-- | xbmc/cores/AudioEngine/Sinks/AESinkXAudio.h | 4 | ||||
-rw-r--r-- | xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin.h | 7 | ||||
-rw-r--r-- | xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin10.cpp | 98 | ||||
-rw-r--r-- | xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin32.cpp | 1 | ||||
-rw-r--r-- | xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWinRT.cpp | 124 | ||||
-rw-r--r-- | xbmc/platform/win10/AsyncHelpers.h | 4 |
8 files changed, 252 insertions, 135 deletions
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..792c8ffc6a 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 <algorithm> #include <stdint.h> #include <ksmedia.h> -#include <mfapi.h> -#include <mmdeviceapi.h> -#include <mmreg.h> -#include <wrl/implements.h> 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,10 +85,10 @@ 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."); + CLog::LogF(LOGERROR, "XAudio initialization failed, error {:X}.", hr); } #ifdef _DEBUG else @@ -89,6 +124,13 @@ void CAESinkXAudio::Register() std::unique_ptr<IAESink> CAESinkXAudio::Create(std::string& device, AEAudioFormat& desiredFormat) { auto sink = std::make_unique<CAESinkXAudio>(); + + if (!sink->m_xAudio2) + { + CLog::LogF(LOGERROR, "XAudio2 not loaded."); + return {}; + } + if (sink->Initialize(desiredFormat, device)) return sink; @@ -283,7 +325,13 @@ void CAESinkXAudio::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo IXAudio2SourceVoice* mSourceVoice = nullptr; Microsoft::WRL::ComPtr<IXAudio2> 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 +339,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 +352,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 +362,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--) { @@ -359,25 +413,31 @@ void CAESinkXAudio::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo for (int j = 0; j < WASAPISampleRateCount; j++) { + if (WASAPISampleRates[j] < XAUDIO2_MIN_SAMPLE_RATE || + WASAPISampleRates[j] > XAUDIO2_MAX_SAMPLE_RATE) + continue; + SafeDestroyVoice(&mSourceVoice); SafeDestroyVoice(&mMasterVoice); wfxex.Format.nSamplesPerSec = WASAPISampleRates[j]; wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; - xaudio2->CreateMasteringVoice(&mMasterVoice, wfxex.Format.nChannels, - wfxex.Format.nSamplesPerSec, 0, deviceId.c_str(), nullptr, - AudioCategory_Media); - hr = xaudio2->CreateSourceVoice(&mSourceVoice, &wfxex.Format); + if (SUCCEEDED(xaudio2->CreateMasteringVoice(&mMasterVoice, wfxex.Format.nChannels, + wfxex.Format.nSamplesPerSec, 0, deviceId.c_str(), + nullptr, streamCategory))) + { + hr = xaudio2->CreateSourceVoice(&mSourceVoice, &wfxex.Format); - if (SUCCEEDED(hr)) - deviceInfo.m_sampleRates.push_back(WASAPISampleRates[j]); + if (SUCCEEDED(hr)) + deviceInfo.m_sampleRates.push_back(WASAPISampleRates[j]); + } } 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 +502,18 @@ bool CAESinkXAudio::InitializeInternal(std::string deviceId, AEAudioFormat &form HRESULT hr; IXAudio2MasteringVoice* pMasterVoice = nullptr; + const wchar_t* pDevice = device.c_str(); + // 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, pDevice, nullptr, + streamCategory); } if (!pMasterVoice) @@ -458,11 +525,12 @@ bool CAESinkXAudio::InitializeInternal(std::string deviceId, AEAudioFormat &form "Trying the default device...", KODI::PLATFORM::WINDOWS::FromW(device)); } - // 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); + pDevice = nullptr; + hr = m_xAudio2->CreateMasteringVoice(&pMasterVoice, wfxex.Format.nChannels, + wfxex.Format.nSamplesPerSec, 0, pDevice, nullptr, + streamCategory); if (FAILED(hr) || !pMasterVoice) { CLog::LogF(LOGINFO, "Could not retrieve the default XAudio audio endpoint ({}).", @@ -525,11 +593,19 @@ bool CAESinkXAudio::InitializeInternal(std::string deviceId, AEAudioFormat &form for (int i = 0 ; i < WASAPISampleRateCount; i++) { + if (WASAPISampleRates[j] < XAUDIO2_MIN_SAMPLE_RATE || + WASAPISampleRates[j] > XAUDIO2_MAX_SAMPLE_RATE) + continue; + + SafeDestroyVoice(&m_sourceVoice); + SafeDestroyVoice(&m_masterVoice); + 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, pDevice, nullptr, + streamCategory); if (SUCCEEDED(hr)) { hr = m_xAudio2->CreateSourceVoice(&m_sourceVoice, &wfxex.Format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &m_voiceCallback); @@ -550,9 +626,19 @@ bool CAESinkXAudio::InitializeInternal(std::string deviceId, AEAudioFormat &form if (closestMatch >= 0) { + // Closest match may be different from the last successful sample rate tested + SafeDestroyVoice(&m_sourceVoice); + SafeDestroyVoice(&m_masterVoice); + wfxex.Format.nSamplesPerSec = WASAPISampleRates[closestMatch]; wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; - goto initialize; + + if (SUCCEEDED(m_xAudio2->CreateMasteringVoice(&m_masterVoice, wfxex.Format.nChannels, + wfxex.Format.nSamplesPerSec, 0, pDevice, + nullptr, streamCategory)) && + SUCCEEDED(m_xAudio2->CreateSourceVoice(&m_sourceVoice, &wfxex.Format, 0, + XAUDIO2_DEFAULT_FREQ_RATIO, &m_voiceCallback))) + goto initialize; } } } 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 <stdint.h> -#include <mmdeviceapi.h> -#include <ppltasks.h> -#include <wrl/implements.h> #include <x3daudio.h> #include <xapofx.h> #include <xaudio2.h> #include <xaudio2fx.h> -#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<RendererDetail> GetRendererDetails(); /* + Gets list of available audio renderers - using WinRT + */ + static std::vector<RendererDetail> 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 <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 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<RendererDetail> 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 <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; + +// 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<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 (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<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; |