Bug 1070216 - Properly manage lifetime of allocated CaptureDevices. r=jib a=lizzard
☠☠ backed out by 4e86a0d1261a ☠ ☠
authorAndreas Pehrson <pehrsons@gmail.com>
Wed, 14 Oct 2015 11:27:15 +0800
changeset 296675 3705005d2190
parent 296674 c458ad434a12
child 296676 4e86a0d1261a
push id5281
push userkwierso@gmail.com
push date2015-11-09 17:50 +0000
treeherdermozilla-beta@3705005d2190 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjib, lizzard
bugs1070216
milestone43.0
Bug 1070216 - Properly manage lifetime of allocated CaptureDevices. r=jib a=lizzard We currently avoid Deallocating a CaptureDevice used for multiple gUMStreams when one of them calls Deallocate() by keeping track of how many called Start(). The normal lifetime sequence however, is: Allocate() Start() Stop() Deallocate() This patches fixes the lifetime management by keeping track of how many users of the CaptureDevice called Allocate().
dom/media/webrtc/MediaEngineCameraVideoSource.h
dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
dom/media/webrtc/MediaEngineWebRTC.h
dom/media/webrtc/MediaEngineWebRTCAudio.cpp
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.h
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.h
@@ -23,16 +23,17 @@ 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;
@@ -110,16 +111,17 @@ 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,17 +92,22 @@ MediaEngineRemoteVideoSource::Shutdown()
 
 nsresult
 MediaEngineRemoteVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
                                        const MediaEnginePrefs& aPrefs,
                                        const nsString& aDeviceId)
 {
   LOG((__PRETTY_FUNCTION__));
 
-  if (mState == kReleased && mInitDone) {
+  if (!mInitDone) {
+    LOG(("Init not done"));
+    return NS_ERROR_FAILURE;
+  }
+
+  if (mState == kReleased) {
     // 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,
@@ -116,29 +121,30 @@ 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((__FUNCTION__));
-  bool empty;
-  {
-    MonitorAutoLock lock(mMonitor);
-    empty = mSources.IsEmpty();
-  }
-  if (empty) {
+  LOG((__PRETTY_FUNCTION__));
+
+  --mNrAllocations;
+  MOZ_ASSERT(mNrAllocations >= 0, "Double-deallocations are prohibited");
+
+  if (mNrAllocations == 0) {
     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,16 +130,17 @@ 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) {
@@ -209,20 +210,21 @@ 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; // When this goes empty, we shut down HW
+  nsTArray<nsRefPtr<SourceMediaStream>> mSources;
   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,28 +301,26 @@ 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()
 {
-  bool empty;
-  {
-    MonitorAutoLock lock(mMonitor);
-    empty = mSources.IsEmpty();
-  }
-  if (empty) {
+  --mNrAllocations;
+  MOZ_ASSERT(mNrAllocations >= 0, "Double-deallocations are prohibited");
+  if (mNrAllocations == 0) {
     // 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 {