--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -179,16 +179,87 @@ static TimeDuration
SuspendBackgroundVideoDelay()
{
return TimeDuration::FromMilliseconds(
MediaPrefs::MDSMSuspendBackgroundVideoDelay());
}
class MediaDecoderStateMachine::StateObject
{
+protected:
+ class SeekHandler
+ {
+ public:
+ SeekHandler(StateObject* aObject) : mObject(aObject), mMaster(aObject->mMaster) {}
+ virtual ~SeekHandler() {}
+ virtual RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) = 0;
+ virtual bool PendingSeekExist() const { return false; }
+ virtual void SwitchToSeekingState()
+ {
+ MOZ_DIAGNOSTIC_ASSERT(false, "Only switch to SeekingState with a pending SeekJob.");
+ }
+
+ protected:
+ using Master = MediaDecoderStateMachine;
+ State GetState() const { return mObject->GetState(); }
+ StateObject* mObject;
+ Master* mMaster;
+ };
+
+ class DefaultSeekHandler final : public SeekHandler
+ {
+ public:
+ DefaultSeekHandler(StateObject* aObject) : SeekHandler(aObject) {}
+ RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
+ {
+ SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
+ SeekJob seekJob;
+ seekJob.mTarget = Some(aTarget);
+ return mObject->SetSeekingState(Move(seekJob), EventVisibility::Observable);
+ }
+ };
+
+ class RejectSeekHandler final : public SeekHandler
+ {
+ public:
+ RejectSeekHandler(StateObject* aObject) : SeekHandler(aObject) {}
+ RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
+ {
+ MOZ_DIAGNOSTIC_ASSERT(false, "Can't seek in this state.");
+ return MediaDecoder::SeekPromise::CreateAndReject(true, __func__);
+ }
+ };
+
+ class PendingSeekHandler final : public SeekHandler
+ {
+ public:
+ PendingSeekHandler(StateObject* aObject) : SeekHandler(aObject) {}
+ ~PendingSeekHandler() { mPendingSeek.RejectIfExists(__func__); }
+ RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
+ {
+ SLOG("Not Enough Data to seek at this stage, queuing seek");
+ mPendingSeek.RejectIfExists(__func__);
+ mPendingSeek.mTarget.emplace(aTarget);
+ return mPendingSeek.mPromise.Ensure(__func__);
+ }
+
+ bool PendingSeekExist() const override
+ {
+ return mPendingSeek.Exists();
+ }
+
+ virtual void SwitchToSeekingState() override
+ {
+ mObject->SetSeekingState(Move(mPendingSeek), EventVisibility::Observable);
+ }
+
+ private:
+ SeekJob mPendingSeek;
+ };
+
public:
virtual ~StateObject() { }
virtual void Exit() { } // Exit action.
virtual void Step() { } // Perform a 'cycle' of this state object.
virtual State GetState() const = 0;
// Event handlers for various events.
virtual void HandleCDMProxyReady() { }
@@ -229,17 +300,20 @@ public:
{
Crash("Unexpected event!", __func__);
}
virtual void HandleEndOfVideo()
{
Crash("Unexpected event!", __func__);
}
- virtual RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget);
+ virtual RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget)
+ {
+ return mSeekHandler->HandleSeek(aTarget);
+ }
virtual RefPtr<ShutdownPromise> HandleShutdown();
virtual void HandleVideoSuspendTimeout() = 0;
virtual void HandleResumeVideoDecoding();
virtual void HandlePlayStateChanged(MediaDecoder::PlayState aPlayState) {}
@@ -262,17 +336,17 @@ private:
protected:
enum class EventVisibility : int8_t
{
Observable,
Suppressed
};
using Master = MediaDecoderStateMachine;
- explicit StateObject(Master* aPtr) : mMaster(aPtr) {}
+ explicit StateObject(Master* aPtr) : mMaster(aPtr), mSeekHandler(new DefaultSeekHandler(this)) {}
TaskQueue* OwnerThread() const { return mMaster->mTaskQueue; }
MediaResource* Resource() const { return mMaster->mResource; }
MediaDecoderReaderWrapper* Reader() const { return mMaster->mReader; }
const MediaInfo& Info() const { return mMaster->Info(); }
bool IsExpectingMoreData() const
{
// We are expecting more data if either the resource states so, or if we
// have a waiting promise pending (such as with non-MSE EME).
@@ -307,31 +381,36 @@ protected:
}
RefPtr<MediaDecoder::SeekPromise>
SetSeekingState(SeekJob&& aSeekJob, EventVisibility aVisibility);
// Take a raw pointer in order not to change the life cycle of MDSM.
// It is guaranteed to be valid by MDSM.
Master* mMaster;
+
+ UniquePtr<SeekHandler> mSeekHandler;
};
/**
* Purpose: decode metadata like duration and dimensions of the media resource.
*
* Transition to other states when decoding metadata is done:
* SHUTDOWN if failing to decode metadata.
* WAIT_FOR_CDM if the media is encrypted and CDM is not available.
* DECODING_FIRSTFRAME otherwise.
*/
class MediaDecoderStateMachine::DecodeMetadataState
: public MediaDecoderStateMachine::StateObject
{
public:
- explicit DecodeMetadataState(Master* aPtr) : StateObject(aPtr) { }
+ explicit DecodeMetadataState(Master* aPtr) : StateObject(aPtr)
+ {
+ mSeekHandler.reset(new RejectSeekHandler(this));
+ }
void Enter()
{
MOZ_ASSERT(!mMaster->mVideoDecodeSuspended);
MOZ_ASSERT(!mMetadataRequest.Exists());
SLOG("Dispatching AsyncReadMetadata");
// Set mode to METADATA since we are about to read metadata.
@@ -355,22 +434,16 @@ public:
mMetadataRequest.DisconnectIfExists();
}
State GetState() const override
{
return DECODER_STATE_DECODING_METADATA;
}
- RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
- {
- MOZ_DIAGNOSTIC_ASSERT(false, "Can't seek while decoding metadata.");
- return MediaDecoder::SeekPromise::CreateAndReject(true, __func__);
- }
-
void HandleVideoSuspendTimeout() override
{
// Do nothing since no decoders are created yet.
}
void HandleResumeVideoDecoding() override
{
// We never suspend video decoding in this state.
@@ -396,58 +469,43 @@ private:
* Transition to other states when CDM is ready:
* SEEKING if any pending seek request.
* DECODING_FIRSTFRAME otherwise.
*/
class MediaDecoderStateMachine::WaitForCDMState
: public MediaDecoderStateMachine::StateObject
{
public:
- explicit WaitForCDMState(Master* aPtr) : StateObject(aPtr) { }
+ explicit WaitForCDMState(Master* aPtr) : StateObject(aPtr)
+ {
+ mSeekHandler.reset(new PendingSeekHandler(this));
+ }
void Enter()
{
MOZ_ASSERT(!mMaster->mVideoDecodeSuspended);
}
- void Exit() override
- {
- // mPendingSeek is either moved in HandleCDMProxyReady() or should be
- // rejected here before transition to SHUTDOWN.
- mPendingSeek.RejectIfExists(__func__);
- }
-
State GetState() const override
{
return DECODER_STATE_WAIT_FOR_CDM;
}
void HandleCDMProxyReady() override;
- RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
- {
- SLOG("Not Enough Data to seek at this stage, queuing seek");
- mPendingSeek.RejectIfExists(__func__);
- mPendingSeek.mTarget.emplace(aTarget);
- return mPendingSeek.mPromise.Ensure(__func__);
- }
-
void HandleVideoSuspendTimeout() override
{
// Do nothing since no decoders are created yet.
}
void HandleResumeVideoDecoding() override
{
// We never suspend video decoding in this state.
MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
}
-
-private:
- SeekJob mPendingSeek;
};
/**
* Purpose: release decoder resources to save memory and hardware resources.
*
* Transition to:
* SEEKING if any seek request or play state changes to PLAYING.
*/
@@ -512,27 +570,26 @@ private:
* SHUTDOWN if any decode error.
* SEEKING if any seek request.
* DECODING when the 'loadeddata' event is fired.
*/
class MediaDecoderStateMachine::DecodingFirstFrameState
: public MediaDecoderStateMachine::StateObject
{
public:
- explicit DecodingFirstFrameState(Master* aPtr) : StateObject(aPtr) { }
+ explicit DecodingFirstFrameState(Master* aPtr) : StateObject(aPtr)
+ {
+ // Delay seek request until decoding first frames for non-MSE media.
+ if (!mMaster->mIsMSE) {
+ mSeekHandler.reset(new PendingSeekHandler(this));
+ }
+ }
void Enter();
- void Exit() override
- {
- // mPendingSeek is either moved in MaybeFinishDecodeFirstFrame()
- // or should be rejected here before transition to SHUTDOWN.
- mPendingSeek.RejectIfExists(__func__);
- }
-
State GetState() const override
{
return DECODER_STATE_DECODING_FIRSTFRAME;
}
void HandleAudioDecoded(MediaData* aAudio) override
{
mMaster->PushAudio(aAudio);
@@ -594,34 +651,21 @@ public:
}
void HandleResumeVideoDecoding() override
{
// We never suspend video decoding in this state.
MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
}
- RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
- {
- if (mMaster->mIsMSE) {
- return StateObject::HandleSeek(aTarget);
- }
- // Delay seek request until decoding first frames for non-MSE media.
- SLOG("Not Enough Data to seek at this stage, queuing seek");
- mPendingSeek.RejectIfExists(__func__);
- mPendingSeek.mTarget.emplace(aTarget);
- return mPendingSeek.mPromise.Ensure(__func__);
- }
private:
// Notify FirstFrameLoaded if having decoded first frames and
// transition to SEEKING if there is any pending seek, or DECODING otherwise.
void MaybeFinishDecodeFirstFrame();
-
- SeekJob mPendingSeek;
};
/**
* Purpose: decode audio/video data for playback.
*
* Transition to:
* DORMANT if playback is paused for a while.
* SEEKING if any seek request.
@@ -1865,36 +1909,33 @@ private:
*
* Transition from:
* Any states other than SHUTDOWN.
*/
class MediaDecoderStateMachine::ShutdownState
: public MediaDecoderStateMachine::StateObject
{
public:
- explicit ShutdownState(Master* aPtr) : StateObject(aPtr) { }
+ explicit ShutdownState(Master* aPtr) : StateObject(aPtr)
+ {
+ mSeekHandler.reset(new RejectSeekHandler(this));
+ }
RefPtr<ShutdownPromise> Enter();
void Exit() override
{
MOZ_DIAGNOSTIC_ASSERT(false, "Shouldn't escape the SHUTDOWN state.");
}
State GetState() const override
{
return DECODER_STATE_SHUTDOWN;
}
- RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
- {
- MOZ_DIAGNOSTIC_ASSERT(false, "Can't seek in shutdown state.");
- return MediaDecoder::SeekPromise::CreateAndReject(true, __func__);
- }
-
RefPtr<ShutdownPromise> HandleShutdown() override
{
MOZ_DIAGNOSTIC_ASSERT(false, "Already shutting down.");
return nullptr;
}
void HandleVideoSuspendTimeout() override
{
@@ -1902,25 +1943,16 @@ public:
}
void HandleResumeVideoDecoding() override
{
MOZ_DIAGNOSTIC_ASSERT(false, "Already shutting down.");
}
};
-RefPtr<MediaDecoder::SeekPromise>
-MediaDecoderStateMachine::
-StateObject::HandleSeek(SeekTarget aTarget)
-{
- SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
- SeekJob seekJob;
- seekJob.mTarget = Some(aTarget);
- return SetSeekingState(Move(seekJob), EventVisibility::Observable);
-}
RefPtr<ShutdownPromise>
MediaDecoderStateMachine::
StateObject::HandleShutdown()
{
return SetState<ShutdownState>();
}
@@ -2075,18 +2107,18 @@ DormantState::HandlePlayStateChanged(Med
SetSeekingState(Move(mPendingSeek), EventVisibility::Suppressed);
}
}
void
MediaDecoderStateMachine::
WaitForCDMState::HandleCDMProxyReady()
{
- if (mPendingSeek.Exists()) {
- SetSeekingState(Move(mPendingSeek), EventVisibility::Observable);
+ if (mSeekHandler->PendingSeekExist()) {
+ mSeekHandler->SwitchToSeekingState();
} else {
SetState<DecodingFirstFrameState>();
}
}
void
MediaDecoderStateMachine::
DecodingFirstFrameState::Enter()
@@ -2115,18 +2147,18 @@ DecodingFirstFrameState::MaybeFinishDeco
MOZ_ASSERT(!mMaster->mSentFirstFrameLoadedEvent);
if ((mMaster->IsAudioDecoding() && AudioQueue().GetSize() == 0)
|| (mMaster->IsVideoDecoding() && VideoQueue().GetSize() == 0)) {
return;
}
mMaster->FinishDecodeFirstFrame();
- if (mPendingSeek.Exists()) {
- SetSeekingState(Move(mPendingSeek), EventVisibility::Observable);
+ if (mSeekHandler->PendingSeekExist()) {
+ mSeekHandler->SwitchToSeekingState();
} else {
SetState<DecodingState>();
}
}
void
MediaDecoderStateMachine::
DecodingState::Enter()