Backed out 4 changesets (bug 1210852, bug 1103188, bug 1070216) for various crashes with mediamanager.cpp assertions a=backout
authorWes Kocher <wkocher@mozilla.com>
Mon, 09 Nov 2015 11:39:46 -0800
changeset 296676 4e86a0d1261a
parent 296675 3705005d2190
child 296679 aa0762e80b9f
push id5282
push userkwierso@gmail.com
push date2015-11-09 19:39 +0000
treeherdermozilla-beta@4e86a0d1261a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1210852, 1103188, 1070216
milestone43.0
backs out3705005d2190
c458ad434a12
1babaebeccc6
b199ea9f265c
Backed out 4 changesets (bug 1210852, bug 1103188, bug 1070216) for various crashes with mediamanager.cpp assertions a=backout Backed out changeset 3705005d2190 (bug 1070216) Backed out changeset c458ad434a12 (bug 1210852) Backed out changeset 1babaebeccc6 (bug 1103188) Backed out changeset b199ea9f265c (bug 1103188)
dom/media/MediaManager.cpp
dom/media/MediaManager.h
dom/media/systemservices/MediaUtils.h
dom/media/webrtc/MediaEngineCameraVideoSource.h
dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
dom/media/webrtc/MediaEngineWebRTC.h
dom/media/webrtc/MediaEngineWebRTCAudio.cpp
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1148,82 +1148,56 @@ GetSources(MediaEngine *engine, dom::Med
     }
   } else {
     for (auto& source : sources) {
       aResult.AppendElement(new DeviceType(source));
     }
   }
 }
 
