Bug 1311872. Part 3 - enter dormant when being paused for a while.
MozReview-Commit-ID: HJjhFebQ8WI
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -557,26 +557,31 @@ private:
* SHUTDOWN if any decode error.
* BUFFERING if playback can't continue due to lack of decoded data.
* COMPLETED when having decoded all audio/video data.
*/
class MediaDecoderStateMachine::DecodingState
: public MediaDecoderStateMachine::StateObject
{
public:
- explicit DecodingState(Master* aPtr) : StateObject(aPtr) {}
+ explicit DecodingState(Master* aPtr)
+ : StateObject(aPtr)
+ , mDormantTimer(OwnerThread())
+ {
+ }
void Enter();
void Exit() override
{
if (!mDecodeStartTime.IsNull()) {
TimeDuration decodeDuration = TimeStamp::Now() - mDecodeStartTime;
SLOG("Exiting DECODING, decoded for %.3lfs", decodeDuration.ToSeconds());
}
+ mDormantTimer.Reset();
}
void Step() override
{
if (mMaster->mPlayState != MediaDecoder::PLAY_STATE_PLAYING &&
mMaster->IsPlaying()) {
// We're playing, but the element/decoder is in paused state. Stop
// playing!
@@ -645,16 +650,22 @@ public:
}
void HandlePlayStateChanged(MediaDecoder::PlayState aPlayState) override
{
if (aPlayState == MediaDecoder::PLAY_STATE_PLAYING) {
// Schedule Step() to check if we can start playback.
mMaster->ScheduleStateMachine();
}
+
+ if (aPlayState == MediaDecoder::PLAY_STATE_PAUSED) {
+ StartDormantTimer();
+ } else {
+ mDormantTimer.Reset();
+ }
}
void DumpDebugInfo() override
{
SDUMP("mIsPrerolling=%d", mIsPrerolling);
}
private:
@@ -711,27 +722,54 @@ private:
(DonePrerollingAudio() || Reader()->IsWaitingAudioData()) &&
(DonePrerollingVideo() || Reader()->IsWaitingVideoData())) {
mIsPrerolling = false;
// Check if we can start playback.
mMaster->ScheduleStateMachine();
}
}
+ void StartDormantTimer()
+ {
+ auto timeout = MediaPrefs::DormantOnPauseTimeout();
+ if (timeout < 0) {
+ // Disabled when timeout is negative.
+ return;
+ } else if (timeout == 0) {
+ // Enter dormant immediately without scheduling a timer.
+ HandleDormant(true);
+ return;
+ }
+
+ TimeStamp target = TimeStamp::Now() +
+ TimeDuration::FromMilliseconds(timeout);
+
+ mDormantTimer.Ensure(target,
+ [this] () {
+ mDormantTimer.CompleteRequest();
+ HandleDormant(true);
+ }, [this] () {
+ mDormantTimer.CompleteRequest();
+ });
+ }
+
// Time at which we started decoding.
TimeStamp mDecodeStartTime;
// When we start decoding (either for the first time, or after a pause)
// we may be low on decoded data. We don't want our "low data" logic to
// kick in and decide that we're low on decoded data because the download
// can't keep up with the decode, and cause us to pause playback. So we
// have a "preroll" stage, where we ignore the results of our "low data"
// logic during the first few frames of our decode. This occurs during
// playback.
bool mIsPrerolling = true;
+
+ // Fired when playback is paused for a while to enter dormant.
+ DelayedScheduler mDormantTimer;
};
/**
* Purpose: seek to a particular new playback position.
*
* Transition to:
* DORMANT if any dormant request.
* SEEKING if any new seek request.
@@ -1437,16 +1475,21 @@ DecodingState::Enter()
mDecodeStartTime = TimeStamp::Now();
MaybeStopPrerolling();
// Ensure that we've got tasks enqueued to decode data if we need to.
mMaster->DispatchDecodeTasksIfNeeded();
mMaster->ScheduleStateMachine();
+
+ // Will enter dormant when playback is paused for a while.
+ if (mMaster->mPlayState == MediaDecoder::PLAY_STATE_PAUSED) {
+ StartDormantTimer();
+ }
}
RefPtr<MediaDecoder::SeekPromise>
MediaDecoderStateMachine::
DecodingState::HandleSeek(SeekTarget aTarget)
{
SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
SeekJob seekJob;
--- a/dom/media/MediaPrefs.h
+++ b/dom/media/MediaPrefs.h
@@ -135,16 +135,17 @@ private:
DECL_MEDIA_PREF("media.decoder.fuzzing.dont-delay-inputexhausted", PDMFuzzingDelayInputExhausted, bool, true);
DECL_MEDIA_PREF("media.gmp.decoder.enabled", PDMGMPEnabled, bool, true);
DECL_MEDIA_PREF("media.gmp.decoder.aac", GMPAACPreferred, uint32_t, 0);
DECL_MEDIA_PREF("media.gmp.decoder.h264", GMPH264Preferred, uint32_t, 0);
// MediaDecoderStateMachine
DECL_MEDIA_PREF("media.suspend-bkgnd-video.enabled", MDSMSuspendBackgroundVideoEnabled, bool, false);
DECL_MEDIA_PREF("media.suspend-bkgnd-video.delay-ms", MDSMSuspendBackgroundVideoDelay, AtomicUint32, SUSPEND_BACKGROUND_VIDEO_DELAY_MS);
+ DECL_MEDIA_PREF("media.dormant-on-pause-timeout-ms", DormantOnPauseTimeout, int32_t, 5000);
// WebSpeech
DECL_MEDIA_PREF("media.webspeech.synth.force_global_queue", WebSpeechForceGlobal, bool, false);
DECL_MEDIA_PREF("media.webspeech.test.enable", WebSpeechTestEnabled, bool, false);
DECL_MEDIA_PREF("media.webspeech.test.fake_fsm_events", WebSpeechFakeFSMEvents, bool, false);
DECL_MEDIA_PREF(TEST_PREFERENCE_FAKE_RECOGNITION_SERVICE, WebSpeechFakeRecognitionService, bool, false);
DECL_MEDIA_PREF("media.webspeech.recognition.enable", WebSpeechRecognitionEnabled, bool, false);
DECL_MEDIA_PREF("media.webspeech.recognition.force_enable", WebSpeechRecognitionForceEnabled, bool, false);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -306,16 +306,23 @@ pref("mathml.scale_stretchy_operators.en
// Disable MediaError.message.
#ifdef RELEASE_OR_BETA
pref("dom.MediaError.message.enabled", false);
#else
pref("dom.MediaError.message.enabled", true);
#endif
+// Enabled on nightly only until we fix mochitest failures.
+#ifdef NIGHTLY_BUILD
+pref("media.dormant-on-pause-timeout-ms", 5000);
+#else
+pref("media.dormant-on-pause-timeout-ms", -1);
+#endif
+
// Media cache size in kilobytes
pref("media.cache_size", 512000);
// When a network connection is suspended, don't resume it until the
// amount of buffered data falls below this threshold (in seconds).
pref("media.cache_resume_threshold", 999999);
// Stop reading ahead when our buffered data is this many seconds ahead
// of the current playback position. This limit can stop us from using arbitrary
// amounts of network bandwidth prefetching huge videos.