Backed out 14 changesets (bug 1840965) for causing bustages at DecoderTemplate.cpp. CLOSED TREE

Backed out changeset 85effc98ea3f (bug 1840965)
Backed out changeset b9d860c9e3db (bug 1840965)
Backed out changeset 1256f6ae660b (bug 1840965)
Backed out changeset de01c0a052af (bug 1840965)
Backed out changeset 63b4c6f83bc9 (bug 1840965)
Backed out changeset fa4ef4d675e3 (bug 1840965)
Backed out changeset e313ff51eaa7 (bug 1840965)
Backed out changeset 5e3e9670fc35 (bug 1840965)
Backed out changeset 2689f9277a89 (bug 1840965)
Backed out changeset 2b449894a1fc (bug 1840965)
Backed out changeset 44adca2aa7b7 (bug 1840965)
Backed out changeset 4c1d4aba133c (bug 1840965)
Backed out changeset 5a6431259f5a (bug 1840965)
Backed out changeset d93847b9a7e7 (bug 1840965)
This commit is contained in:
Butkovits Atila 2023-10-02 19:32:20 +03:00
Родитель 9aa4a47982
Коммит b642419c68
9 изменённых файлов: 1437 добавлений и 1666 удалений

Просмотреть файл

@ -1,988 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <atomic>
#include "DecoderTemplate.h"
#include "MediaInfo.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Try.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/VideoDecoderBinding.h"
#include "mozilla/dom/WorkerCommon.h"
#include "nsGkAtoms.h"
#include "nsString.h"
#include "nsThreadUtils.h"
mozilla::LazyLogModule gWebCodecsLog("WebCodecs");
namespace mozilla::dom {
#ifdef LOG_INTERNAL
# undef LOG_INTERNAL
#endif // LOG_INTERNAL
#define LOG_INTERNAL(level, msg, ...) \
MOZ_LOG(gWebCodecsLog, LogLevel::level, (msg, ##__VA_ARGS__))
#ifdef LOG
# undef LOG
#endif // LOG
#define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__)
#ifdef LOGW
# undef LOGW
#endif // LOGW
#define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__)
#ifdef LOGE
# undef LOGE
#endif // LOGE
#define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__)
#ifdef LOGV
# undef LOGV
#endif // LOGV
#define LOGV(msg, ...) LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__)
/*
* Below are utility helpers
*/
static nsresult FireEvent(DOMEventTargetHelper* aEventTarget,
nsAtom* aTypeWithOn, const nsAString& aEventType) {
MOZ_ASSERT(aEventTarget);
if (aTypeWithOn && !aEventTarget->HasListenersFor(aTypeWithOn)) {
LOGV("EventTarget %p has no %s event listener", aEventTarget,
NS_ConvertUTF16toUTF8(aEventType).get());
return NS_ERROR_ABORT;
}
LOGV("Dispatch %s event to EventTarget %p",
NS_ConvertUTF16toUTF8(aEventType).get(), aEventTarget);
RefPtr<Event> event = new Event(aEventTarget, nullptr, nullptr);
event->InitEvent(aEventType, true, true);
event->SetTrusted(true);
aEventTarget->DispatchEvent(*event);
return NS_OK;
}
/*
* Below are ControlMessage classes implementations
*/
template <typename DecoderType>
DecoderTemplate<DecoderType>::ControlMessage::ControlMessage(
const nsACString& aTitle)
: mTitle(aTitle) {}
template <typename DecoderType>
DecoderTemplate<DecoderType>::ConfigureMessage::ConfigureMessage(
Id aId, UniquePtr<ConfigTypeInternal>&& aConfig)
: ControlMessage(
nsPrintfCString("configure #%d (%s)", aId,
NS_ConvertUTF16toUTF8(aConfig->mCodec).get())),
mId(aId),
mConfig(std::move(aConfig)) {}
/* static */
template <typename DecoderType>
typename DecoderTemplate<DecoderType>::ConfigureMessage*
DecoderTemplate<DecoderType>::ConfigureMessage::Create(
UniquePtr<ConfigTypeInternal>&& aConfig) {
// This needs to be atomic since this can run on the main thread or worker
// thread.
static std::atomic<Id> sNextId = NoId;
return new ConfigureMessage(++sNextId, std::move(aConfig));
}
template <typename DecoderType>
DecoderTemplate<DecoderType>::DecodeMessage::DecodeMessage(
Id aId, ConfigId aConfigId, UniquePtr<InputTypeInternal>&& aData)
: ControlMessage(
nsPrintfCString("decode #%zu (config #%d)", aId, aConfigId)),
mId(aId),
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) {}
template <typename DecoderType>
void DecoderTemplate<DecoderType>::FlushMessage::RejectPromiseIfAny(
const nsresult& aReason) {
if (mPromise) {
mPromise->MaybeReject(aReason);
}
}
/*
* Below are DecoderTemplate implementation
*/
template <typename DecoderType>
DecoderTemplate<DecoderType>::DecoderTemplate(
nsIGlobalObject* aGlobalObject,
RefPtr<WebCodecsErrorCallback>&& aErrorCallback,
RefPtr<OutputCallbackType>&& aOutputCallback)
: DOMEventTargetHelper(aGlobalObject),
mErrorCallback(std::move(aErrorCallback)),
mOutputCallback(std::move(aOutputCallback)),
mState(CodecState::Unconfigured),
mKeyChunkRequired(true),
mMessageQueueBlocked(false),
mDecodeQueueSize(0),
mDequeueEventScheduled(false),
mLatestConfigureId(ConfigureMessage::NoId),
mDecodeCounter(0),
mFlushCounter(0) {}
template <typename DecoderType>
void DecoderTemplate<DecoderType>::Configure(const ConfigType& aConfig,
ErrorResult& aRv) {
AssertIsOnOwningThread();
LOG("%s %p, Configure: codec %s", DecoderType::Name.get(), this,
NS_ConvertUTF16toUTF8(aConfig.mCodec).get());
if (!DecoderType::Validate(aConfig)) {
aRv.ThrowTypeError("config is invalid");
return;
}
if (mState == CodecState::Closed) {
aRv.ThrowInvalidStateError("The codec is no longer usable");
return;
}
// Clone a ConfigType as the active decoder config.
UniquePtr<ConfigTypeInternal> config =
DecoderType::CreateConfigInternal(aConfig);
if (!config) {
aRv.Throw(NS_ERROR_UNEXPECTED); // Invalid description data.
return;
}
mState = CodecState::Configured;
mKeyChunkRequired = true;
mDecodeCounter = 0;
mFlushCounter = 0;
mControlMessageQueue.emplace(
UniquePtr<ControlMessage>(ConfigureMessage::Create(std::move(config))));
mLatestConfigureId = mControlMessageQueue.back()->AsConfigureMessage()->mId;
LOG("%s %p enqueues %s", DecoderType::Name.get(), this,
mControlMessageQueue.back()->ToString().get());
ProcessControlMessageQueue();
}
template <typename DecoderType>
void DecoderTemplate<DecoderType>::Decode(InputType& aInput, ErrorResult& aRv) {
AssertIsOnOwningThread();
LOG("%s %p, Decode", DecoderType::Name.get(), this);
if (mState != CodecState::Configured) {
aRv.ThrowInvalidStateError("Decoder must be configured first");
return;
}
if (mKeyChunkRequired) {
// TODO: Verify input's data is truly a key chunk
if (!DecoderType::IsKeyChunk(aInput)) {
aRv.ThrowDataError(
nsPrintfCString("%s needs a key chunk", DecoderType::Name.get()));
return;
}
mKeyChunkRequired = false;
}
mDecodeQueueSize += 1;
mControlMessageQueue.emplace(UniquePtr<ControlMessage>(
new DecodeMessage(++mDecodeCounter, mLatestConfigureId,
DecoderType::CreateInputInternal(aInput))));
LOGV("%s %p enqueues %s", DecoderType::Name.get(), this,
mControlMessageQueue.back()->ToString().get());
ProcessControlMessageQueue();
}
template <typename DecoderType>
already_AddRefed<Promise> DecoderTemplate<DecoderType>::Flush(
ErrorResult& aRv) {
AssertIsOnOwningThread();
LOG("%s %p, Flush", DecoderType::Name.get(), this);
if (mState != CodecState::Configured) {
LOG("%s %p, wrong state!", DecoderType::Name.get(), this);
aRv.ThrowInvalidStateError("Decoder must be configured first");
return nullptr;
}
RefPtr<Promise> p = Promise::Create(GetParentObject(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return p.forget();
}
mKeyChunkRequired = true;
mControlMessageQueue.emplace(UniquePtr<ControlMessage>(
new FlushMessage(++mFlushCounter, mLatestConfigureId, p)));
LOG("%s %p enqueues %s", DecoderType::Name.get(), this,
mControlMessageQueue.back()->ToString().get());
ProcessControlMessageQueue();
return p.forget();
}
template <typename DecoderType>
void DecoderTemplate<DecoderType>::Reset(ErrorResult& aRv) {
AssertIsOnOwningThread();
LOG("%s %p, Reset", DecoderType::Name.get(), this);
if (auto r = ResetInternal(NS_ERROR_DOM_ABORT_ERR); r.isErr()) {
aRv.Throw(r.unwrapErr());
}
}
template <typename DecoderType>
void DecoderTemplate<DecoderType>::Close(ErrorResult& aRv) {
AssertIsOnOwningThread();
LOG("%s %p, Close", DecoderType::Name.get(), this);
if (auto r = CloseInternal(NS_ERROR_DOM_ABORT_ERR); r.isErr()) {
aRv.Throw(r.unwrapErr());
}
}
template <typename DecoderType>
Result<Ok, nsresult> DecoderTemplate<DecoderType>::ResetInternal(
const nsresult& aResult) {
AssertIsOnOwningThread();
if (mState == CodecState::Closed) {
return Err(NS_ERROR_DOM_INVALID_STATE_ERR);
}
mState = CodecState::Unconfigured;
mDecodeCounter = 0;
mFlushCounter = 0;
CancelPendingControlMessages(aResult);
DestroyDecoderAgentIfAny();
if (mDecodeQueueSize > 0) {
mDecodeQueueSize = 0;
ScheduleDequeueEvent();
}
LOG("%s %p now has its message queue unblocked", DecoderType::Name.get(),
this);
mMessageQueueBlocked = false;
return Ok();
}
template <typename DecoderType>
Result<Ok, nsresult> DecoderTemplate<DecoderType>::CloseInternal(
const nsresult& aResult) {
AssertIsOnOwningThread();
MOZ_TRY(ResetInternal(aResult));
mState = CodecState::Closed;
if (aResult != NS_ERROR_DOM_ABORT_ERR) {
LOGE("%s %p Close on error: 0x%08" PRIx32, DecoderType::Name.get(), this,
static_cast<uint32_t>(aResult));
ScheduleReportError(aResult);
}
return Ok();
}
template <typename DecoderType>
void DecoderTemplate<DecoderType>::ReportError(const nsresult& aResult) {
AssertIsOnOwningThread();
RefPtr<DOMException> e = DOMException::Create(aResult);
RefPtr<WebCodecsErrorCallback> cb(mErrorCallback);
cb->Call(*e);
}
template <typename DecoderType>
void DecoderTemplate<DecoderType>::OutputDecodedData(
nsTArray<RefPtr<MediaData>>&& aData) {
AssertIsOnOwningThread();
MOZ_ASSERT(mState == CodecState::Configured);
MOZ_ASSERT(mActiveConfig);
nsTArray<RefPtr<VideoFrame>> frames = DecodedDataToOutputType(
GetParentObject(), std::move(aData), *mActiveConfig);
RefPtr<VideoFrameOutputCallback> cb(mOutputCallback);
for (RefPtr<VideoFrame>& frame : frames) {
RefPtr<VideoFrame> f = frame;
cb->Call((VideoFrame&)(*f));
}
}
template <typename DecoderType>
class DecoderTemplate<DecoderType>::ErrorRunnable final
: public DiscardableRunnable {
public:
ErrorRunnable(Self* aDecoder, const nsresult& aError)
: DiscardableRunnable("Decoder ErrorRunnable"),
mDecoder(aDecoder),
mError(aError) {
MOZ_ASSERT(mDecoder);
}
~ErrorRunnable() = default;
// MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT.
// See bug 1535398.
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
LOGE("%s %p report error: 0x%08" PRIx32, DecoderType::Name.get(),
mDecoder.get(), static_cast<uint32_t>(mError));
RefPtr<Self> d = std::move(mDecoder);
d->ReportError(mError);
return NS_OK;
}
private:
RefPtr<Self> mDecoder;
const nsresult mError;
};
template <typename DecoderType>
void DecoderTemplate<DecoderType>::ScheduleReportError(
const nsresult& aResult) {
LOGE("%s %p, schedule to report error: 0x%08" PRIx32, DecoderType::Name.get(),
this, static_cast<uint32_t>(aResult));
MOZ_ALWAYS_SUCCEEDS(
NS_DispatchToCurrentThread(MakeAndAddRef<ErrorRunnable>(this, aResult)));
}
template <typename DecoderType>
class DecoderTemplate<DecoderType>::OutputRunnable final
: public DiscardableRunnable {
public:
OutputRunnable(Self* aDecoder, DecoderAgent::Id aAgentId,
const nsACString& aLabel, nsTArray<RefPtr<MediaData>>&& aData)
: DiscardableRunnable("Decoder OutputRunnable"),
mDecoder(aDecoder),
mAgentId(aAgentId),
mLabel(aLabel),
mData(std::move(aData)) {
MOZ_ASSERT(mDecoder);
}
~OutputRunnable() = default;
// MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT.
// See bug 1535398.
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
if (mDecoder->mState != CodecState::Configured) {
LOGV("%s %p has been %s. Discard %s-result for DecoderAgent #%d",
DecoderType::Name.get(), mDecoder.get(),
mDecoder->mState == CodecState::Closed ? "closed" : "reset",
mLabel.get(), mAgentId);
return NS_OK;
}
MOZ_ASSERT(mDecoder->mAgent);
if (mAgentId != mDecoder->mAgent->mId) {
LOGW(
"%s %p has been re-configured. Still yield %s-result for "
"DecoderAgent #%d",
DecoderType::Name.get(), mDecoder.get(), mLabel.get(), mAgentId);
}
LOGV("%s %p, yields %s-result for DecoderAgent #%d",
DecoderType::Name.get(), mDecoder.get(), mLabel.get(), mAgentId);
RefPtr<Self> d = std::move(mDecoder);
d->OutputDecodedData(std::move(mData));
return NS_OK;
}
private:
RefPtr<Self> mDecoder;
const DecoderAgent::Id mAgentId;
const nsCString mLabel;
nsTArray<RefPtr<MediaData>> mData;
};
template <typename DecoderType>
void DecoderTemplate<DecoderType>::ScheduleOutputDecodedData(
nsTArray<RefPtr<MediaData>>&& aData, const nsACString& aLabel) {
MOZ_ASSERT(mState == CodecState::Configured);
MOZ_ASSERT(mAgent);
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(MakeAndAddRef<OutputRunnable>(
this, mAgent->mId, aLabel, std::move(aData))));
}
template <typename DecoderType>
void DecoderTemplate<DecoderType>::ScheduleClose(const nsresult& aResult) {
AssertIsOnOwningThread();
MOZ_ASSERT(mState == CodecState::Configured);
auto task = [self = RefPtr{this}, result = aResult] {
if (self->mState == CodecState::Closed) {
LOGW("%s %p has been closed. Ignore close with 0x%08" PRIx32,
DecoderType::Name.get(), self.get(), static_cast<uint32_t>(result));
return;
}
DebugOnly<Result<Ok, nsresult>> r = self->CloseInternal(result);
MOZ_ASSERT(r.value.isOk());
};
nsISerialEventTarget* target = GetCurrentSerialEventTarget();
if (NS_IsMainThread()) {
MOZ_ALWAYS_SUCCEEDS(target->Dispatch(
NS_NewRunnableFunction("ScheduleClose Runnable (main)", task)));
return;
}
MOZ_ALWAYS_SUCCEEDS(target->Dispatch(NS_NewCancelableRunnableFunction(
"ScheduleClose Runnable (worker)", task)));
}
template <typename DecoderType>
void DecoderTemplate<DecoderType>::ScheduleDequeueEvent() {
AssertIsOnOwningThread();
if (mDequeueEventScheduled) {
return;
}
mDequeueEventScheduled = true;
auto dispatcher = [self = RefPtr{this}] {
FireEvent(self.get(), nsGkAtoms::ondequeue, u"dequeue"_ns);
self->mDequeueEventScheduled = false;
};
nsISerialEventTarget* target = GetCurrentSerialEventTarget();
if (NS_IsMainThread()) {
MOZ_ALWAYS_SUCCEEDS(target->Dispatch(NS_NewRunnableFunction(
"ScheduleDequeueEvent Runnable (main)", dispatcher)));
return;
}
MOZ_ALWAYS_SUCCEEDS(target->Dispatch(NS_NewCancelableRunnableFunction(
"ScheduleDequeueEvent Runnable (worker)", dispatcher)));
}
template <typename DecoderType>
void DecoderTemplate<DecoderType>::SchedulePromiseResolveOrReject(
already_AddRefed<Promise> aPromise, const nsresult& aResult) {
AssertIsOnOwningThread();
RefPtr<Promise> p = aPromise;
auto resolver = [p, result = aResult] {
if (NS_FAILED(result)) {
p->MaybeReject(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
return;
}
p->MaybeResolveWithUndefined();
};
nsISerialEventTarget* target = GetCurrentSerialEventTarget();
if (NS_IsMainThread()) {
MOZ_ALWAYS_SUCCEEDS(target->Dispatch(NS_NewRunnableFunction(
"SchedulePromiseResolveOrReject Runnable (main)", resolver)));
return;
}
MOZ_ALWAYS_SUCCEEDS(target->Dispatch(NS_NewCancelableRunnableFunction(
"SchedulePromiseResolveOrReject Runnable (worker)", resolver)));
}
template <typename DecoderType>
void DecoderTemplate<DecoderType>::ProcessControlMessageQueue() {
AssertIsOnOwningThread();
MOZ_ASSERT(mState == CodecState::Configured);
while (!mMessageQueueBlocked && !mControlMessageQueue.empty()) {
UniquePtr<ControlMessage>& msg = mControlMessageQueue.front();
if (msg->AsConfigureMessage()) {
if (ProcessConfigureMessage(msg) ==
MessageProcessedResult::NotProcessed) {
break;
}
} else if (msg->AsDecodeMessage()) {
if (ProcessDecodeMessage(msg) == MessageProcessedResult::NotProcessed) {
break;
}
} else {
MOZ_ASSERT(msg->AsFlushMessage());
if (ProcessFlushMessage(msg) == MessageProcessedResult::NotProcessed) {
break;
}
}
}
}
template <typename DecoderType>
void DecoderTemplate<DecoderType>::CancelPendingControlMessages(
const nsresult& aResult) {
AssertIsOnOwningThread();
// Cancel the message that is being processed.
if (mProcessingMessage) {
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();
}
// Clear the message queue.
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();
}
}
template <typename DecoderType>
MessageProcessedResult DecoderTemplate<DecoderType>::ProcessConfigureMessage(
UniquePtr<ControlMessage>& aMessage) {
AssertIsOnOwningThread();
MOZ_ASSERT(mState == CodecState::Configured);
MOZ_ASSERT(aMessage->AsConfigureMessage());
if (mProcessingMessage) {
LOG("%s %p is processing %s. Defer %s", DecoderType::Name.get(), this,
mProcessingMessage->ToString().get(), aMessage->ToString().get());
return MessageProcessedResult::NotProcessed;
}
mProcessingMessage.reset(aMessage.release());
mControlMessageQueue.pop();
ConfigureMessage* msg = mProcessingMessage->AsConfigureMessage();
LOG("%s %p starts processing %s", DecoderType::Name.get(), this,
msg->ToString().get());
DestroyDecoderAgentIfAny();
auto i = DecoderType::CreateTrackInfo(msg->Config());
if (!DecoderType::IsSupported(msg->Config()) || i.isErr() ||
!CreateDecoderAgent(msg->mId, msg->TakeConfig(), i.unwrap())) {
mProcessingMessage.reset();
DebugOnly<Result<Ok, nsresult>> r =
CloseInternal(NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR);
MOZ_ASSERT(r.value.isOk());
LOGE("%s %p cannot be configured", DecoderType::Name.get(), this);
return MessageProcessedResult::Processed;
}
MOZ_ASSERT(mAgent);
MOZ_ASSERT(mActiveConfig);
LOG("%s %p now blocks message-queue-processing", DecoderType::Name.get(),
this);
mMessageQueueBlocked = true;
bool preferSW = mActiveConfig->mHardwareAcceleration ==
HardwareAcceleration::Prefer_software;
bool lowLatency = mActiveConfig->mOptimizeForLatency.isSome() &&
mActiveConfig->mOptimizeForLatency.value();
mAgent->Configure(preferSW, lowLatency)
->Then(GetCurrentSerialEventTarget(), __func__,
[self = RefPtr{this}, id = mAgent->mId](
const DecoderAgent::ConfigurePromise::ResolveOrRejectValue&
aResult) {
MOZ_ASSERT(self->mProcessingMessage);
MOZ_ASSERT(self->mProcessingMessage->AsConfigureMessage());
MOZ_ASSERT(self->mState == CodecState::Configured);
MOZ_ASSERT(self->mAgent);
MOZ_ASSERT(id == self->mAgent->mId);
MOZ_ASSERT(self->mActiveConfig);
ConfigureMessage* msg =
self->mProcessingMessage->AsConfigureMessage();
LOG("%s %p, DecodeAgent #%d %s has been %s. now unblocks "
"message-queue-processing",
DecoderType::Name.get(), self.get(), id,
msg->ToString().get(),
aResult.IsResolve() ? "resolved" : "rejected");
msg->Complete();
self->mProcessingMessage.reset();
if (aResult.IsReject()) {
// The spec asks to close the decoder with an
// NotSupportedError so we log the exact error here.
const MediaResult& error = aResult.RejectValue();
LOGE("%s %p, DecodeAgent #%d failed to configure: %s",
DecoderType::Name.get(), self.get(), id,
error.Description().get());
DebugOnly<Result<Ok, nsresult>> r =
self->CloseInternal(NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR);
MOZ_ASSERT(r.value.isOk());
return; // No further process
}
self->mMessageQueueBlocked = false;
self->ProcessControlMessageQueue();
})
->Track(msg->Request());
return MessageProcessedResult::Processed;
}
template <typename DecoderType>
MessageProcessedResult DecoderTemplate<DecoderType>::ProcessDecodeMessage(
UniquePtr<ControlMessage>& aMessage) {
AssertIsOnOwningThread();
MOZ_ASSERT(mState == CodecState::Configured);
MOZ_ASSERT(aMessage->AsDecodeMessage());
if (mProcessingMessage) {
LOGV("%s %p is processing %s. Defer %s", DecoderType::Name.get(), this,
mProcessingMessage->ToString().get(), aMessage->ToString().get());
return MessageProcessedResult::NotProcessed;
}
mProcessingMessage.reset(aMessage.release());
mControlMessageQueue.pop();
DecodeMessage* msg = mProcessingMessage->AsDecodeMessage();
LOGV("%s %p starts processing %s", DecoderType::Name.get(), this,
msg->ToString().get());
mDecodeQueueSize -= 1;
ScheduleDequeueEvent();
// Treat it like decode error if no DecoderAgent is available or the encoded
// data is invalid.
auto closeOnError = [&]() {
mProcessingMessage.reset();
ScheduleClose(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
return MessageProcessedResult::Processed;
};
if (!mAgent) {
LOGE("%s %p is not configured", DecoderType::Name.get(), this);
return closeOnError();
}
MOZ_ASSERT(mActiveConfig);
RefPtr<MediaRawData> data = InputDataToMediaRawData(
std::move(msg->mData), *(mAgent->mInfo), *mActiveConfig);
if (!data) {
LOGE("%s %p, data for %s is empty or invalid", DecoderType::Name.get(),
this, msg->ToString().get());
return closeOnError();
}
mAgent->Decode(data.get())
->Then(GetCurrentSerialEventTarget(), __func__,
[self = RefPtr{this}, id = mAgent->mId](
DecoderAgent::DecodePromise::ResolveOrRejectValue&& aResult) {
MOZ_ASSERT(self->mProcessingMessage);
MOZ_ASSERT(self->mProcessingMessage->AsDecodeMessage());
MOZ_ASSERT(self->mState == CodecState::Configured);
MOZ_ASSERT(self->mAgent);
MOZ_ASSERT(id == self->mAgent->mId);
MOZ_ASSERT(self->mActiveConfig);
DecodeMessage* msg = self->mProcessingMessage->AsDecodeMessage();
LOGV("%s %p, DecodeAgent #%d %s has been %s",
DecoderType::Name.get(), self.get(), id,
msg->ToString().get(),
aResult.IsResolve() ? "resolved" : "rejected");
nsCString msgStr = msg->ToString();
msg->Complete();
self->mProcessingMessage.reset();
if (aResult.IsReject()) {
// The spec asks to queue a task to run close the decoder
// with an EncodingError so we log the exact error here.
const MediaResult& error = aResult.RejectValue();
LOGE("%s %p, DecodeAgent #%d %s failed: %s",
DecoderType::Name.get(), self.get(), id, msgStr.get(),
error.Description().get());
self->ScheduleClose(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
return; // No further process
}
MOZ_ASSERT(aResult.IsResolve());
nsTArray<RefPtr<MediaData>> data =
std::move(aResult.ResolveValue());
if (data.IsEmpty()) {
LOGV("%s %p got no data for %s", DecoderType::Name.get(),
self.get(), msgStr.get());
} else {
LOGV("%s %p, schedule %zu decoded data output for %s",
DecoderType::Name.get(), self.get(), data.Length(),
msgStr.get());
self->ScheduleOutputDecodedData(std::move(data), msgStr);
}
self->ProcessControlMessageQueue();
})
->Track(msg->Request());
return MessageProcessedResult::Processed;
}
template <typename DecoderType>
MessageProcessedResult DecoderTemplate<DecoderType>::ProcessFlushMessage(
UniquePtr<ControlMessage>& aMessage) {
AssertIsOnOwningThread();
MOZ_ASSERT(mState == CodecState::Configured);
MOZ_ASSERT(aMessage->AsFlushMessage());
if (mProcessingMessage) {
LOG("%s %p is processing %s. Defer %s", DecoderType::Name.get(), this,
mProcessingMessage->ToString().get(), aMessage->ToString().get());
return MessageProcessedResult::NotProcessed;
}
mProcessingMessage.reset(aMessage.release());
mControlMessageQueue.pop();
FlushMessage* msg = mProcessingMessage->AsFlushMessage();
LOG("%s %p starts processing %s", DecoderType::Name.get(), this,
msg->ToString().get());
// Treat it like decode error if no DecoderAgent is available.
if (!mAgent) {
LOGE("%s %p is not configured", DecoderType::Name.get(), this);
SchedulePromiseResolveOrReject(msg->TakePromise(),
NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
mProcessingMessage.reset();
ScheduleClose(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
return MessageProcessedResult::Processed;
}
mAgent->DrainAndFlush()
->Then(
GetCurrentSerialEventTarget(), __func__,
[self = RefPtr{this}, id = mAgent->mId](
DecoderAgent::DecodePromise::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);
FlushMessage* msg = self->mProcessingMessage->AsFlushMessage();
LOG("%s %p, DecodeAgent #%d %s has been %s",
DecoderType::Name.get(), self.get(), id, msg->ToString().get(),
aResult.IsResolve() ? "resolved" : "rejected");
nsCString msgStr = msg->ToString();
msg->Complete();
// 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 VideoDecoder with
// an EncodingError.
if (aResult.IsReject()) {
const MediaResult& error = aResult.RejectValue();
LOGE("%s %p, DecodeAgent #%d failed to flush: %s",
DecoderType::Name.get(), self.get(), id,
error.Description().get());
// Reject with an EncodingError instead of the error we got
// above.
self->SchedulePromiseResolveOrReject(
msg->TakePromise(), NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
self->mProcessingMessage.reset();
self->ScheduleClose(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
return; // No further process
}
// If flush succeeded, schedule to output decoded data first
// and then resolve the promise, then keep processing the
// control messages.
MOZ_ASSERT(aResult.IsResolve());
nsTArray<RefPtr<MediaData>> data =
std::move(aResult.ResolveValue());
if (data.IsEmpty()) {
LOG("%s %p gets no data for %s", DecoderType::Name.get(),
self.get(), msgStr.get());
} else {
LOG("%s %p, schedule %zu decoded data output for %s",
DecoderType::Name.get(), self.get(), data.Length(),
msgStr.get());
self->ScheduleOutputDecodedData(std::move(data), msgStr);
}
self->SchedulePromiseResolveOrReject(msg->TakePromise(), NS_OK);
self->mProcessingMessage.reset();
self->ProcessControlMessageQueue();
})
->Track(msg->Request());
return MessageProcessedResult::Processed;
}
// CreateDecoderAgent will create an DecoderAgent paired with a xpcom-shutdown
// blocker and a worker-reference. Besides the needs mentioned in the header
// file, the blocker and the worker-reference also provides an entry point for
// us to clean up the resources. Other than the decoder dtor, Reset(), or
// Close(), the resources should be cleaned up in the following situations:
// 1. Decoder on window, closing document
// 2. Decoder on worker, closing document
// 3. Decoder on worker, terminating worker
//
// In case 1, the entry point to clean up is in the mShutdownBlocker's
// ShutdownpPomise-resolver. In case 2, the entry point is in mWorkerRef's
// shutting down callback. In case 3, the entry point is in mWorkerRef's
// shutting down callback.
template <typename DecoderType>
bool DecoderTemplate<DecoderType>::CreateDecoderAgent(
DecoderAgent::Id aId, UniquePtr<ConfigTypeInternal>&& aConfig,
UniquePtr<TrackInfo>&& aInfo) {
AssertIsOnOwningThread();
MOZ_ASSERT(mState == CodecState::Configured);
MOZ_ASSERT(!mAgent);
MOZ_ASSERT(!mActiveConfig);
MOZ_ASSERT(!mShutdownBlocker);
MOZ_ASSERT_IF(!NS_IsMainThread(), !mWorkerRef);
auto resetOnFailure = MakeScopeExit([&]() {
mAgent = nullptr;
mActiveConfig = nullptr;
mShutdownBlocker = nullptr;
mWorkerRef = nullptr;
});
// If the decoder is on worker, get a worker reference.
if (!NS_IsMainThread()) {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
if (NS_WARN_IF(!workerPrivate)) {
return false;
}
// Clean up all the resources when worker is going away.
RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create(
workerPrivate, "DecoderTemplate::CreateDecoderAgent",
[self = RefPtr{this}]() {
LOG("%s %p, worker is going away", DecoderType::Name.get(),
self.get());
Unused << self->ResetInternal(NS_ERROR_DOM_ABORT_ERR);
});
if (NS_WARN_IF(!workerRef)) {
return false;
}
mWorkerRef = new ThreadSafeWorkerRef(workerRef);
}
mAgent = MakeRefPtr<DecoderAgent>(aId, std::move(aInfo));
mActiveConfig = std::move(aConfig);
// ShutdownBlockingTicket requires an unique name to register its own
// nsIAsyncShutdownBlocker since each blocker needs a distinct name.
// To do that, we use DecodeAgent's unique id to create a unique name.
nsAutoString uniqueName;
uniqueName.AppendPrintf(
"Blocker for DecodeAgent #%d (codec: %s) @ %p", mAgent->mId,
NS_ConvertUTF16toUTF8(mActiveConfig->mCodec).get(), mAgent.get());
mShutdownBlocker = media::ShutdownBlockingTicket::Create(
uniqueName, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__);
if (!mShutdownBlocker) {
LOGE("%s %p failed to create %s", DecoderType::Name.get(), this,
NS_ConvertUTF16toUTF8(uniqueName).get());
return false;
}
// Clean up all the resources when xpcom-will-shutdown arrives since the page
// is going to be closed.
mShutdownBlocker->ShutdownPromise()->Then(
GetCurrentSerialEventTarget(), __func__,
[self = RefPtr{this}, id = mAgent->mId,
ref = mWorkerRef](bool /* aUnUsed*/) {
LOG("%s %p gets xpcom-will-shutdown notification for DecodeAgent #%d",
DecoderType::Name.get(), self.get(), id);
Unused << self->ResetInternal(NS_ERROR_DOM_ABORT_ERR);
},
[self = RefPtr{this}, id = mAgent->mId,
ref = mWorkerRef](bool /* aUnUsed*/) {
LOG("%s %p removes shutdown-blocker #%d before getting any "
"notification. DecodeAgent #%d should have been dropped",
DecoderType::Name.get(), self.get(), id, id);
MOZ_ASSERT(!self->mAgent || self->mAgent->mId != id);
});
LOG("%s %p creates DecodeAgent #%d @ %p and its shutdown-blocker",
DecoderType::Name.get(), this, mAgent->mId, mAgent.get());
resetOnFailure.release();
return true;
}
template <typename DecoderType>
void DecoderTemplate<DecoderType>::DestroyDecoderAgentIfAny() {
AssertIsOnOwningThread();
if (!mAgent) {
LOG("%s %p has no DecodeAgent to destroy", DecoderType::Name.get(), this);
return;
}
MOZ_ASSERT(mActiveConfig);
MOZ_ASSERT(mShutdownBlocker);
MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerRef);
LOG("%s %p destroys DecodeAgent #%d @ %p", DecoderType::Name.get(), this,
mAgent->mId, mAgent.get());
mActiveConfig = nullptr;
RefPtr<DecoderAgent> agent = std::move(mAgent);
// mShutdownBlocker should be kept alive until the shutdown is done.
// mWorkerRef is used to ensure this task won't be discarded in worker.
agent->Shutdown()->Then(
GetCurrentSerialEventTarget(), __func__,
[self = RefPtr{this}, id = agent->mId, ref = std::move(mWorkerRef),
blocker = std::move(mShutdownBlocker)](
const ShutdownPromise::ResolveOrRejectValue& aResult) {
LOG("%s %p, DecoderAgent #%d's shutdown has been %s. Drop its "
"shutdown-blocker now",
DecoderType::Name.get(), self.get(), id,
aResult.IsResolve() ? "resolved" : "rejected");
});
}
#undef LOG
#undef LOGW
#undef LOGE
#undef LOGV
#undef LOG_INTERNAL
} // namespace mozilla::dom

Просмотреть файл

@ -1,280 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_DecoderTemplate_h
#define mozilla_dom_DecoderTemplate_h
#include <queue>
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/DecoderAgent.h"
#include "mozilla/MozPromise.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Result.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/WorkerRef.h"
#include "mozilla/media/MediaUtils.h"
#include "nsStringFwd.h"
namespace mozilla {
class TrackInfo;
namespace dom {
class WebCodecsErrorCallback;
class Promise;
enum class CodecState : uint8_t;
template <typename T>
class MessageRequestHolder {
public:
MessageRequestHolder() = default;
~MessageRequestHolder() = default;
MozPromiseRequestHolder<T>& Request() { return mRequest; }
void Disconnect() { mRequest.Disconnect(); }
void Complete() { mRequest.Complete(); }
bool Exists() const { return mRequest.Exists(); }
protected:
MozPromiseRequestHolder<T> mRequest;
};
enum class MessageProcessedResult { NotProcessed, Processed };
template <typename DecoderType>
class DecoderTemplate : public DOMEventTargetHelper {
using Self = DecoderTemplate<DecoderType>;
using ConfigType = typename DecoderType::ConfigType;
using ConfigTypeInternal = typename DecoderType::ConfigTypeInternal;
using InputType = typename DecoderType::InputType;
using InputTypeInternal = typename DecoderType::InputTypeInternal;
using OutputType = typename DecoderType::OutputType;
using OutputCallbackType = typename DecoderType::OutputCallbackType;
/* ControlMessage classes */
protected:
class ConfigureMessage;
class DecodeMessage;
class FlushMessage;
class ControlMessage {
public:
explicit ControlMessage(const nsACString& aTitle);
virtual ~ControlMessage() = default;
virtual void Cancel() = 0;
virtual bool IsProcessing() = 0;
virtual const nsCString& ToString() const { return mTitle; }
virtual ConfigureMessage* AsConfigureMessage() { return nullptr; }
virtual DecodeMessage* AsDecodeMessage() { return nullptr; }
virtual FlushMessage* AsFlushMessage() { return nullptr; }
const nsCString mTitle; // Used to identify the message in the logs.
};
class ConfigureMessage final
: public ControlMessage,
public MessageRequestHolder<DecoderAgent::ConfigurePromise> {
public:
using Id = DecoderAgent::Id;
static constexpr Id NoId = 0;
static ConfigureMessage* Create(UniquePtr<ConfigTypeInternal>&& aConfig);
~ConfigureMessage() = default;
virtual void Cancel() override { Disconnect(); }
virtual bool IsProcessing() override { return Exists(); };
virtual ConfigureMessage* AsConfigureMessage() override { return this; }
const ConfigTypeInternal& Config() { return *mConfig; }
UniquePtr<ConfigTypeInternal> TakeConfig() { return std::move(mConfig); }
const Id mId; // A unique id shown in log.
private:
ConfigureMessage(Id aId, UniquePtr<ConfigTypeInternal>&& aConfig);
UniquePtr<ConfigTypeInternal> mConfig;
};
class DecodeMessage final
: public ControlMessage,
public MessageRequestHolder<DecoderAgent::DecodePromise> {
public:
using Id = size_t;
using ConfigId = typename Self::ConfigureMessage::Id;
DecodeMessage(Id aId, 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.
UniquePtr<InputTypeInternal> mData;
};
class FlushMessage final
: public ControlMessage,
public MessageRequestHolder<DecoderAgent::DecodePromise> {
public:
using Id = size_t;
using ConfigId = typename Self::ConfigureMessage::Id;
FlushMessage(Id aId, ConfigId aConfigId, Promise* aPromise);
~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;
};
protected:
DecoderTemplate(nsIGlobalObject* aGlobalObject,
RefPtr<WebCodecsErrorCallback>&& aErrorCallback,
RefPtr<OutputCallbackType>&& aOutputCallback);
virtual ~DecoderTemplate() = default;
/* WebCodecs interfaces */
public:
IMPL_EVENT_HANDLER(dequeue)
CodecState State() const { return mState; };
uint32_t DecodeQueueSize() const { return mDecodeQueueSize; };
// TODO: Replace virtual with MOZ_EXPORT (visibility("default"))
virtual void Configure(const ConfigType& aConfig, ErrorResult& aRv);
virtual void Decode(InputType& aInput, ErrorResult& aRv);
virtual already_AddRefed<Promise> Flush(ErrorResult& aRv);
virtual void Reset(ErrorResult& aRv);
virtual void Close(ErrorResult& aRv);
/* Type conversion functions for the Decoder implementation */
protected:
virtual already_AddRefed<MediaRawData> InputDataToMediaRawData(
UniquePtr<InputTypeInternal>&& aData, TrackInfo& aInfo,
const ConfigTypeInternal& aConfig) = 0;
virtual nsTArray<RefPtr<OutputType>> DecodedDataToOutputType(
nsIGlobalObject* aGlobalObject, nsTArray<RefPtr<MediaData>>&& aData,
ConfigTypeInternal& aConfig) = 0;
/* Internal member variables and functions */
protected:
// DecoderTemplate can run on either main thread or worker thread.
void AssertIsOnOwningThread() const {
NS_ASSERT_OWNINGTHREAD(DecoderTemplate);
}
Result<Ok, nsresult> ResetInternal(const nsresult& aResult);
Result<Ok, nsresult> CloseInternal(const nsresult& aResult);
MOZ_CAN_RUN_SCRIPT void ReportError(const nsresult& aResult);
MOZ_CAN_RUN_SCRIPT void OutputDecodedData(
nsTArray<RefPtr<MediaData>>&& aData);
class ErrorRunnable;
void ScheduleReportError(const nsresult& aResult);
class OutputRunnable;
void ScheduleOutputDecodedData(nsTArray<RefPtr<MediaData>>&& aData,
const nsACString& aLabel);
void ScheduleClose(const nsresult& aResult);
void ScheduleDequeueEvent();
void SchedulePromiseResolveOrReject(already_AddRefed<Promise> aPromise,
const nsresult& aResult);
void ProcessControlMessageQueue();
void CancelPendingControlMessages(const nsresult& aResult);
MessageProcessedResult ProcessConfigureMessage(
UniquePtr<ControlMessage>& aMessage);
MessageProcessedResult ProcessDecodeMessage(
UniquePtr<ControlMessage>& aMessage);
MessageProcessedResult ProcessFlushMessage(
UniquePtr<ControlMessage>& aMessage);
// Returns true when mAgent can be created.
bool CreateDecoderAgent(DecoderAgent::Id aId,
UniquePtr<ConfigTypeInternal>&& aConfig,
UniquePtr<TrackInfo>&& aInfo);
void DestroyDecoderAgentIfAny();
// Constant in practice, only set in ctor.
RefPtr<WebCodecsErrorCallback> mErrorCallback;
RefPtr<OutputCallbackType> mOutputCallback;
CodecState mState;
bool mKeyChunkRequired;
bool mMessageQueueBlocked;
std::queue<UniquePtr<ControlMessage>> mControlMessageQueue;
UniquePtr<ControlMessage> mProcessingMessage;
uint32_t mDecodeQueueSize;
bool mDequeueEventScheduled;
// A unique id tracking the ConfigureMessage and will be used as the
// 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.
size_t mDecodeCounter;
// Tracking how many flush request has been enqueued and this number will be
// used as the FlushMessage's Id.
size_t mFlushCounter;
// DecoderAgent will be created every time "configure" is being processed, and
// will be destroyed when "reset" or another "configure" is called (spec
// allows calling two "configure" without a "reset" in between).
RefPtr<DecoderAgent> mAgent;
UniquePtr<ConfigTypeInternal> mActiveConfig;
// Used to add a nsIAsyncShutdownBlocker on main thread to block
// xpcom-shutdown before the underlying MediaDataDecoder is created. The
// blocker will be held until the underlying MediaDataDecoder has been shut
// down. This blocker guarantees RemoteDecoderManagerChild's thread, where the
// underlying RemoteMediaDataDecoder is on, outlives the
// RemoteMediaDataDecoder, since the thread releasing, which happens on main
// thread when getting a xpcom-shutdown signal, is blocked by the added
// blocker. As a result, RemoteMediaDataDecoder can safely work on worker
// thread with a holding blocker (otherwise, if RemoteDecoderManagerChild
// releases its thread on main thread before RemoteMediaDataDecoder's
// Shutdown() task run on worker thread, RemoteMediaDataDecoder has no thread
// to run).
UniquePtr<media::ShutdownBlockingTicket> mShutdownBlocker;
// Held to make sure the dispatched tasks can be done before worker is going
// away. As long as this worker-ref is held somewhere, the tasks dispatched to
// the worker can be executed (otherwise the tasks would be canceled). This
// ref should be activated as long as the underlying MediaDataDecoder is
// alive, and should keep alive until mShutdownBlocker is dropped, so all
// MediaDataDecoder's tasks and mShutdownBlocker-releasing task can be
// executed.
// TODO: Use StrongWorkerRef instead if this is always used in the same
// thread?
RefPtr<ThreadSafeWorkerRef> mWorkerRef;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_DecoderTemplate_h

Просмотреть файл

@ -61,7 +61,7 @@ EncodedVideoChunkData::EncodedVideoChunkData(
static_cast<size_t>(std::numeric_limits<uint32_t>::max()));
}
UniquePtr<EncodedVideoChunkData> EncodedVideoChunkData::Clone() const {
UniquePtr<EncodedVideoChunkData> EncodedVideoChunkData::Clone() {
if (!mBuffer) {
LOGE("No buffer in EncodedVideoChunkData %p to clone!", this);
return nullptr;

Просмотреть файл

@ -45,7 +45,7 @@ class EncodedVideoChunkData {
EncodedVideoChunkData(const EncodedVideoChunkData& aData) = default;
~EncodedVideoChunkData() = default;
UniquePtr<EncodedVideoChunkData> Clone() const;
UniquePtr<EncodedVideoChunkData> Clone();
already_AddRefed<MediaRawData> TakeData();
protected:

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -7,74 +7,120 @@
#ifndef mozilla_dom_VideoDecoder_h
#define mozilla_dom_VideoDecoder_h
#include "DecoderTemplate.h"
#include <queue>
#include "js/TypeDecls.h"
#include "mozilla/Attributes.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/DecoderAgent.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Result.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/VideoFrame.h"
#include "mozilla/dom/VideoColorSpaceBinding.h"
#include "nsCycleCollectionParticipant.h"
#include "nsStringFwd.h"
#include "nsWrapperCache.h"
#include "mozilla/dom/VideoDecoderBinding.h"
class nsIGlobalObject;
namespace mozilla {
class MediaData;
class TrackInfo;
class VideoInfo;
namespace dom {
class EncodedVideoChunk;
class EncodedVideoChunkData;
class EventHandlerNonNull;
class GlobalObject;
class Promise;
class ThreadSafeWorkerRef;
class VideoFrameOutputCallback;
class WebCodecsErrorCallback;
// enum class CodecState : uint8_t;
enum class HardwareAcceleration : uint8_t;
enum class CodecState : uint8_t;
struct VideoDecoderConfig;
struct VideoDecoderInit;
} // namespace dom
namespace media {
class ShutdownBlockingTicket;
}
} // namespace mozilla
namespace mozilla::dom {
class VideoDecoderConfigInternal;
class VideoDecoderTraits {
public:
static constexpr nsLiteralCString Name = "VideoDecoder"_ns;
using ConfigType = VideoDecoderConfig;
using ConfigTypeInternal = VideoDecoderConfigInternal;
using InputType = EncodedVideoChunk;
using InputTypeInternal = EncodedVideoChunkData;
using OutputType = VideoFrame;
using OutputCallbackType = VideoFrameOutputCallback;
static bool IsSupported(const ConfigTypeInternal& aConfig);
static Result<UniquePtr<TrackInfo>, nsresult> CreateTrackInfo(
const ConfigTypeInternal& aConfig);
static bool Validate(const ConfigType& aConfig);
static UniquePtr<ConfigTypeInternal> CreateConfigInternal(
const ConfigType& aConfig);
static bool IsKeyChunk(const InputType& aInput);
static UniquePtr<InputTypeInternal> CreateInputInternal(
const InputType& aInput);
struct VideoColorSpaceInternal {
explicit VideoColorSpaceInternal(const VideoColorSpaceInit& aColorSpaceInit);
VideoColorSpaceInternal() = default;
VideoColorSpaceInit ToColorSpaceInit() const;
Maybe<bool> mFullRange;
Maybe<VideoMatrixCoefficients> mMatrix;
Maybe<VideoColorPrimaries> mPrimaries;
Maybe<VideoTransferCharacteristics> mTransfer;
};
class VideoDecoder final : public DecoderTemplate<VideoDecoderTraits> {
class VideoDecoderConfigInternal {
public:
static UniquePtr<VideoDecoderConfigInternal> Create(
const VideoDecoderConfig& aConfig);
~VideoDecoderConfigInternal() = default;
nsString mCodec;
Maybe<uint32_t> mCodedHeight;
Maybe<uint32_t> mCodedWidth;
Maybe<VideoColorSpaceInternal> mColorSpace;
Maybe<RefPtr<MediaByteBuffer>> mDescription;
Maybe<uint32_t> mDisplayAspectHeight;
Maybe<uint32_t> mDisplayAspectWidth;
HardwareAcceleration mHardwareAcceleration;
Maybe<bool> mOptimizeForLatency;
private:
VideoDecoderConfigInternal(const nsAString& aCodec,
Maybe<uint32_t>&& aCodedHeight,
Maybe<uint32_t>&& aCodedWidth,
Maybe<VideoColorSpaceInternal>&& aColorSpace,
Maybe<RefPtr<MediaByteBuffer>>&& aDescription,
Maybe<uint32_t>&& aDisplayAspectHeight,
Maybe<uint32_t>&& aDisplayAspectWidth,
const HardwareAcceleration& aHardwareAcceleration,
Maybe<bool>&& aOptimizeForLatency);
};
class ConfigureMessage;
class DecodeMessage;
class FlushMessage;
class ControlMessage {
public:
explicit ControlMessage(const nsACString& aTitle);
virtual ~ControlMessage() = default;
virtual void Cancel() = 0;
virtual bool IsProcessing() = 0;
virtual const nsCString& ToString() const { return mTitle; }
virtual ConfigureMessage* AsConfigureMessage() { return nullptr; }
virtual DecodeMessage* AsDecodeMessage() { return nullptr; }
virtual FlushMessage* AsFlushMessage() { return nullptr; }
const nsCString mTitle; // Used to identify the message in the logs.
};
class VideoDecoder final : public DOMEventTargetHelper {
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(VideoDecoder, DOMEventTargetHelper)
IMPL_EVENT_HANDLER(dequeue)
public:
VideoDecoder(nsIGlobalObject* aParent,
RefPtr<WebCodecsErrorCallback>&& aErrorCallback,
@ -91,18 +137,122 @@ class VideoDecoder final : public DecoderTemplate<VideoDecoderTraits> {
const GlobalObject& aGlobal, const VideoDecoderInit& aInit,
ErrorResult& aRv);
CodecState State() const;
uint32_t DecodeQueueSize() const;
void Configure(const VideoDecoderConfig& aConfig, ErrorResult& aRv);
void Decode(EncodedVideoChunk& aChunk, ErrorResult& aRv);
already_AddRefed<Promise> Flush(ErrorResult& aRv);
void Reset(ErrorResult& aRv);
void Close(ErrorResult& aRv);
static already_AddRefed<Promise> IsConfigSupported(
const GlobalObject& aGlobal, const VideoDecoderConfig& aConfig,
ErrorResult& aRv);
protected:
virtual already_AddRefed<MediaRawData> InputDataToMediaRawData(
UniquePtr<EncodedVideoChunkData>&& aData, TrackInfo& aInfo,
const VideoDecoderConfigInternal& aConfig) override;
private:
// VideoDecoder can run on either main thread or worker thread.
void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(VideoDecoder); }
virtual nsTArray<RefPtr<VideoFrame>> DecodedDataToOutputType(
nsIGlobalObject* aGlobalObject, nsTArray<RefPtr<MediaData>>&& aData,
VideoDecoderConfigInternal& aConfig) override;
Result<Ok, nsresult> Reset(const nsresult& aResult);
Result<Ok, nsresult> Close(const nsresult& aResult);
MOZ_CAN_RUN_SCRIPT void ReportError(const nsresult& aResult);
MOZ_CAN_RUN_SCRIPT void OutputVideoFrames(
nsTArray<RefPtr<MediaData>>&& aData);
class ErrorRunnable;
void ScheduleReportError(const nsresult& aResult);
class OutputRunnable;
void ScheduleOutputVideoFrames(nsTArray<RefPtr<MediaData>>&& aData,
const nsACString& aLabel);
void ScheduleClose(const nsresult& aResult);
void ScheduleDequeueEvent();
void SchedulePromiseResolveOrReject(already_AddRefed<Promise> aPromise,
const nsresult& aResult);
void ProcessControlMessageQueue();
void CancelPendingControlMessages(const nsresult& aResult);
enum class MessageProcessedResult { NotProcessed, Processed };
MessageProcessedResult ProcessConfigureMessage(
UniquePtr<ControlMessage>& aMessage);
MessageProcessedResult ProcessDecodeMessage(
UniquePtr<ControlMessage>& aMessage);
MessageProcessedResult ProcessFlushMessage(
UniquePtr<ControlMessage>& aMessage);
// Returns true when mAgent can be created.
bool CreateDecoderAgent(DecoderAgent::Id aId,
UniquePtr<VideoDecoderConfigInternal>&& aConfig,
UniquePtr<TrackInfo>&& aInfo);
void DestroyDecoderAgentIfAny();
// Constant in practice, only set in ::Constructor.
RefPtr<WebCodecsErrorCallback> mErrorCallback;
RefPtr<VideoFrameOutputCallback> mOutputCallback;
CodecState mState;
bool mKeyChunkRequired;
bool mMessageQueueBlocked;
std::queue<UniquePtr<ControlMessage>> mControlMessageQueue;
UniquePtr<ControlMessage> mProcessingMessage;
// DecoderAgent will be created every time "configure" is being processed, and
// will be destroyed when "reset" or another "configure" is called (spec
// allows calling two "configure" without a "reset" in between).
RefPtr<DecoderAgent> mAgent;
UniquePtr<VideoDecoderConfigInternal> mActiveConfig;
uint32_t mDecodeQueueSize;
bool mDequeueEventScheduled;
// A unique id tracking the ConfigureMessage and will be used as the
// 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.
size_t mDecodeCounter;
// Tracking how many flush request has been enqueued and this number will be
// used as the FlushMessage's Id.
size_t mFlushCounter;
// Used to add a nsIAsyncShutdownBlocker on main thread to block
// xpcom-shutdown before the underlying MediaDataDecoder is created. The
// blocker will be held until the underlying MediaDataDecoder has been shut
// down. This blocker guarantees RemoteDecoderManagerChild's thread, where the
// underlying RemoteMediaDataDecoder is on, outlives the
// RemoteMediaDataDecoder, since the thread releasing, which happens on main
// thread when getting a xpcom-shutdown signal, is blocked by the added
// blocker. As a result, RemoteMediaDataDecoder can safely work on worker
// thread with a holding blocker (otherwise, if RemoteDecoderManagerChild
// releases its thread on main thread before RemoteMediaDataDecoder's
// Shutdown() task run on worker thread, RemoteMediaDataDecoder has no thread
// to run).
UniquePtr<media::ShutdownBlockingTicket> mShutdownBlocker;
// Held to make sure the dispatched tasks can be done before worker is going
// away. As long as this worker-ref is held somewhere, the tasks dispatched to
// the worker can be executed (otherwise the tasks would be canceled). This
// ref should be activated as long as the underlying MediaDataDecoder is
// alive, and should keep alive until mShutdownBlocker is dropped, so all
// MediaDataDecoder's tasks and mShutdownBlocker-releasing task can be
// executed.
// TODO: Use StrongWorkerRef instead if this is always used in the same
// thread?
RefPtr<dom::ThreadSafeWorkerRef> mWorkerRef;
};
} // namespace mozilla::dom

Просмотреть файл

@ -42,18 +42,6 @@ nsTArray<nsCString> GuessContainers(const nsAString& aCodec) {
return {};
}
Maybe<nsString> ParseCodecString(const nsAString& aCodec) {
// Trim the spaces on each end.
nsString str(aCodec);
str.Trim(" ");
nsTArray<nsString> codecs;
if (!ParseCodecsString(str, codecs) || codecs.Length() != 1 ||
codecs[0] != str) {
return Nothing();
}
return Some(codecs[0]);
}
/*
* The below are helpers to operate ArrayBuffer or ArrayBufferView.
*/

Просмотреть файл

@ -31,13 +31,11 @@ enum class YUVColorSpace : uint8_t;
namespace dom {
/*
* The followings are helpers for WebCodecs methods.
* The followings are helpers for WebCodecs methods
*/
nsTArray<nsCString> GuessContainers(const nsAString& aCodec);
Maybe<nsString> ParseCodecString(const nsAString& aCodec);
/*
* Below are helpers for conversion among Maybe, Optional, and Nullable.
*/

Просмотреть файл

@ -21,7 +21,6 @@ EXPORTS.mozilla += [
]
EXPORTS.mozilla.dom += [
"DecoderTemplate.h",
"EncodedVideoChunk.h",
"VideoColorSpace.h",
"VideoDecoder.h",
@ -31,7 +30,6 @@ EXPORTS.mozilla.dom += [
UNIFIED_SOURCES += [
"DecoderAgent.cpp",
"DecoderTemplate.cpp",
"EncodedVideoChunk.cpp",
"VideoColorSpace.cpp",
"VideoDecoder.cpp",