-// TODO: Remove once upgraded to GCC 4.8+ on linux. Bogus error on static func:
-// error: 'this' was not captured for this lambda function
-
-static auto& MediaManager_GetInstance = MediaManager::GetInstance;
-static auto& MediaManager_ToJSArray = MediaManager::ToJSArray;
-static auto& MediaManager_AnonymizeDevices = MediaManager::AnonymizeDevices;
-
-already_AddRefed<MediaManager::PledgeChar>
-MediaManager::SelectSettings(
-    MediaStreamConstraints& aConstraints,
-    nsRefPtr<Refcountable<ScopedDeletePtr<SourceSet>>>& aSources)
+static const char*
+SelectSettings(MediaStreamConstraints &aConstraints,
+               nsTArray<nsRefPtr<MediaDevice>>& aSources)
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  nsRefPtr<PledgeChar> p = new PledgeChar();
-  uint32_t id = mOutstandingCharPledges.Append(*p);
-
-  // Algorithm accesses device capabilities code and must run on media thread.
-  // Modifies passed-in aSources.
-
-  MediaManager::PostTask(FROM_HERE, NewTaskFrom([id, aConstraints,
-                                                 aSources]() mutable {
-    auto& sources = **aSources;
-
-    // Since the advanced part of the constraints algorithm needs to know when
-    // a candidate set is overconstrained (zero members), we must split up the
-    // list into videos and audios, and put it back together again at the end.
-
-    nsTArray<nsRefPtr<VideoDevice>> videos;
-    nsTArray<nsRefPtr<AudioDevice>> audios;
-
-    for (auto& source : sources) {
-      if (source->mIsVideo) {
-        nsRefPtr<VideoDevice> video = static_cast<VideoDevice*>(source.get());
-        videos.AppendElement(video);
-      } else {
-        nsRefPtr<AudioDevice> audio = static_cast<AudioDevice*>(source.get());
-        audios.AppendElement(audio);
-      }
+  // Since the advanced part of the constraints algorithm needs to know when
+  // a candidate set is overconstrained (zero members), we must split up the
+  // list into videos and audios, and put it back together again at the end.
+
+  nsTArray<nsRefPtr<VideoDevice>> videos;
+  nsTArray<nsRefPtr<AudioDevice>> audios;
+
+  for (auto& source : aSources) {
+    if (source->mIsVideo) {
+      nsRefPtr<VideoDevice> video = static_cast<VideoDevice*>(source.get());
+      videos.AppendElement(video);
+    } else {
+      nsRefPtr<AudioDevice> audio = static_cast<AudioDevice*>(source.get());
+      audios.AppendElement(audio);
     }
-    sources.Clear();
-    const char* badConstraint = nullptr;
-
-    if (IsOn(aConstraints.mVideo)) {
-      badConstraint = MediaConstraintsHelper::SelectSettings(
-          GetInvariant(aConstraints.mVideo), videos);
-      for (auto& video : videos) {
-        sources.AppendElement(video);
-      }
+  }
+  aSources.Clear();
+  MOZ_ASSERT(!aSources.Length());
+
+  const char* badConstraint = nullptr;
+
+  if (IsOn(aConstraints.mVideo)) {
+    badConstraint = MediaConstraintsHelper::SelectSettings(
+        GetInvariant(aConstraints.mVideo), videos);
+    for (auto& video : videos) {
+      aSources.AppendElement(video);
     }
-    if (audios.Length() && IsOn(aConstraints.mAudio)) {
-      badConstraint = MediaConstraintsHelper::SelectSettings(
-          GetInvariant(aConstraints.mAudio), audios);
-      for (auto& audio : audios) {
-        sources.AppendElement(audio);
-      }
+  }
+  if (audios.Length() && IsOn(aConstraints.mAudio)) {
+    badConstraint = MediaConstraintsHelper::SelectSettings(
+        GetInvariant(aConstraints.mAudio), audios);
+    for (auto& audio : audios) {
+      aSources.AppendElement(audio);
     }
-    NS_DispatchToMainThread(do_AddRef(NewRunnableFrom([id, badConstraint]() mutable {
-      nsRefPtr<MediaManager> mgr = MediaManager_GetInstance();
-      nsRefPtr<PledgeChar> p = mgr->mOutstandingCharPledges.Remove(id);
-      if (p) {
-        p->Resolve(badConstraint);
-      }
-      return NS_OK;
-    })));
-  }));
-  return p.forget();
+  }
+  return badConstraint;
 }
 
 /**
  * Runs on a seperate thread and is responsible for enumerating devices.
  * Depending on whether a picture or stream was asked for, either
  * ProcessGetUserMedia is called, and the results are sent back to the DOM.
  *
  * Do not run this on the main thread. The success and error callbacks *MUST*
@@ -1414,16 +1388,23 @@ public:
     return NS_OK;
   }
 
 private:
   nsAutoPtr<GetUserMediaTask> mTask;
 };
 #endif
 
+// TODO: Remove once upgraded to GCC 4.8+ on linux. Bogus error on static func:
+// error: 'this' was not captured for this lambda function
+
+static auto& MediaManager_GetInstance = MediaManager::GetInstance;
+static auto& MediaManager_ToJSArray = MediaManager::ToJSArray;
+static auto& MediaManager_AnonymizeDevices = MediaManager::AnonymizeDevices;
+
 /**
  * EnumerateRawDevices - Enumerate a list of audio & video devices that
  * satisfy passed-in constraints. List contains raw id's.
  */
 
 already_AddRefed<MediaManager::PledgeSourceSet>
 MediaManager::EnumerateRawDevices(uint64_t aWindowId,
                                   MediaSourceEnum aVideoType,
@@ -2053,105 +2034,90 @@ MediaManager::GetUserMedia(nsPIDOMWindow
   bool askPermission = !privileged &&
       (!fake || Preferences::GetBool("media.navigator.permission.fake"));
 
   nsRefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowID, videoType,
                                                      audioType, fake,
                                                      fakeTracks);
   p->Then([this, onSuccess, onFailure, windowID, c, listener, askPermission,
            prefs, isHTTPS, callID, origin](SourceSet*& aDevices) mutable {
-
-    nsRefPtr<Refcountable<ScopedDeletePtr<SourceSet>>> devices(
-         new Refcountable<ScopedDeletePtr<SourceSet>>(aDevices)); // grab result
-
-    // Ensure that the captured 'this' pointer and our windowID are still good.
-    if (!MediaManager::Exists() ||
-        !nsGlobalWindow::GetInnerWindowWithId(windowID)) {
+    ScopedDeletePtr<SourceSet> devices(aDevices); // grab result
+
+    // Ensure this pointer is still valid, and window is still alive.
+    nsRefPtr<MediaManager> mgr = MediaManager::GetInstance();
+    nsRefPtr<nsPIDOMWindow> window = static_cast<nsPIDOMWindow*>
+        (nsGlobalWindow::GetInnerWindowWithId(windowID));
+    if (!mgr || !window) {
       return;
     }
 
-    // Apply any constraints. This modifies the passed-in list.
-    nsRefPtr<PledgeChar> p2 = SelectSettings(c, devices);
-
-    p2->Then([this, onSuccess, onFailure, windowID, c,
-              listener, askPermission, prefs, isHTTPS,
-              callID, origin, devices](const char*& badConstraint) mutable {
-
-      // Ensure that the captured 'this' pointer and our windowID are still good.
-      nsRefPtr<nsPIDOMWindow> window = static_cast<nsPIDOMWindow*>
-          (nsGlobalWindow::GetInnerWindowWithId(windowID));
-      if (!MediaManager::Exists() || !window) {
+    // Apply any constraints. This modifies the list.
+    const char* badConstraint = SelectSettings(c, *devices);
+    if (badConstraint) {
+      nsString constraint;
+      constraint.AssignASCII(badConstraint);
+      nsRefPtr<MediaStreamError> error =
+          new MediaStreamError(window,
+                               NS_LITERAL_STRING("OverconstrainedError"),
+                               NS_LITERAL_STRING(""),
+                               constraint);
+      onFailure->OnError(error);
+      return;
+    }
+    if (!devices->Length()) {
+      nsRefPtr<MediaStreamError> error =
+          new MediaStreamError(window, NS_LITERAL_STRING("NotFoundError"));
+      onFailure->OnError(error);
+      return;
+    }
+
+    nsCOMPtr<nsISupportsArray> devicesCopy; // before we give up devices below
+    if (!askPermission) {
+      nsresult rv = NS_NewISupportsArray(getter_AddRefs(devicesCopy));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
         return;
       }
-
-      if (badConstraint) {
-        nsString constraint;
-        constraint.AssignASCII(badConstraint);
-        nsRefPtr<MediaStreamError> error =
-            new MediaStreamError(window,
-                                 NS_LITERAL_STRING("OverconstrainedError"),
-                                 NS_LITERAL_STRING(""),
-                                 constraint);
-        onFailure->OnError(error);
-        return;
-      }
-      if (!(*devices)->Length()) {
-        nsRefPtr<MediaStreamError> error =
-            new MediaStreamError(window, NS_LITERAL_STRING("NotFoundError"));
-        onFailure->OnError(error);
-        return;
-      }
-
-      nsCOMPtr<nsISupportsArray> devicesCopy; // before we give up devices below
-      if (!askPermission) {
-        nsresult rv = NS_NewISupportsArray(getter_AddRefs(devicesCopy));
+      for (auto& device : *devices) {
+        rv = devicesCopy->AppendElement(device);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return;
         }
-        for (auto& device : **devices) {
-          rv = devicesCopy->AppendElement(device);
-          if (NS_WARN_IF(NS_FAILED(rv))) {
-            return;
-          }
-        }
       }
-
-      // Pass callbacks and MediaStreamListener along to GetUserMediaTask.
-      nsAutoPtr<GetUserMediaTask> task (new GetUserMediaTask(c, onSuccess.forget(),
-                                                             onFailure.forget(),
-                                                             windowID, listener,
-                                                             prefs, origin,
-                                                             devices->forget()));
-      // Store the task w/callbacks.
-      mActiveCallbacks.Put(callID, task.forget());
-
-      // Add a WindowID cross-reference so OnNavigation can tear things down
-      nsTArray<nsString>* array;
-      if (!mCallIds.Get(windowID, &array)) {
-        array = new nsTArray<nsString>();
-        mCallIds.Put(windowID, array);
-      }
-      array->AppendElement(callID);
-
-      nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-      if (!askPermission) {
-        obs->NotifyObservers(devicesCopy, "getUserMedia:privileged:allow",
-                             callID.BeginReading());
-      } else {
-        nsRefPtr<GetUserMediaRequest> req =
-            new GetUserMediaRequest(window, callID, c, isHTTPS);
-        obs->NotifyObservers(req, "getUserMedia:request", nullptr);
-      }
+    }
+
+    // Pass callbacks and MediaStreamListener along to GetUserMediaTask.
+    nsAutoPtr<GetUserMediaTask> task (new GetUserMediaTask(c, onSuccess.forget(),
+                                                           onFailure.forget(),
+                                                           windowID, listener,
+                                                           prefs, origin,
+                                                           devices.forget()));
+    // Store the task w/callbacks.
+    mActiveCallbacks.Put(callID, task.forget());
+
+    // Add a WindowID cross-reference so OnNavigation can tear things down
+    nsTArray<nsString>* array;
+    if (!mCallIds.Get(windowID, &array)) {
+      array = new nsTArray<nsString>();
+      mCallIds.Put(windowID, array);
+    }
+    array->AppendElement(callID);
+
+    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+    if (!askPermission) {
+      obs->NotifyObservers(devicesCopy, "getUserMedia:privileged:allow",
+                           callID.BeginReading());
+    } else {
+      nsRefPtr<GetUserMediaRequest> req =
+          new GetUserMediaRequest(window, callID, c, isHTTPS);
+      obs->NotifyObservers(req, "getUserMedia:request", nullptr);
+    }
 
 #ifdef MOZ_WEBRTC
-      EnableWebRtcLog();
+    EnableWebRtcLog();
 #endif
-    }, [onFailure](MediaStreamError*& reason) mutable {
-      onFailure->OnError(reason);
-    });
   }, [onFailure](MediaStreamError*& reason) mutable {
     onFailure->OnError(reason);
   });
   return NS_OK;
 }
 
 /* static */ void
 MediaManager::AnonymizeDevices(SourceSet& aDevices, const nsACString& aOriginKey)
@@ -3048,68 +3014,47 @@ GetUserMediaCallbackMediaStreamListener:
     MediaManager::PostTask(FROM_HERE,
       NewRunnableMethod(mAudioDevice->GetSource(), &MediaEngineSource::Config,
                         aEchoOn, aEcho, aAgcOn, aAGC, aNoiseOn,
                         aNoise, aPlayoutDelay));
 #endif
   }
 }
 
+// Can be invoked from EITHER MainThread or MSG thread
 void
 GetUserMediaCallbackMediaStreamListener::Invalidate()
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
-  if (mStopped) {
-    return;
-  }
-  mStopped = true;
-
-  AudioDevice* audioDevice = nullptr;
-  VideoDevice* videoDevice = nullptr;
-  if (!mAudioStopped) {
-    audioDevice = mAudioDevice.get();
-  }
-  if (!mVideoStopped) {
-    videoDevice = mVideoDevice.get();
-  }
-
   // We can't take a chance on blocking here, so proxy this to another
   // thread.
   // Pass a ref to us (which is threadsafe) so it can query us for the
   // source stream info.
   MediaManager::PostTask(FROM_HERE,
     new MediaOperationTask(MEDIA_STOP,
                            this, nullptr, nullptr,
-                           audioDevice, videoDevice,
+                           mAudioDevice, mVideoDevice,
                            mFinished, mWindowID, nullptr));
-  mAudioStopped = !!audioDevice;
-  mVideoStopped = !!videoDevice;
 }
 
 // Doesn't kill audio
 // XXX refactor to combine with Invalidate()?
 void
 GetUserMediaCallbackMediaStreamListener::StopSharing()
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
+  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   if (mVideoDevice && !mStopped &&
       (mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen ||
        mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application ||
        mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window)) {
     // Stop the whole stream if there's no audio; just the video track if we have both
-    if (!mAudioDevice) {
-      Invalidate();
-    } else if (!mVideoStopped) {
-      MediaManager::PostTask(FROM_HERE,
-        new MediaOperationTask(MEDIA_STOP_TRACK,
-                               this, nullptr, nullptr,
-                               nullptr, mVideoDevice,
-                               mFinished, mWindowID, nullptr));
-      mVideoStopped = true;
-    }
+    MediaManager::PostTask(FROM_HERE,
+      new MediaOperationTask(mAudioDevice ? MEDIA_STOP_TRACK : MEDIA_STOP,
+                             this, nullptr, nullptr,
+                             nullptr, mVideoDevice,
+                             mFinished, mWindowID, nullptr));
   } else if (mAudioDevice &&
              mAudioDevice->GetMediaSource() == dom::MediaSourceEnum::AudioCapture) {
     nsCOMPtr<nsPIDOMWindow> window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
     MOZ_ASSERT(window);
     window->SetAudioCapture(false);
     MediaStreamGraph* graph =
       MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER,
                                     dom::AudioChannel::Normal);
