Bug 1251460 - MDSM now waits on a promise to enqueue first frame loaded. r=jya
authorBryce Van Dyk <bvandyk@mozilla.com>
Wed, 09 Mar 2016 15:32:49 +1300
changeset 287991 94aaebb92d0ddfc987a47987467ed4ad59ac5fa2
parent 287990 36fef4bb3dd6389c32b8fb755466ff69a453949d
child 287992 37b591f543635f193c7329451ad54f1b8c647414
push id30071
push usercbook@mozilla.com
push dateThu, 10 Mar 2016 10:51:55 +0000
treeherdermozilla-central@dd1abe874252 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya
bugs1251460
milestone48.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 1251460 - MDSM now waits on a promise to enqueue first frame loaded. r=jya MediaDecoderStateMachine's EnqueueFIrstFrameLoadedEvent would previously call into MediaDecoderReader to update MDR's buffered ranges and enqueue the frame loaded event. This commit aims to instead have the buffered update take place, and only afterwards enqueue the event. This should remove the possibility that the event will be fired and handled before the update of the buffered ranges has taken place. MozReview-Commit-ID: GP8w2nF4xmj
dom/media/MediaDecoderReader.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -83,16 +83,18 @@ public:
 
   // Note that, conceptually, WaitForData makes sense in a non-exclusive sense.
   // But in the current architecture it's only ever used exclusively (by MDSM),
   // so we mark it that way to verify our assumptions. If you have a use-case
   // for multiple WaitForData consumers, feel free to flip the exclusivity here.
   using WaitForDataPromise =
     MozPromise<MediaData::Type, WaitForDataRejectValue, IsExclusive>;
 
+  using BufferedUpdatePromise = MozPromise<bool, bool, IsExclusive>;
+
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReader)
 
   // The caller must ensure that Shutdown() is called before aDecoder is
   // destroyed.
   explicit MediaDecoderReader(AbstractMediaDecoder* aDecoder);
 
   // Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
   // on failure.
@@ -227,16 +229,26 @@ public:
   void NotifyDataArrived()
   {
     MOZ_ASSERT(OnTaskQueue());
     NS_ENSURE_TRUE_VOID(!mShutdown);
     NotifyDataArrivedInternal();
     UpdateBuffered();
   }
 
+  // Update the buffered ranges and upon doing so return a promise
+  // to indicate success. Overrides may need to do extra work to ensure
+  // buffered is up to date.
+  virtual RefPtr<BufferedUpdatePromise> UpdateBufferedWithPromise()
+  {
+    MOZ_ASSERT(OnTaskQueue());
+    UpdateBuffered();
+    return BufferedUpdatePromise::CreateAndResolve(true, __func__);
+  }
+
   virtual MediaQueue<AudioData>& AudioQueue() { return mAudioQueue; }
   virtual MediaQueue<VideoData>& VideoQueue() { return mVideoQueue; }
 
   AbstractCanonical<media::TimeIntervals>* CanonicalBuffered()
   {
     return &mBuffered;
   }
 
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1320,16 +1320,18 @@ MediaDecoderStateMachine::Shutdown()
   MOZ_ASSERT(OnTaskQueue());
 
   // Once we've entered the shutdown state here there's no going back.
   // Change state before issuing shutdown request to threads so those
   // threads can start exiting cleanly during the Shutdown call.
   ScheduleStateMachine();
   SetState(DECODER_STATE_SHUTDOWN);
 
+  mBufferedUpdateRequest.DisconnectIfExists();
+
   mQueuedSeek.RejectIfExists(__func__);
   mPendingSeek.RejectIfExists(__func__);
   mCurrentSeek.RejectIfExists(__func__);
 
 #ifdef MOZ_EME
   mCDMProxyPromise.DisconnectIfExists();
 #endif
 
