Bug 1366362: P3. Have seekToNextFrame properly handle dormant mode. r?jwwang
When coming out of dormant mode, we first perform an accurate seek, that will ensure the MediaDecoderReader has been reinitialised correctly.
MozReview-Commit-ID: 2rWgu9AGAcY
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -474,16 +474,18 @@ public:
}
// Calculate the position to seek to when exiting dormant.
auto t = mMaster->mMediaSink->IsStarted()
? mMaster->GetClock() : mMaster->GetMediaTime();
mPendingSeek.mTarget.emplace(t, SeekTarget::Accurate);
// SeekJob asserts |mTarget.IsValid() == !mPromise.IsEmpty()| so we
// need to create the promise even it is not used at all.
+ // The promise may be used when coming out of DormantState into
+ // SeekingState.
RefPtr<MediaDecoder::SeekPromise> x =
mPendingSeek.mPromise.Ensure(__func__);
// No need to call ResetDecode() and StopMediaSink() here.
// We will do them during seeking when exiting dormant.
// Ignore WAIT_FOR_DATA since we won't decode in dormant.
mMaster->mAudioWaitRequest.DisconnectIfExists();
@@ -496,16 +498,18 @@ public:
{
// mPendingSeek is either moved when exiting dormant or
// should be rejected here before transition to SHUTDOWN.
mPendingSeek.RejectIfExists(__func__);
}
State GetState() const override { return DECODER_STATE_DORMANT; }
+ RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
+
void HandleVideoSuspendTimeout() override
{
// Do nothing since we've released decoders in Enter().
}
void HandleResumeVideoDecoding(const TimeUnit&) override
{
// Do nothing since we won't resume decoding until exiting dormant.
@@ -1698,16 +1702,78 @@ private:
/*
* Internal state.
*/
TimeUnit mCurrentTime;
TimeUnit mDuration;
RefPtr<AysncNextFrameSeekTask> mAsyncSeekTask;
};
+class MediaDecoderStateMachine::NextFrameSeekingFromDormantState
+ : public MediaDecoderStateMachine::AccurateSeekingState
+{
+public:
+ explicit NextFrameSeekingFromDormantState(Master* aPtr)
+ : AccurateSeekingState(aPtr)
+ {
+ }
+
+ RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob&& aCurrentSeekJob,
+ SeekJob&& aFutureSeekJob)
+ {
+ mFutureSeekJob = Move(aFutureSeekJob);
+
+ SeekJob seekJob(Move(aCurrentSeekJob));
+ // Ensure that we don't transition to DecodingState once this seek
+ // completes.
+ seekJob.mTransition = false;
+
+ AccurateSeekingState::Enter(Move(seekJob), EventVisibility::Suppressed)
+ ->Then(OwnerThread(),
+ __func__,
+ [this]() {
+ mAccurateSeekRequest.Complete();
+ SetState<NextFrameSeekingState>(Move(mFutureSeekJob),
+ EventVisibility::Observable);
+ },
+ [this]() { mAccurateSeekRequest.Complete(); })
+ ->Track(mAccurateSeekRequest);
+ return mFutureSeekJob.mPromise.Ensure(__func__);
+ }
+
+ void Exit() override
+ {
+ mAccurateSeekRequest.DisconnectIfExists();
+ mFutureSeekJob.RejectIfExists(__func__);
+ AccurateSeekingState::Exit();
+ }
+
+private:
+ MozPromiseRequestHolder<MediaDecoder::SeekPromise> mAccurateSeekRequest;
+ SeekJob mFutureSeekJob;
+};
+
+RefPtr<MediaDecoder::SeekPromise>
+MediaDecoderStateMachine::DormantState::HandleSeek(SeekTarget aTarget)
+{
+ if (aTarget.IsNextFrame()) {
+ // NextFrameSeekingState doesn't reset the decoder unlike
+ // AccurateSeekingState. So we first must come out of dormant by seeking to
+ // mPendingSeek and continue later with the NextFrameSeek
+ SLOG("Changed state to SEEKING (to %" PRId64 ")",
+ aTarget.GetTime().ToMicroseconds());
+ SeekJob seekJob;
+ seekJob.mTarget = Some(aTarget);
+ return StateObject::SetState<NextFrameSeekingFromDormantState>(
+ Move(mPendingSeek), Move(seekJob));
+ }
+
+ return StateObject::HandleSeek(aTarget);
+}
+
/**
* Purpose: stop playback until enough data is decoded to continue playback.
*
* Transition to:
* SEEKING if any seek request.
* SHUTDOWN if any decode error.
* COMPLETED when having decoded all audio/video data.
* DECODING when having decoded enough data to continue playback.
@@ -2452,17 +2518,19 @@ SeekingState::SeekCompleted()
SLOG("Seek completed, mCurrentPosition=%" PRId64,
mMaster->mCurrentPosition.Ref().ToMicroseconds());
if (mMaster->VideoQueue().PeekFront()) {
mMaster->mMediaSink->Redraw(Info().mVideo);
mMaster->mOnPlaybackEvent.Notify(MediaEventType::Invalidate);
}
- SetState<DecodingState>();
+ if (mSeekJob.mTransition) {
+ SetState<DecodingState>();
+ }
}
void
MediaDecoderStateMachine::
BufferingState::DispatchDecodeTasksIfNeeded()
{
if (mMaster->IsAudioDecoding()
&& !mMaster->HaveEnoughDecodedAudio()
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -243,16 +243,17 @@ private:
class DecodeMetadataState;
class WaitForCDMState;
class DormantState;
class DecodingFirstFrameState;
class DecodingState;
class SeekingState;
class AccurateSeekingState;
class NextFrameSeekingState;
+ class NextFrameSeekingFromDormantState;
class BufferingState;
class CompletedState;
class ShutdownState;
static const char* ToStateStr(State aState);
static const char* ToStr(NextFrameStatus aStatus);
const char* ToStateStr();
--- a/dom/media/SeekJob.h
+++ b/dom/media/SeekJob.h
@@ -22,13 +22,14 @@ struct SeekJob
~SeekJob();
bool Exists() const;
void Resolve(const char* aCallSite);
void RejectIfExists(const char* aCallSite);
Maybe<SeekTarget> mTarget;
MozPromiseHolder<MediaDecoder::SeekPromise> mPromise;
+ bool mTransition = true;
};
} // namespace mozilla
#endif /* SEEK_JOB_H */