@@ -3217,25 +3162,22 @@ GetUserMediaCallbackMediaStreamListener:
 void
 GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aTrackID, bool aIsAudio)
 {
   if (((aIsAudio && mAudioDevice) ||
        (!aIsAudio && mVideoDevice)) && !mStopped)
   {
     // XXX to support multiple tracks of a type in a stream, this should key off
     // the TrackID and not just the type
-    AudioDevice* audioDevice = aIsAudio  && !mAudioStopped ? mAudioDevice.get() : nullptr;
-    VideoDevice* videoDevice = !aIsAudio && !mVideoStopped ? mVideoDevice.get() : nullptr;
     MediaManager::PostTask(FROM_HERE,
       new MediaOperationTask(MEDIA_STOP_TRACK,
                              this, nullptr, nullptr,
-                             audioDevice, videoDevice,
+                             aIsAudio  ? mAudioDevice.get() : nullptr,
+                             !aIsAudio ? mVideoDevice.get() : nullptr,
                              mFinished, mWindowID, nullptr));
-    mAudioStopped = !!audioDevice;
-    mVideoStopped = !!videoDevice;
   } else {
     LOG(("gUM track %d ended, but we don't have type %s",
          aTrackID, aIsAudio ? "audio" : "video"));
   }
 }
 
 // Called from the MediaStreamGraph thread
 void
