Bug 1295921 - P1: Track decoder tainting. r?jwwang draft
authorDan Glastonbury <dglastonbury@mozilla.com>
Mon, 29 Aug 2016 16:19:50 +1000
changeset 450821 016e870f2a6adc2847d0594827c02ccc844e527e
parent 449764 27cd187b287add9bda4e0af58dabae7af01ac091
child 450822 f7de938ddd61070049bdceee1adb10faf41efd91
push id38957
push userbmo:dglastonbury@mozilla.com
push dateMon, 19 Dec 2016 02:15:56 +0000
reviewersjwwang
bugs1295921
milestone53.0a1
Bug 1295921 - P1: Track decoder tainting. r?jwwang Some uses of media elements should 'taint' the element so that the video doesn't participate in video decode suspending. Add the infrastructure to track the taint status on MediaDecoder and mirror the status to MediaDecoderStateMachine. MozReview-Commit-ID: 1nrNqg0KavT
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -410,16 +410,17 @@ MediaDecoder::MediaDecoder(MediaDecoderO
   , INIT_CANONICAL(mNextState, PLAY_STATE_PAUSED)
   , INIT_CANONICAL(mLogicallySeeking, false)
   , INIT_CANONICAL(mSameOriginMedia, false)
   , INIT_CANONICAL(mMediaPrincipalHandle, PRINCIPAL_HANDLE_NONE)
   , INIT_CANONICAL(mPlaybackBytesPerSecond, 0.0)
   , INIT_CANONICAL(mPlaybackRateReliable, true)
   , INIT_CANONICAL(mDecoderPosition, 0)
   , INIT_CANONICAL(mIsVisible, !aOwner->IsHidden())
+  , INIT_CANONICAL(mHasSuspendTaint, false)
   , mTelemetryReported(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MediaMemoryTracker::AddMediaDecoder(this);
 
   mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
   mResourceCallback->Connect(this);
 
@@ -1261,16 +1262,30 @@ void
 MediaDecoder::SetForcedHidden(bool aForcedHidden)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mForcedHidden = aForcedHidden;
   SetElementVisibility(mElementVisible);
 }
 
 void
+MediaDecoder::SetSuspendTaint(bool aTainted)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  mHasSuspendTaint = aTainted;
+}
+
+bool
+MediaDecoder::HasSuspendTaint() const
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return mHasSuspendTaint;
+}
+
+void
 MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mPlayState <= PLAY_STATE_LOADING) {
     return;
   }
 
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -353,16 +353,22 @@ private:
 
   // Called from HTMLMediaElement when owner document activity changes
   virtual void SetElementVisibility(bool aIsVisible);
 
   // Force override the visible state to hidden.
   // Called from HTMLMediaElement when testing of video decode suspend from mochitests.
   void SetForcedHidden(bool aForcedHidden);
 
+  // Mark the decoder as tainted, meaning video decode suspend is disabled.
+  void SetSuspendTaint(bool aTaint);
+
+  // Returns true if the decoder can't participate in video decode suspending.
+  bool HasSuspendTaint() const;
+
   /******
    * The following methods must only be called on the main
    * thread.
    ******/
 
   // Change to a new play state. This updates the mState variable and
   // notifies any thread blocking on this object's monitor of the
   // change. Call on the main thread only.
@@ -761,16 +767,20 @@ protected:
   // is up to consuming the stream. This is not adjusted during decoder
   // seek operations, but it's updated at the end when we start playing
   // back again.
   Canonical<int64_t> mDecoderPosition;
 
   // True if the decoder is visible.
   Canonical<bool> mIsVisible;
 
+  // True if the decoder has a suspend taint - meaning video decode suspend is
+  // disabled.
+  Canonical<bool> mHasSuspendTaint;
+
 public:
   AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() override;
   AbstractCanonical<double>* CanonicalVolume() {
     return &mVolume;
   }
   AbstractCanonical<bool>* CanonicalPreservesPitch() {
     return &mPreservesPitch;
   }
@@ -802,16 +812,19 @@ public:
     return &mPlaybackRateReliable;
   }
   AbstractCanonical<int64_t>* CanonicalDecoderPosition() {
     return &mDecoderPosition;
   }
   AbstractCanonical<bool>* CanonicalIsVisible() {
     return &mIsVisible;
   }
