summaryrefslogtreecommitdiffstats
path: root/dom/media/webcodecs
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webcodecs')
-rw-r--r--dom/media/webcodecs/DecoderAgent.cpp8
-rw-r--r--dom/media/webcodecs/DecoderTemplate.cpp102
-rw-r--r--dom/media/webcodecs/DecoderTemplate.h39
-rw-r--r--dom/media/webcodecs/EncoderTemplate.cpp195
-rw-r--r--dom/media/webcodecs/EncoderTemplate.h17
-rw-r--r--dom/media/webcodecs/VideoEncoder.cpp15
-rw-r--r--dom/media/webcodecs/crashtests/1889831.html21
-rw-r--r--dom/media/webcodecs/crashtests/crashtests.list2
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