@@ -3286,16 +3228,21 @@ GetUserMediaNotificationEvent::Run()
 
   nsString msg;
   switch (mStatus) {
   case STARTING:
     msg = NS_LITERAL_STRING("starting");
     stream->OnTracksAvailable(mOnTracksAvailableCallback.forget());
     break;
   case STOPPING:
+    msg = NS_LITERAL_STRING("shutdown");
+    if (mListener) {
+      mListener->SetStopped();
+    }
+    break;
   case STOPPED_TRACK:
     msg = NS_LITERAL_STRING("shutdown");
     break;
   }
 
   nsCOMPtr<nsPIDOMWindow> window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
 
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -122,18 +122,16 @@ class GetUserMediaCallbackMediaStreamLis
 {
 public:
   // Create in an inactive state
   GetUserMediaCallbackMediaStreamListener(base::Thread *aThread,
     uint64_t aWindowID)
     : mMediaThread(aThread)
     , mWindowID(aWindowID)
     , mStopped(false)
-    , mAudioStopped(false)
-    , mVideoStopped(false)
     , mFinished(false)
     , mLock("mozilla::GUMCMSL")
     , mRemoved(false) {}
 
   ~GetUserMediaCallbackMediaStreamListener()
   {
     unused << mMediaThread;
     // It's OK to release mStream on any thread; they have thread-safe
@@ -217,16 +215,21 @@ public:
   bool CapturingBrowser()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     return mVideoDevice && !mStopped &&
            mVideoDevice->GetSource()->IsAvailable() &&
            mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser;
   }
 
+  void SetStopped()
+  {
+    mStopped = true;
+  }
+
   // implement in .cpp to avoid circular dependency with MediaOperationTask
   // Can be invoked from EITHER MainThread or MSG thread
   void Invalidate();
 
   void
   AudioConfig(bool aEchoOn, uint32_t aEcho,
               bool aAgcOn, uint32_t aAGC,
               bool aNoiseOn, uint32_t aNoise,
@@ -298,24 +301,16 @@ public:
 
 private:
   // Set at construction
   base::Thread* mMediaThread;
   uint64_t mWindowID;
 
   bool mStopped; // MainThread only
 
-  // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mAudioDevice.
-  // MainThread only.
-  bool mAudioStopped;
-
-  // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mAudioDevice.
-  // MainThread only.
-  bool mVideoStopped;
-
   // Set at Activate on MainThread
 
   // Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
   // No locking needed as they're only addrefed except on the MediaManager thread
   nsRefPtr<AudioDevice> mAudioDevice; // threadsafe refcnt
   nsRefPtr<VideoDevice> mVideoDevice; // threadsafe refcnt
   nsRefPtr<SourceMediaStream> mStream; // threadsafe refcnt
   bool mFinished;
@@ -464,17 +459,16 @@ public:
   bool IsActivelyCapturingOrHasAPermission(uint64_t aWindowId);
 
   MediaEnginePrefs mPrefs;
 
   typedef nsTArray<nsRefPtr<MediaDevice>> SourceSet;
   static bool IsPrivateBrowsing(nsPIDOMWindow *window);
 private:
   typedef media::Pledge<SourceSet*, dom::MediaStreamError*> PledgeSourceSet;
-  typedef media::Pledge<const char*, dom::MediaStreamError*> PledgeChar;
 
   static bool IsPrivileged();
   static bool IsLoop(nsIURI* aDocURI);
   static nsresult GenerateUUID(nsAString& aResult);
   static nsresult AnonymizeId(nsAString& aId, const nsACString& aOriginKey);
 public: // TODO: make private once we upgrade to GCC 4.8+ on linux.
   static void AnonymizeDevices(SourceSet& aDevices, const nsACString& aOriginKey);
   static already_AddRefed<nsIWritableVariant> ToJSArray(SourceSet& aDevices);
@@ -484,20 +478,16 @@ private:
                       dom::MediaSourceEnum aVideoType,
                       dom::MediaSourceEnum aAudioType,
                       bool aFake, bool aFakeTracks);
   already_AddRefed<PledgeSourceSet>
   EnumerateDevicesImpl(uint64_t aWindowId,
                        dom::MediaSourceEnum aVideoSrcType,
                        dom::MediaSourceEnum aAudioSrcType,
                        bool aFake = false, bool aFakeTracks = false);
-  already_AddRefed<PledgeChar>
-  SelectSettings(
-      dom::MediaStreamConstraints& aConstraints,
-      nsRefPtr<media::Refcountable<ScopedDeletePtr<SourceSet>>>& aSources);
 
   StreamListeners* AddWindowID(uint64_t aWindowId);
   WindowTable *GetActiveWindows() {
     NS_ASSERTION(NS_IsMainThread(), "Only access windowlist on main thread");
     return &mActiveWindows;
   }
 
   void GetPref(nsIPrefBranch *aBranch, const char *aPref,
@@ -528,17 +518,16 @@ private:
 
   Mutex mMutex;
   // protected with mMutex:
   RefPtr<MediaEngine> mBackend;
 
   static StaticRefPtr<MediaManager> sSingleton;
 
   media::CoatCheck<PledgeSourceSet> mOutstandingPledges;
-  media::CoatCheck<PledgeChar> mOutstandingCharPledges;
   media::CoatCheck<GetUserMediaCallbackMediaStreamListener::PledgeVoid> mOutstandingVoidPledges;
 #if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   nsRefPtr<nsDOMCameraManager> mCameraManager;
 #endif
 public:
   media::CoatCheck<media::Pledge<nsCString>> mGetOriginKeyPledges;
   ScopedDeletePtr<media::Parent<media::NonE10s>> mNonE10sParent;
 };
--- a/dom/media/systemservices/MediaUtils.h
+++ b/dom/media/systemservices/MediaUtils.h
@@ -317,47 +317,12 @@ private:
   static uint32_t GetNextId()
   {
     static uint32_t counter = 0;
     return ++counter;
   };
   nsAutoTArray<Element, 3> mElements;
 };
 
