Bug 1040563 - Move WaitForData/NotifyGotData from MediaSource to MediaSourceDecoder, which is off-main-thread friendly. r=cajbir
authorMatthew Gregan <kinetik@flim.org>
Mon, 25 Aug 2014 16:09:44 +1200
changeset 201267 2c94c5407b1c
parent 201266 f277da7fa9fa
child 201268 d67d68ac4119
push id48139
push usermgregan@mozilla.com
push dateMon, 25 Aug 2014 04:10:24 +0000
treeherdermozilla-inbound@2c94c5407b1c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscajbir
bugs1040563
milestone34.0a1
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
Bug 1040563 - Move WaitForData/NotifyGotData from MediaSource to MediaSourceDecoder, which is off-main-thread friendly. r=cajbir Also remove mMediaSource from MediaSourceReader and rejig SetMediaSourceDuration to go via MediaSourceDecoder and handle mMediaSource being null.
content/media/mediasource/MediaSource.cpp
content/media/mediasource/MediaSource.h
content/media/mediasource/MediaSourceDecoder.cpp
content/media/mediasource/MediaSourceDecoder.h
content/media/mediasource/MediaSourceReader.cpp
content/media/mediasource/MediaSourceReader.h
content/media/mediasource/SourceBuffer.cpp
--- a/content/media/mediasource/MediaSource.cpp
+++ b/content/media/mediasource/MediaSource.cpp
@@ -372,17 +372,16 @@ MediaSource::GetBuffered(TimeRanges* aBu
   MSE_DEBUG("MediaSource(%p)::GetBuffered ranges=%s", this, DumpTimeRanges(intersectionRanges).get());
 }
 
 MediaSource::MediaSource(nsPIDOMWindow* aWindow)
   : DOMEventTargetHelper(aWindow)
   , mDuration(UnspecifiedNaN<double>())
   , mDecoder(nullptr)
   , mReadyState(MediaSourceReadyState::Closed)
-  , mWaitForDataMonitor("MediaSource.WaitForData.Monitor")
 {
   MOZ_ASSERT(NS_IsMainThread());
   mSourceBuffers = new SourceBufferList(this);
   mActiveSourceBuffers = new SourceBufferList(this);
   MSE_API("MediaSource(%p)::MediaSource(aWindow=%p) mSourceBuffers=%p mActiveSourceBuffers=%p",
           this, aWindow, mSourceBuffers.get(), mActiveSourceBuffers.get());
 }
 
@@ -460,32 +459,16 @@ MediaSource::NotifyEvicted(double aStart
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("MediaSource(%p)::NotifyEvicted(aStart=%f, aEnd=%f)", this, aStart, aEnd);
   // Cycle through all SourceBuffers and tell them to evict data in
   // the given range.
   mSourceBuffers->Evict(aStart, aEnd);
 }
 
-void
-MediaSource::WaitForData()
-{
-  MSE_DEBUG("MediaSource(%p)::WaitForData()", this);
-  MonitorAutoLock mon(mWaitForDataMonitor);
-  mon.Wait();
-}
-
-void
-MediaSource::NotifyGotData()
-{
-  MSE_DEBUG("MediaSource(%p)::NotifyGotData()", this);
-  MonitorAutoLock mon(mWaitForDataMonitor);
-  mon.NotifyAll();
-}
-
 nsPIDOMWindow*
 MediaSource::GetParentObject() const
 {
   return GetOwner();
 }
 
 JSObject*
 MediaSource::WrapObject(JSContext* aCx)
--- a/content/media/mediasource/MediaSource.h
+++ b/content/media/mediasource/MediaSource.h
@@ -8,17 +8,16 @@
 #define mozilla_dom_MediaSource_h_
 
 #include "MediaSourceDecoder.h"
 #include "js/RootingAPI.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/MediaSourceBinding.h"
-#include "mozilla/Monitor.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionNoteChild.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsID.h"
 #include "nsISupports.h"
 #include "nscore.h"
 
 struct JSContext;
@@ -86,22 +85,16 @@ public:
     return mDecoder;
   }
 
   // Called by SourceBuffers to notify this MediaSource that data has
   // been evicted from the buffered data. The start and end times
   // that were evicted are provided.
   void NotifyEvicted(double aStart, double aEnd);
 
