From 62cf6e65718877281f358997c15733bbccf218c0 Mon Sep 17 00:00:00 2001 From: Sotaro Ikeda Date: Tue, 27 Jan 2015 19:30:11 +1300 Subject: [PATCH] Bug 1123452 - Make Mp4Reader enter dormant state when it is idle and its document is hidden. r=cpearce --- dom/media/MediaDecoder.cpp | 103 +++++++++++++++++++++++++++++++++---- dom/media/MediaDecoder.h | 23 +++++++++ 2 files changed, 117 insertions(+), 9 deletions(-) diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index 9688688f645..a28e60e6edc 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -36,6 +36,9 @@ 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 @@ -123,27 +126,55 @@ 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 || + !mOwner->GetVideoFrameContainer() || + !mDecoderStateMachine->IsDormantNeeded() || + mPlayState == PLAY_STATE_SHUTDOWN) { + 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 @@ -167,6 +198,45 @@ void MediaDecoder::NotifyOwnerActivityChanged() } } +void MediaDecoder::DormantTimerExpired(nsITimer* aTimer, void* aClosure) +{ + MOZ_ASSERT(aClosure); + MediaDecoder* decoder = static_cast(aClosure); + ReentrantMonitorAutoEnter mon(decoder->GetReentrantMonitor()); + decoder->UpdateDormantState(true /* aDormantTimeout */, + false /* aActivity */); +} + +void MediaDecoder::StartDormantTimer() +{ + if (!mIsHeuristicDormantSupported) { + return; + } + + if (mIsHeuristicDormant || + !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()); @@ -472,7 +542,13 @@ MediaDecoder::MediaDecoder() : 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()); @@ -521,6 +597,8 @@ void MediaDecoder::Shutdown() mResource->Close(); } + CancelDormantTimer(); + ChangeState(PLAY_STATE_SHUTDOWN); mOwner = nullptr; @@ -622,6 +700,8 @@ 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; @@ -644,6 +724,7 @@ 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."); @@ -1192,6 +1273,10 @@ void MediaDecoder::ChangeState(PlayState aState) ApplyStateToStateMachine(mPlayState); + CancelDormantTimer(); + // Start dormant timer if necessary + StartDormantTimer(); + GetReentrantMonitor().NotifyAll(); } diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index 33fc2f41d9a..eae936b49f7 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -188,6 +188,7 @@ destroying the MediaDecoder object. #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" @@ -367,6 +368,8 @@ public: // 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, @@ -1022,6 +1025,14 @@ 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. ******/ @@ -1219,6 +1230,18 @@ protected: // 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 mDormantTimer; }; } // namespace mozilla