Bug 1364038 - Call SourceListener::StopTrack when the coresponding external device is disconnected. r=jib
MozReview-Commit-ID: EfpmXBJf7PP
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -525,16 +525,18 @@ public:
RemoveAll();
}
return true;
}
void StopSharing();
+ void StopRawID(const nsString& removedDeviceID);
+
/**
* Called by one of our SourceListeners when one of its tracks has stopped.
* Schedules an event for the next stable state to update chrome.
*/
void NotifySourceTrackStopped();
/**
* Called in stable state to send a notification to update chrome.
@@ -1723,16 +1725,18 @@ MediaManager::EnumerateRawDevices(uint64
RefPtr<MediaEngine> fakeBackend, realBackend;
if (fakeCams || fakeMics) {
fakeBackend = new MediaEngineDefault();
}
if ((!fakeCams && hasVideo) || (!fakeMics && hasAudio)) {
RefPtr<MediaManager> manager = MediaManager_GetInstance();
realBackend = manager->GetBackend(aWindowId);
+ // We need to listen to this event even JS didn't listen to it.
+ realBackend->AddDeviceChangeCallback(manager);
}
auto result = MakeUnique<SourceSet>();
if (hasVideo) {
nsTArray<RefPtr<VideoDevice>> videos;
GetSources(fakeCams? fakeBackend : realBackend, aVideoType,
&MediaEngine::EnumerateVideoDevices, videos, videoLoopDev);
@@ -2032,29 +2036,78 @@ MediaManager::NotifyRecordingStatusChang
return NS_OK;
}
int MediaManager::AddDeviceChangeCallback(DeviceChangeCallback* aCallback)
{
bool fakeDeviceChangeEventOn = mPrefs.mFakeDeviceChangeEventOn;
MediaManager::PostTask(NewTaskFrom([fakeDeviceChangeEventOn]() {
RefPtr<MediaManager> manager = MediaManager_GetInstance();
- manager->GetBackend(0)->AddDeviceChangeCallback(manager);
if (fakeDeviceChangeEventOn)
manager->GetBackend(0)->SetFakeDeviceChangeEvents();
}));
return DeviceChangeCallback::AddDeviceChangeCallback(aCallback);
}
+static void
+StopRawIDCallback(MediaManager *aThis,
+ uint64_t aWindowID,
+ GetUserMediaWindowListener *aListener,
+ void *aData)
+{
+ if (!aListener || !aData) {
+ return;
+ }
+
+ nsString* removedDeviceID = static_cast<nsString*>(aData);
+ aListener->StopRawID(*removedDeviceID);
+}
+
+
void MediaManager::OnDeviceChange() {
RefPtr<MediaManager> self(this);
- NS_DispatchToMainThread(media::NewRunnableFrom([self,this]() mutable {
+ NS_DispatchToMainThread(media::NewRunnableFrom([self]() mutable {
MOZ_ASSERT(NS_IsMainThread());
- DeviceChangeCallback::OnDeviceChange();
+ self->DeviceChangeCallback::OnDeviceChange();
+
+ // On some Windows machine, if we call EnumertaeRawDevices immediately after receiving
+ // devicechange event, sometimes we would get outdated devices list.
+ PR_Sleep(PR_MillisecondsToInterval(100));
+ RefPtr<PledgeSourceSet> p = self->EnumerateRawDevices(0, MediaSourceEnum::Camera, MediaSourceEnum::Microphone, false);
+ p->Then([self](SourceSet*& aDevices) mutable {
+ UniquePtr<SourceSet> devices(aDevices);
+ nsTArray<nsString> deviceIDs;
+
+ for (auto& device : *devices) {
+ nsString id;
+ device->GetId(id);
+ id.ReplaceSubstring(NS_LITERAL_STRING("default: "), NS_LITERAL_STRING(""));
+ if (!deviceIDs.Contains(id)) {
+ deviceIDs.AppendElement(id);
+ }
+ }
+
+ for (auto& id : self->mDeviceIDs) {
+ if (!deviceIDs.Contains(id)) {
+ // Stop the coresponding SourceListener
+ nsGlobalWindow::WindowByIdTable* windowsById = nsGlobalWindow::GetWindowsTable();
+ if (windowsById) {
+ for (auto iter = windowsById->Iter(); !iter.Done(); iter.Next()) {
+ nsGlobalWindow* window = iter.Data();
+ if (window->IsInnerWindow()) {
+ self->IterateWindowListeners(window->AsInner(), StopRawIDCallback, &id);
+ }
+ }
+ }
+ }
+ }
+
+ self->mDeviceIDs = deviceIDs;
+ }, [](MediaStreamError*& reason) {});
return NS_OK;
}));
}
nsresult MediaManager::GenerateUUID(nsAString& aResult)
{
nsresult rv;
nsCOMPtr<nsIUUIDGenerator> uuidgen =
@@ -2645,24 +2698,44 @@ MediaManager::EnumerateDevicesImpl(uint6
RefPtr<Pledge<nsCString>> p = media::GetPrincipalKey(principalInfo, persist);
p->Then([id, aWindowId, aVideoType, aAudioType,
aFake](const nsCString& aOriginKey) mutable {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<MediaManager> mgr = MediaManager_GetInstance();
RefPtr<PledgeSourceSet> p = mgr->EnumerateRawDevices(aWindowId, aVideoType,
aAudioType, aFake);
- p->Then([id, aWindowId, aOriginKey](SourceSet*& aDevices) mutable {
+ p->Then([id,
+ aWindowId,
+ aOriginKey,
+ aFake,
+ aVideoType,
+ aAudioType](SourceSet*& aDevices) mutable {
UniquePtr<SourceSet> devices(aDevices); // secondary result
// Only run if window is still on our active list.
RefPtr<MediaManager> mgr = MediaManager_GetInstance();
if (!mgr) {
return NS_OK;
}
+
+ if (aVideoType == MediaSourceEnum::Camera &&
+ aAudioType == MediaSourceEnum::Microphone &&
+ !aFake) {
+ mgr->mDeviceIDs.Clear();
+ for (auto& device : *devices) {
+ nsString id;
+ device->GetId(id);
+ id.ReplaceSubstring(NS_LITERAL_STRING("default: "), NS_LITERAL_STRING(""));
+ if (!mgr->mDeviceIDs.Contains(id)) {
+ mgr->mDeviceIDs.AppendElement(id);
+ }
+ }
+ }
+
RefPtr<PledgeSourceSet> p = mgr->mOutstandingPledges.Remove(id);
if (!p || !mgr->IsWindowStillActive(aWindowId)) {
return NS_OK;
}
MediaManager_AnonymizeDevices(*devices, aOriginKey);
p->Resolve(devices.release());
return NS_OK;
});
@@ -2980,16 +3053,17 @@ MediaManager::Shutdown()
prefs->RemoveObserver("media.navigator.audio.full_duplex", this);
}
// Close off any remaining active windows.
GetActiveWindows()->Clear();
mActiveCallbacks.Clear();
mCallIds.Clear();
mPendingGUMRequest.Clear();
+ mDeviceIDs.Clear();
#ifdef MOZ_WEBRTC
StopWebRtcLog();
#endif
// Because mMediaThread is not an nsThread, we must dispatch to it so it can
// clean up BackgroundChild. Continue stopping thread once this is done.
class ShutdownTask : public Runnable
@@ -3971,16 +4045,39 @@ GetUserMediaWindowListener::StopSharing(
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
for (auto& source : mActiveListeners) {
source->StopSharing();
}
}
void
+GetUserMediaWindowListener::StopRawID(const nsString& removedDeviceID)
+{
+ MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
+
+ for (auto& source : mActiveListeners) {
+ if (source->GetAudioDevice()) {
+ nsString id;
+ source->GetAudioDevice()->GetRawId(id);
+ if (removedDeviceID.Equals(id)) {
+ source->Stop();
+ }
+ }
+ if (source->GetVideoDevice()) {
+ nsString id;
+ source->GetVideoDevice()->GetRawId(id);
+ if (removedDeviceID.Equals(id)) {
+ source->Stop();
+ }
+ }
+ }
+}
+
+void
GetUserMediaWindowListener::NotifySourceTrackStopped()
{
MOZ_ASSERT(NS_IsMainThread());
// We wait until stable state before notifying chrome so chrome only does one
// update if more tracks are stopped in this event loop.
if (mChromeNotificationTaskPosted) {
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -329,16 +329,17 @@ private:
// ONLY accessed from MediaManagerThread
RefPtr<MediaEngine> mBackend;
static StaticRefPtr<MediaManager> sSingleton;
media::CoatCheck<PledgeSourceSet> mOutstandingPledges;
media::CoatCheck<PledgeChar> mOutstandingCharPledges;
media::CoatCheck<PledgeVoid> mOutstandingVoidPledges;
+ nsTArray<nsString> mDeviceIDs;
public:
media::CoatCheck<media::Pledge<nsCString>> mGetPrincipalKeyPledges;
RefPtr<media::Parent<media::NonE10s>> mNonE10sParent;
};
} // namespace mozilla
#endif // MOZILLA_MEDIAMANAGER_H