-  // Block thread waiting for data to be appended to a SourceBuffer.
-  void WaitForData();
-
-  // Unblock threads waiting for data to be appended to a SourceBuffer.
-  void NotifyGotData();
-
 private:
   ~MediaSource();
 
   explicit MediaSource(nsPIDOMWindow* aWindow);
 
   friend class AsyncEventRunner<MediaSource>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
@@ -111,20 +104,16 @@ private:
   double mDuration;
 
   nsRefPtr<SourceBufferList> mSourceBuffers;
   nsRefPtr<SourceBufferList> mActiveSourceBuffers;
 
   nsRefPtr<MediaSourceDecoder> mDecoder;
 
   MediaSourceReadyState mReadyState;
-
-  // Monitor for waiting for when new data is appended to
-  // a Source Buffer.
-  Monitor mWaitForDataMonitor;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(MediaSource, MOZILLA_DOM_MEDIASOURCE_IMPLEMENTATION_IID)
 
 } // namespace dom
 
 } // namespace mozilla
 #endif /* mozilla_dom_MediaSource_h_ */
--- a/content/media/mediasource/MediaSourceDecoder.cpp
+++ b/content/media/mediasource/MediaSourceDecoder.cpp
@@ -42,17 +42,17 @@ MediaSourceDecoder::Clone()
 {
   // TODO: Sort out cloning.
   return nullptr;
 }
 
 MediaDecoderStateMachine*
 MediaSourceDecoder::CreateStateMachine()
 {
-  mReader = new MediaSourceReader(this, mMediaSource);
+  mReader = new MediaSourceReader(this);
   return new MediaDecoderStateMachine(this, mReader);
 }
 
 nsresult
 MediaSourceDecoder::Load(nsIStreamListener**, MediaDecoder*)
 {
   MOZ_ASSERT(!mDecoderStateMachine);
   mDecoderStateMachine = CreateStateMachine();
@@ -67,50 +67,92 @@ MediaSourceDecoder::Load(nsIStreamListen
   SetStateMachineParameters();
 
   return NS_OK;
 }
 
 nsresult
 MediaSourceDecoder::GetSeekable(dom::TimeRanges* aSeekable)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!mMediaSource) {
+    return NS_ERROR_FAILURE;
+  }
   double duration = mMediaSource->Duration();
   if (IsNaN(duration)) {
     // Return empty range.
   } else if (duration > 0 && mozilla::IsInfinite(duration)) {
     nsRefPtr<dom::TimeRanges> bufferedRanges = new dom::TimeRanges();
     mMediaSource->GetBuffered(bufferedRanges);
     aSeekable->Add(bufferedRanges->GetStartTime(), bufferedRanges->GetEndTime());
   } else {
     aSeekable->Add(0, duration);
   }
   MSE_DEBUG("MediaSourceDecoder(%p)::GetSeekable ranges=%s", this, DumpTimeRanges(aSeekable).get());
   return NS_OK;
 }
 
+void
+MediaSourceDecoder::Shutdown()
+{
+  MediaDecoder::Shutdown();
+
+  // Kick WaitForData out of its slumber.
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  mon.NotifyAll();
+}
+
 /*static*/
 already_AddRefed<MediaResource>
 MediaSourceDecoder::CreateResource()
 {
   return nsRefPtr<MediaResource>(new MediaSourceResource()).forget();
 }
 
 void
 MediaSourceDecoder::AttachMediaSource(dom::MediaSource* aMediaSource)
 {
-  MOZ_ASSERT(!mMediaSource && !mDecoderStateMachine);
+  MOZ_ASSERT(!mMediaSource && !mDecoderStateMachine && NS_IsMainThread());
   mMediaSource = aMediaSource;
 }
 
 void
 MediaSourceDecoder::DetachMediaSource()
 {
+  MOZ_ASSERT(mMediaSource && NS_IsMainThread());
   mMediaSource = nullptr;
 }
 
 already_AddRefed<SourceBufferDecoder>
 MediaSourceDecoder::CreateSubDecoder(const nsACString& aType)
 {
   MOZ_ASSERT(mReader);
   return mReader->CreateSubDecoder(aType);
 }
 
