Backed out 4 changesets (bug 1436074, bug 1436694) for frequent asertion failures at MediaEngineWebRTCAudio.cpp CLOSED TREE
authorBrindusan Cristian <cbrindusan@mozilla.com>
Mon, 26 Feb 2018 17:54:18 +0200
changeset 405286 6e0b46a7a0a05b1e3f1d46147a46bbe37a7f55f5
parent 405285 ef89c3c179cd91f4a42c470957aa7544f34c4a4c
child 405287 8934930b9199c69ec3d89ff119efafda87fef38c
push id100200
push usercbrindusan@mozilla.com
push dateMon, 26 Feb 2018 15:54:32 +0000
treeherdermozilla-inbound@6e0b46a7a0a0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1436074, 1436694
milestone60.0a1
backs out1aff350b83b8d504dbaa3d5d8d4fe6cd99512786
f5cc71d38e4a7c0e4db830242c1d454fcbdb9e48
6c38cc382d2114199842dddb14097be8b6d9a961
ae428bfb913fb929da4d83864e58727177ccd655
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 4 changesets (bug 1436074, bug 1436694) for frequent asertion failures at MediaEngineWebRTCAudio.cpp CLOSED TREE Backed out changeset 1aff350b83b8 (bug 1436074) Backed out changeset f5cc71d38e4a (bug 1436694) Backed out changeset 6c38cc382d21 (bug 1436694) Backed out changeset ae428bfb913f (bug 1436694)
dom/media/MediaManager.cpp
dom/media/MediaManager.h
dom/media/webrtc/MediaEngineDefault.cpp
dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
dom/media/webrtc/MediaEngineSource.h
dom/media/webrtc/MediaEngineTabVideoSource.cpp
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -30,17 +30,16 @@
 #include "nsICryptoHash.h"
 #include "nsICryptoHMAC.h"
 #include "nsIKeyModule.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsIInputStream.h"
 #include "nsILineInputStream.h"
 #include "nsPIDOMWindow.h"
 #include "mozilla/EventStateManager.h"
-#include "mozilla/MozPromise.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Types.h"
 #include "mozilla/PeerIdentity.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MediaStreamBinding.h"
@@ -152,52 +151,49 @@ using media::Refcountable;
 
 static Atomic<bool> sHasShutdown;
 
 typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
 
 struct DeviceState {
   DeviceState(const RefPtr<MediaDevice>& aDevice, bool aOffWhileDisabled)
     : mOffWhileDisabled(aOffWhileDisabled)
+    , mDisableTimer(new MediaTimer())
     , mDevice(aDevice)
   {
     MOZ_ASSERT(mDevice);
   }
 
   // true if we have stopped mDevice, this is a terminal state.
   // MainThread only.
   bool mStopped = false;
 
   // true if mDevice is currently enabled, i.e., turned on and capturing.
   // MainThread only.
-  bool mDeviceEnabled = false;
+  bool mDeviceEnabled = true;
 
   // true if the application has currently enabled mDevice.
   // MainThread only.
-  bool mTrackEnabled = false;
-
-  // Time when the application last enabled mDevice.
-  // MainThread only.
-  TimeStamp mTrackEnabledTime;
+  bool mTrackEnabled = true;
 
   // true if an operation to Start() or Stop() mDevice has been dispatched to
   // the media thread and is not finished yet.
   // MainThread only.
   bool mOperationInProgress = false;
 
   // true if we are allowed to turn off the underlying source while all tracks
   // are disabled.
   // MainThread only.
   bool mOffWhileDisabled = false;
 
   // Timer triggered by a MediaStreamTrackSource signaling that all tracks got
   // disabled. When the timer fires we initiate Stop()ing mDevice.
   // If set we allow dynamically stopping and starting mDevice.
   // Any thread.
-  const RefPtr<MediaTimer> mDisableTimer = new MediaTimer();
+  const RefPtr<MediaTimer> mDisableTimer;
 
   // The underlying device we keep state for. Always non-null.
   // Threadsafe access, but see method declarations for individual constraints.
   const RefPtr<MediaDevice> mDevice;
 };
 
 /**
  * This mimics the capture state from nsIMediaManagerService.
@@ -238,18 +234,16 @@ FromCaptureState(CaptureState aState)
  * don't hold a reference to it during late shutdown.
  *
  * There's also a hard reference to the SourceListener through its
  * SourceStreamListener and the MediaStreamGraph. MediaStreamGraph
  * clears this on XPCOM_WILL_SHUTDOWN, before MediaManager enters shutdown.
  */
 class SourceListener : public SupportsWeakPtr<SourceListener> {
 public:
-  typedef MozPromise<bool /* aIgnored */, RefPtr<MediaMgrError>, true> InitPromise;
-
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(SourceListener)
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(SourceListener)
 
   SourceListener();
 
   /**
    * Registers this source listener as belonging to the given window listener.
    */
@@ -258,21 +252,16 @@ public:
   /**
    * Marks this listener as active and adds itself as a listener to aStream.
    */
   void Activate(SourceMediaStream* aStream,
                 MediaDevice* aAudioDevice,
                 MediaDevice* aVideoDevice);
 
   /**
-   * Posts a task to initialize and start all associated devices.
-   */
-  RefPtr<InitPromise> InitializeAsync();
-
-  /**
    * Stops all live tracks, finishes the associated MediaStream and cleans up.
    */
   void Stop();
 
   /**
    * Removes this SourceListener from its associated MediaStream and marks it
    * removed. Also removes the weak reference to the associated window listener.
    */
@@ -1380,42 +1369,99 @@ public:
                                       mOnSuccess,
                                       mWindowID,
                                       domStream))));
 
     // Dispatch to the media thread to ask it to start the sources,
     // because that can take a while.
     // Pass ownership of domStream through the lambda to the nested chrome
     // notification lambda to ensure it's kept alive until that lambda runs or is discarded.
