Bug 1123452 - Make Mp4Reader enter dormant state when it is idle and its document is hidden. r=cpearce

This commit is contained in:
Sotaro Ikeda 2015-01-28 13:56:58 +13:00
parent b86c52f82a
commit 9c64ff755d
2 changed files with 119 additions and 9 deletions

View File

@ -36,6 +36,9 @@
using namespace mozilla::layers; using namespace mozilla::layers;
using namespace mozilla::dom; 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 { namespace mozilla {
// Number of estimated seconds worth of data we need to have buffered // Number of estimated seconds worth of data we need to have buffered
@ -123,27 +126,55 @@ void MediaDecoder::NotifyOwnerActivityChanged()
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (!mDecoderStateMachine ||
!mDecoderStateMachine->IsDormantNeeded() ||
mPlayState == PLAY_STATE_SHUTDOWN) {
return;
}
if (!mOwner) { if (!mOwner) {
NS_WARNING("MediaDecoder without a decoder owner, can't update dormant"); NS_WARNING("MediaDecoder without a decoder owner, can't update dormant");
return; 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; bool prevDormant = mIsDormant;
mIsDormant = false; mIsDormant = false;
if (!mOwner->IsActive() && mOwner->GetVideoFrameContainer()) { if (!mOwner->IsActive()) {
mIsDormant = true; mIsDormant = true;
} }
#ifdef MOZ_WIDGET_GONK #ifdef MOZ_WIDGET_GONK
if (mOwner->IsHidden() && mOwner->GetVideoFrameContainer()) { if (mOwner->IsHidden()) {
mIsDormant = true; mIsDormant = true;
} }
#endif #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) { if (prevDormant == mIsDormant) {
// No update to dormant state // No update to dormant state
@ -167,6 +198,47 @@ void MediaDecoder::NotifyOwnerActivityChanged()
} }
} }
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() void MediaDecoder::Pause()
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
@ -472,7 +544,13 @@ MediaDecoder::MediaDecoder() :
mPausedForPlaybackRateNull(false), mPausedForPlaybackRateNull(false),
mMinimizePreroll(false), mMinimizePreroll(false),
mMediaTracksConstructed(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_COUNT_CTOR(MediaDecoder);
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
@ -521,6 +599,8 @@ void MediaDecoder::Shutdown()
mResource->Close(); mResource->Close();
} }
CancelDormantTimer();
ChangeState(PLAY_STATE_SHUTDOWN); ChangeState(PLAY_STATE_SHUTDOWN);
mOwner = nullptr; mOwner = nullptr;
@ -622,6 +702,8 @@ nsresult MediaDecoder::Play()
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
UpdateDormantState(false /* aDormantTimeout */, true /* aActivity */);
NS_ASSERTION(mDecoderStateMachine != nullptr, "Should have state machine."); NS_ASSERTION(mDecoderStateMachine != nullptr, "Should have state machine.");
if (mPausedForPlaybackRateNull) { if (mPausedForPlaybackRateNull) {
return NS_OK; return NS_OK;
@ -644,6 +726,7 @@ nsresult MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
UpdateDormantState(false /* aDormantTimeout */, true /* aActivity */);
NS_ABORT_IF_FALSE(aTime >= 0.0, "Cannot seek to a negative value."); NS_ABORT_IF_FALSE(aTime >= 0.0, "Cannot seek to a negative value.");
@ -1192,6 +1275,10 @@ void MediaDecoder::ChangeState(PlayState aState)
ApplyStateToStateMachine(mPlayState); ApplyStateToStateMachine(mPlayState);
CancelDormantTimer();
// Start dormant timer if necessary
StartDormantTimer();
GetReentrantMonitor().NotifyAll(); GetReentrantMonitor().NotifyAll();
} }

View File

@ -188,6 +188,7 @@ destroying the MediaDecoder object.
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsIObserver.h" #include "nsIObserver.h"
#include "nsAutoPtr.h" #include "nsAutoPtr.h"
#include "nsITimer.h"
#include "MediaResource.h" #include "MediaResource.h"
#include "mozilla/dom/AudioChannelBinding.h" #include "mozilla/dom/AudioChannelBinding.h"
#include "mozilla/gfx/Rect.h" #include "mozilla/gfx/Rect.h"
@ -367,6 +368,8 @@ public:
// It is used to share scarece media resources in system. // It is used to share scarece media resources in system.
virtual void NotifyOwnerActivityChanged(); virtual void NotifyOwnerActivityChanged();
void UpdateDormantState(bool aDormantTimeout, bool aActivity);
// Pause video playback. // Pause video playback.
virtual void Pause(); virtual void Pause();
// Adjust the speed of the playback, optionally with pitch correction, // Adjust the speed of the playback, optionally with pitch correction,
@ -1022,6 +1025,14 @@ protected:
virtual ~MediaDecoder(); virtual ~MediaDecoder();
void SetStateMachineParameters(); 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. * The following members should be accessed with the decoder lock held.
******/ ******/
@ -1219,6 +1230,18 @@ protected:
// True if MediaDecoder is in dormant state. // True if MediaDecoder is in dormant state.
bool mIsDormant; 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 } // namespace mozilla