# HG changeset patch # User Paul Adenot # Date 1517421720 -3600 # Wed Jan 31 19:02:00 2018 +0100 # Node ID 88520bbb43546f5358139040680da2055a1da7d0 # Parent 3cc4c46bc25e7de29212980b56dedeb619b2b3f1 Bug 1434600 - Add a method to clean up by window ID, on a MediaEngine. r=pehrsons MozReview-Commit-ID: 12w4StZE2eg diff --git a/dom/media/webrtc/MediaEngine.h b/dom/media/webrtc/MediaEngine.h --- a/dom/media/webrtc/MediaEngine.h +++ b/dom/media/webrtc/MediaEngine.h @@ -43,16 +43,17 @@ public: /** * Populate an array of sources of the requested type in the nsTArray. * Also include devices that are currently unavailable. */ virtual void EnumerateDevices(uint64_t aWindowId, dom::MediaSourceEnum, nsTArray>*) = 0; + virtual void ReleaseResourcesForWindow(uint64_t aWindowId) = 0; virtual void Shutdown() = 0; virtual void SetFakeDeviceChangeEvents() {} protected: virtual ~MediaEngine() = default; }; diff --git a/dom/media/webrtc/MediaEngineDefault.cpp b/dom/media/webrtc/MediaEngineDefault.cpp --- a/dom/media/webrtc/MediaEngineDefault.cpp +++ b/dom/media/webrtc/MediaEngineDefault.cpp @@ -554,50 +554,91 @@ MediaEngineDefault::EnumerateDevices(uin switch (aMediaSource) { case dom::MediaSourceEnum::Camera: { // Only supports camera video sources. See Bug 1038241. // We once had code here to find a VideoSource with the same settings and // re-use that. This is no longer possible since the resolution gets set // in Allocate(). + + nsTArray>* + devicesForThisWindow = mVSources.LookupOrAdd(aWindowId); auto newSource = MakeRefPtr(); - mVSources.AppendElement(newSource); + devicesForThisWindow->AppendElement(newSource); aSources->AppendElement(newSource); return; } case dom::MediaSourceEnum::Microphone: { - for (const RefPtr& source : mASources) { + nsTArray>* + devicesForThisWindow = mASources.LookupOrAdd(aWindowId); + for (const RefPtr& source : *devicesForThisWindow) { if (source->IsAvailable()) { aSources->AppendElement(source); } } if (aSources->IsEmpty()) { // All streams are currently busy, just make a new one. auto newSource = MakeRefPtr(); - mASources.AppendElement(newSource); + devicesForThisWindow->AppendElement(newSource); aSources->AppendElement(newSource); } return; } default: MOZ_ASSERT_UNREACHABLE("Unsupported source type"); return; } } void +MediaEngineDefault::ReleaseResourcesForWindow(uint64_t aWindowId) +{ + nsTArray>* audioDevicesForThisWindow = + mASources.Get(aWindowId); + + if (audioDevicesForThisWindow) { + for (const RefPtr& source : + *audioDevicesForThisWindow) { + source->Shutdown(); + } + } + + mASources.Remove(aWindowId); + + nsTArray>* videoDevicesForThisWindow = + mVSources.Get(aWindowId); + + if (videoDevicesForThisWindow) { + for (const RefPtr& source : + *videoDevicesForThisWindow) { + source->Shutdown(); + } + } + + mVSources.Remove(aWindowId); +} + +void MediaEngineDefault::Shutdown() { AssertIsOnOwningThread(); - for (RefPtr& source : mVSources) { - source->Shutdown(); + for (auto iter = mVSources.Iter(); !iter.Done(); iter.Next()) { + for (const RefPtr& source : *iter.UserData()) { + if (source) { + source->Shutdown(); + } + } } - for (RefPtr& source : mASources) { - source->Shutdown(); + for (auto iter = mASources.Iter(); !iter.Done(); iter.Next()) { + for (const RefPtr& source : *iter.UserData()) { + if (source) { + source->Shutdown(); + } + } } mVSources.Clear(); mASources.Clear(); }; } // namespace mozilla diff --git a/dom/media/webrtc/MediaEngineDefault.h b/dom/media/webrtc/MediaEngineDefault.h --- a/dom/media/webrtc/MediaEngineDefault.h +++ b/dom/media/webrtc/MediaEngineDefault.h @@ -186,19 +186,23 @@ class MediaEngineDefault : public MediaE { public: MediaEngineDefault() = default; void EnumerateDevices(uint64_t aWindowId, dom::MediaSourceEnum, nsTArray>*) override; void Shutdown() override; + void ReleaseResourcesForWindow(uint64_t aWindowId) override; private: ~MediaEngineDefault() = default; - nsTArray> mVSources; - nsTArray> mASources; + // WindowID -> Array of devices. + nsClassHashtable>> mVSources; + nsClassHashtable>> mASources; }; } // namespace mozilla #endif /* NSMEDIAENGINEDEFAULT_H_ */ diff --git a/dom/media/webrtc/MediaEngineWebRTC.cpp b/dom/media/webrtc/MediaEngineWebRTC.cpp --- a/dom/media/webrtc/MediaEngineWebRTC.cpp +++ b/dom/media/webrtc/MediaEngineWebRTC.cpp @@ -233,25 +233,30 @@ MediaEngineWebRTC::EnumerateDevices(uint if (uniqueId[0] == '\0') { // In case a device doesn't set uniqueId! strncpy(uniqueId, deviceName, sizeof(uniqueId)); uniqueId[sizeof(uniqueId)-1] = '\0'; // strncpy isn't safe } NS_ConvertUTF8toUTF16 uuid(uniqueId); - RefPtr vSource = mVideoSources.Get(uuid); - if (vSource && vSource->RequiresSharing()) { + RefPtr vSource; + + nsRefPtrHashtable* + devicesForThisWindow = mVideoSources.LookupOrAdd(aWindowId); + + if (devicesForThisWindow->Get(uuid, getter_AddRefs(vSource)) && + vSource->RequiresSharing()) { // We've already seen this shared device, just refresh and append. static_cast(vSource.get())->Refresh(i); aSources->AppendElement(vSource.get()); } else { vSource = new MediaEngineRemoteVideoSource(i, capEngine, aMediaSource, scaryKind || scarySource); - mVideoSources.Put(uuid, vSource); + devicesForThisWindow->Put(uuid, vSource); aSources->AppendElement(vSource); } } if (mHasTabVideoSource || dom::MediaSourceEnum::Browser == aMediaSource) { aSources->AppendElement(new MediaEngineTabVideoSource()); } } else { @@ -295,65 +300,106 @@ MediaEngineWebRTC::EnumerateDevices(uint continue; } if (uniqueId[0] == '\0') { // Mac and Linux don't set uniqueId! strcpy(uniqueId, deviceName); // safe given assert and initialization/error-check } + + RefPtr aSource; NS_ConvertUTF8toUTF16 uuid(uniqueId); - RefPtr aSource = mAudioSources.Get(uuid); - if (aSource && aSource->RequiresSharing()) { + + nsRefPtrHashtable* + devicesForThisWindow = mAudioSources.LookupOrAdd(aWindowId); + + if (devicesForThisWindow->Get(uuid, getter_AddRefs(aSource)) && + aSource->RequiresSharing()) { // We've already seen this device, just append. aSources->AppendElement(aSource.get()); } else { aSource = new MediaEngineWebRTCMicrophoneSource( new mozilla::AudioInputCubeb(i), i, deviceName, uniqueId, mDelayAgnostic, mExtendedFilter); - mAudioSources.Put(uuid, aSource); // Hashtable takes ownership. + devicesForThisWindow->Put(uuid, aSource); aSources->AppendElement(aSource); } } } } bool MediaEngineWebRTC::SupportsDuplex() { return mFullDuplex; } void +MediaEngineWebRTC::ReleaseResourcesForWindow(uint64_t aWindowId) +{ + { + nsRefPtrHashtable* + audioDevicesForThisWindow = mAudioSources.Get(aWindowId); + + if (audioDevicesForThisWindow) { + for (auto iter = audioDevicesForThisWindow->Iter(); !iter.Done(); + iter.Next()) { + iter.UserData()->Shutdown(); + } + + // This makes audioDevicesForThisWindow invalid. + mAudioSources.Remove(aWindowId); + } + } + + { + nsRefPtrHashtable* + videoDevicesForThisWindow = mVideoSources.Get(aWindowId); + if (videoDevicesForThisWindow) { + for (auto iter = videoDevicesForThisWindow->Iter(); !iter.Done(); + iter.Next()) { + iter.UserData()->Shutdown(); + } + + // This makes videoDevicesForThisWindow invalid. + mVideoSources.Remove(aWindowId); + } + } +} + +namespace { +template +void ShutdownSources(T& aHashTable) +{ + for (auto iter = aHashTable.Iter(); !iter.Done(); iter.Next()) { + for (auto iterInner = iter.UserData()->Iter(); !iterInner.Done(); + iterInner.Next()) { + MediaEngineSource* source = iterInner.UserData(); + source->Shutdown(); + } + } +} +} + +void MediaEngineWebRTC::Shutdown() { // This is likely paranoia MutexAutoLock lock(mMutex); if (camera::GetCamerasChildIfExists()) { camera::GetChildAndCall( &camera::CamerasChild::RemoveDeviceChangeCallback, this); } LOG(("%s", __FUNCTION__)); // Shutdown all the sources, since we may have dangling references to the // sources in nsDOMUserMediaStreams waiting for GC/CC - for (auto iter = mVideoSources.Iter(); !iter.Done(); iter.Next()) { - MediaEngineSource* source = iter.UserData(); - if (source) { - source->Shutdown(); - } - } - for (auto iter = mAudioSources.Iter(); !iter.Done(); iter.Next()) { - MediaEngineSource* source = iter.UserData(); - if (source) { - source->Shutdown(); - } - } - mVideoSources.Clear(); - mAudioSources.Clear(); + ShutdownSources(mVideoSources); + ShutdownSources(mAudioSources); mozilla::camera::Shutdown(); AudioInputCubeb::CleanupGlobalData(); } } diff --git a/dom/media/webrtc/MediaEngineWebRTC.h b/dom/media/webrtc/MediaEngineWebRTC.h --- a/dom/media/webrtc/MediaEngineWebRTC.h +++ b/dom/media/webrtc/MediaEngineWebRTC.h @@ -616,30 +616,35 @@ public: void Shutdown() override; // Returns whether the host supports duplex audio stream. bool SupportsDuplex(); void EnumerateDevices(uint64_t aWindowId, dom::MediaSourceEnum, nsTArray>*) override; + void ReleaseResourcesForWindow(uint64_t aWindowId) override; private: ~MediaEngineWebRTC() = default; nsCOMPtr mThread; // gUM runnables can e.g. Enumerate from multiple threads Mutex mMutex; RefPtr mAudioInput; bool mFullDuplex; bool mDelayAgnostic; bool mExtendedFilter; bool mHasTabVideoSource; - // Store devices we've already seen in a hashtable for quick return. - // Maps UUID to MediaEngineSource (one set for audio, one for video). - nsRefPtrHashtable mVideoSources; - nsRefPtrHashtable mAudioSources; + // Maps WindowID to a map of device uuid to their MediaEngineSource, + // separately for audio and video. + nsClassHashtable> mVideoSources; + nsClassHashtable> mAudioSources; }; } #endif /* NSMEDIAENGINEWEBRTC_H_ */