-    mSourceListener->InitializeAsync()->Then(
-      GetMainThreadSerialEventTarget(), __func__,
-      [manager = mManager, domStream, callback,
-       windowListener = mWindowListener]()
-      {
-        // Initiating and starting devices succeeded.
-        // onTracksAvailableCallback must be added to domStream on main thread.
-        domStream->OnTracksAvailable(callback->release());
-        windowListener->ChromeAffectingStateChanged();
-        manager->SendPendingGUMRequest();
-      },[manager = mManager, windowID = mWindowID,
-         onFailure = Move(mOnFailure)](const RefPtr<MediaMgrError>& error)
-      {
-        // Initiating and starting devices failed.
-
-        // Only run if the window is still active for our window listener.
-        if (!(manager->IsWindowStillActive(windowID))) {
+    RefPtr<GetUserMediaStreamRunnable> self = this;
+    MediaManager::PostTask(NewTaskFrom([self, domStream, callback]() mutable {
+      MOZ_ASSERT(MediaManager::IsInMediaThread());
+      RefPtr<SourceMediaStream> source =
+        self->mSourceListener->GetSourceStream();
+
+      RefPtr<MediaMgrError> error = nullptr;
+      if (self->mAudioDevice) {
+        nsresult rv = self->mAudioDevice->SetTrack(source,
+                                                   kAudioTrack,
+                                                   self->mSourceListener->GetPrincipalHandle());
+        if (NS_SUCCEEDED(rv)) {
+          rv = self->mAudioDevice->Start();
+        } else {
+          nsString log;
+          if (rv == NS_ERROR_NOT_AVAILABLE) {
+            log.AssignASCII("Concurrent mic process limit.");
+            error = new MediaMgrError(NS_LITERAL_STRING("NotReadableError"), log);
+          } else {
+            log.AssignASCII("Starting audio failed");
+            error = new MediaMgrError(NS_LITERAL_STRING("InternalError"), log);
+          }
+        }
+      }
+
+      if (!error && self->mVideoDevice) {
+        nsresult rv = self->mVideoDevice->SetTrack(source,
+                                                   kVideoTrack,
+                                                   self->mSourceListener->GetPrincipalHandle());
+        if (NS_SUCCEEDED(rv)) {
+          rv = self->mVideoDevice->Start();
+        }
+        if (NS_FAILED(rv)) {
+          nsString log;
+          log.AssignASCII("Starting video failed");
+          error = new MediaMgrError(NS_LITERAL_STRING("InternalError"), log);
+        }
+      }
+
+      if (error) {
+        // Dispatch the error callback on main thread.
+        NS_DispatchToMainThread(MakeAndAddRef<ErrorCallbackRunnable>(
+          self->mOnFailure, *error, self->mWindowID));
+        return NS_OK;
+      }
+
+      // Start() queued the tracks to be added synchronously to avoid races
+      source->FinishAddTracks();
+
+      source->AdvanceKnownTracksTime(STREAM_TIME_MAX);
+
+      LOG(("started all sources"));
+
+      // onTracksAvailableCallback must be added to domStream on the main thread.
+      uint64_t windowID = self->mWindowID;
+      NS_DispatchToMainThread(NS_NewRunnableFunction("MediaManager::NotifyChromeOfStart",
+                                                     [source, domStream, callback, windowID]() mutable {
+        source->SetPullEnabled(true);
+
+        MediaManager* manager = MediaManager::GetIfExists();
+        if (!manager) {
           return;
         }
-        // This is safe since we're on main-thread, and the windowlist can only
-        // be invalidated from the main-thread (see OnNavigation)
-        if (auto* window = nsGlobalWindowInner::GetInnerWindowWithId(windowID)) {
-          auto streamError = MakeRefPtr<MediaStreamError>(window->AsInner(), *error);
-          onFailure->OnError(streamError);
+
+        nsGlobalWindowInner* window =
+          nsGlobalWindowInner::GetInnerWindowWithId(windowID);
+        if (!window) {
+          MOZ_ASSERT_UNREACHABLE("Should have window");
+          return;
         }
-      });
+
+        domStream->OnTracksAvailable(callback->release());
+
+        nsresult rv = MediaManager::NotifyRecordingStatusChange(window->AsInner());
+        if (NS_FAILED(rv)) {
+          MOZ_ASSERT_UNREACHABLE("Should be able to notify chrome");
+          return;
+        }
+
+        manager->SendPendingGUMRequest();
+      }));
+      return NS_OK;
+    }));
 
     if (!IsPincipalInfoPrivate(mPrincipalInfo)) {
       // Call GetPrincipalKey again, this time w/persist = true, to promote
       // deviceIds to persistent, in case they're not already. Fire'n'forget.
       RefPtr<Pledge<nsCString>> p =
         media::GetPrincipalKey(mPrincipalInfo, true);
     }
     return NS_OK;
@@ -2098,30 +2144,16 @@ MediaManager::PostTask(already_AddRefed<
     MOZ_CRASH();
     return;
   }
   NS_ASSERTION(Get(), "MediaManager singleton?");
   NS_ASSERTION(Get()->mMediaThread, "No thread yet");
   Get()->mMediaThread->message_loop()->PostTask(Move(task));
 }
 
-template<typename MozPromiseType, typename FunctionType>
-/* static */ RefPtr<MozPromiseType>
-MediaManager::PostTask(const char* aName, FunctionType&& aFunction)
-{
-  MozPromiseHolder<MozPromiseType> holder;
-  RefPtr<MozPromiseType> promise = holder.Ensure(aName);
-  MediaManager::PostTask(NS_NewRunnableFunction(aName,
-        [h = Move(holder), func = Forward<FunctionType>(aFunction)]() mutable
-        {
-          func(h);
-        }));
-  return promise;
-}
-
 /* static */ nsresult
 MediaManager::NotifyRecordingStatusChange(nsPIDOMWindowInner* aWindow)
 {
   NS_ENSURE_ARG(aWindow);
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (!obs) {
     NS_WARNING("Could not get the Observer service for GetUserMedia recording notification.");
@@ -3823,120 +3855,16 @@ SourceListener::Activate(SourceMediaStre
           aVideoDevice,
           aVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera &&
           Preferences::GetBool("media.getusermedia.camera.off_while_disabled.enabled", true));
   }
 
   mStream->AddListener(mStreamListener);
 }
 
-RefPtr<SourceListener::InitPromise>
-SourceListener::InitializeAsync()
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
-  MOZ_DIAGNOSTIC_ASSERT(!mStopped);
-
-  RefPtr<InitPromise> init = MediaManager::PostTask<InitPromise>(__func__,
-    [ stream = mStream
-    , principal = GetPrincipalHandle()
-    , audioDevice = mAudioDeviceState ? mAudioDeviceState->mDevice : nullptr
-    , videoDevice = mVideoDeviceState ? mVideoDeviceState->mDevice : nullptr
-    ](MozPromiseHolder<InitPromise>& aHolder)
-    {
-      if (audioDevice) {
-        nsresult rv = audioDevice->SetTrack(stream, kAudioTrack, principal);
-        if (NS_SUCCEEDED(rv)) {
-          rv = audioDevice->Start();
-        }
-        if (NS_FAILED(rv)) {
-          nsString log;
-          if (rv == NS_ERROR_NOT_AVAILABLE) {
-            log.AssignASCII("Concurrent mic process limit.");
-            aHolder.Reject(MakeRefPtr<MediaMgrError>(
-                  NS_LITERAL_STRING("NotReadableError"), log), __func__);
-            return;
-          }
-          log.AssignASCII("Starting audio failed");
-          aHolder.Reject(MakeRefPtr<MediaMgrError>(
-                NS_LITERAL_STRING("InternalError"), log), __func__);
-          return;
-        }
-      }
-
-      if (videoDevice) {
-        nsresult rv = videoDevice->SetTrack(stream, kVideoTrack, principal);
-        if (NS_SUCCEEDED(rv)) {
-          rv = videoDevice->Start();
-        }
-        if (NS_FAILED(rv)) {
-          if (audioDevice) {
-            if (NS_WARN_IF(NS_FAILED(audioDevice->Stop()))) {
-              MOZ_ASSERT_UNREACHABLE("Stopping audio failed");
-            }
-          }
-          nsString log;
-          log.AssignASCII("Starting video failed");
-          aHolder.Reject(MakeRefPtr<MediaMgrError>(NS_LITERAL_STRING("InternalError"), log), __func__);
-          return;
-        }
-      }
-
-      // Start() queued the tracks to be added synchronously to avoid races
-      stream->FinishAddTracks();
-      stream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
-      LOG(("started all sources"));
-
-      aHolder.Resolve(true, __func__);
-    });
-
-  return init->Then(GetMainThreadSerialEventTarget(), __func__,
-    [self = RefPtr<SourceListener>(this), this]()
-    {
-      if (mStopped) {
-        // We were shut down during the async init
-        return InitPromise::CreateAndResolve(true, __func__);
-      }
-
-      mStream->SetPullEnabled(true);
-
-      for (DeviceState* state : {mAudioDeviceState.get(),
-                                 mVideoDeviceState.get()}) {
-        if (!state) {
-          continue;
-        }
-        MOZ_DIAGNOSTIC_ASSERT(!state->mTrackEnabled);
-        MOZ_DIAGNOSTIC_ASSERT(!state->mDeviceEnabled);
-        MOZ_DIAGNOSTIC_ASSERT(!state->mStopped);
-
-        state->mDeviceEnabled = true;
-        state->mTrackEnabled = true;
-        state->mTrackEnabledTime = TimeStamp::Now();
-      }
-      return InitPromise::CreateAndResolve(true, __func__);
-    }, [self = RefPtr<SourceListener>(this), this](RefPtr<MediaMgrError>&& aResult)
-    {
-      if (mStopped) {
-        return InitPromise::CreateAndReject(Move(aResult), __func__);
-      }
-
-      for (DeviceState* state : {mAudioDeviceState.get(),
-                                 mVideoDeviceState.get()}) {
-        if (!state) {
-          continue;
-        }
-        MOZ_DIAGNOSTIC_ASSERT(!state->mTrackEnabled);
-        MOZ_DIAGNOSTIC_ASSERT(!state->mDeviceEnabled);
-        MOZ_DIAGNOSTIC_ASSERT(!state->mStopped);
-
-        state->mStopped = true;
-      }
-      return InitPromise::CreateAndReject(Move(aResult), __func__);
-    });
-}
-
 void
 SourceListener::Stop()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
 
   if (mStopped) {
     return;
   }