@@ -2015,22 +2017,35 @@ MediaDecoderStateMachine::EnqueueLoadedM
                               Move(visibility));
   mSentLoadedMetadataEvent = true;
 }
 
 void
 MediaDecoderStateMachine::EnqueueFirstFrameLoadedEvent()
 {
   MOZ_ASSERT(OnTaskQueue());
-  MediaDecoderEventVisibility visibility =
-    mSentFirstFrameLoadedEvent ? MediaDecoderEventVisibility::Suppressed
-                               : MediaDecoderEventVisibility::Observable;
-  mFirstFrameLoadedEvent.Notify(nsAutoPtr<MediaInfo>(new MediaInfo(mInfo)),
-                                Move(visibility));
+  // Track value of mSentFirstFrameLoadedEvent from before updating it
+  bool firstFrameBeenLoaded = mSentFirstFrameLoadedEvent;
   mSentFirstFrameLoadedEvent = true;
+  RefPtr<MediaDecoderStateMachine> self = this;
+  mBufferedUpdateRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
+    &MediaDecoderReader::UpdateBufferedWithPromise)
+    ->Then(OwnerThread(),
+    __func__,
+    // Resolve
+    [self, firstFrameBeenLoaded]() {
+      self->mBufferedUpdateRequest.Complete();
+      MediaDecoderEventVisibility visibility =
+        firstFrameBeenLoaded ? MediaDecoderEventVisibility::Suppressed
+                             : MediaDecoderEventVisibility::Observable;
+      self->mFirstFrameLoadedEvent.Notify(nsAutoPtr<MediaInfo>(new MediaInfo(self->mInfo)),
+                                          Move(visibility));
+    },
+    // Reject
+    []() { MOZ_CRASH("Should not reach"); }));
 }
 
 bool
 MediaDecoderStateMachine::IsDecodingFirstFrame()
 {
   return mState == DECODER_STATE_DECODING && mDecodingFirstFrame;
 }
 
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -1131,16 +1131,19 @@ private:
   MediaInfo mInfo;
 
   nsAutoPtr<MetadataTags> mMetadataTags;
 
   mozilla::MediaMetadataManager mMetadataManager;
 
   mozilla::RollingMean<uint32_t, uint32_t> mCorruptFrames;
 
+  // Track our request to update the buffered ranges
+  MozPromiseRequestHolder<MediaDecoderReader::BufferedUpdatePromise> mBufferedUpdateRequest;
+
   // True if we need to call FinishDecodeFirstFrame() upon frame decoding
   // successeeding.
   bool mDecodingFirstFrame;
 
   // True if we are back from DECODER_STATE_DORMANT state and
   // LoadedMetadataEvent was already sent.
   bool mSentLoadedMetadataEvent;
   // True if we are back from DECODER_STATE_DORMANT state and
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1614,16 +1614,30 @@ MediaFormatReader::GetBuffered()
   if (!intervals.Length() ||
       intervals.GetStart() == media::TimeUnit::FromMicroseconds(0)) {
     // IntervalSet already starts at 0 or is empty, nothing to shift.
     return intervals;
   }
   return intervals.Shift(media::TimeUnit::FromMicroseconds(-startTime));
 }
 
+// For the MediaFormatReader override we need to force an update to the
+// buffered ranges, so we call NotifyDataArrive
+RefPtr<MediaDecoderReader::BufferedUpdatePromise>
+MediaFormatReader::UpdateBufferedWithPromise() {
+  MOZ_ASSERT(OnTaskQueue());
+  // Call NotifyDataArrive to force a recalculation of the buffered
+  // ranges. UpdateBuffered alone will not force a recalculation, so we
+  // use NotifyDataArrived which sets flags to force this recalculation.
+  // See MediaFormatReader::UpdateReceivedNewData for an example of where
+  // the new data flag is used.
+  NotifyDataArrived();
+  return BufferedUpdatePromise::CreateAndResolve(true, __func__);
+}
+
 void MediaFormatReader::ReleaseMediaResources()
 {
   // Before freeing a video codec, all video buffers needed to be released
   // even from graphics pipeline.
   if (mVideoFrameContainer) {
     mVideoFrameContainer->ClearCurrentFrame();
   }
   mVideo.mInitPromise.DisconnectIfExists();
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -50,16 +50,18 @@ public:
   Seek(SeekTarget aTarget, int64_t aUnused) override;
 
 protected:
   void NotifyDataArrivedInternal() override;
 
 public:
   media::TimeIntervals GetBuffered() override;
 
+  RefPtr<BufferedUpdatePromise> UpdateBufferedWithPromise() override;
+
   bool ForceZeroStartTime() const override;
 
   // For Media Resource Management
   void ReleaseMediaResources() override;
 
   nsresult ResetDecode() override;
 
   RefPtr<ShutdownPromise> Shutdown() override;