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.
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"
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) \
30 CLog::LogF(LOGERROR, reason " - {}", hr, CWIN32Util::FormatHRESULT(hr)); \
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
;
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
53 (hr
= pEnumerator
->GetDefaultAudioEndpoint(eRender
, eConsole
, pDefaultDevice
.GetAddressOf())))
55 if (S_OK
== (hr
= pDefaultDevice
->GetId(&pwszID
)))
58 CoTaskMemFree(pwszID
);
62 CLog::LogF(LOGERROR
, "Unable to retrieve default endpoint identifier ({})",
63 CWIN32Util::FormatHRESULT(hr
));
65 pDefaultDevice
.Reset();
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;
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
);
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
);
141 CLog::LogF(LOGERROR
, "Unable to retrieve device id ({})", CWIN32Util::FormatHRESULT(hr
));
144 list
.push_back(details
);
151 CLog::Log(LOGERROR
, "Failed to enumerate audio renderer devices.");
155 struct AEWASAPIDeviceWin32
: public IAEWASAPIDevice
157 friend CAESinkFactoryWin
;
159 HRESULT
AEWASAPIDeviceWin32::Activate(IAudioClient
** ppAudioClient
)
161 HRESULT hr
= S_FALSE
;
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();
177 CLog::LogF(LOGERROR
, "unable to activate IAudioClient ({})", CWIN32Util::FormatHRESULT(hr
));
184 int AEWASAPIDeviceWin32::Release() override
190 bool AEWASAPIDeviceWin32::IsUSBDevice() override
193 ComPtr
<IPropertyStore
> pProperty
= nullptr;
195 PropVariantInit(&varName
);
197 HRESULT hr
= m_pDevice
->OpenPropertyStore(STGM_READ
, pProperty
.GetAddressOf());
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
);
210 AEWASAPIDeviceWin32(IMMDevice
* pDevice
)
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;
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
);
250 HRESULT
CAESinkFactoryWin::ActivateWASAPIDevice(std::string
&device
, IAEWASAPIDevice
**ppDevice
)
252 ComPtr
<IMMDevice
> pDevice
= nullptr;
253 ComPtr
<IMMDeviceEnumerator
> pEnumerator
= nullptr;
254 ComPtr
<IMMDeviceCollection
> pEnumDevices
= nullptr;
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;
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
)
292 PropVariantClear(&varName
);
297 AEWASAPIDeviceWin32
* pAEDevice
= new AEWASAPIDeviceWin32(pDevice
.Get());
298 pAEDevice
->deviceId
= device
;
299 *ppDevice
= pAEDevice
;
306 CLog::LogF(LOGERROR
, "WASAPI initialization failed.");