+  AbstractCanonical<bool>* CanonicalHasSuspendTaint() {
+    return &mHasSuspendTaint;
+  }
 
 private:
   // Notify owner when the audible state changed
   void NotifyAudibleStateChanged();
 
   /* Functions called by ResourceCallback */
 
   // A media stream is assumed to be infinite if the metadata doesn't
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -603,21 +603,29 @@ public:
   {
     MaybeStopPrerolling();
     // MediaSink is changed. Schedule Step() to check if we can start playback.
     mMaster->ScheduleStateMachine();
   }
 
   void HandleVideoSuspendTimeout() override
   {
-    if (mMaster->HasVideo()) {
-      mMaster->mVideoDecodeSuspended = true;
-      mMaster->mOnPlaybackEvent.Notify(MediaEventType::EnterVideoSuspend);
-      Reader()->SetVideoBlankDecode(true);
+    // No video, so nothing to suspend.
+    if (!mMaster->HasVideo()) {
+      return;
     }
+
+    // The decoder is tainted, so nothing to suspend.
+    if (mMaster->mHasSuspendTaint) {
+      return;
+    }
+
+    mMaster->mVideoDecodeSuspended = true;
+    mMaster->mOnPlaybackEvent.Notify(MediaEventType::EnterVideoSuspend);
+    Reader()->SetVideoBlankDecode(true);
   }
 
   void HandlePlayStateChanged(MediaDecoder::PlayState aPlayState) override
   {
     if (aPlayState == MediaDecoder::PLAY_STATE_PLAYING) {
       // Schedule Step() to check if we can start playback.
       mMaster->ScheduleStateMachine();
     }
@@ -1477,21 +1485,29 @@ public:
     mMaster->Push(aVideo);
     mMaster->ScheduleStateMachine();
   }
 
   void HandleEndOfStream() override;
 
   void HandleVideoSuspendTimeout() override
   {
-    if (mMaster->HasVideo()) {
-      mMaster->mVideoDecodeSuspended = true;
-      mMaster->mOnPlaybackEvent.Notify(MediaEventType::EnterVideoSuspend);
-      Reader()->SetVideoBlankDecode(true);
+    // No video, so nothing to suspend.
+    if (!mMaster->HasVideo()) {
+      return;
     }
+
+    // The decoder is tainted, so nothing to suspend.
+    if (mMaster->mHasSuspendTaint) {
+      return;
+    }
+
+    mMaster->mVideoDecodeSuspended = true;
+    mMaster->mOnPlaybackEvent.Notify(MediaEventType::EnterVideoSuspend);
+    Reader()->SetVideoBlankDecode(true);
   }
 
 private:
   TimeStamp mBufferingStart;
 
   // The maximum number of second we spend buffering when we are short on
   // unbuffered data.
   const uint32_t mBufferingWait = 15;
@@ -2154,16 +2170,17 @@ ShutdownState::Enter()
   master->mVolume.DisconnectIfConnected();
   master->mPreservesPitch.DisconnectIfConnected();
   master->mSameOriginMedia.DisconnectIfConnected();
   master->mMediaPrincipalHandle.DisconnectIfConnected();
   master->mPlaybackBytesPerSecond.DisconnectIfConnected();
   master->mPlaybackRateReliable.DisconnectIfConnected();
   master->mDecoderPosition.DisconnectIfConnected();
   master->mIsVisible.DisconnectIfConnected();
+  master->mHasSuspendTaint.DisconnectIfConnected();
 
   master->mDuration.DisconnectAll();
   master->mIsShutdown.DisconnectAll();
   master->mNextFrameStatus.DisconnectAll();
   master->mCurrentPosition.DisconnectAll();
   master->mPlaybackOffset.DisconnectAll();
   master->mIsAudioDataAudible.DisconnectAll();
 
@@ -2220,16 +2237,17 @@ MediaDecoderStateMachine::MediaDecoderSt
   INIT_MIRROR(mVolume, 1.0),
   INIT_MIRROR(mPreservesPitch, true),
   INIT_MIRROR(mSameOriginMedia, false),
   INIT_MIRROR(mMediaPrincipalHandle, PRINCIPAL_HANDLE_NONE),
   INIT_MIRROR(mPlaybackBytesPerSecond, 0.0),
   INIT_MIRROR(mPlaybackRateReliable, true),
   INIT_MIRROR(mDecoderPosition, 0),
   INIT_MIRROR(mIsVisible, true),
+  INIT_MIRROR(mHasSuspendTaint, false),
   INIT_CANONICAL(mDuration, NullableTimeUnit()),
   INIT_CANONICAL(mIsShutdown, false),
   INIT_CANONICAL(mNextFrameStatus, MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE),
   INIT_CANONICAL(mCurrentPosition, 0),
   INIT_CANONICAL(mPlaybackOffset, 0),
   INIT_CANONICAL(mIsAudioDataAudible, false)
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
@@ -2286,16 +2304,17 @@ MediaDecoderStateMachine::Initialization
   mWatchManager.Watch(mPreservesPitch, &MediaDecoderStateMachine::PreservesPitchChanged);
   mWatchManager.Watch(mEstimatedDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mExplicitDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mObservedDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged);
 
   if (MediaPrefs::MDSMSuspendBackgroundVideoEnabled()) {
     mIsVisible.Connect(aDecoder->CanonicalIsVisible());
+    mHasSuspendTaint.Connect(aDecoder->CanonicalHasSuspendTaint());
     mWatchManager.Watch(mIsVisible, &MediaDecoderStateMachine::VisibilityChanged);
   }
 
   // Configure MediaDecoderReaderWrapper.
   SetMediaDecoderReaderWrapperCallback();
 }
 
 void
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -788,16 +788,19 @@ private:
   Mirror<bool> mPlaybackRateReliable;
 
   // Current decoding position in the stream.
   Mirror<int64_t> mDecoderPosition;
 
   // IsVisible, mirrored from the media decoder.
   Mirror<bool> mIsVisible;
 
+  // HasSuspendTaint, mirrored from the media decoder.
+  Mirror<bool> mHasSuspendTaint;
+
   // Duration of the media. This is guaranteed to be non-null after we finish
   // decoding the first frame.
   Canonical<media::NullableTimeUnit> mDuration;
 
   // Whether we're currently in or transitioning to shutdown state.
   Canonical<bool> mIsShutdown;
 
   // The status of our next frame. Mirrored on the main thread and used to