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"
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"
25 #include "nsThreadUtils.h"
27 mozilla::LazyLogModule
gWebCodecsLog("WebCodecs");
29 namespace mozilla::dom
{
33 #endif // LOG_INTERNAL
34 #define LOG_INTERNAL(level, msg, ...) \
35 MOZ_LOG(gWebCodecsLog, LogLevel::level, (msg, ##__VA_ARGS__))
40 #define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__)
45 #define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__)
50 #define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__)
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
)
66 template <typename DecoderType
>
67 DecoderTemplate
<DecoderType
>::ConfigureMessage::ConfigureMessage(
68 Id aId
, UniquePtr
<ConfigTypeInternal
>&& aConfig
)
70 nsPrintfCString("configure #%d (%s)", aId
,
71 NS_ConvertUTF16toUTF8(aConfig
->mCodec
).get())),
73 mConfig(std::move(aConfig
)) {}
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
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
)
90 nsPrintfCString("decode #%zu (config #%d)", aId
, aConfigId
)),
92 mData(std::move(aData
)) {}
94 template <typename DecoderType
>
95 DecoderTemplate
<DecoderType
>::FlushMessage::FlushMessage(Id aId
,
99 nsPrintfCString("flush #%zu (config #%d)", aId
, aConfigId
)),
101 mPromise(aPromise
) {}
103 template <typename DecoderType
>
104 void DecoderTemplate
<DecoderType
>::FlushMessage::RejectPromiseIfAny(
105 const nsresult
& aReason
) {
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),
127 mDequeueEventScheduled(false),
128 mLatestConfigureId(ConfigureMessage::NoId
),
132 template <typename DecoderType
>
133 void DecoderTemplate
<DecoderType
>::Configure(const ConfigType
& aConfig
,
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
);
147 if (mState
== CodecState::Closed
) {
148 LOG("Configure: CodecState::Closed, rejecting with InvalidState");
149 aRv
.ThrowInvalidStateError("The codec is no longer usable");
153 // Clone a ConfigType as the active decoder config.
154 UniquePtr
<ConfigTypeInternal
> config
=
155 DecoderType::CreateConfigInternal(aConfig
);
157 aRv
.Throw(NS_ERROR_UNEXPECTED
); // Invalid description data.
161 mState
= CodecState::Configured
;
162 mKeyChunkRequired
= true;
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");
185 if (mKeyChunkRequired
) {
186 // TODO: Verify input's data is truly a key chunk
187 if (!DecoderType::IsKeyChunk(aInput
)) {
189 nsPrintfCString("%s needs a key chunk", DecoderType::Name
.get()));
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(
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");
217 RefPtr
<Promise
> p
= Promise::Create(GetParentObject(), aRv
);
218 if (NS_WARN_IF(aRv
.Failed())) {
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();
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
;
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(),
277 mMessageQueueBlocked
= false;
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
;
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
);
298 GetErrorName(r
.unwrapErr(), name
);
299 LOGE("Error in ResetInternal during CloseInternal: %s", name
.get());
301 mState
= CodecState::Closed
;
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
);
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
) {
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
);
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
) {
379 } else if (msg
->AsDecodeMessage()) {
380 if (ProcessDecodeMessage(msg
) == MessageProcessedResult::NotProcessed
) {
384 MOZ_ASSERT(msg
->AsFlushMessage());
385 if (ProcessFlushMessage(msg
) == MessageProcessedResult::NotProcessed
) {
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
,
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());
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
;
481 MOZ_ASSERT(mActiveConfig
);
483 LOG("%s %p now blocks message-queue-processing", DecoderType::Name
.get(),
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
&
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");
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());
522 "Error during configure",
523 [self
= RefPtr
{self
}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY
{
524 MOZ_ASSERT(self
->mState
!= CodecState::Closed
);
526 NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR
);
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
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
;
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
);
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())
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();
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());
618 "Error during decode runnable",
619 [self
= RefPtr
{self
}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY
{
620 MOZ_ASSERT(self
->mState
!= CodecState::Closed
);
622 NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR
);
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());
634 LOGV("%s %p, schedule %zu decoded data output for %s",
635 DecoderType::Name
.get(), self
.get(), data
.Length(),
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.
673 LOGE("%s %p no agent, nothing to do", DecoderType::Name
.get(), this);
674 mProcessingMessage
.reset();
675 return MessageProcessedResult::Processed
;
678 mAgent
->DrainAndFlush()
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();
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
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
);
719 NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR
);
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());
731 LOG("%s %p, schedule %zu decoded data output for %s",
732 DecoderType::Name
.get(), self
.get(), data
.Length(),
736 RefPtr
<Promise
> promise
= msg
->TakePromise();
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
);
773 MOZ_ASSERT(!mActiveConfig
);
774 MOZ_ASSERT(!mShutdownBlocker
);
775 MOZ_ASSERT_IF(!NS_IsMainThread(), !mWorkerRef
);
777 auto resetOnFailure
= MakeScopeExit([&]() {
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
)) {
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(),
797 Unused
<< self
->ResetInternal(NS_ERROR_DOM_ABORT_ERR
);
799 if (NS_WARN_IF(!workerRef
)) {
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());
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();
850 template <typename DecoderType
>
851 void DecoderTemplate
<DecoderType
>::DestroyDecoderAgentIfAny() {
852 AssertIsOnOwningThread();
855 LOG("%s %p has no DecoderAgent to destroy", DecoderType::Name
.get(), this);
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
>;
890 } // namespace mozilla::dom