58 if (!enumerator || !deviceId)
61 CComPtr<IMMDevice> device;
62 CComPtr<IPropertyStore> props;
64 if (FAILED(enumerator->GetDevice(deviceId, &device)))
67 if (FAILED(device->OpenPropertyStore(STGM_READ, &props)))
71 PropVariantInit(&varName);
72 HRESULT hr = props->GetValue(PKEY_Device_FriendlyName, &varName);
75 if (SUCCEEDED(hr) && varName.vt == VT_LPWSTR && varName.pwszVal) {
78 PropVariantClear(&varName);
90 if (FAILED(CoCreateInstance(__uuidof(MMDeviceEnumerator),
93 __uuidof(IMMDeviceEnumerator),
94 (
void**) &deviceEnumerator_))) {
95 JAMI_ERR(
"Failed to create device enumerator");
104 if (deviceEnumerator_) {
105 deviceEnumerator_->UnregisterEndpointNotificationCallback(
this);
111 if (!deviceEnumerator_) {
112 JAMI_ERR(
"Device enumerator not initialized");
117 std::lock_guard<std::mutex> lock(mutex_);
118 deviceEventCallback_ = callback;
122 deviceEnumerator_->RegisterEndpointNotificationCallback(
this);
126 ULONG STDMETHODCALLTYPE
AddRef()
override
128 auto count = InterlockedIncrement(&_refCount);
134 auto count = InterlockedDecrement(&_refCount);
143 if (riid == __uuidof(IUnknown) || riid == __uuidof(IMMNotificationClient)) {
144 *ppv =
static_cast<IMMNotificationClient*
>(
this);
149 return E_NOINTERFACE;
155 handleStateChanged(deviceId, newState);
162 handleStateChanged(deviceId, DEVICE_STATE_ACTIVE);
169 handleStateChanged(deviceId, DEVICE_STATE_NOTPRESENT);
178 if (role == eCommunications && deviceId && deviceEventCallback_) {
188 if (key == PKEY_AudioEngine_DeviceFormat) {
191 deviceEnumerator_->GetDevice(deviceId, &pDevice);
193 IPropertyStore* pProps;
194 pDevice->OpenPropertyStore(STGM_READ, &pProps);
197 PropVariantInit(&var);
198 pProps->GetValue(PKEY_AudioEngine_DeviceFormat, &var);
200 if (var.vt == VT_BLOB) {
201 auto* pWaveFormat =
reinterpret_cast<WAVEFORMATEXTENSIBLE*
>(var.blob.pBlobData);
202 UINT sampleRate = pWaveFormat->Format.nSamplesPerSec;
203 JAMI_DBG(
"Sample rate changed to %u", sampleRate);
208 PropVariantClear(&var);
216 CComPtr<IMMDeviceEnumerator> deviceEnumerator_;
219 std::unordered_set<std::wstring> activeDevices_;
226 void enumerateDevices()
228 if (!deviceEnumerator_)
231 std::lock_guard<std::mutex> lock(mutex_);
232 activeDevices_.clear();
234 enumerateDevicesOfType(eRender);
235 enumerateDevicesOfType(eCapture);
238 void enumerateDevicesOfType(EDataFlow flow)
240 CComPtr<IMMDeviceCollection> devices;
241 CComPtr<IMMDevice> device;
242 UINT deviceCount = 0;
244 if (FAILED(deviceEnumerator_->EnumAudioEndpoints(flow, DEVICE_STATE_ACTIVE, &devices))) {
245 JAMI_ERR(
"Failed to enumerate devices");
249 if (FAILED(devices->GetCount(&deviceCount))) {
250 JAMI_ERR(
"Failed to get device count");
254 for (UINT i = 0; i < deviceCount; ++i) {
255 if (FAILED(devices->Item(i, &device)))
258 LPWSTR deviceId =
nullptr;
259 if (FAILED(device->GetId(&deviceId)))
262 DWORD deviceState = 0;
263 if (SUCCEEDED(device->GetState(&deviceState)) && deviceState == DEVICE_STATE_ACTIVE) {
264 activeDevices_.insert(deviceId);
267 CoTaskMemFree(deviceId);
271 void handleStateChanged(LPCWSTR deviceId, DWORD newState)
273 if (!deviceId || !deviceEnumerator_ || !deviceEventCallback_)
276 std::lock_guard<std::mutex> lock(mutex_);
278 std::wstring wsId(deviceId);
282 bool isActive = (newState == DEVICE_STATE_ACTIVE);
283 auto it = activeDevices_.find(wsId);
285 if (isActive && it == activeDevices_.end()) {
287 activeDevices_.insert(wsId);
289 }
else if (!isActive && it != activeDevices_.end()) {
291 activeDevices_.erase(it);