Bug 1309116. Part 2 - rewrite StateObject::SetState using variadic template.
MozReview-Commit-ID: 3ofT2po9B4Q
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -221,17 +221,35 @@ protected:
explicit StateObject(Master* aPtr) : mMaster(aPtr) {}
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(); }
// Note this function will delete the current state object.
// Don't access members to avoid UAF after this call.
- void SetState(State aState) { mMaster->SetState(aState); }
+ template <class S, typename... Ts>
+ void SetState(Ts&&... aArgs)
+ {
+ // keep mMaster in a local object because mMaster will become invalid after
+ // the current state object is deleted.
+ auto master = mMaster;
+
+ UniquePtr<StateObject> s = MakeUnique<S>(master, Forward<Ts>(aArgs)...);
+ if (master->mState == s->GetState()) {
+ return;
+ }
+
+ SLOG("change state to: %s", ToStateStr(s->GetState()));
+
+ Exit();
+ master->mState = s->GetState();
+ master->mStateObj = Move(s); // Will delete |this|!
+ master->mStateObj->Enter();
+ }
// Take a raw pointer in order not to change the life cycle of MDSM.
// It is guaranteed to be valid by MDSM.
Master* mMaster;
};
class MediaDecoderStateMachine::DecodeMetadataState
: public MediaDecoderStateMachine::StateObject
@@ -873,17 +891,17 @@ StateObject::HandleDormant(bool aDormant
mMaster->mQueuedSeek.mTarget =
SeekTarget(mMaster->mCurrentPosition,
SeekTarget::Accurate,
MediaDecoderEventVisibility::Suppressed);
// SeekJob asserts |mTarget.IsValid() == !mPromise.IsEmpty()| so we
// need to create the promise even it is not used at all.
RefPtr<MediaDecoder::SeekPromise> unused =
mMaster->mQueuedSeek.mPromise.Ensure(__func__);
- SetState(DECODER_STATE_DORMANT);
+ SetState<DormantState>();
return true;
}
void
MediaDecoderStateMachine::
DecodeMetadataState::OnMetadataRead(MetadataHolder* aMetadata)
{
mMetadataRequest.Complete();
@@ -931,59 +949,61 @@ DecodeMetadataState::OnMetadataRead(Meta
mMaster->mDuration.Ref().isSome() || waitingForCDM;
if (mMaster->mNotifyMetadataBeforeFirstFrame) {
mMaster->EnqueueLoadedMetadataEvent();
}
if (mPendingDormant) {
// No need to store mQueuedSeek because we are at position 0.
- SetState(DECODER_STATE_DORMANT);
+ SetState<DormantState>();
return;
}
if (waitingForCDM) {
// Metadata parsing was successful but we're still waiting for CDM caps
// to become available so that we can build the correct decryptor/decoder.
- SetState(DECODER_STATE_WAIT_FOR_CDM);
+ SetState<WaitForCDMState>();
return;
}
- SetState(DECODER_STATE_DECODING_FIRSTFRAME);
+ SetState<DecodingFirstFrameState>();
}
bool
MediaDecoderStateMachine::
WaitForCDMState::HandleDormant(bool aDormant)
{
if (aDormant) {
// No need to store mQueuedSeek because we are at position 0.
- SetState(DECODER_STATE_DORMANT);
+ SetState<DormantState>();
}
return true;
}
bool
MediaDecoderStateMachine::
DormantState::HandleDormant(bool aDormant)
{
if (!aDormant) {
// Exit dormant state. Check if we need the CDMProxy to start decoding.
- SetState(Info().IsEncrypted() && !mMaster->mCDMProxy
- ? DECODER_STATE_WAIT_FOR_CDM
- : DECODER_STATE_DECODING_FIRSTFRAME);
+ if (Info().IsEncrypted() && !mMaster->mCDMProxy) {
+ SetState<WaitForCDMState>();
+ } else {
+ SetState<DecodingFirstFrameState>();
+ }
}
return true;
}
bool
MediaDecoderStateMachine::
WaitForCDMState::HandleCDMProxyReady()
{
- SetState(DECODER_STATE_DECODING_FIRSTFRAME);
+ SetState<DecodingFirstFrameState>();
return true;
}
void
MediaDecoderStateMachine::
DecodingFirstFrameState::Enter()
{
// Handle pending seek.
@@ -991,17 +1011,17 @@ DecodingFirstFrameState::Enter()
(mMaster->mSentFirstFrameLoadedEvent ||
Reader()->ForceZeroStartTime())) {
mMaster->InitiateSeek(Move(mMaster->mQueuedSeek));
return;
}
// Transition to DECODING if we've decoded first frames.
if (mMaster->mSentFirstFrameLoadedEvent) {
- SetState(DECODER_STATE_DECODING);
+ SetState<DecodingState>();
return;
}
// Dispatch tasks to decode first frames.
mMaster->DispatchDecodeTasksIfNeeded();
}
RefPtr<MediaDecoder::SeekPromise>
@@ -1042,31 +1062,31 @@ DecodingFirstFrameState::MaybeFinishDeco
return;
}
mMaster->FinishDecodeFirstFrame();
if (mMaster->mQueuedSeek.Exists()) {
mMaster->InitiateSeek(Move(mMaster->mQueuedSeek));
} else {
- SetState(DECODER_STATE_DECODING);
+ SetState<DecodingState>();
}
}
void
MediaDecoderStateMachine::
DecodingState::Enter()
{
MOZ_ASSERT(mMaster->mSentFirstFrameLoadedEvent);
// Pending seek should've been handled by DECODING_FIRSTFRAME before
// transitioning to DECODING.
MOZ_ASSERT(!mMaster->mQueuedSeek.Exists());
if (mMaster->CheckIfDecodeComplete()) {
- SetState(DECODER_STATE_COMPLETED);
+ SetState<CompletedState>();
return;
}
mDecodeStartTime = TimeStamp::Now();
MaybeStopPrerolling();
// Ensure that we've got tasks enqueued to decode data if we need to.
@@ -1088,17 +1108,17 @@ DecodingState::HandleSeek(SeekTarget aTa
return p.forget();
}
bool
MediaDecoderStateMachine::
DecodingState::HandleEndOfStream()
{
if (mMaster->CheckIfDecodeComplete()) {
- SetState(DECODER_STATE_COMPLETED);
+ SetState<CompletedState>();
} else {
MaybeStopPrerolling();
}
return true;
}
bool
MediaDecoderStateMachine::
@@ -1113,17 +1133,17 @@ SeekingState::HandleDormant(bool aDorman
// method later, we treat a VideoOnly seek task as a normal Accurate
// seek task so that while it is resumed, both audio and video playback
// are handled.
if (mSeekJob.mTarget.IsVideoOnly()) {
mSeekJob.mTarget.SetType(SeekTarget::Accurate);
mSeekJob.mTarget.SetVideoOnly(false);
}
mMaster->mQueuedSeek = Move(mSeekJob);
- SetState(DECODER_STATE_DORMANT);
+ SetState<DormantState>();
return true;
}
RefPtr<MediaDecoder::SeekPromise>
MediaDecoderStateMachine::
SeekingState::HandleSeek(SeekTarget aTarget)
{
mMaster->mQueuedSeek.RejectIfExists(__func__);
@@ -1203,17 +1223,21 @@ SeekingState::SeekCompleted()
// Try to decode another frame to detect if we're at the end...
SLOG("Seek completed, mCurrentPosition=%lld", mMaster->mCurrentPosition.Ref());
if (video) {
mMaster->mMediaSink->Redraw(Info().mVideo);
mMaster->mOnPlaybackEvent.Notify(MediaEventType::Invalidate);
}
- SetState(nextState);
+ if (nextState == DECODER_STATE_COMPLETED) {
+ SetState<CompletedState>();
+ } else {
+ SetState<DecodingState>();
+ }
}
void
MediaDecoderStateMachine::
BufferingState::Step()
{
TimeStamp now = TimeStamp::Now();
MOZ_ASSERT(!mBufferingStart.IsNull(), "Must know buffering start time.");
@@ -1248,25 +1272,25 @@ BufferingState::Step()
SLOG("In buffering mode, waiting to be notified: outOfAudio: %d, "
"mAudioStatus: %s, outOfVideo: %d, mVideoStatus: %s",
mMaster->OutOfDecodedAudio(), mMaster->AudioRequestStatus(),
mMaster->OutOfDecodedVideo(), mMaster->VideoRequestStatus());
return;
}
SLOG("Buffered for %.3lfs", (now - mBufferingStart).ToSeconds());
- SetState(DECODER_STATE_DECODING);
+ SetState<DecodingState>();
}
bool
MediaDecoderStateMachine::
BufferingState::HandleEndOfStream()
{
if (mMaster->CheckIfDecodeComplete()) {
- SetState(DECODER_STATE_COMPLETED);
+ SetState<CompletedState>();
} else {
// Check if we can exit buffering.
mMaster->ScheduleStateMachine();
}
return true;
}
RefPtr<MediaDecoder::SeekPromise>