[Windows] Additional logging in Windows Sink Factory
[xbmc.git] / xbmc / cores / AudioEngine / Sinks / windows / AESinkFactoryWin32.cpp
blobb4d30af2d1db70ede8aa51b68b588c1668d67708
1 /*
2 * Copyright (C) 2010-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8 #include "AESinkFactoryWin.h"
9 #include "utils/StringUtils.h"
10 #include "utils/log.h"
12 #include "platform/win32/CharsetConverter.h"
13 #include "platform/win32/WIN32Util.h"
15 #include <algorithm>
17 #include <mmdeviceapi.h>
18 #include <wrl/client.h>
20 const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
21 const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
22 const IID IID_IAudioClient = __uuidof(IAudioClient);
24 DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);
25 DEFINE_PROPERTYKEY(PKEY_Device_EnumeratorName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 24);
27 #define EXIT_ON_FAILURE(hr, reason) \
28 if (FAILED(hr)) \
29 { \
30 CLog::LogF(LOGERROR, reason " - {}", hr, CWIN32Util::FormatHRESULT(hr)); \
31 goto failed; \
34 using namespace Microsoft::WRL;
36 std::vector<RendererDetail> CAESinkFactoryWin::GetRendererDetails()
38 std::vector<RendererDetail> list;
39 ComPtr<IMMDeviceEnumerator> pEnumerator = nullptr;
40 ComPtr<IMMDeviceCollection> pEnumDevices = nullptr;
41 ComPtr<IMMDevice> pDefaultDevice = nullptr;
42 LPWSTR pwszID = nullptr;
43 std::wstring wstrDDID;
44 HRESULT hr;
45 UINT uiCount = 0;
47 hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator,
48 reinterpret_cast<void**>(pEnumerator.GetAddressOf()));
49 EXIT_ON_FAILURE(hr, "Could not allocate WASAPI device enumerator.")
51 // get the default audio endpoint
52 if (S_OK ==
53 (hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, pDefaultDevice.GetAddressOf())))
55 if (S_OK == (hr = pDefaultDevice->GetId(&pwszID)))
57 wstrDDID = pwszID;
58 CoTaskMemFree(pwszID);
60 else
62 CLog::LogF(LOGERROR, "Unable to retrieve default endpoint identifier ({})",
63 CWIN32Util::FormatHRESULT(hr));
65 pDefaultDevice.Reset();
67 else
69 CLog::LogF(LOGERROR, "Unable to retrieve default endpoint ({})", CWIN32Util::FormatHRESULT(hr));
72 // enumerate over all audio endpoints
73 hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, pEnumDevices.GetAddressOf());
74 EXIT_ON_FAILURE(hr, "Retrieval of audio endpoint enumeration failed.")
76 hr = pEnumDevices->GetCount(&uiCount);
77 EXIT_ON_FAILURE(hr, "Retrieval of audio endpoint count failed.")
79 for (UINT i = 0; i < uiCount; i++)
81 RendererDetail details{};
82 ComPtr<IMMDevice> pDevice = nullptr;
83 ComPtr<IPropertyStore> pProperty = nullptr;
84 PROPVARIANT varName;
85 PropVariantInit(&varName);
87 hr = pEnumDevices->Item(i, pDevice.GetAddressOf());
88 EXIT_ON_FAILURE(hr, "Retrieval of WASAPI endpoint failed.")
90 hr = pDevice->OpenPropertyStore(STGM_READ, pProperty.ReleaseAndGetAddressOf());
91 EXIT_ON_FAILURE(hr, "Retrieval of WASAPI endpoint properties failed.")
93 hr = pProperty->GetValue(PKEY_Device_FriendlyName, &varName);
94 EXIT_ON_FAILURE(hr, "Retrieval of WASAPI endpoint device name failed.")
96 details.strDescription = KODI::PLATFORM::WINDOWS::FromW(varName.pwszVal);
97 PropVariantClear(&varName);
99 hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName);
100 EXIT_ON_FAILURE(hr, "Retrieval of WASAPI endpoint GUID failed.")
102 details.strDeviceId = KODI::PLATFORM::WINDOWS::FromW(varName.pwszVal);
103 PropVariantClear(&varName);
105 hr = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &varName);
106 EXIT_ON_FAILURE(hr, "Retrieval of WASAPI endpoint form factor failed.")
108 details.strWinDevType = winEndpoints[(EndpointFormFactor)varName.uiVal].winEndpointType;
109 details.eDeviceType = winEndpoints[(EndpointFormFactor)varName.uiVal].aeDeviceType;
111 PropVariantClear(&varName);
113 hr = pProperty->GetValue(PKEY_AudioEndpoint_PhysicalSpeakers, &varName);
114 EXIT_ON_FAILURE(hr, "Retrieval of WASAPI endpoint speaker layout failed.")
116 details.uiChannelMask = std::max(varName.uintVal, (unsigned int)KSAUDIO_SPEAKER_STEREO);
117 PropVariantClear(&varName);
119 hr = pProperty->GetValue(PKEY_Device_EnumeratorName, &varName);
120 if (SUCCEEDED(hr) && varName.pwszVal != nullptr)
122 details.strDeviceEnumerator = KODI::PLATFORM::WINDOWS::FromW(varName.pwszVal);
123 StringUtils::ToUpper(details.strDeviceEnumerator);
125 else
127 CLog::LogF(LOGDEBUG, "Retrieval of endpoint enumerator name failed: {}.",
128 (FAILED(hr)) ? "'GetValue' has failed" : "'varName.pwszVal' is NULL");
130 PropVariantClear(&varName);
132 if (S_OK == (hr = pDevice->GetId(&pwszID)))
134 if (wstrDDID.compare(pwszID) == 0)
135 details.bDefault = true;
137 CoTaskMemFree(pwszID);
139 else
141 CLog::LogF(LOGERROR, "Unable to retrieve device id ({})", CWIN32Util::FormatHRESULT(hr));
144 list.push_back(details);
147 return list;
149 failed:
151 CLog::Log(LOGERROR, "Failed to enumerate audio renderer devices.");
152 return list;
155 struct AEWASAPIDeviceWin32 : public IAEWASAPIDevice
157 friend CAESinkFactoryWin;
159 HRESULT AEWASAPIDeviceWin32::Activate(IAudioClient** ppAudioClient)
161 HRESULT hr = S_FALSE;
163 if (!ppAudioClient)
164 return E_POINTER;
168 ComPtr<IAudioClient> pClient = nullptr;
169 hr = m_pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, reinterpret_cast<void**>(pClient.GetAddressOf()));
170 if (SUCCEEDED(hr) && pClient)
172 *ppAudioClient = pClient.Detach();
173 return hr;
175 else
177 CLog::LogF(LOGERROR, "unable to activate IAudioClient ({})", CWIN32Util::FormatHRESULT(hr));
180 catch (...) {}
181 return hr;
184 int AEWASAPIDeviceWin32::Release() override
186 delete this;
187 return 0;
190 bool AEWASAPIDeviceWin32::IsUSBDevice() override
192 bool ret = false;
193 ComPtr<IPropertyStore> pProperty = nullptr;
194 PROPVARIANT varName;
195 PropVariantInit(&varName);
197 HRESULT hr = m_pDevice->OpenPropertyStore(STGM_READ, pProperty.GetAddressOf());
198 if (!SUCCEEDED(hr))
199 return ret;
200 hr = pProperty->GetValue(PKEY_Device_EnumeratorName, &varName);
202 std::string str = KODI::PLATFORM::WINDOWS::FromW(varName.pwszVal);
203 StringUtils::ToUpper(str);
204 ret = (str == "USB");
205 PropVariantClear(&varName);
206 return ret;
209 protected:
210 AEWASAPIDeviceWin32(IMMDevice* pDevice)
211 : m_pDevice(pDevice)
215 private:
216 ComPtr<IMMDevice> m_pDevice{ nullptr };
219 std::string CAESinkFactoryWin::GetDefaultDeviceId()
221 std::string strDeviceId = "";
222 ComPtr<IMMDevice> pDevice = nullptr;
223 ComPtr<IMMDeviceEnumerator> pEnumerator = nullptr;
224 std::wstring wstrDDID;
226 HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, reinterpret_cast<void**>(pEnumerator.GetAddressOf()));
227 EXIT_ON_FAILURE(hr, "Could not allocate WASAPI device enumerator.")
229 // get the default audio endpoint
230 if (pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, pDevice.GetAddressOf()) == S_OK)
232 ComPtr<IPropertyStore> pProperty = nullptr;
233 PROPVARIANT varName;
234 PropVariantInit(&varName);
236 hr = pDevice->OpenPropertyStore(STGM_READ, pProperty.GetAddressOf());
237 EXIT_ON_FAILURE(hr, "Retrieval of WASAPI endpoint properties failed.")
239 hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName);
240 EXIT_ON_FAILURE(hr, "Retrieval of WASAPI endpoint GUID failed.")
242 strDeviceId = KODI::PLATFORM::WINDOWS::FromW(varName.pwszVal);
243 PropVariantClear(&varName);
246 failed:
247 return strDeviceId;
250 HRESULT CAESinkFactoryWin::ActivateWASAPIDevice(std::string &device, IAEWASAPIDevice **ppDevice)
252 ComPtr<IMMDevice> pDevice = nullptr;
253 ComPtr<IMMDeviceEnumerator> pEnumerator = nullptr;
254 ComPtr<IMMDeviceCollection> pEnumDevices = nullptr;
255 UINT uiCount = 0;
257 if (!ppDevice)
258 return E_POINTER;
260 HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, reinterpret_cast<void**>(pEnumerator.GetAddressOf()));
261 EXIT_ON_FAILURE(hr, "Could not allocate WASAPI device enumerator.")
263 /* Get our device. First try to find the named device. */
265 hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, pEnumDevices.GetAddressOf());
266 EXIT_ON_FAILURE(hr, "Retrieval of audio endpoint enumeration failed.")
268 hr = pEnumDevices->GetCount(&uiCount);
269 EXIT_ON_FAILURE(hr, "Retrieval of audio endpoint count failed.")
271 for (UINT i = 0; i < uiCount; i++)
273 ComPtr<IPropertyStore> pProperty = nullptr;
274 PROPVARIANT varName;
276 hr = pEnumDevices->Item(i, pDevice.GetAddressOf());
277 EXIT_ON_FAILURE(hr, "Retrieval of WASAPI endpoint failed.")
279 hr = pDevice->OpenPropertyStore(STGM_READ, pProperty.GetAddressOf());
280 EXIT_ON_FAILURE(hr, "Retrieval of WASAPI endpoint properties failed.")
282 hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName);
283 EXIT_ON_FAILURE(hr, "Retrieval of WASAPI endpoint GUID failed.")
285 std::string strDevName = KODI::PLATFORM::WINDOWS::FromW(varName.pwszVal);
287 if (device == strDevName)
288 i = uiCount;
289 else
290 pDevice.Reset();
292 PropVariantClear(&varName);
295 if (pDevice)
297 AEWASAPIDeviceWin32* pAEDevice = new AEWASAPIDeviceWin32(pDevice.Get());
298 pAEDevice->deviceId = device;
299 *ppDevice = pAEDevice;
300 return S_OK;
303 return E_FAIL;
305 failed:
306 CLog::LogF(LOGERROR, "WASAPI initialization failed.");
307 return hr;