Bug 1388228. P2 - cache the result of CanPlayThrough() and mirror it to MDSM. draft
authorJW Wang <jwwang@mozilla.com>
Fri, 04 Aug 2017 17:38:20 +0800
changeset 642345 6fb5b74536c05dba010290d6edd0357fd0bf1870
parent 642344 e798a152f933606b960da43467d1c47a2f5aa05f
child 642346 11ee7abb90ab5f780453de4a1294e972e965e538
push id72707
push userjwwang@mozilla.com
push dateTue, 08 Aug 2017 02:32:36 +0000
bugs1388228
milestone57.0a1
Bug 1388228. P2 - cache the result of CanPlayThrough() and mirror it to MDSM. So we don't duplicate the code of calculating CanPlayThrough from download rate and playback rate in MediaDecoder. MozReview-Commit-ID: 7M5JAuUxFFc
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
dom/media/mediasource/MediaSourceDecoder.cpp
dom/media/mediasource/MediaSourceDecoder.h
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -1570,23 +1570,37 @@ MediaDecoder::UnpinForSeek()
   if (!resource || !mPinnedForSeek) {
     return;
   }
   mPinnedForSeek = false;
   resource->Unpin();
 }
 
 bool
-MediaDecoder::CanPlayThrough()
+MediaDecoder::CanPlayThroughImpl()
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE(mDecoderStateMachine, false);
   return GetStatistics().CanPlayThrough();
 }
 
+bool
+MediaDecoder::CanPlayThrough()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
+  AbstractThread::AutoEnter context(AbstractMainThread());
+  bool val = CanPlayThroughImpl();
+  if (val != mCanPlayThrough) {
+    mCanPlayThrough = val;
+    mDecoderStateMachine->DispatchCanPlayThrough(val);
+  }
+  return val;
+}
+
 RefPtr<MediaDecoder::CDMProxyPromise>
 MediaDecoder::RequestCDMProxy() const
 {
   return mCDMProxyPromise;
 }
 
 void
 MediaDecoder::SetCDMProxy(CDMProxy* aProxy)
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -310,17 +310,17 @@ private:
   // so recompute it. The monitor must be held.
   virtual void UpdatePlaybackRate();
 
   // The actual playback rate computation. The monitor must be held.
   void ComputePlaybackRate();
 
   // Returns true if we can play the entire media through without stopping
   // to buffer, given the current download and playback rates.
-  virtual bool CanPlayThrough();
+  bool CanPlayThrough();
 
   dom::AudioChannel GetAudioChannel() { return mAudioChannel; }
 
   // Called from HTMLMediaElement when owner document activity changes
   virtual void SetElementVisibility(bool aIsDocumentVisible,
                                     Visibility aElementVisibility,
                                     bool aIsElementInTree);
 
@@ -527,16 +527,18 @@ private:
     mMediaSeekable = false;
   }
 
   void FinishShutdown();
 
   void ConnectMirrors(MediaDecoderStateMachine* aObject);
   void DisconnectMirrors();
 
+  virtual bool CanPlayThroughImpl();
+
   // The state machine object for handling the decoding. It is safe to
   // call methods of this object from other threads. Its internal data
   // is synchronised on a monitor. The lifetime of this object is
   // after mPlayState is LOADING and before mPlayState is SHUTDOWN. It
   // is safe to access it during this period.
   //
   // Explicitly prievate to force access via accessors.
   RefPtr<MediaDecoderStateMachine> mDecoderStateMachine;
@@ -768,13 +770,14 @@ public:
   }
 
 private:
   // Notify owner when the audible state changed
   void NotifyAudibleStateChanged();
 
   bool mTelemetryReported;
   const MediaContainerType mContainerType;
