Bug 1122387: Update locking logic for getUserMedia video r=roc a=abillings
authorRandell Jesup <rjesup@jesup.org>
Fri, 16 Jan 2015 16:27:56 -0500
changeset 243082 0043397aaa19
parent 243081 0e2a17da6dd9
child 243088 fac8edb83df7
push id4385
push userrjesup@wgate.com
push date2015-01-28 20:42 +0000
treeherdermozilla-beta@0043397aaa19 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, abillings
bugs1122387
milestone36.0
Bug 1122387: Update locking logic for getUserMedia video r=roc a=abillings
dom/media/webrtc/MediaEngineCameraVideoSource.h
dom/media/webrtc/MediaEngineGonkVideoSource.cpp
dom/media/webrtc/MediaEngineWebRTCAudio.cpp
dom/media/webrtc/MediaEngineWebRTCVideo.cpp
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.h
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.h
@@ -75,26 +75,28 @@ protected:
   static bool Intersect(dom::ConstrainLongRange& aA, const dom::ConstrainLongRange& aB);
   void GuessCapability(const VideoTrackConstraintsN& aConstraints,
                        const MediaEnginePrefs& aPrefs);
 
   // Engine variables.
 
   // mMonitor protects mImage access/changes, and transitions of mState
   // from kStarted to kStopped (which are combined with EndTrack() and
-  // image changes).  Note that mSources is not accessed from other threads
-  // for video and is not protected.
+  // image changes).
+  // mMonitor also protects mSources[] access/changes.
+  // mSources[] is accessed from webrtc threads.
+
   // All the mMonitor accesses are from the child classes.
   Monitor mMonitor; // Monitor for processing Camera frames.
+  nsTArray<SourceMediaStream*> mSources; // When this goes empty, we shut down HW
   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
 
-  nsTArray<SourceMediaStream*> mSources; // When this goes empty, we shut down HW
 
   bool mInitDone;
   bool mHasDirectListeners;
   int mCaptureIndex;
   TrackID mTrackID;
   int mFps; // Track rate (30 fps by default)
 
   webrtc::CaptureCapability mCapability; // Doesn't work on OS X.