@@ -4059,16 +3987,17 @@ SourceListener::SetEnabledFor(TrackID aT
 
   state.mTrackEnabled = aEnable;
 
   if (state.mStopped) {
     // Device terminally stopped. Updating device state is pointless.
     return;
   }
 
+
   if (state.mOperationInProgress) {
     // If a timer is in progress, it needs to be canceled now so the next
     // DisableTrack() gets a fresh start. Canceling will trigger another
     // operation.
     state.mDisableTimer->Cancel();
     return;
   }
 
@@ -4079,36 +4008,30 @@ SourceListener::SetEnabledFor(TrackID aT
 
   // All paths from here on must end in setting `state.mOperationInProgress`
   // to false.
   state.mOperationInProgress = true;
 
   RefPtr<MediaTimerPromise> timerPromise;
   if (aEnable) {
     timerPromise = MediaTimerPromise::CreateAndResolve(true, __func__);
-    state.mTrackEnabledTime = TimeStamp::Now();
   } else {
-    const TimeDuration maxDelay = TimeDuration::FromMilliseconds(
+    const TimeDuration offDelay = TimeDuration::FromMilliseconds(
       Preferences::GetUint(
         aTrackID == kAudioTrack
           ? "media.getusermedia.microphone.off_while_disabled.delay_ms"
           : "media.getusermedia.camera.off_while_disabled.delay_ms",
         3000));
-    const TimeDuration durationEnabled =
-      TimeStamp::Now() - state.mTrackEnabledTime;
-    const TimeDuration delay =
-      TimeDuration::Max(TimeDuration::FromMilliseconds(0),
-                        maxDelay - durationEnabled);
-    timerPromise = state.mDisableTimer->WaitFor(delay, __func__);
+    timerPromise = state.mDisableTimer->WaitFor(offDelay, __func__);
   }
 
   typedef MozPromise<nsresult, bool, /* IsExclusive = */ true> DeviceOperationPromise;
   RefPtr<SourceListener> self = this;
   timerPromise->Then(GetMainThreadSerialEventTarget(), __func__,
-    [self, this, &state, aTrackID, aEnable]() mutable {
+    [self, this, &state, aTrackID, aEnable](bool aDummy) mutable {
       MOZ_ASSERT(state.mDeviceEnabled != aEnable,
                  "Device operation hasn't started");
       MOZ_ASSERT(state.mOperationInProgress,
                  "It's our responsibility to reset the inProgress state");
 
       LOG(("SourceListener %p %s %s track %d - starting device operation",
            this, aEnable ? "enabling" : "disabling",
            aTrackID == kAudioTrack ? "audio" : "video",
@@ -4122,22 +4045,25 @@ SourceListener::SetEnabledFor(TrackID aT
 
       if (!state.mOffWhileDisabled) {
         // If the feature to turn a device off while disabled is itself disabled
         // we shortcut the device operation and tell the ux-updating code
         // that everything went fine.
         return DeviceOperationPromise::CreateAndResolve(NS_OK, __func__);
       }
 
-      return MediaManager::PostTask<DeviceOperationPromise>(__func__,
-          [self, device = state.mDevice, aEnable]
-          (MozPromiseHolder<DeviceOperationPromise>& h) {
-            h.Resolve(aEnable ? device->Start() : device->Stop(), __func__);
-          });
-    }, []() {
+      RefPtr<DeviceOperationPromise::Private> promise =
+        new DeviceOperationPromise::Private(__func__);
+      MediaManager::PostTask(NewTaskFrom([self, device = state.mDevice,
+                                          aEnable, promise]() mutable {
+        promise->Resolve(aEnable ? device->Start() : device->Stop(), __func__);
+      }));
+      RefPtr<DeviceOperationPromise> result = promise.get();
+      return result;
+    }, [](bool aDummy) {
       // Timer was canceled by us. We signal this with NS_ERROR_ABORT.
       return DeviceOperationPromise::CreateAndResolve(NS_ERROR_ABORT, __func__);
     })->Then(GetMainThreadSerialEventTarget(), __func__,
     [self, this, &state, aTrackID, aEnable](nsresult aResult) mutable {
       MOZ_ASSERT(state.mOperationInProgress);
       state.mOperationInProgress = false;
 
       if (state.mStopped) {
@@ -4187,17 +4113,17 @@ SourceListener::SetEnabledFor(TrackID aT
       }
 
       // Track state changed during this operation. We'll start over.
       if (state.mTrackEnabled) {
         SetEnabledFor(aTrackID, true);
       } else {
         SetEnabledFor(aTrackID, false);
       }
-    }, []() {
+    }, [](bool aDummy) {
       MOZ_ASSERT_UNREACHABLE("Unexpected and unhandled reject");
     });
 }
 
 void
 SourceListener::StopSharing()
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -143,28 +143,16 @@ public:
 
   // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
   // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
   // from MediaManager thread.
   static MediaManager* Get();
   static MediaManager* GetIfExists();
   static void StartupInit();
   static void PostTask(already_AddRefed<Runnable> task);
-
-  /**
-   * Posts an async operation to the media manager thread.
-   * FunctionType must be a function that takes a `MozPromiseHolder&`.
-   *
-   * The returned promise is resolved or rejected by aFunction on the media
-   * manager thread.
-   */
-  template<typename MozPromiseType, typename FunctionType>
-  static RefPtr<MozPromiseType>
-  PostTask(const char* aName, FunctionType&& aFunction);
-
 #ifdef DEBUG
   static bool IsInMediaThread();
 #endif
 
   static bool Exists()
   {
     return !!sSingleton;
   }
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -233,20 +233,16 @@ MediaEngineDefaultVideoSource::Start(con
   return NS_OK;
 }
 
 nsresult
 MediaEngineDefaultVideoSource::Stop(const RefPtr<const AllocationHandle>& aHandle)
 {
   AssertIsOnOwningThread();
 
-  if (mState == kStopped || mState == kAllocated) {
-    return NS_OK;
-  }
-
   MOZ_ASSERT(mState == kStarted);
   MOZ_ASSERT(mTimer);
   MOZ_ASSERT(mStream);
   MOZ_ASSERT(IsTrackIDExplicit(mTrackID));
 
   mTimer->Cancel();
   mTimer = nullptr;
 
@@ -497,21 +493,18 @@ MediaEngineDefaultAudioSource::Start(con
   return NS_OK;
 }
 
 nsresult
 MediaEngineDefaultAudioSource::Stop(const RefPtr<const AllocationHandle>& aHandle)
 {
   AssertIsOnOwningThread();
 
-  if (mState == kStopped || mState == kAllocated) {
-    return NS_OK;
-  }
+  MOZ_ASSERT(mState == kStarted);
 
-  MOZ_ASSERT(mState == kStarted);
 
   MutexAutoLock lock(mMutex);
   mState = kStopped;
   return NS_OK;
 }
 
 nsresult
 MediaEngineDefaultAudioSource::Reconfigure(
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -319,20 +319,16 @@ MediaEngineRemoteVideoSource::Start(cons
 }
 
 nsresult
 MediaEngineRemoteVideoSource::Stop(const RefPtr<const AllocationHandle>& aHandle)
 {
   LOG((__PRETTY_FUNCTION__));
   AssertIsOnOwningThread();
 
-  if (mState == kStopped || mState == kAllocated) {
-    return NS_OK;
-  }
-
   MOZ_ASSERT(mState == kStarted);
 
   if (camera::GetChildAndCall(&camera::CamerasChild::StopCapture,
                               mCapEngine, mCaptureIndex)) {
     MOZ_DIAGNOSTIC_ASSERT(false, "Stopping a started capture failed");
   }
 
   {
--- a/dom/media/webrtc/MediaEngineSource.h
+++ b/dom/media/webrtc/MediaEngineSource.h
@@ -166,19 +166,16 @@ public:
                                const char** aOutBadConstraint) = 0;
 
   /**
    * Called by MediaEngine to stop feeding data to the track associated with
    * the given AllocationHandle.
    *
    * If this was the last AllocationHandle that had been started,
    * the underlying device will be stopped.
-   *
-   * Double-stopping a given allocation handle is allowed and will return NS_OK.
-   * This is necessary sometimes during shutdown.
    */
   virtual nsresult Stop(const RefPtr<const AllocationHandle>& aHandle) = 0;
 
   /**
    * Called by MediaEngine to deallocate a handle to this source.
    *
    * If this was the last registered AllocationHandle, the underlying device
    * will be deallocated.
--- a/dom/media/webrtc/MediaEngineTabVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -397,21 +397,16 @@ MediaEngineTabVideoSource::Draw() {
   mImage = image;
   mImageSize = size;
 }
 
 nsresult
 MediaEngineTabVideoSource::Stop(const RefPtr<const AllocationHandle>& aHandle)
 {
   AssertIsOnOwningThread();
-
-  if (mState == kStopped || mState == kAllocated) {
-    return NS_OK;
-  }
-
   MOZ_ASSERT(mState == kStarted);
 
   // If mBlackedoutWindow is true, we may be running
   // despite mWindow == nullptr.
   if (!mWindow && !mBlackedoutWindow) {
     return NS_OK;
   }