Bug 1062669 - Consider only SourceBuffers created before the first initialization segment is appended as essential for parent decoder initialization. r=cajbir
authorMatthew Gregan <kinetik@flim.org>
Fri, 05 Sep 2014 12:04:54 +1200
changeset 203756 2e6011266bea6b6dde758b5ffb09a931ff1c3afc
parent 203755 3997eb3154aab1467650530295906837c830df56
child 203757 fb651803afe99adf4fcacb3014391abcf8774d66
push id27435
push userryanvm@gmail.com
push dateFri, 05 Sep 2014 15:34:32 +0000
treeherdermozilla-central@d934dc4a99ac [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscajbir
bugs1062669
milestone35.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 1062669 - Consider only SourceBuffers created before the first initialization segment is appended as essential for parent decoder initialization. r=cajbir
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
@@ -334,16 +334,17 @@ MediaSource::Detach()
             this, mDecoder.get(), mDecoder ? mDecoder->GetOwner() : nullptr);
   if (!mDecoder) {
     MOZ_ASSERT(mReadyState == MediaSourceReadyState::Closed);
     MOZ_ASSERT(mActiveSourceBuffers->IsEmpty() && mSourceBuffers->IsEmpty());
     return;
   }
   mDecoder->DetachMediaSource();
   mDecoder = nullptr;
+  mFirstSourceBufferInitialization = false;
   SetReadyState(MediaSourceReadyState::Closed);
   mDuration = UnspecifiedNaN<double>();
   if (mActiveSourceBuffers) {
     mActiveSourceBuffers->Clear();
   }
   if (mSourceBuffers) {
     mSourceBuffers->Clear();
   }
@@ -390,16 +391,17 @@ 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)
+  , mFirstSourceBufferInitialization(false)
 {
   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());
 }
 
@@ -477,16 +479,39 @@ 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::QueueInitializationEvent()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!mFirstSourceBufferInitialization) {
+    mFirstSourceBufferInitialization = true;
+  }
+  MSE_DEBUG("MediaSource(%p)::QueueInitializationEvent()", this);
+  nsRefPtr<nsIRunnable> task =
+    NS_NewRunnableMethod(this, &MediaSource::InitializationEvent);
+  NS_DispatchToMainThread(task);
+}
+
+void
+MediaSource::InitializationEvent()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MSE_DEBUG("MediaSource(%p)::InitializationEvent()", this);
+  if (mDecoder) {
+    mDecoder->PrepareReaderInitialization();
+  }
+}
+
 nsPIDOMWindow*
 MediaSource::GetParentObject() const
 {
   return GetOwner();
 }
 
 JSObject*
 MediaSource::WrapObject(JSContext* aCx)
--- a/content/media/mediasource/MediaSource.h
+++ b/content/media/mediasource/MediaSource.h
@@ -85,35 +85,47 @@ 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);
 
+  // Queue InitializationEvent to run on the main thread.  Called when a
+  // SourceBuffer has an initialization segment appended, but only
+  // dispatched the first time (using mFirstSourceBufferInitialization).
+  // Demarcates the point in time at which only currently registered
+  // TrackBuffers are treated as essential by the MediaSourceReader for
+  // initialization.
+  void QueueInitializationEvent();
+
 private:
   ~MediaSource();
 
   explicit MediaSource(nsPIDOMWindow* aWindow);
 
   friend class AsyncEventRunner<MediaSource>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
   void DurationChange(double aNewDuration, ErrorResult& aRv);
 
+  void InitializationEvent();
+
   double mDuration;
 
   nsRefPtr<SourceBufferList> mSourceBuffers;
   nsRefPtr<SourceBufferList> mActiveSourceBuffers;
 
   nsRefPtr<MediaSourceDecoder> mDecoder;
 
   MediaSourceReadyState mReadyState;
+
+  bool mFirstSourceBufferInitialization;
 };
 
 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
@@ -183,9 +183,16 @@ MediaSourceDecoder::WaitForData()
 void
 MediaSourceDecoder::NotifyGotData()
 {
   MSE_DEBUG("MediaSourceDecoder(%p)::NotifyGotData()", this);
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mon.NotifyAll();
 }
 
+void
+MediaSourceDecoder::PrepareReaderInitialization()
+{
+  MOZ_ASSERT(mReader);
+  mReader->PrepareInitialization();
+}
+
 } // namespace mozilla
--- a/content/media/mediasource/MediaSourceDecoder.h
+++ b/content/media/mediasource/MediaSourceDecoder.h
@@ -56,16 +56,20 @@ public:
   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();
 
+  // Indicates the point in time at which the reader should consider
+  // registered TrackBuffers essential for initialization.
+  void PrepareReaderInitialization();
+
 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