+void
+MediaSourceDecoder::SetMediaSourceDuration(double aDuration)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!mMediaSource) {
+    return;
+  }
+  ErrorResult dummy;
+  mMediaSource->SetDuration(aDuration, dummy);
+}
+
+void
+MediaSourceDecoder::WaitForData()
+{
+  MSE_DEBUG("MediaSourceDecoder(%p)::WaitForData()", this);
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  mon.Wait();
+}
+
+void
+MediaSourceDecoder::NotifyGotData()
+{
+  MSE_DEBUG("MediaSourceDecoder(%p)::NotifyGotData()", this);
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  mon.NotifyAll();
+}
+
 } // namespace mozilla
--- a/content/media/mediasource/MediaSourceDecoder.h
+++ b/content/media/mediasource/MediaSourceDecoder.h
@@ -33,23 +33,33 @@ class MediaSourceDecoder : public MediaD
 public:
   MediaSourceDecoder(dom::HTMLMediaElement* aElement);
 
   virtual MediaDecoder* Clone() MOZ_OVERRIDE;
   virtual MediaDecoderStateMachine* CreateStateMachine() MOZ_OVERRIDE;
   virtual nsresult Load(nsIStreamListener**, MediaDecoder*) MOZ_OVERRIDE;
   virtual nsresult GetSeekable(dom::TimeRanges* aSeekable) MOZ_OVERRIDE;
 
+  virtual void Shutdown() MOZ_OVERRIDE;
+
   static already_AddRefed<MediaResource> CreateResource();
 
   void AttachMediaSource(dom::MediaSource* aMediaSource);
   void DetachMediaSource();
 
   already_AddRefed<SourceBufferDecoder> CreateSubDecoder(const nsACString& aType);
 
+  void SetMediaSourceDuration(double aDuration);
+
+  // Provide a mechanism for MediaSourceReader to block waiting on data from a SourceBuffer.
+  void WaitForData();
+
+  // Called whenever a SourceBuffer has new data appended.
+  void NotifyGotData();
+
 private:
   // The owning MediaSource holds a strong reference to this decoder, and
   // calls Attach/DetachMediaSource on this decoder to set and clear
   // mMediaSource.
   dom::MediaSource* mMediaSource;
   nsRefPtr<MediaSourceReader> mReader;
 };
 
--- a/content/media/mediasource/MediaSourceReader.cpp
+++ b/content/media/mediasource/MediaSourceReader.cpp
@@ -30,22 +30,21 @@ extern PRLogModuleInfo* GetMediaSourceAP
 #else
 #define MSE_DEBUG(...)
 #define MSE_DEBUGV(...)
 #define MSE_API(...)
 #endif
 
 namespace mozilla {
 
-MediaSourceReader::MediaSourceReader(MediaSourceDecoder* aDecoder, dom::MediaSource* aSource)
+MediaSourceReader::MediaSourceReader(MediaSourceDecoder* aDecoder)
   : MediaDecoderReader(aDecoder)
   , mTimeThreshold(-1)
   , mDropAudioBeforeThreshold(false)
   , mDropVideoBeforeThreshold(false)
-  , mMediaSource(aSource)
 {
 }
 
 bool
 MediaSourceReader::IsWaitingMediaResources()
 {
   return mDecoders.IsEmpty() && mPendingDecoders.IsEmpty();
 }
@@ -244,24 +243,16 @@ MediaSourceReader::SwitchReaders(SwitchT
         didSwitch |= SwitchVideoReader(mDecoders[i]->GetReader());
       }
     }
   }
 
   return didSwitch;
 }
 
