Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / dom / media / webcodecs / DecoderTemplate.cpp
blob2fc2471a241bd39e7811d371e15ce8d3f33ffc1b
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "DecoderTemplate.h"
9 #include <atomic>
10 #include <utility>
12 #include "DecoderTypes.h"
13 #include "MediaInfo.h"
14 #include "mozilla/ScopeExit.h"
15 #include "mozilla/Try.h"
16 #include "mozilla/Unused.h"
17 #include "mozilla/dom/DOMException.h"
18 #include "mozilla/dom/Event.h"
19 #include "mozilla/dom/Promise.h"
20 #include "mozilla/dom/VideoDecoderBinding.h"
21 #include "mozilla/dom/VideoFrame.h"
22 #include "mozilla/dom/WorkerCommon.h"
23 #include "nsGkAtoms.h"
24 #include "nsString.h"
25 #include "nsThreadUtils.h"
27 mozilla::LazyLogModule gWebCodecsLog("WebCodecs");
29 namespace mozilla::dom {
31 #ifdef LOG_INTERNAL
32 # undef LOG_INTERNAL
33 #endif // LOG_INTERNAL
34 #define LOG_INTERNAL(level, msg, ...) \
35 MOZ_LOG(gWebCodecsLog, LogLevel::level, (msg, ##__VA_ARGS__))
37 #ifdef LOG
38 # undef LOG
39 #endif // LOG
40 #define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__)
42 #ifdef LOGW
43 # undef LOGW
44 #endif // LOGW
45 #define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__)
47 #ifdef LOGE
48 # undef LOGE
49 #endif // LOGE
50 #define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__)
52 #ifdef LOGV
53 # undef LOGV
54 #endif // LOGV
55 #define LOGV(msg, ...) LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__)
58 * Below are ControlMessage classes implementations
61 template <typename DecoderType>
62 DecoderTemplate<DecoderType>::ControlMessage::ControlMessage(
63 const nsACString& aTitle)
64 : mTitle(aTitle) {}
66 template <typename DecoderType>
67 DecoderTemplate<DecoderType>::ConfigureMessage::ConfigureMessage(
68 Id aId, UniquePtr<ConfigTypeInternal>&& aConfig)
69 : ControlMessage(
70 nsPrintfCString("configure #%d (%s)", aId,
71 NS_ConvertUTF16toUTF8(aConfig->mCodec).get())),
72 mId(aId),
73 mConfig(std::move(aConfig)) {}
75 /* static */
76 template <typename DecoderType>
77 typename DecoderTemplate<DecoderType>::ConfigureMessage*
78 DecoderTemplate<DecoderType>::ConfigureMessage::Create(
79 UniquePtr<ConfigTypeInternal>&& aConfig) {
80 // This needs to be atomic since this can run on the main thread or worker
81 // thread.
82 static std::atomic<Id> sNextId = NoId;
83 return new ConfigureMessage(++sNextId, std::move(aConfig));
86 template <typename DecoderType>
87 DecoderTemplate<DecoderType>::DecodeMessage::DecodeMessage(
88 Id aId, ConfigId aConfigId, UniquePtr<InputTypeInternal>&& aData)
89 : ControlMessage(
90 nsPrintfCString("decode #%zu (config #%d)", aId, aConfigId)),
91 mId(aId),
92 mData(std::move(aData)) {}
94 template <typename DecoderType>
95 DecoderTemplate<DecoderType>::FlushMessage::FlushMessage(Id aId,
96 ConfigId aConfigId,
97 Promise* aPromise)
98 : ControlMessage(
99 nsPrintfCString("flush #%zu (config #%d)", aId, aConfigId)),
100 mId(aId),
101 mPromise(aPromise) {}
103 template <typename DecoderType>
104 void DecoderTemplate<DecoderType>::FlushMessage::RejectPromiseIfAny(
105 const nsresult& aReason) {
106 if (mPromise) {
107 mPromise->MaybeReject(aReason);
112 * Below are DecoderTemplate implementation
115 template <typename DecoderType>
116 DecoderTemplate<DecoderType>::DecoderTemplate(
117 nsIGlobalObject* aGlobalObject,
118 RefPtr<WebCodecsErrorCallback>&& aErrorCallback,
119 RefPtr<OutputCallbackType>&& aOutputCallback)
120 : DOMEventTargetHelper(aGlobalObject),
121 mErrorCallback(std::move(aErrorCallback)),
122 mOutputCallback(std::move(aOutputCallback)),
123 mState(CodecState::Unconfigured),
124 mKeyChunkRequired(true),
125 mMessageQueueBlocked(false),
126 mDecodeQueueSize(0),
127 mDequeueEventScheduled(false),
128 mLatestConfigureId(ConfigureMessage::NoId),
129 mDecodeCounter(0),
130 mFlushCounter(0) {}
132 template <typename DecoderType>
133 void DecoderTemplate<DecoderType>::Configure(const ConfigType& aConfig,
134 ErrorResult& aRv) {
135 AssertIsOnOwningThread();
137 LOG("%s %p, Configure: codec %s", DecoderType::Name.get(), this,
138 NS_ConvertUTF16toUTF8(aConfig.mCodec).get());
140 nsCString errorMessage;
141 if (!DecoderType::Validate(aConfig, errorMessage)) {
142 LOG("Configure: Validate error: %s", errorMessage.get());
143 aRv.ThrowTypeError(errorMessage);
144 return;
147 if (mState == CodecState::Closed) {
148 LOG("Configure: CodecState::Closed, rejecting with InvalidState");
149 aRv.ThrowInvalidStateError("The codec is no longer usable");
150 return;
153 // Clone a ConfigType as the active decoder config.
154 UniquePtr<ConfigTypeInternal> config =
155 DecoderType::CreateConfigInternal(aConfig);
156 if (!config) {
157 aRv.Throw(NS_ERROR_UNEXPECTED); // Invalid description data.
158 return;
161 mState = CodecState::Configured;
162 mKeyChunkRequired = true;
163 mDecodeCounter = 0;
164 mFlushCounter = 0;
166 mControlMessageQueue.emplace(
167 UniquePtr<ControlMessage>(ConfigureMessage::Create(std::move(config))));
168 mLatestConfigureId = mControlMessageQueue.back()->AsConfigureMessage()->mId;
169 LOG("%s %p enqueues %s", DecoderType::Name.get(), this,
170 mControlMessageQueue.back()->ToString().get());
171 ProcessControlMessageQueue();
174 template <typename DecoderType>
175 void DecoderTemplate<DecoderType>::Decode(InputType& aInput, ErrorResult& aRv) {
176 AssertIsOnOwningThread();
178 LOG("%s %p, Decode", DecoderType::Name.get(), this);
180 if (mState != CodecState::Configured) {
181 aRv.ThrowInvalidStateError("Decoder must be configured first");
182 return;
185 if (mKeyChunkRequired) {
186 // TODO: Verify input's data is truly a key chunk
187 if (!DecoderType::IsKeyChunk(aInput)) {
188 aRv.ThrowDataError(
189 nsPrintfCString("%s needs a key chunk", DecoderType::Name.get()));
190 return;
192 mKeyChunkRequired = false;
195 mDecodeQueueSize += 1;
196 mControlMessageQueue.emplace(UniquePtr<ControlMessage>(
197 new DecodeMessage(++mDecodeCounter, mLatestConfigureId,
198 DecoderType::CreateInputInternal(aInput))));
199 LOGV("%s %p enqueues %s", DecoderType::Name.get(), this,
200 mControlMessageQueue.back()->ToString().get());
201 ProcessControlMessageQueue();
204 template <typename DecoderType>
205 already_AddRefed<Promise> DecoderTemplate<DecoderType>::Flush(
206 ErrorResult& aRv) {
207 AssertIsOnOwningThread();
209 LOG("%s %p, Flush", DecoderType::Name.get(), this);
211 if (mState != CodecState::Configured) {
212 LOG("%s %p, wrong state!", DecoderType::Name.get(), this);
213 aRv.ThrowInvalidStateError("Decoder must be configured first");
214 return nullptr;
217 RefPtr<Promise> p = Promise::Create(GetParentObject(), aRv);
218 if (NS_WARN_IF(aRv.Failed())) {
219 return p.forget();
222 mKeyChunkRequired = true;
224 mControlMessageQueue.emplace(UniquePtr<ControlMessage>(
225 new FlushMessage(++mFlushCounter, mLatestConfigureId, p)));
226 LOG("%s %p enqueues %s", DecoderType::Name.get(), this,
227 mControlMessageQueue.back()->ToString().get());
228 ProcessControlMessageQueue();
229 return p.forget();
232 template <typename DecoderType>
233 void DecoderTemplate<DecoderType>::Reset(ErrorResult& aRv) {
234 AssertIsOnOwningThread();
236 LOG("%s %p, Reset", DecoderType::Name.get(), this);
238 if (auto r = ResetInternal(NS_ERROR_DOM_ABORT_ERR); r.isErr()) {
239 aRv.Throw(r.unwrapErr());
243 template <typename DecoderType>
244 void DecoderTemplate<DecoderType>::Close(ErrorResult& aRv) {
245 AssertIsOnOwningThread();
247 LOG("%s %p, Close", DecoderType::Name.get(), this);
249 if (auto r = CloseInternalWithAbort(); r.isErr()) {
250 aRv.Throw(r.unwrapErr());
254 template <typename DecoderType>
255 Result<Ok, nsresult> DecoderTemplate<DecoderType>::ResetInternal(
256 const nsresult& aResult) {
257 AssertIsOnOwningThread();
259 if (mState == CodecState::Closed) {
260 return Err(NS_ERROR_DOM_INVALID_STATE_ERR);
263 mState = CodecState::Unconfigured;
264 mDecodeCounter = 0;
265 mFlushCounter = 0;
267 CancelPendingControlMessages(aResult);
268 DestroyDecoderAgentIfAny();
270 if (mDecodeQueueSize > 0) {
271 mDecodeQueueSize = 0;
272 ScheduleDequeueEventIfNeeded();
275 LOG("%s %p now has its message queue unblocked", DecoderType::Name.get(),
276 this);
277 mMessageQueueBlocked = false;
279 return Ok();
281 template <typename DecoderType>
282 Result<Ok, nsresult> DecoderTemplate<DecoderType>::CloseInternalWithAbort() {
283 AssertIsOnOwningThread();
285 MOZ_TRY(ResetInternal(NS_ERROR_DOM_ABORT_ERR));
286 mState = CodecState::Closed;
287 return Ok();
290 template <typename DecoderType>
291 void DecoderTemplate<DecoderType>::CloseInternal(const nsresult& aResult) {
292 AssertIsOnOwningThread();
293 MOZ_ASSERT(aResult != NS_ERROR_DOM_ABORT_ERR, "Use CloseInternalWithAbort");
295 auto r = ResetInternal(aResult);
296 if (r.isErr()) {
297 nsCString name;
298 GetErrorName(r.unwrapErr(), name);
299 LOGE("Error in ResetInternal during CloseInternal: %s", name.get());
301 mState = CodecState::Closed;
302 nsCString error;
303 GetErrorName(aResult, error);
304 LOGE("%s %p Close on error: %s", DecoderType::Name.get(), this, error.get());
305 ReportError(aResult);
308 template <typename DecoderType>
309 void DecoderTemplate<DecoderType>::ReportError(const nsresult& aResult) {
310 AssertIsOnOwningThread();
312 RefPtr<DOMException> e = DOMException::Create(aResult);
313 RefPtr<WebCodecsErrorCallback> cb(mErrorCallback);
314 cb->Call(*e);
317 template <typename DecoderType>
318 void DecoderTemplate<DecoderType>::OutputDecodedData(
319 const nsTArray<RefPtr<MediaData>>&& aData) {
320 AssertIsOnOwningThread();
321 MOZ_ASSERT(mState == CodecState::Configured);
322 MOZ_ASSERT(mActiveConfig);
324 nsTArray<RefPtr<OutputType>> frames = DecodedDataToOutputType(
325 GetParentObject(), std::move(aData), *mActiveConfig);
326 RefPtr<OutputCallbackType> cb(mOutputCallback);
327 for (RefPtr<OutputType>& frame : frames) {
328 LOG("Outputing decoded data: ts: %" PRId64, frame->Timestamp());
329 RefPtr<OutputType> f = frame;
330 cb->Call((OutputType&)(*f));
334 template <typename DecoderType>
335 void DecoderTemplate<DecoderType>::ScheduleDequeueEventIfNeeded() {
336 AssertIsOnOwningThread();
338 if (mDequeueEventScheduled) {
339 return;
341 mDequeueEventScheduled = true;
343 QueueATask("dequeue event task", [self = RefPtr{this}]() {
344 self->FireEvent(nsGkAtoms::ondequeue, u"dequeue"_ns);
345 self->mDequeueEventScheduled = false;
349 template <typename DecoderType>
350 nsresult DecoderTemplate<DecoderType>::FireEvent(nsAtom* aTypeWithOn,
351 const nsAString& aEventType) {
352 if (aTypeWithOn && !HasListenersFor(aTypeWithOn)) {
353 LOGV("%s %p has no %s event listener", DecoderType::Name.get(), this,
354 NS_ConvertUTF16toUTF8(aEventType).get());
355 return NS_ERROR_ABORT;
358 LOGV("Dispatch %s event to %s %p", NS_ConvertUTF16toUTF8(aEventType).get(),
359 DecoderType::Name.get(), this);
360 RefPtr<Event> event = new Event(this, nullptr, nullptr);
361 event->InitEvent(aEventType, true, true);
362 event->SetTrusted(true);
363 this->DispatchEvent(*event);
364 return NS_OK;
367 template <typename DecoderType>
368 void DecoderTemplate<DecoderType>::ProcessControlMessageQueue() {
369 AssertIsOnOwningThread();
370 MOZ_ASSERT(mState == CodecState::Configured);
372 while (!mMessageQueueBlocked && !mControlMessageQueue.empty()) {
373 UniquePtr<ControlMessage>& msg = mControlMessageQueue.front();
374 if (msg->AsConfigureMessage()) {
375 if (ProcessConfigureMessage(msg) ==
376 MessageProcessedResult::NotProcessed) {
377 break;
379 } else if (msg->AsDecodeMessage()) {
380 if (ProcessDecodeMessage(msg) == MessageProcessedResult::NotProcessed) {
381 break;
383 } else {
384 MOZ_ASSERT(msg->AsFlushMessage());
385 if (ProcessFlushMessage(msg) == MessageProcessedResult::NotProcessed) {
386 break;
392 template <typename DecoderType>
393 void DecoderTemplate<DecoderType>::CancelPendingControlMessages(
394 const nsresult& aResult) {
395 AssertIsOnOwningThread();
397 // Cancel the message that is being processed.
398 if (mProcessingMessage) {
399 LOG("%s %p cancels current %s", DecoderType::Name.get(), this,
400 mProcessingMessage->ToString().get());
401 mProcessingMessage->Cancel();
403 if (FlushMessage* flush = mProcessingMessage->AsFlushMessage()) {
404 flush->RejectPromiseIfAny(aResult);
407 mProcessingMessage.reset();
410 // Clear the message queue.
411 while (!mControlMessageQueue.empty()) {
412 LOG("%s %p cancels pending %s", DecoderType::Name.get(), this,
413 mControlMessageQueue.front()->ToString().get());
415 MOZ_ASSERT(!mControlMessageQueue.front()->IsProcessing());
416 if (FlushMessage* flush = mControlMessageQueue.front()->AsFlushMessage()) {
417 flush->RejectPromiseIfAny(aResult);
420 mControlMessageQueue.pop();
424 template <typename DecoderType>
425 template <typename Func>
426 void DecoderTemplate<DecoderType>::QueueATask(const char* aName,
427 Func&& aSteps) {
428 AssertIsOnOwningThread();
429 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(
430 NS_NewRunnableFunction(aName, std::forward<Func>(aSteps))));
433 template <typename DecoderType>
434 MessageProcessedResult DecoderTemplate<DecoderType>::ProcessConfigureMessage(
435 UniquePtr<ControlMessage>& aMessage) {
436 AssertIsOnOwningThread();
437 MOZ_ASSERT(mState == CodecState::Configured);
438 MOZ_ASSERT(aMessage->AsConfigureMessage());
440 if (mProcessingMessage) {
441 LOG("%s %p is processing %s. Defer %s", DecoderType::Name.get(), this,
442 mProcessingMessage->ToString().get(), aMessage->ToString().get());
443 return MessageProcessedResult::NotProcessed;
446 mProcessingMessage.reset(aMessage.release());
447 mControlMessageQueue.pop();
449 ConfigureMessage* msg = mProcessingMessage->AsConfigureMessage();
450 LOG("%s %p starts processing %s", DecoderType::Name.get(), this,
451 msg->ToString().get());
453 DestroyDecoderAgentIfAny();
455 mMessageQueueBlocked = true;
457 nsAutoCString errorMessage;
458 auto i = DecoderType::CreateTrackInfo(msg->Config());
459 if (i.isErr()) {
460 nsCString res;
461 GetErrorName(i.unwrapErr(), res);
462 errorMessage.AppendPrintf("CreateTrackInfo failed: %s", res.get());
463 } else if (!DecoderType::IsSupported(msg->Config())) {
464 errorMessage.Append("Not supported.");
465 } else if (!CreateDecoderAgent(msg->mId, msg->TakeConfig(), i.unwrap())) {
466 errorMessage.Append("DecoderAgent creation failed.");
468 if (!errorMessage.IsEmpty()) {
469 LOGE("%s %p ProcessConfigureMessage error (sync): %s",
470 DecoderType::Name.get(), this, errorMessage.get());
472 mProcessingMessage.reset();
473 QueueATask("Error while configuring decoder",
474 [self = RefPtr{this}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY {
475 self->CloseInternal(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
477 return MessageProcessedResult::Processed;
480 MOZ_ASSERT(mAgent);
481 MOZ_ASSERT(mActiveConfig);
483 LOG("%s %p now blocks message-queue-processing", DecoderType::Name.get(),
484 this);
486 bool preferSW = mActiveConfig->mHardwareAcceleration ==
487 HardwareAcceleration::Prefer_software;
488 bool lowLatency = mActiveConfig->mOptimizeForLatency.isSome() &&
489 mActiveConfig->mOptimizeForLatency.value();
490 mAgent->Configure(preferSW, lowLatency)
491 ->Then(GetCurrentSerialEventTarget(), __func__,
492 [self = RefPtr{this}, id = mAgent->mId](
493 const DecoderAgent::ConfigurePromise::ResolveOrRejectValue&
494 aResult) {
495 MOZ_ASSERT(self->mProcessingMessage);
496 MOZ_ASSERT(self->mProcessingMessage->AsConfigureMessage());
497 MOZ_ASSERT(self->mState == CodecState::Configured);
498 MOZ_ASSERT(self->mAgent);
499 MOZ_ASSERT(id == self->mAgent->mId);
500 MOZ_ASSERT(self->mActiveConfig);
502 ConfigureMessage* msg =
503 self->mProcessingMessage->AsConfigureMessage();
504 LOG("%s %p, DecoderAgent #%d %s has been %s. now unblocks "
505 "message-queue-processing",
506 DecoderType::Name.get(), self.get(), id,
507 msg->ToString().get(),
508 aResult.IsResolve() ? "resolved" : "rejected");
510 msg->Complete();
511 self->mProcessingMessage.reset();
513 if (aResult.IsReject()) {
514 // The spec asks to close the decoder with an
515 // NotSupportedError so we log the exact error here.
516 const MediaResult& error = aResult.RejectValue();
517 LOGE("%s %p, DecoderAgent #%d failed to configure: %s",
518 DecoderType::Name.get(), self.get(), id,
519 error.Description().get());
521 self->QueueATask(
522 "Error during configure",
523 [self = RefPtr{self}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY {
524 MOZ_ASSERT(self->mState != CodecState::Closed);
525 self->CloseInternal(
526 NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
528 return;
531 self->mMessageQueueBlocked = false;
532 self->ProcessControlMessageQueue();
534 ->Track(msg->Request());
536 return MessageProcessedResult::Processed;
539 template <typename DecoderType>
540 MessageProcessedResult DecoderTemplate<DecoderType>::ProcessDecodeMessage(
541 UniquePtr<ControlMessage>& aMessage) {
542 AssertIsOnOwningThread();
543 MOZ_ASSERT(mState == CodecState::Configured);
544 MOZ_ASSERT(aMessage->AsDecodeMessage());
546 if (mProcessingMessage) {
547 LOGV("%s %p is processing %s. Defer %s", DecoderType::Name.get(), this,
548 mProcessingMessage->ToString().get(), aMessage->ToString().get());
549 return MessageProcessedResult::NotProcessed;
552 mProcessingMessage.reset(aMessage.release());
553 mControlMessageQueue.pop();
555 DecodeMessage* msg = mProcessingMessage->AsDecodeMessage();
556 LOGV("%s %p starts processing %s", DecoderType::Name.get(), this,
557 msg->ToString().get());
559 mDecodeQueueSize -= 1;
560 ScheduleDequeueEventIfNeeded();
562 // Treat it like decode error if no DecoderAgent is available or the encoded
563 // data is invalid.
564 auto closeOnError = [&]() {
565 mProcessingMessage.reset();
566 QueueATask("Error during decode",
567 [self = RefPtr{this}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY {
568 MOZ_ASSERT(self->mState != CodecState::Closed);
569 self->CloseInternal(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
571 return MessageProcessedResult::Processed;
574 if (!mAgent) {
575 LOGE("%s %p is not configured", DecoderType::Name.get(), this);
576 return closeOnError();
579 MOZ_ASSERT(mActiveConfig);
580 RefPtr<MediaRawData> data = InputDataToMediaRawData(
581 std::move(msg->mData), *(mAgent->mInfo), *mActiveConfig);
582 if (!data) {
583 LOGE("%s %p, data for %s is empty or invalid", DecoderType::Name.get(),
584 this, msg->ToString().get());
585 return closeOnError();
588 mAgent->Decode(data.get())
589 ->Then(
590 GetCurrentSerialEventTarget(), __func__,
591 [self = RefPtr{this}, id = mAgent->mId](
592 DecoderAgent::DecodePromise::ResolveOrRejectValue&& aResult) {
593 MOZ_ASSERT(self->mProcessingMessage);
594 MOZ_ASSERT(self->mProcessingMessage->AsDecodeMessage());
595 MOZ_ASSERT(self->mState == CodecState::Configured);
596 MOZ_ASSERT(self->mAgent);
597 MOZ_ASSERT(id == self->mAgent->mId);
598 MOZ_ASSERT(self->mActiveConfig);
600 DecodeMessage* msg = self->mProcessingMessage->AsDecodeMessage();
601 LOGV("%s %p, DecoderAgent #%d %s has been %s",
602 DecoderType::Name.get(), self.get(), id, msg->ToString().get(),
603 aResult.IsResolve() ? "resolved" : "rejected");
605 nsCString msgStr = msg->ToString();
607 msg->Complete();
608 self->mProcessingMessage.reset();
610 if (aResult.IsReject()) {
611 // The spec asks to queue a task to run close the decoder
612 // with an EncodingError so we log the exact error here.
613 const MediaResult& error = aResult.RejectValue();
614 LOGE("%s %p, DecoderAgent #%d %s failed: %s",
615 DecoderType::Name.get(), self.get(), id, msgStr.get(),
616 error.Description().get());
617 self->QueueATask(
618 "Error during decode runnable",
619 [self = RefPtr{self}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY {
620 MOZ_ASSERT(self->mState != CodecState::Closed);
621 self->CloseInternal(
622 NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
624 return;
627 MOZ_ASSERT(aResult.IsResolve());
628 nsTArray<RefPtr<MediaData>> data =
629 std::move(aResult.ResolveValue());
630 if (data.IsEmpty()) {
631 LOGV("%s %p got no data for %s", DecoderType::Name.get(),
632 self.get(), msgStr.get());
633 } else {
634 LOGV("%s %p, schedule %zu decoded data output for %s",
635 DecoderType::Name.get(), self.get(), data.Length(),
636 msgStr.get());
637 self->QueueATask("Output Decoded Data",
638 [self = RefPtr{self}, data = std::move(data)]()
639 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
640 self->OutputDecodedData(std::move(data));
643 self->ProcessControlMessageQueue();
645 ->Track(msg->Request());
647 return MessageProcessedResult::Processed;
650 template <typename DecoderType>
651 MessageProcessedResult DecoderTemplate<DecoderType>::ProcessFlushMessage(
652 UniquePtr<ControlMessage>& aMessage) {
653 AssertIsOnOwningThread();
654 MOZ_ASSERT(mState == CodecState::Configured);
655 MOZ_ASSERT(aMessage->AsFlushMessage());
657 if (mProcessingMessage) {
658 LOG("%s %p is processing %s. Defer %s", DecoderType::Name.get(), this,
659 mProcessingMessage->ToString().get(), aMessage->ToString().get());
660 return MessageProcessedResult::NotProcessed;
663 mProcessingMessage.reset(aMessage.release());
664 mControlMessageQueue.pop();
666 FlushMessage* msg = mProcessingMessage->AsFlushMessage();
667 LOG("%s %p starts processing %s", DecoderType::Name.get(), this,
668 msg->ToString().get());
670 // No agent, no thing to do. The promise has been rejected with the
671 // appropriate error in ResetInternal already.
672 if (!mAgent) {
673 LOGE("%s %p no agent, nothing to do", DecoderType::Name.get(), this);
674 mProcessingMessage.reset();
675 return MessageProcessedResult::Processed;
678 mAgent->DrainAndFlush()
679 ->Then(
680 GetCurrentSerialEventTarget(), __func__,
681 [self = RefPtr{this}, id = mAgent->mId,
682 this](DecoderAgent::DecodePromise::ResolveOrRejectValue&& aResult) {
683 MOZ_ASSERT(self->mProcessingMessage);
684 MOZ_ASSERT(self->mProcessingMessage->AsFlushMessage());
685 MOZ_ASSERT(self->mState == CodecState::Configured);
686 MOZ_ASSERT(self->mAgent);
687 MOZ_ASSERT(id == self->mAgent->mId);
688 MOZ_ASSERT(self->mActiveConfig);
690 FlushMessage* msg = self->mProcessingMessage->AsFlushMessage();
691 LOG("%s %p, DecoderAgent #%d %s has been %s",
692 DecoderType::Name.get(), self.get(), id, msg->ToString().get(),
693 aResult.IsResolve() ? "resolved" : "rejected");
695 nsCString msgStr = msg->ToString();
697 msg->Complete();
699 // If flush failed, it means decoder fails to decode the data
700 // sent before, so we treat it like decode error. We reject
701 // the promise first and then queue a task to close
702 // VideoDecoder with an EncodingError.
703 if (aResult.IsReject()) {
704 const MediaResult& error = aResult.RejectValue();
705 LOGE("%s %p, DecoderAgent #%d failed to flush: %s",
706 DecoderType::Name.get(), self.get(), id,
707 error.Description().get());
708 RefPtr<Promise> promise = msg->TakePromise();
709 // Reject with an EncodingError instead of the error we got
710 // above.
711 self->QueueATask(
712 "Error during flush runnable",
713 [self = RefPtr{this}, promise]() MOZ_CAN_RUN_SCRIPT_BOUNDARY {
714 promise->MaybeReject(
715 NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
716 self->mProcessingMessage.reset();
717 MOZ_ASSERT(self->mState != CodecState::Closed);
718 self->CloseInternal(
719 NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
721 return;
724 nsTArray<RefPtr<MediaData>> data =
725 std::move(aResult.ResolveValue());
727 if (data.IsEmpty()) {
728 LOG("%s %p gets no data for %s", DecoderType::Name.get(),
729 self.get(), msgStr.get());
730 } else {
731 LOG("%s %p, schedule %zu decoded data output for %s",
732 DecoderType::Name.get(), self.get(), data.Length(),
733 msgStr.get());
736 RefPtr<Promise> promise = msg->TakePromise();
737 self->QueueATask(
738 "Flush: output decoding data task",
739 [self = RefPtr{self}, promise, data = std::move(data)]()
740 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
741 self->OutputDecodedData(std::move(data));
742 promise->MaybeResolveWithUndefined();
744 self->mProcessingMessage.reset();
745 self->ProcessControlMessageQueue();
747 ->Track(msg->Request());
749 return MessageProcessedResult::Processed;
752 // CreateDecoderAgent will create an DecoderAgent paired with a xpcom-shutdown
753 // blocker and a worker-reference. Besides the needs mentioned in the header
754 // file, the blocker and the worker-reference also provides an entry point for
755 // us to clean up the resources. Other than the decoder dtor, Reset(), or
756 // Close(), the resources should be cleaned up in the following situations:
757 // 1. Decoder on window, closing document
758 // 2. Decoder on worker, closing document
759 // 3. Decoder on worker, terminating worker
761 // In case 1, the entry point to clean up is in the mShutdownBlocker's
762 // ShutdownpPomise-resolver. In case 2, the entry point is in mWorkerRef's
763 // shutting down callback. In case 3, the entry point is in mWorkerRef's
764 // shutting down callback.
766 template <typename DecoderType>
767 bool DecoderTemplate<DecoderType>::CreateDecoderAgent(
768 DecoderAgent::Id aId, UniquePtr<ConfigTypeInternal>&& aConfig,
769 UniquePtr<TrackInfo>&& aInfo) {
770 AssertIsOnOwningThread();
771 MOZ_ASSERT(mState == CodecState::Configured);
772 MOZ_ASSERT(!mAgent);
773 MOZ_ASSERT(!mActiveConfig);
774 MOZ_ASSERT(!mShutdownBlocker);
775 MOZ_ASSERT_IF(!NS_IsMainThread(), !mWorkerRef);
777 auto resetOnFailure = MakeScopeExit([&]() {
778 mAgent = nullptr;
779 mActiveConfig = nullptr;
780 mShutdownBlocker = nullptr;
781 mWorkerRef = nullptr;
784 // If the decoder is on worker, get a worker reference.
785 if (!NS_IsMainThread()) {
786 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
787 if (NS_WARN_IF(!workerPrivate)) {
788 return false;
791 // Clean up all the resources when worker is going away.
792 RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create(
793 workerPrivate, "DecoderTemplate::CreateDecoderAgent",
794 [self = RefPtr{this}]() {
795 LOG("%s %p, worker is going away", DecoderType::Name.get(),
796 self.get());
797 Unused << self->ResetInternal(NS_ERROR_DOM_ABORT_ERR);
799 if (NS_WARN_IF(!workerRef)) {
800 return false;
803 mWorkerRef = new ThreadSafeWorkerRef(workerRef);
806 mAgent = MakeRefPtr<DecoderAgent>(aId, std::move(aInfo));
807 mActiveConfig = std::move(aConfig);
809 // ShutdownBlockingTicket requires an unique name to register its own
810 // nsIAsyncShutdownBlocker since each blocker needs a distinct name.
811 // To do that, we use DecoderAgent's unique id to create a unique name.
812 nsAutoString uniqueName;
813 uniqueName.AppendPrintf(
814 "Blocker for DecoderAgent #%d (codec: %s) @ %p", mAgent->mId,
815 NS_ConvertUTF16toUTF8(mActiveConfig->mCodec).get(), mAgent.get());
817 mShutdownBlocker = media::ShutdownBlockingTicket::Create(
818 uniqueName, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__);
819 if (!mShutdownBlocker) {
820 LOGE("%s %p failed to create %s", DecoderType::Name.get(), this,
821 NS_ConvertUTF16toUTF8(uniqueName).get());
822 return false;
825 // Clean up all the resources when xpcom-will-shutdown arrives since the page
826 // is going to be closed.
827 mShutdownBlocker->ShutdownPromise()->Then(
828 GetCurrentSerialEventTarget(), __func__,
829 [self = RefPtr{this}, id = mAgent->mId,
830 ref = mWorkerRef](bool /* aUnUsed*/) {
831 LOG("%s %p gets xpcom-will-shutdown notification for DecoderAgent #%d",
832 DecoderType::Name.get(), self.get(), id);
833 Unused << self->ResetInternal(NS_ERROR_DOM_ABORT_ERR);
835 [self = RefPtr{this}, id = mAgent->mId,
836 ref = mWorkerRef](bool /* aUnUsed*/) {
837 LOG("%s %p removes shutdown-blocker #%d before getting any "
838 "notification. DecoderAgent #%d should have been dropped",
839 DecoderType::Name.get(), self.get(), id, id);
840 MOZ_ASSERT(!self->mAgent || self->mAgent->mId != id);
843 LOG("%s %p creates DecoderAgent #%d @ %p and its shutdown-blocker",
844 DecoderType::Name.get(), this, mAgent->mId, mAgent.get());
846 resetOnFailure.release();
847 return true;
850 template <typename DecoderType>
851 void DecoderTemplate<DecoderType>::DestroyDecoderAgentIfAny() {
852 AssertIsOnOwningThread();
854 if (!mAgent) {
855 LOG("%s %p has no DecoderAgent to destroy", DecoderType::Name.get(), this);
856 return;
859 MOZ_ASSERT(mActiveConfig);
860 MOZ_ASSERT(mShutdownBlocker);
861 MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerRef);
863 LOG("%s %p destroys DecoderAgent #%d @ %p", DecoderType::Name.get(), this,
864 mAgent->mId, mAgent.get());
865 mActiveConfig = nullptr;
866 RefPtr<DecoderAgent> agent = std::move(mAgent);
867 // mShutdownBlocker should be kept alive until the shutdown is done.
868 // mWorkerRef is used to ensure this task won't be discarded in worker.
869 agent->Shutdown()->Then(
870 GetCurrentSerialEventTarget(), __func__,
871 [self = RefPtr{this}, id = agent->mId, ref = std::move(mWorkerRef),
872 blocker = std::move(mShutdownBlocker)](
873 const ShutdownPromise::ResolveOrRejectValue& aResult) {
874 LOG("%s %p, DecoderAgent #%d's shutdown has been %s. Drop its "
875 "shutdown-blocker now",
876 DecoderType::Name.get(), self.get(), id,
877 aResult.IsResolve() ? "resolved" : "rejected");
881 template class DecoderTemplate<VideoDecoderTraits>;
882 template class DecoderTemplate<AudioDecoderTraits>;
884 #undef LOG
885 #undef LOGW
886 #undef LOGE
887 #undef LOGV
888 #undef LOG_INTERNAL
890 } // namespace mozilla::dom