diff --git a/dom/media/ExternalEngineStateMachine.cpp b/dom/media/ExternalEngineStateMachine.cpp index 29d0dc3d42bc..1728728dc613 100644 --- a/dom/media/ExternalEngineStateMachine.cpp +++ b/dom/media/ExternalEngineStateMachine.cpp @@ -365,6 +365,58 @@ bool ExternalEngineStateMachine::IsFormatSupportedByExternalEngine( #endif } +RefPtr ExternalEngineStateMachine::InvokeSeek( + const SeekTarget& aTarget) { + return InvokeAsync( + OwnerThread(), __func__, + [self = RefPtr(this), this, + target = aTarget]() -> RefPtr { + AssertOnTaskQueue(); + if (!mEngine || !mEngine->IsInited()) { + LOG("Can't perform seek (%" PRId64 ") now, add a pending seek task", + target.GetTime().ToMicroseconds()); + // We haven't added any pending seek before + if (mPendingSeek.mPromise.IsEmpty()) { + mPendingTasks.AppendElement(NS_NewRunnableFunction( + "ExternalEngineStateMachine::InvokeSeek", + [self = RefPtr{this}, this] { + if (!mPendingSeek.Exists()) { + return; + } + Seek(*mPendingSeek.mTarget) + ->Then(OwnerThread(), __func__, + [self = RefPtr{this}, + this](const MediaDecoder::SeekPromise:: + ResolveOrRejectValue& aVal) { + mPendingSeekRequest.Complete(); + if (aVal.IsResolve()) { + mPendingSeek.Resolve(__func__); + } else { + mPendingSeek.RejectIfExists(__func__); + } + mPendingSeek = SeekJob(); + }) + ->Track(mPendingSeekRequest); + })); + } else { + // Reject previous pending promise, as we will create a new one + LOG("Replace previous pending seek with a new one"); + mPendingSeek.RejectIfExists(__func__); + mPendingSeekRequest.DisconnectIfExists(); + } + mPendingSeek.mTarget = Some(target); + return mPendingSeek.mPromise.Ensure(__func__); + } + if (mPendingSeek.Exists()) { + LOG("Discard pending seek because another new seek happens"); + mPendingSeek.RejectIfExists(__func__); + mPendingSeek = SeekJob(); + mPendingSeekRequest.DisconnectIfExists(); + } + return self->Seek(target); + }); +} + RefPtr ExternalEngineStateMachine::Seek( const SeekTarget& aTarget) { AssertOnTaskQueue(); @@ -571,6 +623,9 @@ RefPtr ExternalEngineStateMachine::Shutdown() { mSetCDMProxyPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_ABORT_ERR, __func__); mSetCDMProxyRequest.DisconnectIfExists(); + mPendingSeek.RejectIfExists(__func__); + mPendingSeekRequest.DisconnectIfExists(); + mPendingTasks.Clear(); if (mEngine) { diff --git a/dom/media/ExternalEngineStateMachine.h b/dom/media/ExternalEngineStateMachine.h index 798fff944cb2..83250b0f3c81 100644 --- a/dom/media/ExternalEngineStateMachine.h +++ b/dom/media/ExternalEngineStateMachine.h @@ -56,6 +56,9 @@ class ExternalEngineStateMachine final ExternalEngineStateMachine(MediaDecoder* aDecoder, MediaFormatReader* aReader); + RefPtr InvokeSeek( + const SeekTarget& aTarget) override; + RefPtr InvokeSetSink( const RefPtr& aSink) override; @@ -309,6 +312,11 @@ class ExternalEngineStateMachine final MozPromiseHolder mSetCDMProxyPromise; MozPromiseRequestHolder mSetCDMProxyRequest; + // If seek happens while the engine is still initializing, then we would + // postpone the seek until the engine is ready. + SeekJob mPendingSeek; + MozPromiseRequestHolder mPendingSeekRequest; + // It would be zero for audio-only playback. gfx::IntSize mVideoDisplay; diff --git a/dom/media/MediaDecoderStateMachineBase.h b/dom/media/MediaDecoderStateMachineBase.h index 58721510153f..fb172b2d2277 100644 --- a/dom/media/MediaDecoderStateMachineBase.h +++ b/dom/media/MediaDecoderStateMachineBase.h @@ -85,7 +85,8 @@ class MediaDecoderStateMachineBase { RefPtr BeginShutdown(); // Seeks to the decoder to aTarget asynchronously. - RefPtr InvokeSeek(const SeekTarget& aTarget); + virtual RefPtr InvokeSeek( + const SeekTarget& aTarget); virtual size_t SizeOfVideoQueue() const = 0; virtual size_t SizeOfAudioQueue() const = 0;