-void
-MediaSourceReader::SetMediaSourceDuration(double aDuration)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  ErrorResult dummy;
-  mMediaSource->SetDuration(aDuration, dummy);
-}
-
 class ReleaseDecodersTask : public nsRunnable {
 public:
   ReleaseDecodersTask(nsTArray<nsRefPtr<SourceBufferDecoder>>& aDecoders)
   {
     mDecoders.SwapElements(aDecoders);
   }
 
   NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
@@ -418,17 +409,17 @@ MediaSourceReader::Seek(int64_t aTime, i
   }
 
   // Loop until we have the requested time range in the source buffers.
   // This is a workaround for our lack of async functionality in the
   // MediaDecoderStateMachine. Bug 979104 implements what we need and
   // we'll remove this for an async approach based on that in bug XXXXXXX.
   while (!DecodersContainTime(target) && !IsShutdown()) {
     MSE_DEBUG("MediaSourceReader(%p)::Seek waiting for target=%f", this, target);
-    mMediaSource->WaitForData();
+    static_cast<MediaSourceDecoder*>(mDecoder)->WaitForData();
     SwitchReaders(SWITCH_FORCED);
   }
 
   if (IsShutdown()) {
     return NS_OK;
   }
 
   ResetDecode();
@@ -486,17 +477,18 @@ MediaSourceReader::ReadMetadata(MediaInf
                 this, reader, maxDuration);
     }
   }
 
   if (maxDuration != -1) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mDecoder->SetMediaDuration(maxDuration);
     nsRefPtr<nsIRunnable> task (
-      NS_NewRunnableMethodWithArg<double>(this, &MediaSourceReader::SetMediaSourceDuration,
+      NS_NewRunnableMethodWithArg<double>(static_cast<MediaSourceDecoder*>(mDecoder),
+                                          &MediaSourceDecoder::SetMediaSourceDuration,
                                           static_cast<double>(maxDuration) / USECS_PER_S));
     NS_DispatchToMainThread(task);
   }
 
   *aInfo = mInfo;
   *aTags = nullptr; // TODO: Handle metadata.
 
   return NS_OK;
--- a/content/media/mediasource/MediaSourceReader.h
+++ b/content/media/mediasource/MediaSourceReader.h
@@ -24,17 +24,17 @@ namespace dom {
 
 class MediaSource;
 
 } // namespace dom
 
 class MediaSourceReader : public MediaDecoderReader
 {
 public:
-  MediaSourceReader(MediaSourceDecoder* aDecoder, dom::MediaSource* aSource);
+  MediaSourceReader(MediaSourceDecoder* aDecoder);
 
   nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE
   {
     // Although we technically don't implement anything here, we return NS_OK
     // so that when the state machine initializes and calls this function
     // we don't return an error code back to the media element.
     return NS_OK;
   }
@@ -93,27 +93,23 @@ private:
     SWITCH_FORCED
   };
 
   bool SwitchReaders(SwitchType aType);
 
   bool SwitchAudioReader(MediaDecoderReader* aTargetReader);
   bool SwitchVideoReader(MediaDecoderReader* aTargetReader);
 
-  void SetMediaSourceDuration(double aDuration) ;
-
   // These are read and written on the decode task queue threads.
   int64_t mTimeThreshold;
   bool mDropAudioBeforeThreshold;
   bool mDropVideoBeforeThreshold;
 
   nsTArray<nsRefPtr<SourceBufferDecoder>> mPendingDecoders;
   nsTArray<nsRefPtr<SourceBufferDecoder>> mDecoders;
 
   nsRefPtr<MediaDecoderReader> mAudioReader;
   nsRefPtr<MediaDecoderReader> mVideoReader;
-
-  dom::MediaSource* mMediaSource;
 };
 
 } // namespace mozilla
 
 #endif /* MOZILLA_MEDIASOURCEREADER_H_ */
--- a/content/media/mediasource/SourceBuffer.cpp
+++ b/content/media/mediasource/SourceBuffer.cpp
@@ -494,17 +494,17 @@ SourceBuffer::AppendData(const uint8_t* 
     mMediaSource->NotifyEvicted(0.0, GetBufferedStart());
   }
   StopUpdating();
 
   // Schedule the state machine thread to ensure playback starts
   // if required when data is appended.
   mMediaSource->GetDecoder()->ScheduleStateMachineThread();
 
-  mMediaSource->NotifyGotData();
+  mMediaSource->GetDecoder()->NotifyGotData();
 }
 
 double
 SourceBuffer::GetBufferedStart()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ErrorResult dummy;
   nsRefPtr<TimeRanges> ranges = GetBuffered(dummy);