+  bool mCanPlayThrough = false;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -2578,17 +2578,17 @@ BufferingState::Step()
   MOZ_ASSERT(!mBufferingStart.IsNull(), "Must know buffering start time.");
 
   // With buffering heuristics we will remain in the buffering state if
   // we've not decoded enough data to begin playback, or if we've not
   // downloaded a reasonable amount of data inside our buffering time.
   if (Reader()->UseBufferingHeuristics()) {
     TimeDuration elapsed = now - mBufferingStart;
     bool isLiveStream = Resource()->IsLiveStream();
-    if ((isLiveStream || !mMaster->CanPlayThrough())
+    if ((isLiveStream || !mMaster->mCanPlayThrough)
         && elapsed
            < TimeDuration::FromSeconds(mBufferingWait * mMaster->mPlaybackRate)
         && mMaster->HasLowBufferedData(TimeUnit::FromSeconds(mBufferingWait))
         && IsExpectingMoreData()) {
       SLOG("Buffering: wait %ds, timeout in %.3lfs",
            mBufferingWait, mBufferingWait - elapsed.ToSeconds());
       mMaster->ScheduleStateMachineIn(TimeUnit::FromMicroseconds(USECS_PER_S));
       DispatchDecodeTasksIfNeeded();
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -203,16 +203,27 @@ public:
         // A negative number means we don't have a fragment end time at all.
         self->mFragmentEndTime = aEndTime >= media::TimeUnit::Zero()
                                    ? aEndTime
                                    : media::TimeUnit::Invalid();
       });
     OwnerThread()->Dispatch(r.forget());
   }
 
+  void DispatchCanPlayThrough(bool aCanPlayThrough)
+  {
+    RefPtr<MediaDecoderStateMachine> self = this;
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+      "MediaDecoderStateMachine::DispatchCanPlayThrough",
+      [self, aCanPlayThrough]() {
+        self->mCanPlayThrough = aCanPlayThrough;
+      });
+    OwnerThread()->DispatchStateChange(r.forget());
+  }
+
   // Drop reference to mResource. Only called during shutdown dance.
   void BreakCycles() {
     MOZ_ASSERT(NS_IsMainThread());
     mResource = nullptr;
   }
 
   TimedMetadataEventSource& TimedMetadataEvent() {
     return mMetadataManager.TimedMetadataEvent();
@@ -571,16 +582,18 @@ private:
   MozPromiseRequestHolder<WaitForDataPromise> mVideoWaitRequest;
 
   const char* AudioRequestStatus() const;
   const char* VideoRequestStatus() const;
 
   void OnSuspendTimerResolved();
   void CancelSuspendTimer();
 
+  bool mCanPlayThrough = false;
+
   // True if we shouldn't play our audio (but still write it to any capturing
   // streams). When this is true, the audio thread will never start again after
   // it has stopped.
   bool mAudioCaptured;
 
   // True if all audio frames are already rendered.
   bool mAudioCompleted = false;
 
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -297,17 +297,17 @@ MediaSourceDecoder::NextFrameBufferedSta
     currentPosition,
     currentPosition + DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED);
   return buffered.ContainsStrict(ClampIntervalToEnd(interval))
          ? MediaDecoderOwner::NEXT_FRAME_AVAILABLE
          : MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
 }
 
 bool
-MediaSourceDecoder::CanPlayThrough()
+MediaSourceDecoder::CanPlayThroughImpl()
 {
   MOZ_ASSERT(NS_IsMainThread());
   AbstractThread::AutoEnter context(AbstractMainThread());
 
   if (NextFrameBufferedStatus() == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE) {
     return false;
   }
 
--- a/dom/media/mediasource/MediaSourceDecoder.h
+++ b/dom/media/mediasource/MediaSourceDecoder.h
@@ -64,25 +64,25 @@ public:
 
   // Returns a string describing the state of the MediaSource internal
   // buffered data. Used for debugging purposes.
   void GetMozDebugReaderData(nsACString& aString) override;
 
   void AddSizeOfResources(ResourceSizes* aSizes) override;
 
   MediaDecoderOwner::NextFrameStatus NextFrameBufferedStatus() override;
-  bool CanPlayThrough() override;
 
   bool IsMSE() const override { return true; }
 
   void NotifyInitDataArrived();
 
 private:
   void DoSetMediaSourceDuration(double aDuration);
   media::TimeInterval ClampIntervalToEnd(const media::TimeInterval& aInterval);
+  bool CanPlayThroughImpl() override;
 
   RefPtr<MediaSourceResource> mResource;
 
   // 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;
   RefPtr<MediaSourceDemuxer> mDemuxer;