@@ -41,29 +41,43 @@ MediaSourceReader::MediaSourceReader(Med
   , mLastAudioTime(-1)
   , mLastVideoTime(-1)
   , mTimeThreshold(-1)
   , mDropAudioBeforeThreshold(false)
   , mDropVideoBeforeThreshold(false)
   , mEnded(false)
   , mAudioIsSeeking(false)
   , mVideoIsSeeking(false)
+  , mHasEssentialTrackBuffers(false)
 {
 }
 
+void
+MediaSourceReader::PrepareInitialization()
+{
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  MSE_DEBUG("MediaSourceReader(%p)::PrepareInitialization trackBuffers=%u",
+            this, mTrackBuffers.Length());
+  mEssentialTrackBuffers.AppendElements(mTrackBuffers);
+  mHasEssentialTrackBuffers = true;
+  mDecoder->NotifyWaitingForResourcesStatusChanged();
+}
+
 bool
 MediaSourceReader::IsWaitingMediaResources()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) {
-    if (!mTrackBuffers[i]->IsReady()) {
+
+  for (uint32_t i = 0; i < mEssentialTrackBuffers.Length(); ++i) {
+    if (!mEssentialTrackBuffers[i]->IsReady()) {
       return true;
     }
   }
-  return mTrackBuffers.IsEmpty();
+
+  return !mHasEssentialTrackBuffers;
 }
 
 void
 MediaSourceReader::RequestAudioData()
 {
   MSE_DEBUGV("MediaSourceReader(%p)::RequestAudioData", this);
   if (!mAudioReader) {
     MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called with no audio reader", this);
@@ -362,16 +376,17 @@ MediaSourceReader::RemoveTrackBuffer(Tra
     mVideoTrack = nullptr;
   }
 }
 
 void
 MediaSourceReader::OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  MOZ_ASSERT(aTrackBuffer->IsReady());
   MOZ_ASSERT(mTrackBuffers.Contains(aTrackBuffer));
   if (aInfo.HasAudio() && !mAudioTrack) {
     MSE_DEBUG("MediaSourceReader(%p)::OnTrackBufferConfigured %p audio", this, aTrackBuffer);
     mAudioTrack = aTrackBuffer;
   }
   if (aInfo.HasVideo() && !mVideoTrack) {
     MSE_DEBUG("MediaSourceReader(%p)::OnTrackBufferConfigured %p video", this, aTrackBuffer);
     mVideoTrack = aTrackBuffer;
@@ -467,21 +482,25 @@ MediaSourceReader::Seek(int64_t aTime, i
     }
   }
   return NS_OK;
 }
 
 nsresult
 MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
 {
-  MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata tracks=%u", this, mTrackBuffers.Length());
+  bool waiting = IsWaitingMediaResources();
+  MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata waiting=%d tracks=%u/%u audio=%p video=%p",
+            this, waiting, mEssentialTrackBuffers.Length(), mTrackBuffers.Length(),
+            mAudioTrack.get(), mVideoTrack.get());
   // ReadMetadata is called *before* checking IsWaitingMediaResources.
-  if (IsWaitingMediaResources()) {
+  if (waiting) {
     return NS_OK;
   }
+  mEssentialTrackBuffers.Clear();
   if (!mAudioTrack && !mVideoTrack) {
     MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata missing track: mAudioTrack=%p mVideoTrack=%p",
               this, mAudioTrack.get(), mVideoTrack.get());
     return NS_ERROR_FAILURE;
   }
 
   int64_t maxDuration = -1;
 
--- a/content/media/mediasource/MediaSourceReader.h
+++ b/content/media/mediasource/MediaSourceReader.h
@@ -35,16 +35,20 @@ public:
   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;
   }
 
+  // Indicates the point in time at which the reader should consider
+  // registered TrackBuffers essential for initialization.
+  void PrepareInitialization();
+
   bool IsWaitingMediaResources() MOZ_OVERRIDE;
 
   void RequestAudioData() MOZ_OVERRIDE;
 
   void OnAudioDecoded(AudioData* aSample);
 
   void OnAudioEOS();
 
@@ -100,16 +104,17 @@ public:
 private:
   bool SwitchAudioReader(double aTarget);
   bool SwitchVideoReader(double aTarget);
 
   nsRefPtr<MediaDecoderReader> mAudioReader;
   nsRefPtr<MediaDecoderReader> mVideoReader;
 
   nsTArray<nsRefPtr<TrackBuffer>> mTrackBuffers;
+  nsTArray<nsRefPtr<TrackBuffer>> mEssentialTrackBuffers;
   nsRefPtr<TrackBuffer> mAudioTrack;
   nsRefPtr<TrackBuffer> mVideoTrack;
 
   // These are read and written on the decode task queue threads.
   int64_t mLastAudioTime;
   int64_t mLastVideoTime;
 
   int64_t mTimeThreshold;
@@ -120,13 +125,15 @@ private:
 
   // For a seek to complete we need to send a sample with
   // the mDiscontinuity field set to true once we have the
   // first decoded sample. These flags are set during seeking
   // so we can detect when we have the first decoded sample
   // after a seek.
   bool mAudioIsSeeking;
   bool mVideoIsSeeking;
+
+  bool mHasEssentialTrackBuffers;
 };
 
 } // namespace mozilla
 
 #endif /* MOZILLA_MEDIASOURCEREADER_H_ */
--- a/content/media/mediasource/SourceBuffer.cpp
+++ b/content/media/mediasource/SourceBuffer.cpp
@@ -559,16 +559,17 @@ SourceBuffer::AppendData(const uint8_t* 
     mMediaSource->SetReadyState(MediaSourceReadyState::Open);
   }
   // TODO: Run coded frame eviction algorithm.
   // TODO: Test buffer full flag.
   StartUpdating();
   // TODO: Run buffer append algorithm asynchronously (would call StopUpdating()).
   if (mParser->IsInitSegmentPresent(aData, aLength)) {
     MSE_DEBUG("SourceBuffer(%p)::AppendData: New initialization segment.", this);
+    mMediaSource->QueueInitializationEvent();
     mTrackBuffer->DiscardDecoder();
     if (!mTrackBuffer->NewDecoder()) {
       aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
       return;
     }
     MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
   } else if (!mTrackBuffer->HasInitSegment()) {
     MSE_DEBUG("SourceBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);