-/* media::Refcountable - Add threadsafe ref-counting to something that isn't.
- *
- * Often, reference counting is the most practical way to share an object with
- * another thread without imposing lifetime restrictions, even if there's
- * otherwise no concurrent access happening on the object.  For instance, an
- * algorithm on another thread may find it more expedient to modify a passed-in
- * object, rather than pass expensive copies back and forth.
- *
- * Lists in particular often aren't ref-countable, yet are expensive to copy,
- * e.g. nsTArray<nsRefPtr<Foo>>. Refcountable can be used to make such objects
- * (or owning smart-pointers to such objects) refcountable.
- *
- * Technical limitation: A template specialization is needed for types that take
- * a constructor. Please add below (ScopedDeletePtr covers a lot of ground though).
- */
-
-template<typename T>
-class Refcountable : public T
-{
-public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Refcountable<T>)
-private:
-  ~Refcountable<T>() {}
-};
-
-template<typename T>
-class Refcountable<ScopedDeletePtr<T>> : public ScopedDeletePtr<T>
-{
-public:
-  explicit Refcountable<ScopedDeletePtr<T>>(T* aPtr) : ScopedDeletePtr<T>(aPtr) {}
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Refcountable<T>)
-private:
-  ~Refcountable<ScopedDeletePtr<T>>() {}
-};
-
 } // namespace media
 } // namespace mozilla
 
 #endif // mozilla_MediaUtils_h
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.h
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.h
@@ -23,17 +23,16 @@ public:
   explicit MediaEngineCameraVideoSource(int aIndex,
                                         const char* aMonitorName = "Camera.Monitor")
     : MediaEngineVideoSource(kReleased)
     , mMonitor(aMonitorName)
     , mWidth(0)
     , mHeight(0)
     , mInitDone(false)
     , mHasDirectListeners(false)
