Bug 1123452 - Try to enter dormant state when document is hidden r=cpearce a=sledru
authorSotaro Ikeda <sikeda@mozilla.com>
Wed, 28 Jan 2015 06:31:31 -0800
changeset 243587 7436b2d2e790
parent 243098 e25b169e456b
child 243588 5e118e867ccf
push id4405
push userrgiles@mozilla.com
push date2015-01-29 19:44 +0000
treeherdermozilla-beta@5e118e867ccf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, sledru
bugs1123452
milestone36.0
Bug 1123452 - Try to enter dormant state when document is hidden r=cpearce a=sledru
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -31,16 +31,19 @@
 
 #ifdef MOZ_WMF
 #include "WMFDecoder.h"
 #endif
 
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 
+// Default timeout msecs until try to enter dormant state by heuristic.
+static const int DEFAULT_HEURISTIC_DORMANT_TIMEOUT_MSECS = 60000;
+
 namespace mozilla {
 
 // Number of estimated seconds worth of data we need to have buffered
 // ahead of the current playback position before we allow the media decoder
 // to report that it can play through the entire media without the decode
 // catching up with the download. Having this margin make the
 // MediaDecoder::CanPlayThrough() calculation more stable in the case of
 // fluctuating bitrates.
@@ -118,37 +121,66 @@ NS_IMPL_ISUPPORTS(MediaMemoryTracker, ns
 
 NS_IMPL_ISUPPORTS(MediaDecoder, nsIObserver)
 
 void MediaDecoder::NotifyOwnerActivityChanged()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
-  if (!mDecoderStateMachine ||
-      !mDecoderStateMachine->IsDormantNeeded() ||
-      mPlayState == PLAY_STATE_SHUTDOWN) {
-    return;
-  }
-
   if (!mOwner) {
     NS_WARNING("MediaDecoder without a decoder owner, can't update dormant");
     return;
   }
 
+  UpdateDormantState(false /* aDormantTimeout */, false /* aActivity */);
+  // Start dormant timer if necessary
+  StartDormantTimer();
+}
+
+void MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  GetReentrantMonitor().AssertCurrentThreadIn();
+
+  if (!mDecoderStateMachine ||
+      mPlayState == PLAY_STATE_SHUTDOWN ||
+      !mOwner->GetVideoFrameContainer() ||
+      !mDecoderStateMachine->IsDormantNeeded())
+  {
+    return;
+  }
+
   bool prevDormant = mIsDormant;
   mIsDormant = false;
-  if (!mOwner->IsActive() && mOwner->GetVideoFrameContainer()) {
+  if (!mOwner->IsActive()) {
     mIsDormant = true;
   }
 #ifdef MOZ_WIDGET_GONK
-  if (mOwner->IsHidden() && mOwner->GetVideoFrameContainer()) {
+  if (mOwner->IsHidden()) {
     mIsDormant = true;
   }
 #endif
+  // Try to enable dormant by idle heuristic, when the owner is hidden.
+  bool prevHeuristicDormant = mIsHeuristicDormant;
+  mIsHeuristicDormant = false;
+  if (mIsHeuristicDormantSupported && mOwner->IsHidden()) {
+    if (aDormantTimeout && !aActivity &&
+        (mPlayState == PLAY_STATE_PAUSED || mPlayState == PLAY_STATE_ENDED)) {
+      // Enable heuristic dormant
+      mIsHeuristicDormant = true;
+    } else if(prevHeuristicDormant && !aActivity) {
+      // Continue heuristic dormant
+      mIsHeuristicDormant = true;
+    }
+
+    if (mIsHeuristicDormant) {
+      mIsDormant = true;
+    }
+  }
 
   if (prevDormant == mIsDormant) {
     // No update to dormant state
     return;
   }
 
   if (mIsDormant) {
     // enter dormant state
@@ -162,16 +194,57 @@ void MediaDecoder::NotifyOwnerActivityCh
     ChangeState(PLAY_STATE_LOADING);
   } else {
     // exit dormant state
     // trigger to state machine.
     mDecoderStateMachine->SetDormant(false);
   }
 }
 
+void MediaDecoder::DormantTimerExpired(nsITimer* aTimer, void* aClosure)
+{
+  MOZ_ASSERT(aClosure);
+  MediaDecoder* decoder = static_cast<MediaDecoder*>(aClosure);
+  ReentrantMonitorAutoEnter mon(decoder->GetReentrantMonitor());
+  decoder->UpdateDormantState(true /* aDormantTimeout */,
+                              false /* aActivity */);
+}
+
+void MediaDecoder::StartDormantTimer()
+{
+  if (!mIsHeuristicDormantSupported) {
+    return;
+  }
+
+  if (mIsHeuristicDormant ||
+      mShuttingDown ||
+      !mOwner ||
+      !mOwner->IsHidden() ||
+      (mPlayState != PLAY_STATE_PAUSED &&
+       mPlayState != PLAY_STATE_ENDED))
+  {
+    return;
+  }
+
+  if (!mDormantTimer) {
+    mDormantTimer = do_CreateInstance("@mozilla.org/timer;1");
+  }
+  mDormantTimer->InitWithFuncCallback(&MediaDecoder::DormantTimerExpired,
+                                      this,
+                                      mHeuristicDormantTimeout,
+                                      nsITimer::TYPE_ONE_SHOT);
+}
+
+void MediaDecoder::CancelDormantTimer()
+{
+  if (mDormantTimer) {
+    mDormantTimer->Cancel();
+  }
+}
+
 void MediaDecoder::Pause()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   if (mPlayState == PLAY_STATE_LOADING ||
       mPlayState == PLAY_STATE_SEEKING ||
       mPlayState == PLAY_STATE_ENDED) {
     mNextState = PLAY_STATE_PAUSED;
@@ -454,17 +527,23 @@ MediaDecoder::MediaDecoder() :
   mInfiniteStream(false),
   mOwner(nullptr),
   mPlaybackStatistics(new MediaChannelStatistics()),
   mPinnedForSeek(false),
   mShuttingDown(false),
   mPausedForPlaybackRateNull(false),
   mMinimizePreroll(false),
   mMediaTracksConstructed(false),
-  mIsDormant(false)
+  mIsDormant(false),
+  mIsHeuristicDormantSupported(
+    Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false)),
+  mHeuristicDormantTimeout(
+    Preferences::GetInt("media.decoder.heuristic.dormant.timeout",
+                        DEFAULT_HEURISTIC_DORMANT_TIMEOUT_MSECS)),
+  mIsHeuristicDormant(false)
 {
   MOZ_COUNT_CTOR(MediaDecoder);
   MOZ_ASSERT(NS_IsMainThread());
   MediaMemoryTracker::AddMediaDecoder(this);
 #ifdef PR_LOGGING
   if (!gMediaDecoderLog) {
     gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
   }
@@ -503,16 +582,18 @@ void MediaDecoder::Shutdown()
   }
 
   // Force any outstanding seek and byterange requests to complete
   // to prevent shutdown from deadlocking.
   if (mResource) {
     mResource->Close();
   }
 
+  CancelDormantTimer();
+
   ChangeState(PLAY_STATE_SHUTDOWN);
 
   mOwner = nullptr;
 
   MediaShutdownManager::Instance().Unregister(this);
 }
 
 MediaDecoder::~MediaDecoder()
@@ -604,16 +685,18 @@ nsresult MediaDecoder::ScheduleStateMach
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   return mDecoderStateMachine->ScheduleStateMachine();
 }
 
 nsresult MediaDecoder::Play()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  UpdateDormantState(false /* aDormantTimeout */, true /* aActivity */);
