Bug 1434600 - Add a method to clean up by window ID, on a MediaEngine. r?pehrsons
MozReview-Commit-ID: 12w4StZE2eg
--- 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<RefPtr<MediaEngineSource>>*) = 0;
+ virtual void ReleaseResourcesForWindow(uint64_t aWindowId) = 0;
virtual void Shutdown() = 0;
virtual void SetFakeDeviceChangeEvents() {}
protected:
virtual ~MediaEngine() {}
};
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -556,50 +556,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<RefPtr<MediaEngineSource>>*
+ devicesForThisWindow = mVSources.LookupOrAdd(aWindowId);
auto newSource = MakeRefPtr<MediaEngineDefaultVideoSource>();
- mVSources.AppendElement(newSource);
+ devicesForThisWindow->AppendElement(newSource);
aSources->AppendElement(newSource);
return;
}
case dom::MediaSourceEnum::Microphone: {
- for (const RefPtr<MediaEngineDefaultAudioSource>& source : mASources) {
+ nsTArray<RefPtr<MediaEngineDefaultAudioSource>>*
+ devicesForThisWindow = mASources.LookupOrAdd(aWindowId);
+ for (const RefPtr<MediaEngineDefaultAudioSource>& source : *devicesForThisWindow) {
if (source->IsAvailable()) {
aSources->AppendElement(source);
}
}
if (aSources->IsEmpty()) {
// All streams are currently busy, just make a new one.
auto newSource = MakeRefPtr<MediaEngineDefaultAudioSource>();
- 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<RefPtr<MediaEngineDefaultAudioSource>>* audioDevicesForThisWindow =
+ mASources.Get(aWindowId);
+
+ if (audioDevicesForThisWindow) {
+ for (const RefPtr<MediaEngineDefaultAudioSource>& source :
+ *audioDevicesForThisWindow) {
+ source->Shutdown();
+ }
+ }
+
+ mASources.Remove(aWindowId);
+
+ nsTArray<RefPtr<MediaEngineSource>>* videoDevicesForThisWindow =
+ mVSources.Get(aWindowId);
+
+ if (videoDevicesForThisWindow) {
+ for (const RefPtr<MediaEngineSource>& source :
+ *videoDevicesForThisWindow) {
+ source->Shutdown();
+ }
+ }
+
+ mVSources.Remove(aWindowId);
+}
+
+void
MediaEngineDefault::Shutdown()
{
AssertIsOnOwningThread();
- for (RefPtr<MediaEngineDefaultVideoSource>& source : mVSources) {
- source->Shutdown();
+ for (auto iter = mVSources.Iter(); !iter.Done(); iter.Next()) {
+ for (const RefPtr<MediaEngineSource>& source : *iter.UserData()) {
+ if (source) {
+ source->Shutdown();
+ }
+ }
}
- for (RefPtr<MediaEngineDefaultAudioSource>& source : mASources) {
- source->Shutdown();
+ for (auto iter = mASources.Iter(); !iter.Done(); iter.Next()) {
+ for (const RefPtr<MediaEngineDefaultAudioSource>& source : *iter.UserData()) {
+ if (source) {
+ source->Shutdown();
+ }
+ }
}
mVSources.Clear();
mASources.Clear();
};
} // namespace mozilla
--- 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<RefPtr<MediaEngineSource>>*) override;
void Shutdown() override;
+ void ReleaseResourcesForWindow(uint64_t aWindowId) override;
private:
~MediaEngineDefault() = default;
- nsTArray<RefPtr<MediaEngineDefaultVideoSource>> mVSources;
- nsTArray<RefPtr<MediaEngineDefaultAudioSource>> mASources;
+ // WindowID -> Array of devices.
+ nsClassHashtable<nsUint64HashKey,
+ nsTArray<RefPtr<MediaEngineSource>>> mVSources;
+ nsClassHashtable<nsUint64HashKey,
+ nsTArray<RefPtr<MediaEngineDefaultAudioSource>>> mASources;
};
} // namespace mozilla
#endif /* NSMEDIAENGINEDEFAULT_H_ */
--- 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<MediaEngineSource> vSource = mVideoSources.Get(uuid);
- if (vSource && vSource->RequiresSharing()) {
+ RefPtr<MediaEngineSource> vSource;
+
+ nsRefPtrHashtable<nsStringHashKey, MediaEngineSource>*
+ 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<MediaEngineRemoteVideoSource*>(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<MediaEngineSource> aSource;
NS_ConvertUTF8toUTF16 uuid(uniqueId);
- RefPtr<MediaEngineSource> aSource = mAudioSources.Get(uuid);
- if (aSource && aSource->RequiresSharing()) {
+
+ nsRefPtrHashtable<nsStringHashKey, MediaEngineSource>*
+ 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<nsStringHashKey, MediaEngineSource>*
+ 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<nsStringHashKey, MediaEngineSource>*
+ 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<typename T>
+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();
}
}
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -620,30 +620,35 @@ public:
void Shutdown() override;
// Returns whether the host supports duplex audio stream.
bool SupportsDuplex();
void EnumerateDevices(uint64_t aWindowId,
dom::MediaSourceEnum,
nsTArray<RefPtr<MediaEngineSource>>*) override;
+ void ReleaseResourcesForWindow(uint64_t aWindowId) override;
private:
~MediaEngineWebRTC() = default;
nsCOMPtr<nsIThread> mThread;
// gUM runnables can e.g. Enumerate from multiple threads
Mutex mMutex;
RefPtr<mozilla::AudioInput> 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<nsStringHashKey, MediaEngineSource> mVideoSources;
- nsRefPtrHashtable<nsStringHashKey, MediaEngineSource> mAudioSources;
+ // Maps WindowID to a map of device uuid to their MediaEngineSource,
+ // separately for audio and video.
+ nsClassHashtable<nsUint64HashKey,
+ nsRefPtrHashtable<nsStringHashKey,
+ MediaEngineSource>> mVideoSources;
+ nsClassHashtable<nsUint64HashKey,
+ nsRefPtrHashtable<nsStringHashKey,
+ MediaEngineSource>> mAudioSources;
};
}
#endif /* NSMEDIAENGINEWEBRTC_H_ */