diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index 944e76783215..4441b3f6c9ad 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -2217,8 +2217,12 @@ void MediaManager::DeviceListChanged() { } mDeviceListChangeEvent.Notify(); - // On some Windows machines, if we call EnumerateRawDevices immediately after - // receiving devicechange event, we would get an outdated devices list. + // Wait 200 ms, because + // A) on some Windows machines, if we call EnumerateRawDevices immediately + // after receiving devicechange event, we'd get an outdated devices list. + // B) Waiting helps coalesce multiple calls on us into one, which can happen + // if a device with both audio input and output is attached or removed. + // We want to react & fire a devicechange event only once in that case. if (mDeviceChangeTimer) { mDeviceChangeTimer->Cancel(); @@ -2255,18 +2259,18 @@ void MediaManager::DeviceListChanged() { return; } - nsTArray deviceIDs; + MediaManager::DeviceIdSet deviceIDs; for (auto& device : *devices) { nsString id; device->GetId(id); - if (!deviceIDs.Contains(id)) { - deviceIDs.AppendElement(id); - } + MOZ_ALWAYS_TRUE(deviceIDs.put(std::move(id))); } - // For any removed devices, notify their listeners cleanly that the - // source has stopped, so JS knows and usage indicators update. - for (auto& id : mDeviceIDs) { - if (deviceIDs.Contains(id)) { + // For any real removed cameras, microphones or speakers, notify + // their listeners cleanly that the source has stopped, so JS knows + // and usage indicators update. + for (auto iter = mDeviceIDs.iter(); !iter.done(); iter.next()) { + const auto& id = iter.get(); + if (deviceIDs.has(id)) { // Device has not been removed continue; } @@ -3127,7 +3131,8 @@ RefPtr MediaManager::EnumerateDevicesImpl( }) ->Then( GetMainThreadSerialEventTarget(), __func__, - [aWindowId, originKey, aOutDevices](bool) { + [aWindowId, originKey, aVideoInputEnumType, aAudioInputEnumType, + aOutDevices](bool) { // Only run if window is still on our active list. MediaManager* mgr = MediaManager::GetIfExists(); if (!mgr || !mgr->IsWindowStillActive(aWindowId)) { @@ -3136,12 +3141,17 @@ RefPtr MediaManager::EnumerateDevicesImpl( __func__); } - mgr->mDeviceIDs.Clear(); for (auto& device : *aOutDevices) { - nsString id; - device->GetId(id); - if (!mgr->mDeviceIDs.Contains(id)) { - mgr->mDeviceIDs.AppendElement(id); + if (device->mKind == MediaDeviceKind::Audiooutput || + (device->mKind == MediaDeviceKind::Audioinput && + aAudioInputEnumType != DeviceEnumerationType::Fake && + device->GetMediaSource() == MediaSourceEnum::Microphone) || + (device->mKind == MediaDeviceKind::Videoinput && + aVideoInputEnumType != DeviceEnumerationType::Fake && + device->GetMediaSource() == MediaSourceEnum::Camera)) { + nsString id; + device->GetId(id); + MOZ_ALWAYS_TRUE(mgr->mDeviceIDs.put(std::move(id))); } } @@ -3670,7 +3680,7 @@ void MediaManager::Shutdown() { mActiveCallbacks.Clear(); mCallIds.Clear(); mPendingGUMRequest.Clear(); - mDeviceIDs.Clear(); + mDeviceIDs.clear(); #ifdef MOZ_WEBRTC StopWebRtcLog(); #endif diff --git a/dom/media/MediaManager.h b/dom/media/MediaManager.h index 1169288c8a87..1659e55b7c2b 100644 --- a/dom/media/MediaManager.h +++ b/dom/media/MediaManager.h @@ -358,7 +358,21 @@ class MediaManager final : public nsIMediaManagerService, public nsIObserver { static StaticRefPtr sSingleton; static StaticMutex sSingletonMutex; - nsTArray mDeviceIDs; + struct nsStringHasher { + using Key = nsString; + using Lookup = nsString; + + static HashNumber hash(const Lookup& aLookup) { + return HashString(aLookup.get()); + } + + static bool match(const Key& aKey, const Lookup& aLookup) { + return aKey == aLookup; + } + }; + + using DeviceIdSet = HashSet; + DeviceIdSet mDeviceIDs; // Connect/Disconnect on media thread only MediaEventListener mDeviceListChangeListener; diff --git a/dom/media/webrtc/MediaEngine.h b/dom/media/webrtc/MediaEngine.h index efce5b4a341b..75b39e843324 100644 --- a/dom/media/webrtc/MediaEngine.h +++ b/dom/media/webrtc/MediaEngine.h @@ -47,7 +47,9 @@ class MediaEngine { virtual void Shutdown() = 0; - virtual void SetFakeDeviceChangeEventsEnabled(bool aEnable) {} + virtual void SetFakeDeviceChangeEventsEnabled(bool aEnable) { + MOZ_DIAGNOSTIC_ASSERT(false, "Fake events may not have started/stopped"); + } virtual MediaEventSource& DeviceListChangeEvent() = 0;