+
   NS_ASSERTION(mDecoderStateMachine != nullptr, "Should have state machine.");
   if (mPausedForPlaybackRateNull) {
     return NS_OK;
   }
   nsresult res = ScheduleStateMachineThread();
   NS_ENSURE_SUCCESS(res,res);
   if (mPlayState == PLAY_STATE_LOADING || mPlayState == PLAY_STATE_SEEKING) {
     mNextState = PLAY_STATE_PLAYING;
@@ -626,16 +709,17 @@ nsresult MediaDecoder::Play()
   ChangeState(PLAY_STATE_PLAYING);
   return NS_OK;
 }
 
 nsresult MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  UpdateDormantState(false /* aDormantTimeout */, true /* aActivity */);
 
   NS_ABORT_IF_FALSE(aTime >= 0.0, "Cannot seek to a negative value.");
 
   int64_t timeUsecs = 0;
   nsresult rv = SecondsToUsecs(aTime, timeUsecs);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mRequestedSeekTarget = SeekTarget(timeUsecs, aSeekType);
@@ -1172,16 +1256,20 @@ void MediaDecoder::ChangeState(PlayState
   if (mPlayState == PLAY_STATE_PLAYING) {
     ConstructMediaTracks();
   } else if (mPlayState == PLAY_STATE_ENDED) {
     RemoveMediaTracks();
   }
 
   ApplyStateToStateMachine(mPlayState);
 
+  CancelDormantTimer();
+  // Start dormant timer if necessary
+  StartDormantTimer();
+
   GetReentrantMonitor().NotifyAll();
 }
 
 void MediaDecoder::ApplyStateToStateMachine(PlayState aState)
 {
   MOZ_ASSERT(NS_IsMainThread());
   GetReentrantMonitor().AssertCurrentThreadIn();
 
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -183,16 +183,17 @@ destroying the MediaDecoder object.
 */
 #if !defined(MediaDecoder_h_)
 #define MediaDecoder_h_
 
 #include "nsISupports.h"
 #include "nsCOMPtr.h"
 #include "nsIObserver.h"
 #include "nsAutoPtr.h"
+#include "nsITimer.h"
 #include "MediaResource.h"
 #include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "MediaStreamGraph.h"
 #include "AbstractMediaDecoder.h"
 #include "necko-config.h"
 #ifdef MOZ_EME
@@ -362,16 +363,18 @@ public:
 
   // Notify activity of the decoder owner is changed.
   // Based on the activity, dormant state is updated.
   // Dormant state is a state to free all scarce media resources
   //  (like hw video codec), did not decoding and stay dormant.
   // It is used to share scarece media resources in system.
   virtual void NotifyOwnerActivityChanged();
 
+  void UpdateDormantState(bool aDormantTimeout, bool aActivity);
+
   // Pause video playback.
   virtual void Pause();
   // Adjust the speed of the playback, optionally with pitch correction,
   virtual void SetVolume(double aVolume);
   // Sets whether audio is being captured. If it is, we won't play any
   // of our audio.
   virtual void SetAudioCaptured(bool aCaptured);
 
@@ -1014,16 +1017,24 @@ public:
   {
     GetFrameStatistics().NotifyDecodedFrames(aParsed, aDecoded);
   }
 
 protected:
   virtual ~MediaDecoder();
   void SetStateMachineParameters();
 
+  static void DormantTimerExpired(nsITimer *aTimer, void *aClosure);
+
+  // Start a timer for heuristic dormant.
+  void StartDormantTimer();
+
+  // Cancel a timer for heuristic dormant.
+  void CancelDormantTimer();
+
   /******
    * The following members should be accessed with the decoder lock held.
    ******/
 
   // Current decoding position in the stream. This is where the decoder
   // 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.
@@ -1211,13 +1222,25 @@ protected:
   bool mMediaTracksConstructed;
 
   // Stores media info, including info of audio tracks and video tracks, should
   // only be accessed from main thread.
   nsAutoPtr<MediaInfo> mInfo;
 
   // True if MediaDecoder is in dormant state.
   bool mIsDormant;
+
+  // True if heuristic dormant is supported.
+  const bool mIsHeuristicDormantSupported;
+
+  // Timeout ms of heuristic dormant timer.
+  const int mHeuristicDormantTimeout;
+
+  // True if MediaDecoder is in dormant by heuristic.
+  bool mIsHeuristicDormant;
+
+  // Timer to schedule updating dormant state.
+  nsCOMPtr<nsITimer> mDormantTimer;
 };
 
 } // namespace mozilla
 
 #endif