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);
175 LPCWSTR deviceId)
override
180 if (role == eCommunications && deviceId && deviceEventCallback_) {
188 const PROPERTYKEY key)
override
191 if (key == PKEY_AudioEngine_DeviceFormat) {
194 deviceEnumerator_->GetDevice(deviceId, &pDevice);
196 IPropertyStore* pProps;
197 pDevice->OpenPropertyStore(STGM_READ, &pProps);
200 PropVariantInit(&var);
201 pProps->GetValue(PKEY_AudioEngine_DeviceFormat, &var);
203 if (var.vt == VT_BLOB) {
204 auto* pWaveFormat =
reinterpret_cast<WAVEFORMATEXTENSIBLE*
>(var.blob.pBlobData);
205 UINT sampleRate = pWaveFormat->Format.nSamplesPerSec;
206 JAMI_DBG(
"Sample rate changed to %u", sampleRate);
212 PropVariantClear(&var);
220 CComPtr<IMMDeviceEnumerator> deviceEnumerator_;
223 std::unordered_set<std::wstring> activeDevices_;
230 void enumerateDevices()
232 if (!deviceEnumerator_)
235 std::lock_guard<std::mutex> lock(mutex_);
236 activeDevices_.clear();
238 enumerateDevicesOfType(eRender);
239 enumerateDevicesOfType(eCapture);
242 void enumerateDevicesOfType(EDataFlow flow)
244 CComPtr<IMMDeviceCollection> devices;
245 CComPtr<IMMDevice> device;
246 UINT deviceCount = 0;
248 if (FAILED(deviceEnumerator_->EnumAudioEndpoints(flow, DEVICE_STATE_ACTIVE, &devices))) {
249 JAMI_ERR(
"Failed to enumerate devices");
253 if (FAILED(devices->GetCount(&deviceCount))) {
254 JAMI_ERR(
"Failed to get device count");
258 for (UINT i = 0; i < deviceCount; ++i) {
259 if (FAILED(devices->Item(i, &device)))
262 LPWSTR deviceId =
nullptr;
263 if (FAILED(device->GetId(&deviceId)))
266 DWORD deviceState = 0;
267 if (SUCCEEDED(device->GetState(&deviceState)) && deviceState == DEVICE_STATE_ACTIVE) {
268 activeDevices_.insert(deviceId);
271 CoTaskMemFree(deviceId);
275 void handleStateChanged(LPCWSTR deviceId, DWORD newState)
277 if (!deviceId || !deviceEnumerator_ || !deviceEventCallback_)
280 std::lock_guard<std::mutex> lock(mutex_);
282 std::wstring wsId(deviceId);
286 bool isActive = (newState == DEVICE_STATE_ACTIVE);
287 auto it = activeDevices_.find(wsId);
289 if (isActive && it == activeDevices_.end()) {
291 activeDevices_.insert(wsId);
293 }
else if (!isActive && it != activeDevices_.end()) {
295 activeDevices_.erase(it);