diff options
Diffstat (limited to 'dom/media/webcodecs')
-rw-r--r-- | dom/media/webcodecs/DecoderAgent.cpp | 8 | ||||
-rw-r--r-- | dom/media/webcodecs/DecoderTemplate.cpp | 102 | ||||
-rw-r--r-- | dom/media/webcodecs/DecoderTemplate.h | 39 | ||||
-rw-r--r-- | dom/media/webcodecs/EncoderTemplate.cpp | 195 | ||||
-rw-r--r-- | dom/media/webcodecs/EncoderTemplate.h | 17 | ||||
-rw-r--r-- | dom/media/webcodecs/VideoEncoder.cpp | 15 | ||||
-rw-r--r-- | dom/media/webcodecs/crashtests/1889831.html | 21 | ||||
-rw-r--r-- | dom/media/webcodecs/crashtests/crashtests.list | 2 |
8 files changed, 221 insertions, 178 deletions
diff --git a/dom/media/webcodecs/DecoderAgent.cpp b/dom/media/webcodecs/DecoderAgent.cpp index 095852c01d..f7a539fa18 100644 --- a/dom/media/webcodecs/DecoderAgent.cpp +++ b/dom/media/webcodecs/DecoderAgent.cpp @@ -96,16 +96,18 @@ RefPtr<DecoderAgent::ConfigurePromise> DecoderAgent::Configure( auto params = CreateDecoderParams{ *mInfo, CreateDecoderParams::OptionSet( - CreateDecoderParams::Option::LowLatency, aPreferSoftwareDecoder ? CreateDecoderParams::Option::HardwareDecoderNotAllowed : CreateDecoderParams::Option::Default), mInfo->GetType(), mImageContainer, knowsCompositor}; + if (aLowLatency) { + params.mOptions += CreateDecoderParams::Option::LowLatency; + } LOG("DecoderAgent #%d (%p) is creating a decoder - PreferSW: %s, " - "low-latency: %syes", + "low-latency: %s", mId, this, aPreferSoftwareDecoder ? "yes" : "no", - aLowLatency ? "" : "forcibly "); + aLowLatency ? "yes" : "no"); RefPtr<ConfigurePromise> p = mConfigurePromise.Ensure(__func__); diff --git a/dom/media/webcodecs/DecoderTemplate.cpp b/dom/media/webcodecs/DecoderTemplate.cpp index 2fc2471a24..896f83b352 100644 --- a/dom/media/webcodecs/DecoderTemplate.cpp +++ b/dom/media/webcodecs/DecoderTemplate.cpp @@ -85,28 +85,26 @@ DecoderTemplate<DecoderType>::ConfigureMessage::Create( template <typename DecoderType> DecoderTemplate<DecoderType>::DecodeMessage::DecodeMessage( - Id aId, ConfigId aConfigId, UniquePtr<InputTypeInternal>&& aData) + SeqId aSeqId, ConfigId aConfigId, UniquePtr<InputTypeInternal>&& aData) : ControlMessage( - nsPrintfCString("decode #%zu (config #%d)", aId, aConfigId)), - mId(aId), + nsPrintfCString("decode #%zu (config #%d)", aSeqId, aConfigId)), + mSeqId(aSeqId), mData(std::move(aData)) {} -template <typename DecoderType> -DecoderTemplate<DecoderType>::FlushMessage::FlushMessage(Id aId, - ConfigId aConfigId, - Promise* aPromise) - : ControlMessage( - nsPrintfCString("flush #%zu (config #%d)", aId, aConfigId)), - mId(aId), - mPromise(aPromise) {} +static int64_t GenerateUniqueId() { + // This needs to be atomic since this can run on the main thread or worker + // thread. + static std::atomic<int64_t> sNextId = 0; + return ++sNextId; +} template <typename DecoderType> -void DecoderTemplate<DecoderType>::FlushMessage::RejectPromiseIfAny( - const nsresult& aReason) { - if (mPromise) { - mPromise->MaybeReject(aReason); - } -} +DecoderTemplate<DecoderType>::FlushMessage::FlushMessage(SeqId aSeqId, + ConfigId aConfigId) + : ControlMessage( + nsPrintfCString("flush #%zu (config #%d)", aSeqId, aConfigId)), + mSeqId(aSeqId), + mUniqueId(GenerateUniqueId()) {} /* * Below are DecoderTemplate implementation @@ -221,10 +219,16 @@ already_AddRefed<Promise> DecoderTemplate<DecoderType>::Flush( mKeyChunkRequired = true; - mControlMessageQueue.emplace(UniquePtr<ControlMessage>( - new FlushMessage(++mFlushCounter, mLatestConfigureId, p))); - LOG("%s %p enqueues %s", DecoderType::Name.get(), this, - mControlMessageQueue.back()->ToString().get()); + auto msg = UniquePtr<ControlMessage>( + new FlushMessage(++mFlushCounter, mLatestConfigureId)); + const auto flushPromiseId = msg->AsFlushMessage()->mUniqueId; + MOZ_ASSERT(!mPendingFlushPromises.Contains(flushPromiseId)); + mPendingFlushPromises.Insert(flushPromiseId, p); + + mControlMessageQueue.emplace(std::move(msg)); + + LOG("%s %p enqueues %s, with unique id %" PRId64, DecoderType::Name.get(), + this, mControlMessageQueue.back()->ToString().get(), flushPromiseId); ProcessControlMessageQueue(); return p.forget(); } @@ -264,7 +268,7 @@ Result<Ok, nsresult> DecoderTemplate<DecoderType>::ResetInternal( mDecodeCounter = 0; mFlushCounter = 0; - CancelPendingControlMessages(aResult); + CancelPendingControlMessagesAndFlushPromises(aResult); DestroyDecoderAgentIfAny(); if (mDecodeQueueSize > 0) { @@ -390,7 +394,7 @@ void DecoderTemplate<DecoderType>::ProcessControlMessageQueue() { } template <typename DecoderType> -void DecoderTemplate<DecoderType>::CancelPendingControlMessages( +void DecoderTemplate<DecoderType>::CancelPendingControlMessagesAndFlushPromises( const nsresult& aResult) { AssertIsOnOwningThread(); @@ -399,11 +403,6 @@ void DecoderTemplate<DecoderType>::CancelPendingControlMessages( LOG("%s %p cancels current %s", DecoderType::Name.get(), this, mProcessingMessage->ToString().get()); mProcessingMessage->Cancel(); - - if (FlushMessage* flush = mProcessingMessage->AsFlushMessage()) { - flush->RejectPromiseIfAny(aResult); - } - mProcessingMessage.reset(); } @@ -411,14 +410,18 @@ void DecoderTemplate<DecoderType>::CancelPendingControlMessages( while (!mControlMessageQueue.empty()) { LOG("%s %p cancels pending %s", DecoderType::Name.get(), this, mControlMessageQueue.front()->ToString().get()); - MOZ_ASSERT(!mControlMessageQueue.front()->IsProcessing()); - if (FlushMessage* flush = mControlMessageQueue.front()->AsFlushMessage()) { - flush->RejectPromiseIfAny(aResult); - } - mControlMessageQueue.pop(); } + + // If there are pending flush promises, reject them. + mPendingFlushPromises.ForEach( + [&](const int64_t& id, const RefPtr<Promise>& p) { + LOG("%s %p, reject the promise for flush %" PRId64 " (unique id)", + DecoderType::Name.get(), this, id); + p->MaybeReject(aResult); + }); + mPendingFlushPromises.Clear(); } template <typename DecoderType> @@ -565,7 +568,6 @@ MessageProcessedResult DecoderTemplate<DecoderType>::ProcessDecodeMessage( mProcessingMessage.reset(); QueueATask("Error during decode", [self = RefPtr{this}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { - MOZ_ASSERT(self->mState != CodecState::Closed); self->CloseInternal(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); }); return MessageProcessedResult::Processed; @@ -696,6 +698,8 @@ MessageProcessedResult DecoderTemplate<DecoderType>::ProcessFlushMessage( msg->Complete(); + const auto flushPromiseId = msg->mUniqueId; + // If flush failed, it means decoder fails to decode the data // sent before, so we treat it like decode error. We reject // the promise first and then queue a task to close @@ -705,14 +709,15 @@ MessageProcessedResult DecoderTemplate<DecoderType>::ProcessFlushMessage( LOGE("%s %p, DecoderAgent #%d failed to flush: %s", DecoderType::Name.get(), self.get(), id, error.Description().get()); - RefPtr<Promise> promise = msg->TakePromise(); // Reject with an EncodingError instead of the error we got // above. self->QueueATask( "Error during flush runnable", - [self = RefPtr{this}, promise]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { - promise->MaybeReject( - NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); + [self = RefPtr{this}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { + // If Reset() was invoked before this task executes, the + // promise in mPendingFlushPromises is handled there. + // Otherwise, the promise is going to be rejected by + // CloseInternal() below. self->mProcessingMessage.reset(); MOZ_ASSERT(self->mState != CodecState::Closed); self->CloseInternal( @@ -733,14 +738,23 @@ MessageProcessedResult DecoderTemplate<DecoderType>::ProcessFlushMessage( msgStr.get()); } - RefPtr<Promise> promise = msg->TakePromise(); self->QueueATask( "Flush: output decoding data task", - [self = RefPtr{self}, promise, data = std::move(data)]() - MOZ_CAN_RUN_SCRIPT_BOUNDARY { - self->OutputDecodedData(std::move(data)); - promise->MaybeResolveWithUndefined(); - }); + [self = RefPtr{self}, data = std::move(data), + flushPromiseId]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { + self->OutputDecodedData(std::move(data)); + // If Reset() was invoked before this task executes, or + // during the output callback above in the execution of this + // task, the promise in mPendingFlushPromises is handled + // there. Otherwise, the promise is resolved here. + if (Maybe<RefPtr<Promise>> p = + self->mPendingFlushPromises.Take(flushPromiseId)) { + LOG("%s %p, resolving the promise for flush %" PRId64 + " (unique id)", + DecoderType::Name.get(), self.get(), flushPromiseId); + p.value()->MaybeResolveWithUndefined(); + } + }); self->mProcessingMessage.reset(); self->ProcessControlMessageQueue(); }) diff --git a/dom/media/webcodecs/DecoderTemplate.h b/dom/media/webcodecs/DecoderTemplate.h index fe0cb5baee..69d4e2f03d 100644 --- a/dom/media/webcodecs/DecoderTemplate.h +++ b/dom/media/webcodecs/DecoderTemplate.h @@ -9,6 +9,8 @@ #include <queue> +#include "SimpleMap.h" +#include "WebCodecsUtils.h" #include "mozilla/DOMEventTargetHelper.h" #include "mozilla/DecoderAgent.h" #include "mozilla/MozPromise.h" @@ -18,7 +20,6 @@ #include "mozilla/dom/WorkerRef.h" #include "mozilla/media/MediaUtils.h" #include "nsStringFwd.h" -#include "WebCodecsUtils.h" namespace mozilla { @@ -76,7 +77,8 @@ class DecoderTemplate : public DOMEventTargetHelper { const ConfigTypeInternal& Config() { return *mConfig; } UniquePtr<ConfigTypeInternal> TakeConfig() { return std::move(mConfig); } - const Id mId; // A unique id shown in log. + // The id of a configure request. + const Id mId; private: ConfigureMessage(Id aId, UniquePtr<ConfigTypeInternal>&& aConfig); @@ -88,16 +90,18 @@ class DecoderTemplate : public DOMEventTargetHelper { : public ControlMessage, public MessageRequestHolder<DecoderAgent::DecodePromise> { public: - using Id = size_t; + using SeqId = size_t; using ConfigId = typename Self::ConfigureMessage::Id; - DecodeMessage(Id aId, ConfigId aConfigId, + DecodeMessage(SeqId aSeqId, ConfigId aConfigId, UniquePtr<InputTypeInternal>&& aData); ~DecodeMessage() = default; virtual void Cancel() override { Disconnect(); } virtual bool IsProcessing() override { return Exists(); }; virtual DecodeMessage* AsDecodeMessage() override { return this; } - const Id mId; // A unique id shown in log. + // The sequence id of a decode request associated with a specific + // configuration. + const SeqId mSeqId; UniquePtr<InputTypeInternal> mData; }; @@ -105,20 +109,18 @@ class DecoderTemplate : public DOMEventTargetHelper { : public ControlMessage, public MessageRequestHolder<DecoderAgent::DecodePromise> { public: - using Id = size_t; + using SeqId = size_t; using ConfigId = typename Self::ConfigureMessage::Id; - FlushMessage(Id aId, ConfigId aConfigId, Promise* aPromise); + FlushMessage(SeqId aSeqId, ConfigId aConfigId); ~FlushMessage() = default; virtual void Cancel() override { Disconnect(); } virtual bool IsProcessing() override { return Exists(); }; virtual FlushMessage* AsFlushMessage() override { return this; } - already_AddRefed<Promise> TakePromise() { return mPromise.forget(); } - void RejectPromiseIfAny(const nsresult& aReason); - - const Id mId; // A unique id shown in log. - private: - RefPtr<Promise> mPromise; + // The sequence id of a flush request associated with a specific + // configuration. + const SeqId mSeqId; + const int64_t mUniqueId; }; protected: @@ -176,7 +178,7 @@ class DecoderTemplate : public DOMEventTargetHelper { nsresult FireEvent(nsAtom* aTypeWithOn, const nsAString& aEventType); void ProcessControlMessageQueue(); - void CancelPendingControlMessages(const nsresult& aResult); + void CancelPendingControlMessagesAndFlushPromises(const nsresult& aResult); // Queue a task to the control thread. This is to be used when a task needs to // perform multiple steps. @@ -209,6 +211,11 @@ class DecoderTemplate : public DOMEventTargetHelper { std::queue<UniquePtr<ControlMessage>> mControlMessageQueue; UniquePtr<ControlMessage> mProcessingMessage; + // When a flush request is initiated, a promise is created and stored in + // mPendingFlushPromises until it is settled in the task delivering the flush + // result or Reset() is called before the promise is settled. + SimpleMap<int64_t, RefPtr<Promise>> mPendingFlushPromises; + uint32_t mDecodeQueueSize; bool mDequeueEventScheduled; @@ -216,10 +223,10 @@ class DecoderTemplate : public DOMEventTargetHelper { // DecoderAgent's Id. uint32_t mLatestConfigureId; // Tracking how many decode data has been enqueued and this number will be - // used as the DecodeMessage's Id. + // used as the DecodeMessage's sequence Id. size_t mDecodeCounter; // Tracking how many flush request has been enqueued and this number will be - // used as the FlushMessage's Id. + // used as the FlushMessage's sequence Id. size_t mFlushCounter; // DecoderAgent will be created every time "configure" is being processed, and diff --git a/dom/media/webcodecs/EncoderTemplate.cpp b/dom/media/webcodecs/EncoderTemplate.cpp index 34edfae822..2f70380519 100644 --- a/dom/media/webcodecs/EncoderTemplate.cpp +++ b/dom/media/webcodecs/EncoderTemplate.cpp @@ -71,16 +71,8 @@ EncoderTemplate<EncoderType>::EncodeMessage::EncodeMessage( template <typename EncoderType> EncoderTemplate<EncoderType>::FlushMessage::FlushMessage( - WebCodecsId aConfigureId, Promise* aPromise) - : ControlMessage(aConfigureId), mPromise(aPromise) {} - -template <typename EncoderType> -void EncoderTemplate<EncoderType>::FlushMessage::RejectPromiseIfAny( - const nsresult& aReason) { - if (mPromise) { - mPromise->MaybeReject(aReason); - } -} + WebCodecsId aConfigureId) + : ControlMessage(aConfigureId) {} /* * Below are EncoderTemplate implementation @@ -215,7 +207,13 @@ already_AddRefed<Promise> EncoderTemplate<EncoderType>::Flush( return p.forget(); } - mControlMessageQueue.push(MakeRefPtr<FlushMessage>(mLatestConfigureId, p)); + auto msg = MakeRefPtr<FlushMessage>(mLatestConfigureId); + const auto flushPromiseId = static_cast<int64_t>(msg->mMessageId); + MOZ_ASSERT(!mPendingFlushPromises.Contains(flushPromiseId)); + mPendingFlushPromises.Insert(flushPromiseId, p); + + mControlMessageQueue.emplace(std::move(msg)); + LOG("%s %p enqueues %s", EncoderType::Name.get(), this, mControlMessageQueue.back()->ToString().get()); ProcessControlMessageQueue(); @@ -259,7 +257,7 @@ Result<Ok, nsresult> EncoderTemplate<EncoderType>::ResetInternal( mEncodeCounter = 0; mFlushCounter = 0; - CancelPendingControlMessages(aResult); + CancelPendingControlMessagesAndFlushPromises(aResult); DestroyEncoderAgentIfAny(); if (mEncodeQueueSize > 0) { @@ -403,8 +401,8 @@ void EncoderTemplate<VideoEncoderTraits>::OutputEncodedVideoData( metadata.mDecoderConfig.Construct(std::move(decoderConfig)); mOutputNewDecoderConfig = false; - LOGE("New config passed to output callback: %s", - decoderConfigInternal.ToString().get()); + LOG("New config passed to output callback: %s", + decoderConfigInternal.ToString().get()); } nsAutoCString metadataInfo; @@ -462,7 +460,7 @@ void EncoderTemplate<AudioEncoderTraits>::OutputEncodedAudioData( this->EncoderConfigToDecoderConfig(GetParentObject(), data, *mActiveConfig); - // Convert VideoDecoderConfigInternal to VideoDecoderConfig + // Convert AudioDecoderConfigInternal to AudioDecoderConfig RootedDictionary<AudioDecoderConfig> decoderConfig(cx); decoderConfig.mCodec = decoderConfigInternal.mCodec; decoderConfig.mNumberOfChannels = decoderConfigInternal.mNumberOfChannels; @@ -473,8 +471,8 @@ void EncoderTemplate<AudioEncoderTraits>::OutputEncodedAudioData( metadata.mDecoderConfig.Construct(std::move(decoderConfig)); mOutputNewDecoderConfig = false; - LOGE("New config passed to output callback: %s", - decoderConfigInternal.ToString().get()); + LOG("New config passed to output callback: %s", + decoderConfigInternal.ToString().get()); } nsAutoCString metadataInfo; @@ -578,7 +576,7 @@ void EncoderTemplate<EncoderType>::ProcessControlMessageQueue() { } template <typename EncoderType> -void EncoderTemplate<EncoderType>::CancelPendingControlMessages( +void EncoderTemplate<EncoderType>::CancelPendingControlMessagesAndFlushPromises( const nsresult& aResult) { AssertIsOnOwningThread(); @@ -587,11 +585,6 @@ void EncoderTemplate<EncoderType>::CancelPendingControlMessages( LOG("%s %p cancels current %s", EncoderType::Name.get(), this, mProcessingMessage->ToString().get()); mProcessingMessage->Cancel(); - - if (RefPtr<FlushMessage> flush = mProcessingMessage->AsFlushMessage()) { - flush->RejectPromiseIfAny(aResult); - } - mProcessingMessage = nullptr; } @@ -601,13 +594,17 @@ void EncoderTemplate<EncoderType>::CancelPendingControlMessages( mControlMessageQueue.front()->ToString().get()); MOZ_ASSERT(!mControlMessageQueue.front()->IsProcessing()); - if (RefPtr<FlushMessage> flush = - mControlMessageQueue.front()->AsFlushMessage()) { - flush->RejectPromiseIfAny(aResult); - } - mControlMessageQueue.pop(); } + + // If there are pending flush promises, reject them. + mPendingFlushPromises.ForEach( + [&](const int64_t& id, const RefPtr<Promise>& p) { + LOG("%s %p, reject the promise for flush %" PRId64, + EncoderType::Name.get(), this, id); + p->MaybeReject(aResult); + }); + mPendingFlushPromises.Clear(); } template <typename EncoderType> @@ -1020,78 +1017,88 @@ MessageProcessedResult EncoderTemplate<EncoderType>::ProcessFlushMessage( } mAgent->Drain() - ->Then( - GetCurrentSerialEventTarget(), __func__, - [self = RefPtr{this}, id = mAgent->mId, aMessage, - this](EncoderAgent::EncodePromise::ResolveOrRejectValue&& aResult) { - MOZ_ASSERT(self->mProcessingMessage); - MOZ_ASSERT(self->mProcessingMessage->AsFlushMessage()); - MOZ_ASSERT(self->mState == CodecState::Configured); - MOZ_ASSERT(self->mAgent); - MOZ_ASSERT(id == self->mAgent->mId); - MOZ_ASSERT(self->mActiveConfig); + ->Then(GetCurrentSerialEventTarget(), __func__, + [self = RefPtr{this}, id = mAgent->mId, aMessage, this]( + EncoderAgent::EncodePromise::ResolveOrRejectValue&& aResult) { + MOZ_ASSERT(self->mProcessingMessage); + MOZ_ASSERT(self->mProcessingMessage->AsFlushMessage()); + MOZ_ASSERT(self->mState == CodecState::Configured); + MOZ_ASSERT(self->mAgent); + MOZ_ASSERT(id == self->mAgent->mId); + MOZ_ASSERT(self->mActiveConfig); - LOG("%s %p, EncoderAgent #%zu %s has been %s", - EncoderType::Name.get(), self.get(), id, - aMessage->ToString().get(), - aResult.IsResolve() ? "resolved" : "rejected"); + LOG("%s %p, EncoderAgent #%zu %s has been %s", + EncoderType::Name.get(), self.get(), id, + aMessage->ToString().get(), + aResult.IsResolve() ? "resolved" : "rejected"); - nsCString msgStr = aMessage->ToString(); + nsCString msgStr = aMessage->ToString(); - aMessage->Complete(); + aMessage->Complete(); - // If flush failed, it means encoder fails to encode the data - // sent before, so we treat it like an encode error. We reject - // the promise first and then queue a task to close VideoEncoder - // with an EncodingError. - if (aResult.IsReject()) { - const MediaResult& error = aResult.RejectValue(); - LOGE("%s %p, EncoderAgent #%zu failed to flush: %s", - EncoderType::Name.get(), self.get(), id, - error.Description().get()); - RefPtr<Promise> promise = aMessage->TakePromise(); - // Reject with an EncodingError instead of the error we got - // above. - self->QueueATask( - "Error during flush runnable", - [self = RefPtr{this}, promise]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { - promise->MaybeReject( - NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); - self->mProcessingMessage = nullptr; - MOZ_ASSERT(self->mState != CodecState::Closed); - self->CloseInternal( - NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); - }); - return; - } + // If flush failed, it means encoder fails to encode the data + // sent before, so we treat it like an encode error. We reject + // the promise first and then queue a task to close VideoEncoder + // with an EncodingError. + if (aResult.IsReject()) { + const MediaResult& error = aResult.RejectValue(); + LOGE("%s %p, EncoderAgent #%zu failed to flush: %s", + EncoderType::Name.get(), self.get(), id, + error.Description().get()); + // Reject with an EncodingError instead of the error we got + // above. + self->QueueATask( + "Error during flush runnable", + [self = RefPtr{this}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { + // If Reset() was invoked before this task executes, the + // promise in mPendingFlushPromises is handled there. + // Otherwise, the promise is going to be rejected by + // CloseInternal() below. + self->mProcessingMessage = nullptr; + MOZ_ASSERT(self->mState != CodecState::Closed); + self->CloseInternal( + NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); + }); + return; + } - // If flush succeeded, schedule to output encoded data first - // and then resolve the promise, then keep processing the - // control messages. - MOZ_ASSERT(aResult.IsResolve()); - nsTArray<RefPtr<MediaRawData>> data = - std::move(aResult.ResolveValue()); - - if (data.IsEmpty()) { - LOG("%s %p gets no data for %s", EncoderType::Name.get(), - self.get(), msgStr.get()); - } else { - LOG("%s %p, schedule %zu encoded data output for %s", - EncoderType::Name.get(), self.get(), data.Length(), - msgStr.get()); - } + // If flush succeeded, schedule to output encoded data first + // and then resolve the promise, then keep processing the + // control messages. + MOZ_ASSERT(aResult.IsResolve()); + nsTArray<RefPtr<MediaRawData>> data = + std::move(aResult.ResolveValue()); - RefPtr<Promise> promise = aMessage->TakePromise(); - self->QueueATask( - "Flush: output encoded data task", - [self = RefPtr{self}, promise, data = std::move(data)]() - MOZ_CAN_RUN_SCRIPT_BOUNDARY { - self->OutputEncodedData(std::move(data)); - promise->MaybeResolveWithUndefined(); - }); - self->mProcessingMessage = nullptr; - self->ProcessControlMessageQueue(); - }) + if (data.IsEmpty()) { + LOG("%s %p gets no data for %s", EncoderType::Name.get(), + self.get(), msgStr.get()); + } else { + LOG("%s %p, schedule %zu encoded data output for %s", + EncoderType::Name.get(), self.get(), data.Length(), + msgStr.get()); + } + + const auto flushPromiseId = + static_cast<int64_t>(aMessage->mMessageId); + self->QueueATask( + "Flush: output encoded data task", + [self = RefPtr{self}, data = std::move(data), + flushPromiseId]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { + self->OutputEncodedData(std::move(data)); + // If Reset() was invoked before this task executes, or + // during the output callback above in the execution of + // this task, the promise in mPendingFlushPromises is + // handled there. Otherwise, the promise is resolved here. + if (Maybe<RefPtr<Promise>> p = + self->mPendingFlushPromises.Take(flushPromiseId)) { + LOG("%s %p, resolving the promise for flush %" PRId64, + EncoderType::Name.get(), self.get(), flushPromiseId); + p.value()->MaybeResolveWithUndefined(); + } + }); + self->mProcessingMessage = nullptr; + self->ProcessControlMessageQueue(); + }) ->Track(aMessage->Request()); return MessageProcessedResult::Processed; diff --git a/dom/media/webcodecs/EncoderTemplate.h b/dom/media/webcodecs/EncoderTemplate.h index bc65edca46..ecadf68681 100644 --- a/dom/media/webcodecs/EncoderTemplate.h +++ b/dom/media/webcodecs/EncoderTemplate.h @@ -11,14 +11,15 @@ #include "EncoderAgent.h" #include "MediaData.h" +#include "SimpleMap.h" #include "WebCodecsUtils.h" #include "mozilla/DOMEventTargetHelper.h" #include "mozilla/MozPromise.h" #include "mozilla/RefPtr.h" #include "mozilla/Result.h" #include "mozilla/UniquePtr.h" -#include "mozilla/dom/VideoEncoderBinding.h" #include "mozilla/dom/AudioEncoderBinding.h" +#include "mozilla/dom/VideoEncoderBinding.h" #include "mozilla/dom/WorkerRef.h" #include "mozilla/media/MediaUtils.h" #include "nsStringFwd.h" @@ -116,12 +117,10 @@ class EncoderTemplate : public DOMEventTargetHelper { : public ControlMessage, public MessageRequestHolder<EncoderAgent::EncodePromise> { public: - FlushMessage(WebCodecsId aConfigureId, Promise* aPromise); + explicit FlushMessage(WebCodecsId aConfigureId); virtual void Cancel() override { Disconnect(); } virtual bool IsProcessing() override { return Exists(); }; virtual RefPtr<FlushMessage> AsFlushMessage() override { return this; } - already_AddRefed<Promise> TakePromise() { return mPromise.forget(); } - void RejectPromiseIfAny(const nsresult& aReason); nsCString ToString() const override { nsCString rv; @@ -129,9 +128,6 @@ class EncoderTemplate : public DOMEventTargetHelper { this->mMessageId); return rv; } - - private: - RefPtr<Promise> mPromise; }; protected: @@ -207,7 +203,7 @@ class EncoderTemplate : public DOMEventTargetHelper { const nsresult& aResult); void ProcessControlMessageQueue(); - void CancelPendingControlMessages(const nsresult& aResult); + void CancelPendingControlMessagesAndFlushPromises(const nsresult& aResult); template <typename Func> void QueueATask(const char* aName, Func&& aSteps); @@ -236,6 +232,11 @@ class EncoderTemplate : public DOMEventTargetHelper { std::queue<RefPtr<ControlMessage>> mControlMessageQueue; RefPtr<ControlMessage> mProcessingMessage; + // When a flush request is initiated, a promise is created and stored in + // mPendingFlushPromises until it is settled in the task delivering the flush + // result or Reset() is called before the promise is settled. + SimpleMap<int64_t, RefPtr<Promise>> mPendingFlushPromises; + uint32_t mEncodeQueueSize; bool mDequeueEventScheduled; diff --git a/dom/media/webcodecs/VideoEncoder.cpp b/dom/media/webcodecs/VideoEncoder.cpp index 5407e917b6..4ce74fa0cb 100644 --- a/dom/media/webcodecs/VideoEncoder.cpp +++ b/dom/media/webcodecs/VideoEncoder.cpp @@ -343,25 +343,16 @@ static bool CanEncode(const RefPtr<VideoEncoderConfigInternal>& aConfig) { if (!IsSupportedVideoCodec(parsedCodecString)) { return false; } - - // TODO (bug 1872879, bug 1872880): Support this on Windows and Mac. if (aConfig->mScalabilityMode.isSome()) { - // We only support L1T2 and L1T3 ScalabilityMode in VP8 and VP9 encoders on - // Linux. - bool supported = IsOnLinux() && (IsVP8CodecString(parsedCodecString) || - IsVP9CodecString(parsedCodecString)) - ? aConfig->mScalabilityMode->EqualsLiteral("L1T2") || - aConfig->mScalabilityMode->EqualsLiteral("L1T3") - : false; - - if (!supported) { + // Check if ScalabilityMode string is valid. + if (!aConfig->mScalabilityMode->EqualsLiteral("L1T2") && + !aConfig->mScalabilityMode->EqualsLiteral("L1T3")) { LOGE("Scalability mode %s not supported for codec: %s", NS_ConvertUTF16toUTF8(aConfig->mScalabilityMode.value()).get(), NS_ConvertUTF16toUTF8(parsedCodecString).get()); return false; } } - return EncoderSupport::Supports(aConfig); } diff --git a/dom/media/webcodecs/crashtests/1889831.html b/dom/media/webcodecs/crashtests/1889831.html new file mode 100644 index 0000000000..e88a028d16 --- /dev/null +++ b/dom/media/webcodecs/crashtests/1889831.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<script> +document.addEventListener("DOMContentLoaded", async () => { + const decoder = new VideoDecoder({ + 'output': (e) => {}, + 'error': (e) => {}, + }); + decoder.configure({ + codec: 'vp8', + codedWidth: 320, + codedHeight: 240, + visibleRect: {x: 0, y: 0, width: 320, height: 240}, + displayWidth: 320, + displayHeight: 240, + }); + decoder.decode(new EncodedVideoChunk( + {type: 'key', timestamp: 0, data: new ArrayBuffer(0)})); + decoder.decode(new EncodedVideoChunk( + {type: 'key', timestamp: 1, data: new ArrayBuffer(0)})); +}) +</script> diff --git a/dom/media/webcodecs/crashtests/crashtests.list b/dom/media/webcodecs/crashtests/crashtests.list index 16fbd90ff5..9d9a453a42 100644 --- a/dom/media/webcodecs/crashtests/crashtests.list +++ b/dom/media/webcodecs/crashtests/crashtests.list @@ -3,4 +3,4 @@ skip-if(Android) pref(dom.media.webcodecs.enabled,true) load 1848460.html skip-if(Android) pref(dom.media.webcodecs.enabled,true) load 1849271.html skip-if(Android) pref(dom.media.webcodecs.enabled,true) load 1864475.html skip-if(Android) pref(dom.media.webcodecs.enabled,true) load 1881079.html - +skip-if(Android) pref(dom.media.webcodecs.enabled,true) load 1889831.html |