/* -*- 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/. */
/* static */ template <typename DecoderType> typename DecoderTemplate<DecoderType>::ConfigureMessage*
DecoderTemplate<DecoderType>::ConfigureMessage::Create(
already_AddRefed<ConfigTypeInternal> aConfig) { // This needs to be atomic since this can run on the main thread or worker // thread. static std::atomic<WebCodecsId> sNextId = 0; returnnew ConfigureMessage(++sNextId, std::move(aConfig));
}
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;
}
if (mState == CodecState::Closed) {
LOG("Configure: CodecState::Closed, rejecting with InvalidState");
aRv.ThrowInvalidStateError("The codec is no longer usable"); return;
}
// Clone a ConfigType as the active decoder config.
RefPtr<ConfigTypeInternal> config =
DecoderType::CreateConfigInternal(aConfig); if (!config) {
aRv.Throw(NS_ERROR_UNEXPECTED); // Invalid description data. return;
}
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;
}
// Cancel the message that is being processed. if (mProcessingMessage) {
LOG("%s %p cancels current %s", DecoderType::Name.get(), this,
mProcessingMessage->ToString().get());
mProcessingMessage->Cancel();
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());
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();
}
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, DecoderAgent #%d failed to configure: %s",
DecoderType::Name.get(), self.get(), id,
error.Description().get());
// Treat it like decode error if no DecoderAgent is available or the encoded // data is invalid. auto closeOnError = [&]() {
mProcessingMessage.reset();
QueueATask("Error during decode",
[self = RefPtr{this}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY {
self->CloseInternal(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();
}
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, DecoderAgent #%d %s failed: %s",
DecoderType::Name.get(), self.get(), id, msgStr.get(),
error.Description().get());
self->QueueATask( "Error during decode runnable",
[self = RefPtr{self}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY {
MOZ_ASSERT(self->mState != CodecState::Closed);
self->CloseInternal(
NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
}); return;
}
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->QueueATask("Output Decoded Data",
[self = RefPtr{self}, data = std::move(data),
config = RefPtr{self->mActiveConfig}]()
MOZ_CAN_RUN_SCRIPT_BOUNDARY {
self->OutputDecodedData(std::move(data),
*config);
});
}
self->ProcessControlMessageQueue();
})
->Track(msg->Request());
// No agent, no thing to do. The promise has been rejected with the // appropriate error in ResetInternal already. if (!mAgent) {
LOGE("%s %p no agent, nothing to do", DecoderType::Name.get(), this);
mProcessingMessage.reset(); return MessageProcessedResult::Processed;
}
// 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, DecoderAgent #%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->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.reset();
MOZ_ASSERT(self->mState != CodecState::Closed);
self->CloseInternal(
NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
}); return;
}
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->QueueATask( "Flush: output decoding data task",
[self = RefPtr{self}, data = std::move(data),
config = RefPtr{self->mActiveConfig},
flushPromiseId]() MOZ_CAN_RUN_SCRIPT_BOUNDARY {
self->OutputDecodedData(std::move(data), *config); // 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();
})
->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.
// If the decoder is on worker, get a worker reference. if (!NS_IsMainThread()) {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); if (NS_WARN_IF(!workerPrivate)) { returnfalse;
}
// 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)) { returnfalse;
}
mWorkerRef = new ThreadSafeWorkerRef(workerRef);
}
// ShutdownBlockingTicket requires an unique name to register its own // nsIAsyncShutdownBlocker since each blocker needs a distinct name. // To do that, we use DecoderAgent's unique id to create a unique name.
nsAutoString uniqueName;
uniqueName.AppendPrintf( "Blocker for DecoderAgent #%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()); returnfalse;
}
// 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 DecoderAgent #%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. DecoderAgent #%d should have been dropped",
DecoderType::Name.get(), self.get(), id, id);
MOZ_ASSERT(!self->mAgent || self->mAgent->mId != id);
});
LOG("%s %p creates DecoderAgent #%d @ %p and its shutdown-blocker",
DecoderType::Name.get(), this, mAgent->mId, mAgent.get());
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.