--- a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
@@ -102,17 +102,22 @@ MediaEngineGonkVideoSource::Allocate(con
 
   return NS_OK;
 }
 
 nsresult
 MediaEngineGonkVideoSource::Deallocate()
 {
   LOG((__FUNCTION__));
-  if (mSources.IsEmpty()) {
+  bool empty;
+  {
+    MonitorAutoLock lock(mMonitor);
+    empty = mSources.IsEmpty();
+  }
+  if (empty) {
 
     ReentrantMonitorAutoEnter sync(mCallbackMonitor);
 
     if (mState != kStopped && mState != kAllocated) {
       return NS_ERROR_FAILURE;
     }
 
     // We do not register success callback here
@@ -135,17 +140,20 @@ MediaEngineGonkVideoSource::Deallocate()
 nsresult
 MediaEngineGonkVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
 {
   LOG((__FUNCTION__));
   if (!mInitDone || !aStream) {
     return NS_ERROR_FAILURE;
   }
 
-  mSources.AppendElement(aStream);
+  {
+    MonitorAutoLock lock(mMonitor);
+    mSources.AppendElement(aStream);
+  }
 
   aStream->AddTrack(aID, 0, new VideoSegment());
   aStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
 
   ReentrantMonitorAutoEnter sync(mCallbackMonitor);
 
   if (mState == kStarted) {
     return NS_OK;
@@ -163,22 +171,26 @@ MediaEngineGonkVideoSource::Start(Source
 
   return NS_OK;
 }
 
 nsresult
 MediaEngineGonkVideoSource::Stop(SourceMediaStream* aSource, TrackID aID)
 {
   LOG((__FUNCTION__));
-  if (!mSources.RemoveElement(aSource)) {
-    // Already stopped - this is allowed
-    return NS_OK;
-  }
-  if (!mSources.IsEmpty()) {
-    return NS_OK;
+  {
+    MonitorAutoLock lock(mMonitor);
+
+    if (!mSources.RemoveElement(aSource)) {
+      // Already stopped - this is allowed
+      return NS_OK;
+    }
+    if (!mSources.IsEmpty()) {
+      return NS_OK;
+    }
   }
 
   ReentrantMonitorAutoEnter sync(mCallbackMonitor);
 
   if (mState != kStarted) {
     return NS_ERROR_FAILURE;
   }
 
@@ -219,18 +231,29 @@ MediaEngineGonkVideoSource::Shutdown()
   LOG((__FUNCTION__));
   if (!mInitDone) {
     return;
   }
 
   ReentrantMonitorAutoEnter sync(mCallbackMonitor);
 
   if (mState == kStarted) {
-    while (!mSources.IsEmpty()) {
-      Stop(mSources[0], kVideoTrack); // XXX change to support multiple tracks
+    SourceMediaStream *source;
+    bool empty;
+
+    while (1) {
+      {
+        MonitorAutoLock lock(mMonitor);
+        empty = mSources.IsEmpty();
+        if (empty) {
+          break;
+        }
+        source = mSources[0];
+      }
+      Stop(source, kVideoTrack); // XXX change to support multiple tracks
     }
     MOZ_ASSERT(mState == kStopped);
   }
 
   if (mState == kAllocated || mState == kStopped) {
     Deallocate();
   }
 
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -275,28 +275,39 @@ MediaEngineWebRTCAudioSource::Allocate(c
         return NS_ERROR_FAILURE;
       }
       mState = kAllocated;
       LOG(("Audio device %d allocated", mCapIndex));
     } else {
       LOG(("Audio device is not initalized"));
       return NS_ERROR_FAILURE;
     }
-  } else if (mSources.IsEmpty()) {
-    LOG(("Audio device %d reallocated", mCapIndex));
   } else {
-    LOG(("Audio device %d allocated shared", mCapIndex));
+#ifdef PR_LOGGING
+    MonitorAutoLock lock(mMonitor);
+    if (mSources.IsEmpty()) {
+      LOG(("Audio device %d reallocated", mCapIndex));
+    } else {
+      LOG(("Audio device %d allocated shared", mCapIndex));
+    }
+#endif
   }
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCAudioSource::Deallocate()
 {
-  if (mSources.IsEmpty()) {
+  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 {
     LOG(("Audio device %d deallocated but still in use", mCapIndex));
@@ -483,18 +494,29 @@ MediaEngineWebRTCAudioSource::Shutdown()
       mVoENetwork->DeRegisterExternalTransport(mChannel);
     }
 
     delete mNullTransport;
     return;
   }
 
   if (mState == kStarted) {
-    while (!mSources.IsEmpty()) {
-      Stop(mSources[0], kAudioTrack); // XXX change to support multiple tracks
+    SourceMediaStream *source;
+    bool empty;
+
+    while (1) {
+      {
+        MonitorAutoLock lock(mMonitor);
+        empty = mSources.IsEmpty();
+        if (empty) {
+          break;
+        }
+        source = mSources[0];
+      }
+      Stop(source, kAudioTrack); // XXX change to support multiple tracks
     }
     MOZ_ASSERT(mState == kStopped);
   }
 
   if (mState == kAllocated || mState == kStopped) {
     Deallocate();
   }
 
--- a/dom/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -333,30 +333,41 @@ MediaEngineWebRTCVideoSource::Allocate(c
     ChooseCapability(aConstraints, aPrefs);
 
     if (mViECapture->AllocateCaptureDevice(NS_ConvertUTF16toUTF8(mUniqueId).get(),
                                            kMaxUniqueIdLength, mCaptureIndex)) {
       return NS_ERROR_FAILURE;
     }
     mState = kAllocated;
     LOG(("Video device %d allocated", mCaptureIndex));
-  } else if (mSources.IsEmpty()) {
-    LOG(("Video device %d reallocated", mCaptureIndex));
   } else {
-    LOG(("Video device %d allocated shared", mCaptureIndex));
+#ifdef PR_LOGGING
+    MonitorAutoLock lock(mMonitor);
+    if (mSources.IsEmpty()) {
+      LOG(("Video device %d reallocated", mCaptureIndex));
+    } else {
+      LOG(("Video device %d allocated shared", mCaptureIndex));
+    }
+#endif
   }
 
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCVideoSource::Deallocate()
 {
   LOG((__FUNCTION__));
-  if (mSources.IsEmpty()) {
+  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;
     }
 #ifdef XP_MACOSX
     // Bug 829907 - on mac, in shutdown, the mainthread stops processing
     // 'native' events, and the QTKit code uses events to the main native CFRunLoop
     // in order to provide thread safety.  In order to avoid this locking us up,
     // release the ViE capture device synchronously on MainThread (so the native
@@ -387,17 +398,20 @@ nsresult
 MediaEngineWebRTCVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
 {
   LOG((__FUNCTION__));
   int error = 0;
   if (!mInitDone || !aStream) {
     return NS_ERROR_FAILURE;
   }
 
-  mSources.AppendElement(aStream);
+  {
+    MonitorAutoLock lock(mMonitor);
+    mSources.AppendElement(aStream);
+  }
 
   aStream->AddTrack(aID, 0, new VideoSegment());
   aStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
 
   if (mState == kStarted) {
     return NS_OK;
   }
   mImageContainer = layers::LayerManager::CreateImageContainer();
@@ -421,32 +435,33 @@ MediaEngineWebRTCVideoSource::Start(Sour
 
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCVideoSource::Stop(SourceMediaStream *aSource, TrackID aID)
 {
   LOG((__FUNCTION__));
-  if (!mSources.RemoveElement(aSource)) {
-    // Already stopped - this is allowed
-    return NS_OK;
-  }
-
-  aSource->EndTrack(aID);
-
-  if (!mSources.IsEmpty()) {
-    return NS_OK;
-  }
-  if (mState != kStarted) {
-    return NS_ERROR_FAILURE;
-  }
-
   {
     MonitorAutoLock lock(mMonitor);
+
+    if (!mSources.RemoveElement(aSource)) {
+      // Already stopped - this is allowed
+      return NS_OK;
+    }
+
+    aSource->EndTrack(aID);
+
+    if (!mSources.IsEmpty()) {
+      return NS_OK;
+    }
+    if (mState != kStarted) {
+      return NS_ERROR_FAILURE;
+    }
+
     mState = kStopped;
     // Drop any cached image so we don't start with a stale image on next
     // usage
     mImage = nullptr;
   }
   mViERender->StopRender(mCaptureIndex);
   mViERender->RemoveRenderer(mCaptureIndex);
   mViECapture->StopCapture(mCaptureIndex);
@@ -496,18 +511,29 @@ MediaEngineWebRTCVideoSource::Init()
 void
 MediaEngineWebRTCVideoSource::Shutdown()
 {
   LOG((__FUNCTION__));
   if (!mInitDone) {
     return;
   }
   if (mState == kStarted) {
-    while (!mSources.IsEmpty()) {
-      Stop(mSources[0], kVideoTrack); // XXX change to support multiple tracks
+    SourceMediaStream *source;
+    bool empty;
+
+    while (1) {
+      {
+        MonitorAutoLock lock(mMonitor);
+        empty = mSources.IsEmpty();
+        if (empty) {
+          break;
+        }
+        source = mSources[0];
+      }
+      Stop(source, kVideoTrack); // XXX change to support multiple tracks
     }
     MOZ_ASSERT(mState == kStopped);
   }
 
   if (mState == kAllocated || mState == kStopped) {
     Deallocate();
   }
   mViECapture->Release();