-    , mNrAllocations(0)
     , mCaptureIndex(aIndex)
     , mTrackID(0)
   {}
 
 
   virtual void GetName(nsAString& aName) override;
   virtual void GetUUID(nsACString& aUUID) override;
   virtual void SetDirectListeners(bool aHasListeners) override;
@@ -111,17 +110,16 @@ static void LogCapability(const char* aH
   nsRefPtr<layers::Image> mImage;
   nsRefPtr<layers::ImageContainer> mImageContainer;
   int mWidth, mHeight; // protected with mMonitor on Gonk due to different threading
   // end of data protected by mMonitor
 
 
   bool mInitDone;
   bool mHasDirectListeners;
-  int mNrAllocations; // When this becomes 0, we shut down HW
   int mCaptureIndex;
   TrackID mTrackID;
 
   webrtc::CaptureCapability mCapability; // Doesn't work on OS X.
 
   nsTArray<webrtc::CaptureCapability> mHardcodedCapabilities; // For OSX & B2G
 private:
   nsString mDeviceName;
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -92,22 +92,17 @@ MediaEngineRemoteVideoSource::Shutdown()
 
 nsresult
 MediaEngineRemoteVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
                                        const MediaEnginePrefs& aPrefs,
                                        const nsString& aDeviceId)
 {
   LOG((__PRETTY_FUNCTION__));
 
-  if (!mInitDone) {
-    LOG(("Init not done"));
-    return NS_ERROR_FAILURE;
-  }
-
-  if (mState == kReleased) {
+  if (mState == kReleased && mInitDone) {
     // Note: if shared, we don't allow a later opener to affect the resolution.
     // (This may change depending on spec changes for Constraints/settings)
 
     if (!ChooseCapability(aConstraints, aPrefs, aDeviceId)) {
       return NS_ERROR_UNEXPECTED;
     }
 
     if (mozilla::camera::AllocateCaptureDevice(mCapEngine,
@@ -121,30 +116,29 @@ MediaEngineRemoteVideoSource::Allocate(c
     MonitorAutoLock lock(mMonitor);
     if (mSources.IsEmpty()) {
       LOG(("Video device %d reallocated", mCaptureIndex));
     } else {
       LOG(("Video device %d allocated shared", mCaptureIndex));
     }
   }
 
-  ++mNrAllocations;
-
   return NS_OK;
 }
 
 nsresult
 MediaEngineRemoteVideoSource::Deallocate()
 {
-  LOG((__PRETTY_FUNCTION__));
-
-  --mNrAllocations;
-  MOZ_ASSERT(mNrAllocations >= 0, "Double-deallocations are prohibited");
-
-  if (mNrAllocations == 0) {
+  LOG((__FUNCTION__));
+  bool empty;
+  {
+    MonitorAutoLock lock(mMonitor);
+    empty = mSources.IsEmpty();
+  }
+  if (empty) {
     if (mState != kStopped && mState != kAllocated) {
       return NS_ERROR_FAILURE;
     }
     mozilla::camera::ReleaseCaptureDevice(mCapEngine, mCaptureIndex);
     mState = kReleased;
     LOG(("Video device %d deallocated", mCaptureIndex));
   } else {
     LOG(("Video device %d deallocated but still in use", mCaptureIndex));
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -130,17 +130,16 @@ public:
                                     const char* name,
                                     const char* uuid)
     : MediaEngineAudioSource(kReleased)
     , mVoiceEngine(aVoiceEnginePtr)
     , mMonitor("WebRTCMic.Monitor")
     , mThread(aThread)
     , mCapIndex(aIndex)
     , mChannel(-1)
-    , mNrAllocations(0)
     , mInitDone(false)
     , mStarted(false)
     , mEchoOn(false), mAgcOn(false), mNoiseOn(false)
     , mEchoCancel(webrtc::kEcDefault)
     , mAGC(webrtc::kAgcDefault)
     , mNoiseSuppress(webrtc::kNsDefault)
     , mPlayoutDelay(0)
     , mNullTransport(nullptr) {
@@ -210,21 +209,20 @@ private:
   ScopedCustomReleasePtr<webrtc::VoEExternalMedia> mVoERender;
   ScopedCustomReleasePtr<webrtc::VoENetwork> mVoENetwork;
   ScopedCustomReleasePtr<webrtc::VoEAudioProcessing> mVoEProcessing;
 
   // mMonitor protects mSources[] access/changes, and transitions of mState
   // from kStarted to kStopped (which are combined with EndTrack()).
   // mSources[] is accessed from webrtc threads.
   Monitor mMonitor;
-  nsTArray<nsRefPtr<SourceMediaStream>> mSources;
+  nsTArray<nsRefPtr<SourceMediaStream>> mSources; // When this goes empty, we shut down HW
   nsCOMPtr<nsIThread> mThread;
   int mCapIndex;
   int mChannel;
-  int mNrAllocations; // When this becomes 0, we shut down HW
   TrackID mTrackID;
   bool mInitDone;
   bool mStarted;
 
   nsString mDeviceName;
   nsCString mDeviceUUID;
 
   bool mEchoOn, mAgcOn, mNoiseOn;
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -301,26 +301,28 @@ MediaEngineWebRTCMicrophoneSource::Alloc
   } else if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) {
     MonitorAutoLock lock(mMonitor);
     if (mSources.IsEmpty()) {
       LOG(("Audio device %d reallocated", mCapIndex));
     } else {
       LOG(("Audio device %d allocated shared", mCapIndex));
     }
   }
-  ++mNrAllocations;
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCMicrophoneSource::Deallocate()
 {
-  --mNrAllocations;
-  MOZ_ASSERT(mNrAllocations >= 0, "Double-deallocations are prohibited");
-  if (mNrAllocations == 0) {
+  bool empty;
+  {
+    MonitorAutoLock lock(mMonitor);
+    empty = mSources.IsEmpty();
+  }
+  if (empty) {
     // If empty, no callbacks to deliver data should be occuring
     if (mState != kStopped && mState != kAllocated) {
       return NS_ERROR_FAILURE;
     }
 
     mState = kReleased;
     LOG(("Audio device %d deallocated", mCapIndex));
   } else {