DecoderTemplate.cpp (34941B)
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/. */ 6 7 #include "DecoderTemplate.h" 8 9 #include <atomic> 10 #include <utility> 11 12 #include "DecoderTypes.h" 13 #include "MediaInfo.h" 14 #include "mozilla/ScopeExit.h" 15 #include "mozilla/Try.h" 16 #include "mozilla/dom/DOMException.h" 17 #include "mozilla/dom/Event.h" 18 #include "mozilla/dom/Promise.h" 19 #include "mozilla/dom/VideoDecoderBinding.h" 20 #include "mozilla/dom/VideoFrame.h" 21 #include "mozilla/dom/WorkerCommon.h" 22 #include "nsGkAtoms.h" 23 #include "nsString.h" 24 #include "nsThreadUtils.h" 25 26 mozilla::LazyLogModule gWebCodecsLog("WebCodecs"); 27 28 namespace mozilla::dom { 29 30 #ifdef LOG_INTERNAL 31 # undef LOG_INTERNAL 32 #endif // LOG_INTERNAL 33 #define LOG_INTERNAL(level, msg, ...) \ 34 MOZ_LOG(gWebCodecsLog, LogLevel::level, (msg, ##__VA_ARGS__)) 35 36 #ifdef LOG 37 # undef LOG 38 #endif // LOG 39 #define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__) 40 41 #ifdef LOGW 42 # undef LOGW 43 #endif // LOGW 44 #define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__) 45 46 #ifdef LOGE 47 # undef LOGE 48 #endif // LOGE 49 #define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__) 50 51 #ifdef LOGV 52 # undef LOGV 53 #endif // LOGV 54 #define LOGV(msg, ...) LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__) 55 56 #define AUTO_DECODER_MARKER(var, postfix) \ 57 AutoWebCodecsMarker var(DecoderType::Name.get(), postfix); 58 59 /* 60 * Below are ControlMessage classes implementations 61 */ 62 63 template <typename DecoderType> 64 DecoderTemplate<DecoderType>::ConfigureMessage::ConfigureMessage( 65 WebCodecsId aConfigId, already_AddRefed<ConfigTypeInternal> aConfig) 66 : ControlMessage(aConfigId), 67 mConfig(aConfig), 68 mCodec(NS_ConvertUTF16toUTF8(mConfig->mCodec)) {} 69 70 template <typename DecoderType> 71 nsCString DecoderTemplate<DecoderType>::ConfigureMessage::ToString() const { 72 return nsPrintfCString("configure #%zu (%s)", ControlMessage::mConfigId, 73 mCodec.get()); 74 } 75 76 /* static */ 77 template <typename DecoderType> 78 typename DecoderTemplate<DecoderType>::ConfigureMessage* 79 DecoderTemplate<DecoderType>::ConfigureMessage::Create( 80 already_AddRefed<ConfigTypeInternal> aConfig) { 81 // This needs to be atomic since this can run on the main thread or worker 82 // thread. 83 static std::atomic<WebCodecsId> sNextId = 0; 84 return new ConfigureMessage(++sNextId, std::move(aConfig)); 85 } 86 87 template <typename DecoderType> 88 DecoderTemplate<DecoderType>::DecodeMessage::DecodeMessage( 89 WebCodecsId aSeqId, WebCodecsId aConfigId, 90 UniquePtr<InputTypeInternal>&& aData) 91 : ControlMessage(aConfigId), mSeqId(aSeqId), mData(std::move(aData)) {} 92 93 template <typename DecoderType> 94 nsCString DecoderTemplate<DecoderType>::DecodeMessage::ToString() const { 95 return nsPrintfCString("decode #%zu (config #%zu)", mSeqId, 96 ControlMessage::mConfigId); 97 } 98 99 static int64_t GenerateUniqueId() { 100 // This needs to be atomic since this can run on the main thread or worker 101 // thread. 102 static std::atomic<int64_t> sNextId = 0; 103 return ++sNextId; 104 } 105 106 template <typename DecoderType> 107 DecoderTemplate<DecoderType>::FlushMessage::FlushMessage(WebCodecsId aSeqId, 108 WebCodecsId aConfigId) 109 : ControlMessage(aConfigId), 110 mSeqId(aSeqId), 111 mUniqueId(GenerateUniqueId()) {} 112 113 template <typename DecoderType> 114 nsCString DecoderTemplate<DecoderType>::FlushMessage::ToString() const { 115 return nsPrintfCString("flush #%zu (config #%zu)", mSeqId, 116 ControlMessage::mConfigId); 117 } 118 119 /* 120 * Below are DecoderTemplate implementation 121 */ 122 123 template <typename DecoderType> 124 DecoderTemplate<DecoderType>::DecoderTemplate( 125 nsIGlobalObject* aGlobalObject, 126 RefPtr<WebCodecsErrorCallback>&& aErrorCallback, 127 RefPtr<OutputCallbackType>&& aOutputCallback) 128 : DOMEventTargetHelper(aGlobalObject), 129 mErrorCallback(std::move(aErrorCallback)), 130 mOutputCallback(std::move(aOutputCallback)), 131 mState(CodecState::Unconfigured), 132 mKeyChunkRequired(true), 133 mMessageQueueBlocked(false), 134 mDecodeQueueSize(0), 135 mDequeueEventScheduled(false), 136 mLatestConfigureId(0), 137 mDecodeCounter(0), 138 mFlushCounter(0) {} 139 140 template <typename DecoderType> 141 void DecoderTemplate<DecoderType>::Configure(const ConfigType& aConfig, 142 ErrorResult& aRv) { 143 AssertIsOnOwningThread(); 144 145 LOG("%s %p, Configure: codec %s", DecoderType::Name.get(), this, 146 NS_ConvertUTF16toUTF8(aConfig.mCodec).get()); 147 148 nsCString errorMessage; 149 if (!DecoderType::Validate(aConfig, errorMessage)) { 150 LOG("Configure: Validate error: %s", errorMessage.get()); 151 aRv.ThrowTypeError(errorMessage); 152 return; 153 } 154 155 if (mState == CodecState::Closed) { 156 LOG("Configure: CodecState::Closed, rejecting with InvalidState"); 157 aRv.ThrowInvalidStateError("The codec is no longer usable"); 158 return; 159 } 160 161 // Clone a ConfigType as the active decoder config. 162 RefPtr<ConfigTypeInternal> config = 163 DecoderType::CreateConfigInternal(aConfig); 164 if (!config) { 165 aRv.Throw(NS_ERROR_UNEXPECTED); // Invalid description data. 166 return; 167 } 168 169 // Audio encoders are all software, no need to do anything. 170 // This is incomplete and will be implemented fully in bug 1967793 171 if constexpr (std::is_same_v<ConfigType, VideoDecoderConfig>) { 172 ApplyResistFingerprintingIfNeeded(config, GetOwnerGlobal()); 173 } 174 175 mState = CodecState::Configured; 176 mKeyChunkRequired = true; 177 mDecodeCounter = 0; 178 mFlushCounter = 0; 179 180 mControlMessageQueue.emplace( 181 UniquePtr<ControlMessage>(ConfigureMessage::Create(config.forget()))); 182 mLatestConfigureId = mControlMessageQueue.back()->mConfigId; 183 LOG("%s %p enqueues %s", DecoderType::Name.get(), this, 184 mControlMessageQueue.back()->ToString().get()); 185 ProcessControlMessageQueue(); 186 } 187 188 template <typename DecoderType> 189 void DecoderTemplate<DecoderType>::Decode(InputType& aInput, ErrorResult& aRv) { 190 AssertIsOnOwningThread(); 191 192 LOG("%s %p, Decode %s", DecoderType::Name.get(), this, 193 aInput.ToString().get()); 194 195 if (mState != CodecState::Configured) { 196 aRv.ThrowInvalidStateError("Decoder must be configured first"); 197 return; 198 } 199 200 if (mKeyChunkRequired) { 201 // TODO: Verify input's data is truly a key chunk 202 if (!DecoderType::IsKeyChunk(aInput)) { 203 aRv.ThrowDataError( 204 nsPrintfCString("%s needs a key chunk", DecoderType::Name.get())); 205 return; 206 } 207 mKeyChunkRequired = false; 208 } 209 210 mAsyncDurationTracker.Start( 211 aInput.Timestamp(), 212 AutoWebCodecsMarker(DecoderType::Name.get(), ".decode-duration")); 213 mDecodeQueueSize += 1; 214 mControlMessageQueue.emplace(UniquePtr<ControlMessage>( 215 new DecodeMessage(++mDecodeCounter, mLatestConfigureId, 216 DecoderType::CreateInputInternal(aInput)))); 217 LOGV("%s %p enqueues %s", DecoderType::Name.get(), this, 218 mControlMessageQueue.back()->ToString().get()); 219 ProcessControlMessageQueue(); 220 } 221 222 template <typename DecoderType> 223 already_AddRefed<Promise> DecoderTemplate<DecoderType>::Flush( 224 ErrorResult& aRv) { 225 AssertIsOnOwningThread(); 226 227 LOG("%s %p, Flush", DecoderType::Name.get(), this); 228 229 if (mState != CodecState::Configured) { 230 LOG("%s %p, wrong state!", DecoderType::Name.get(), this); 231 aRv.ThrowInvalidStateError("Decoder must be configured first"); 232 return nullptr; 233 } 234 235 RefPtr<Promise> p = Promise::Create(GetParentObject(), aRv); 236 if (NS_WARN_IF(aRv.Failed())) { 237 return p.forget(); 238 } 239 240 mKeyChunkRequired = true; 241 242 auto msg = UniquePtr<ControlMessage>( 243 new FlushMessage(++mFlushCounter, mLatestConfigureId)); 244 const auto flushPromiseId = msg->AsFlushMessage()->mUniqueId; 245 MOZ_ASSERT(!mPendingFlushPromises.Contains(flushPromiseId)); 246 mPendingFlushPromises.Insert(flushPromiseId, p); 247 248 mControlMessageQueue.emplace(std::move(msg)); 249 250 LOG("%s %p enqueues %s, with unique id %" PRId64, DecoderType::Name.get(), 251 this, mControlMessageQueue.back()->ToString().get(), flushPromiseId); 252 ProcessControlMessageQueue(); 253 return p.forget(); 254 } 255 256 template <typename DecoderType> 257 void DecoderTemplate<DecoderType>::Reset(ErrorResult& aRv) { 258 AssertIsOnOwningThread(); 259 260 LOG("%s %p, Reset", DecoderType::Name.get(), this); 261 262 if (auto r = ResetInternal(NS_ERROR_DOM_ABORT_ERR); r.isErr()) { 263 aRv.Throw(r.unwrapErr()); 264 } 265 } 266 267 template <typename DecoderType> 268 void DecoderTemplate<DecoderType>::Close(ErrorResult& aRv) { 269 AssertIsOnOwningThread(); 270 271 LOG("%s %p, Close", DecoderType::Name.get(), this); 272 273 if (auto r = CloseInternalWithAbort(); r.isErr()) { 274 aRv.Throw(r.unwrapErr()); 275 } 276 } 277 278 template <typename DecoderType> 279 Result<Ok, nsresult> DecoderTemplate<DecoderType>::ResetInternal( 280 const nsresult& aResult) { 281 AssertIsOnOwningThread(); 282 283 if (mState == CodecState::Closed) { 284 return Err(NS_ERROR_DOM_INVALID_STATE_ERR); 285 } 286 287 mState = CodecState::Unconfigured; 288 mDecodeCounter = 0; 289 mFlushCounter = 0; 290 291 CancelPendingControlMessagesAndFlushPromises(aResult); 292 DestroyDecoderAgentIfAny(); 293 294 if (mDecodeQueueSize > 0) { 295 mDecodeQueueSize = 0; 296 ScheduleDequeueEventIfNeeded(); 297 } 298 299 LOG("%s %p now has its message queue unblocked", DecoderType::Name.get(), 300 this); 301 mMessageQueueBlocked = false; 302 303 return Ok(); 304 } 305 template <typename DecoderType> 306 Result<Ok, nsresult> DecoderTemplate<DecoderType>::CloseInternalWithAbort() { 307 AssertIsOnOwningThread(); 308 309 MOZ_TRY(ResetInternal(NS_ERROR_DOM_ABORT_ERR)); 310 mState = CodecState::Closed; 311 return Ok(); 312 } 313 314 template <typename DecoderType> 315 void DecoderTemplate<DecoderType>::CloseInternal(const nsresult& aResult) { 316 AssertIsOnOwningThread(); 317 MOZ_ASSERT(aResult != NS_ERROR_DOM_ABORT_ERR, "Use CloseInternalWithAbort"); 318 319 auto r = ResetInternal(aResult); 320 if (r.isErr()) { 321 nsCString name; 322 GetErrorName(r.unwrapErr(), name); 323 LOGE("Error in ResetInternal during CloseInternal: %s", name.get()); 324 } 325 mState = CodecState::Closed; 326 nsCString error; 327 GetErrorName(aResult, error); 328 LOGE("%s %p Close on error: %s", DecoderType::Name.get(), this, error.get()); 329 ReportError(aResult); 330 } 331 332 template <typename DecoderType> 333 void DecoderTemplate<DecoderType>::ReportError(const nsresult& aResult) { 334 AssertIsOnOwningThread(); 335 336 RefPtr<DOMException> e = DOMException::Create(aResult); 337 RefPtr<WebCodecsErrorCallback> cb(mErrorCallback); 338 cb->Call(*e); 339 } 340 341 template <typename DecoderType> 342 void DecoderTemplate<DecoderType>::OutputDecodedData( 343 const nsTArray<RefPtr<MediaData>>&& aData, 344 const ConfigTypeInternal& aConfig) { 345 AssertIsOnOwningThread(); 346 MOZ_ASSERT(mState == CodecState::Configured); 347 348 if (!GetParentObject()) { 349 LOGE("%s %p Canceling output callbacks since parent-object is gone", 350 DecoderType::Name.get(), this); 351 return; 352 } 353 354 nsTArray<RefPtr<OutputType>> frames = 355 DecodedDataToOutputType(GetParentObject(), std::move(aData), aConfig); 356 RefPtr<OutputCallbackType> cb(mOutputCallback); 357 for (RefPtr<OutputType>& frame : frames) { 358 LOG("Outputing decoded data: ts: %" PRId64, frame->Timestamp()); 359 RefPtr<OutputType> f = frame; 360 mAsyncDurationTracker.End(f->Timestamp()); 361 cb->Call((OutputType&)(*f)); 362 } 363 } 364 365 template <typename DecoderType> 366 void DecoderTemplate<DecoderType>::ScheduleDequeueEventIfNeeded() { 367 AssertIsOnOwningThread(); 368 369 if (mDequeueEventScheduled) { 370 return; 371 } 372 mDequeueEventScheduled = true; 373 374 QueueATask("dequeue event task", [self = RefPtr{this}]() { 375 self->FireEvent(nsGkAtoms::ondequeue, u"dequeue"_ns); 376 self->mDequeueEventScheduled = false; 377 }); 378 } 379 380 template <typename DecoderType> 381 nsresult DecoderTemplate<DecoderType>::FireEvent(nsAtom* aTypeWithOn, 382 const nsAString& aEventType) { 383 if (aTypeWithOn && !HasListenersFor(aTypeWithOn)) { 384 LOGV("%s %p has no %s event listener", DecoderType::Name.get(), this, 385 NS_ConvertUTF16toUTF8(aEventType).get()); 386 return NS_ERROR_ABORT; 387 } 388 389 LOGV("Dispatch %s event to %s %p", NS_ConvertUTF16toUTF8(aEventType).get(), 390 DecoderType::Name.get(), this); 391 RefPtr<Event> event = new Event(this, nullptr, nullptr); 392 event->InitEvent(aEventType, true, true); 393 event->SetTrusted(true); 394 this->DispatchEvent(*event); 395 return NS_OK; 396 } 397 398 template <typename DecoderType> 399 void DecoderTemplate<DecoderType>::ProcessControlMessageQueue() { 400 AssertIsOnOwningThread(); 401 MOZ_ASSERT(mState == CodecState::Configured); 402 403 while (!mMessageQueueBlocked && !mControlMessageQueue.empty()) { 404 UniquePtr<ControlMessage>& msg = mControlMessageQueue.front(); 405 if (msg->AsConfigureMessage()) { 406 if (ProcessConfigureMessage(msg) == 407 MessageProcessedResult::NotProcessed) { 408 break; 409 } 410 } else if (msg->AsDecodeMessage()) { 411 if (ProcessDecodeMessage(msg) == MessageProcessedResult::NotProcessed) { 412 break; 413 } 414 } else { 415 MOZ_ASSERT(msg->AsFlushMessage()); 416 if (ProcessFlushMessage(msg) == MessageProcessedResult::NotProcessed) { 417 break; 418 } 419 } 420 } 421 } 422 423 template <typename DecoderType> 424 void DecoderTemplate<DecoderType>::CancelPendingControlMessagesAndFlushPromises( 425 const nsresult& aResult) { 426 AssertIsOnOwningThread(); 427 428 // Cancel the message that is being processed. 429 if (mProcessingMessage) { 430 LOG("%s %p cancels current %s", DecoderType::Name.get(), this, 431 mProcessingMessage->ToString().get()); 432 mProcessingMessage->Cancel(); 433 mProcessingMessage.reset(); 434 } 435 436 // Clear the message queue. 437 while (!mControlMessageQueue.empty()) { 438 LOG("%s %p cancels pending %s", DecoderType::Name.get(), this, 439 mControlMessageQueue.front()->ToString().get()); 440 MOZ_ASSERT(!mControlMessageQueue.front()->IsProcessing()); 441 mControlMessageQueue.pop(); 442 } 443 444 // If there are pending flush promises, reject them. 445 mPendingFlushPromises.ForEach( 446 [&](const int64_t& id, const RefPtr<Promise>& p) { 447 LOG("%s %p, reject the promise for flush %" PRId64 " (unique id)", 448 DecoderType::Name.get(), this, id); 449 p->MaybeReject(aResult); 450 }); 451 mPendingFlushPromises.Clear(); 452 } 453 454 template <typename DecoderType> 455 template <typename Func> 456 void DecoderTemplate<DecoderType>::QueueATask(const char* aName, 457 Func&& aSteps) { 458 AssertIsOnOwningThread(); 459 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread( 460 NS_NewRunnableFunction(aName, std::forward<Func>(aSteps)))); 461 } 462 463 template <typename DecoderType> 464 MessageProcessedResult DecoderTemplate<DecoderType>::ProcessConfigureMessage( 465 UniquePtr<ControlMessage>& aMessage) { 466 AssertIsOnOwningThread(); 467 MOZ_ASSERT(mState == CodecState::Configured); 468 MOZ_ASSERT(aMessage->AsConfigureMessage()); 469 470 AUTO_DECODER_MARKER(marker, ".configure"); 471 472 if (mProcessingMessage) { 473 LOG("%s %p is processing %s. Defer %s", DecoderType::Name.get(), this, 474 mProcessingMessage->ToString().get(), aMessage->ToString().get()); 475 return MessageProcessedResult::NotProcessed; 476 } 477 478 mProcessingMessage.reset(aMessage.release()); 479 mControlMessageQueue.pop(); 480 481 ConfigureMessage* msg = mProcessingMessage->AsConfigureMessage(); 482 LOG("%s %p starts processing %s", DecoderType::Name.get(), this, 483 msg->ToString().get()); 484 485 DestroyDecoderAgentIfAny(); 486 487 mMessageQueueBlocked = true; 488 489 nsAutoCString errorMessage; 490 auto i = DecoderType::CreateTrackInfo(msg->Config()); 491 if (i.isErr()) { 492 nsCString res; 493 GetErrorName(i.unwrapErr(), res); 494 errorMessage.AppendPrintf("CreateTrackInfo failed: %s", res.get()); 495 } else if (!DecoderType::IsSupported(msg->Config())) { 496 errorMessage.Append("Not supported."); 497 } else if (!CreateDecoderAgent(msg->mConfigId, msg->TakeConfig(), 498 i.unwrap())) { 499 errorMessage.Append("DecoderAgent creation failed."); 500 } 501 if (!errorMessage.IsEmpty()) { 502 LOGE("%s %p ProcessConfigureMessage error (sync): %s", 503 DecoderType::Name.get(), this, errorMessage.get()); 504 505 mProcessingMessage.reset(); 506 QueueATask("Error while configuring decoder", 507 [self = RefPtr{this}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { 508 self->CloseInternal(NS_ERROR_DOM_NOT_SUPPORTED_ERR); 509 }); 510 return MessageProcessedResult::Processed; 511 } 512 513 MOZ_ASSERT(mAgent); 514 MOZ_ASSERT(mActiveConfig); 515 516 LOG("%s %p now blocks message-queue-processing", DecoderType::Name.get(), 517 this); 518 519 bool preferSW = mActiveConfig->mHardwareAcceleration == 520 HardwareAcceleration::Prefer_software; 521 bool lowLatency = mActiveConfig->mOptimizeForLatency.isSome() && 522 mActiveConfig->mOptimizeForLatency.value(); 523 524 mAgent->Configure(preferSW, lowLatency) 525 ->Then(GetCurrentSerialEventTarget(), __func__, 526 [self = RefPtr{this}, id = mAgent->mId, m = std::move(marker)]( 527 const DecoderAgent::ConfigurePromise::ResolveOrRejectValue& 528 aResult) mutable { 529 MOZ_ASSERT(self->mProcessingMessage); 530 MOZ_ASSERT(self->mProcessingMessage->AsConfigureMessage()); 531 MOZ_ASSERT(self->mState == CodecState::Configured); 532 MOZ_ASSERT(self->mAgent); 533 MOZ_ASSERT(id == self->mAgent->mId); 534 MOZ_ASSERT(self->mActiveConfig); 535 536 ConfigureMessage* msg = 537 self->mProcessingMessage->AsConfigureMessage(); 538 LOG("%s %p, DecoderAgent #%d %s has been %s. now unblocks " 539 "message-queue-processing", 540 DecoderType::Name.get(), self.get(), id, 541 msg->ToString().get(), 542 aResult.IsResolve() ? "resolved" : "rejected"); 543 544 msg->Complete(); 545 self->mProcessingMessage.reset(); 546 547 if (aResult.IsReject()) { 548 // The spec asks to close the decoder with an 549 // NotSupportedError so we log the exact error here. 550 const MediaResult& error = aResult.RejectValue(); 551 LOGE("%s %p, DecoderAgent #%d failed to configure: %s", 552 DecoderType::Name.get(), self.get(), id, 553 error.Description().get()); 554 555 self->QueueATask( 556 "Error during configure", 557 [self = RefPtr{self}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { 558 MOZ_ASSERT(self->mState != CodecState::Closed); 559 self->CloseInternal( 560 NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); 561 }); 562 return; 563 } 564 565 LOG("%s %p, DecoderAgent #%d configured successfully. %u decode " 566 "requests are pending", 567 DecoderType::Name.get(), self.get(), id, 568 self->mDecodeQueueSize); 569 self->mMessageQueueBlocked = false; 570 self->ProcessControlMessageQueue(); 571 }) 572 ->Track(msg->Request()); 573 574 return MessageProcessedResult::Processed; 575 } 576 577 template <typename DecoderType> 578 MessageProcessedResult DecoderTemplate<DecoderType>::ProcessDecodeMessage( 579 UniquePtr<ControlMessage>& aMessage) { 580 AssertIsOnOwningThread(); 581 MOZ_ASSERT(mState == CodecState::Configured); 582 MOZ_ASSERT(aMessage->AsDecodeMessage()); 583 584 AUTO_DECODER_MARKER(marker, ".decode-process"); 585 586 if (mProcessingMessage) { 587 LOGV("%s %p is processing %s. Defer %s", DecoderType::Name.get(), this, 588 mProcessingMessage->ToString().get(), aMessage->ToString().get()); 589 return MessageProcessedResult::NotProcessed; 590 } 591 592 mProcessingMessage.reset(aMessage.release()); 593 mControlMessageQueue.pop(); 594 595 DecodeMessage* msg = mProcessingMessage->AsDecodeMessage(); 596 LOGV("%s %p starts processing %s", DecoderType::Name.get(), this, 597 msg->ToString().get()); 598 599 mDecodeQueueSize -= 1; 600 ScheduleDequeueEventIfNeeded(); 601 602 // Treat it like decode error if no DecoderAgent is available or the encoded 603 // data is invalid. 604 auto closeOnError = [&]() { 605 mProcessingMessage.reset(); 606 QueueATask("Error during decode", 607 [self = RefPtr{this}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { 608 self->CloseInternal(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); 609 }); 610 return MessageProcessedResult::Processed; 611 }; 612 613 if (!mAgent) { 614 LOGE("%s %p is not configured", DecoderType::Name.get(), this); 615 return closeOnError(); 616 } 617 618 MOZ_ASSERT(mActiveConfig); 619 RefPtr<MediaRawData> data = InputDataToMediaRawData( 620 std::move(msg->mData), *(mAgent->mInfo), *mActiveConfig); 621 if (!data) { 622 LOGE("%s %p, data for %s is empty or invalid", DecoderType::Name.get(), 623 this, msg->ToString().get()); 624 return closeOnError(); 625 } 626 627 mAgent->Decode(data.get()) 628 ->Then(GetCurrentSerialEventTarget(), __func__, 629 [self = RefPtr{this}, id = mAgent->mId, m = std::move(marker)]( 630 DecoderAgent::DecodePromise::ResolveOrRejectValue&& 631 aResult) mutable { 632 MOZ_ASSERT(self->mProcessingMessage); 633 MOZ_ASSERT(self->mProcessingMessage->AsDecodeMessage()); 634 MOZ_ASSERT(self->mState == CodecState::Configured); 635 MOZ_ASSERT(self->mAgent); 636 MOZ_ASSERT(id == self->mAgent->mId); 637 MOZ_ASSERT(self->mActiveConfig); 638 639 DecodeMessage* msg = self->mProcessingMessage->AsDecodeMessage(); 640 LOGV("%s %p, DecoderAgent #%d %s has been %s", 641 DecoderType::Name.get(), self.get(), id, 642 msg->ToString().get(), 643 aResult.IsResolve() ? "resolved" : "rejected"); 644 645 nsCString msgStr = msg->ToString(); 646 647 msg->Complete(); 648 self->mProcessingMessage.reset(); 649 650 if (aResult.IsReject()) { 651 // The spec asks to queue a task to run close the decoder 652 // with an EncodingError so we log the exact error here. 653 const MediaResult& error = aResult.RejectValue(); 654 LOGE("%s %p, DecoderAgent #%d %s failed: %s", 655 DecoderType::Name.get(), self.get(), id, msgStr.get(), 656 error.Description().get()); 657 self->QueueATask( 658 "Error during decode runnable", 659 [self = RefPtr{self}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { 660 MOZ_ASSERT(self->mState != CodecState::Closed); 661 self->CloseInternal( 662 NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); 663 }); 664 return; 665 } 666 667 MOZ_ASSERT(aResult.IsResolve()); 668 nsTArray<RefPtr<MediaData>> data = 669 std::move(aResult.ResolveValue()); 670 if (data.IsEmpty()) { 671 LOGV("%s %p got no data for %s", DecoderType::Name.get(), 672 self.get(), msgStr.get()); 673 } else { 674 LOGV("%s %p, schedule %zu decoded data output for %s", 675 DecoderType::Name.get(), self.get(), data.Length(), 676 msgStr.get()); 677 678 m.End(); 679 AUTO_DECODER_MARKER(outMarker, ".decode-output"); 680 681 self->QueueATask("Output Decoded Data", 682 [self = RefPtr{self}, data = std::move(data), 683 config = RefPtr{self->mActiveConfig}, 684 om = std::move(outMarker)]() 685 MOZ_CAN_RUN_SCRIPT_BOUNDARY mutable { 686 self->OutputDecodedData(std::move(data), 687 *config); 688 }); 689 } 690 self->ProcessControlMessageQueue(); 691 }) 692 ->Track(msg->Request()); 693 694 return MessageProcessedResult::Processed; 695 } 696 697 template <typename DecoderType> 698 MessageProcessedResult DecoderTemplate<DecoderType>::ProcessFlushMessage( 699 UniquePtr<ControlMessage>& aMessage) { 700 AssertIsOnOwningThread(); 701 MOZ_ASSERT(mState == CodecState::Configured); 702 MOZ_ASSERT(aMessage->AsFlushMessage()); 703 704 AUTO_DECODER_MARKER(marker, ".flush"); 705 706 if (mProcessingMessage) { 707 LOG("%s %p is processing %s. Defer %s", DecoderType::Name.get(), this, 708 mProcessingMessage->ToString().get(), aMessage->ToString().get()); 709 return MessageProcessedResult::NotProcessed; 710 } 711 712 mProcessingMessage.reset(aMessage.release()); 713 mControlMessageQueue.pop(); 714 715 FlushMessage* msg = mProcessingMessage->AsFlushMessage(); 716 LOG("%s %p starts processing %s", DecoderType::Name.get(), this, 717 msg->ToString().get()); 718 719 // No agent, no thing to do. The promise has been rejected with the 720 // appropriate error in ResetInternal already. 721 if (!mAgent) { 722 LOGE("%s %p no agent, nothing to do", DecoderType::Name.get(), this); 723 mProcessingMessage.reset(); 724 return MessageProcessedResult::Processed; 725 } 726 727 mAgent->DrainAndFlush() 728 ->Then(GetCurrentSerialEventTarget(), __func__, 729 [self = RefPtr{this}, id = mAgent->mId, m = std::move(marker), 730 this](DecoderAgent::DecodePromise::ResolveOrRejectValue&& 731 aResult) mutable { 732 MOZ_ASSERT(self->mProcessingMessage); 733 MOZ_ASSERT(self->mProcessingMessage->AsFlushMessage()); 734 MOZ_ASSERT(self->mState == CodecState::Configured); 735 MOZ_ASSERT(self->mAgent); 736 MOZ_ASSERT(id == self->mAgent->mId); 737 MOZ_ASSERT(self->mActiveConfig); 738 739 FlushMessage* msg = self->mProcessingMessage->AsFlushMessage(); 740 LOG("%s %p, DecoderAgent #%d %s has been %s", 741 DecoderType::Name.get(), self.get(), id, 742 msg->ToString().get(), 743 aResult.IsResolve() ? "resolved" : "rejected"); 744 745 nsCString msgStr = msg->ToString(); 746 747 msg->Complete(); 748 749 const auto flushPromiseId = msg->mUniqueId; 750 751 // If flush failed, it means decoder fails to decode the data 752 // sent before, so we treat it like decode error. We reject 753 // the promise first and then queue a task to close 754 // VideoDecoder with an EncodingError. 755 if (aResult.IsReject()) { 756 const MediaResult& error = aResult.RejectValue(); 757 LOGE("%s %p, DecoderAgent #%d failed to flush: %s", 758 DecoderType::Name.get(), self.get(), id, 759 error.Description().get()); 760 // Reject with an EncodingError instead of the error we got 761 // above. 762 self->QueueATask( 763 "Error during flush runnable", 764 [self = RefPtr{this}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { 765 // If Reset() was invoked before this task executes, the 766 // promise in mPendingFlushPromises is handled there. 767 // Otherwise, the promise is going to be rejected by 768 // CloseInternal() below. 769 self->mProcessingMessage.reset(); 770 MOZ_ASSERT(self->mState != CodecState::Closed); 771 self->CloseInternal( 772 NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); 773 }); 774 return; 775 } 776 777 nsTArray<RefPtr<MediaData>> data = 778 std::move(aResult.ResolveValue()); 779 780 if (data.IsEmpty()) { 781 LOG("%s %p gets no data for %s", DecoderType::Name.get(), 782 self.get(), msgStr.get()); 783 } else { 784 LOG("%s %p, schedule %zu decoded data output for %s", 785 DecoderType::Name.get(), self.get(), data.Length(), 786 msgStr.get()); 787 } 788 789 m.End(); 790 AUTO_DECODER_MARKER(outMarker, ".flush-output"); 791 792 self->QueueATask( 793 "Flush: output decoding data task", 794 [self = RefPtr{self}, data = std::move(data), 795 config = RefPtr{self->mActiveConfig}, flushPromiseId, 796 om = std::move( 797 outMarker)]() MOZ_CAN_RUN_SCRIPT_BOUNDARY mutable { 798 self->OutputDecodedData(std::move(data), *config); 799 // If Reset() was invoked before this task executes, or 800 // during the output callback above in the execution of 801 // this task, the promise in mPendingFlushPromises is 802 // handled there. Otherwise, the promise is resolved here. 803 if (Maybe<RefPtr<Promise>> p = 804 self->mPendingFlushPromises.Take(flushPromiseId)) { 805 LOG("%s %p, resolving the promise for flush %" PRId64 806 " (unique id)", 807 DecoderType::Name.get(), self.get(), flushPromiseId); 808 p.value()->MaybeResolveWithUndefined(); 809 } 810 }); 811 self->mProcessingMessage.reset(); 812 self->ProcessControlMessageQueue(); 813 }) 814 ->Track(msg->Request()); 815 816 return MessageProcessedResult::Processed; 817 } 818 819 // CreateDecoderAgent will create an DecoderAgent paired with a xpcom-shutdown 820 // blocker and a worker-reference. Besides the needs mentioned in the header 821 // file, the blocker and the worker-reference also provides an entry point for 822 // us to clean up the resources. Other than the decoder dtor, Reset(), or 823 // Close(), the resources should be cleaned up in the following situations: 824 // 1. Decoder on window, closing document 825 // 2. Decoder on worker, closing document 826 // 3. Decoder on worker, terminating worker 827 // 828 // In case 1, the entry point to clean up is in the mShutdownBlocker's 829 // ShutdownpPomise-resolver. In case 2, the entry point is in mWorkerRef's 830 // shutting down callback. In case 3, the entry point is in mWorkerRef's 831 // shutting down callback. 832 833 template <typename DecoderType> 834 bool DecoderTemplate<DecoderType>::CreateDecoderAgent( 835 DecoderAgent::Id aId, already_AddRefed<ConfigTypeInternal> aConfig, 836 UniquePtr<TrackInfo>&& aInfo) { 837 AssertIsOnOwningThread(); 838 MOZ_ASSERT(mState == CodecState::Configured); 839 MOZ_ASSERT(!mAgent); 840 MOZ_ASSERT(!mActiveConfig); 841 MOZ_ASSERT(!mShutdownBlocker); 842 MOZ_ASSERT_IF(!NS_IsMainThread(), !mWorkerRef); 843 844 auto resetOnFailure = MakeScopeExit([&]() { 845 mAgent = nullptr; 846 mActiveConfig = nullptr; 847 mShutdownBlocker = nullptr; 848 mWorkerRef = nullptr; 849 }); 850 851 // If the decoder is on worker, get a worker reference. 852 if (!NS_IsMainThread()) { 853 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 854 if (NS_WARN_IF(!workerPrivate)) { 855 return false; 856 } 857 858 // Clean up all the resources when worker is going away. 859 RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create( 860 workerPrivate, "DecoderTemplate::CreateDecoderAgent", 861 [self = RefPtr{this}]() { 862 LOG("%s %p, worker is going away", DecoderType::Name.get(), 863 self.get()); 864 (void)self->ResetInternal(NS_ERROR_DOM_ABORT_ERR); 865 }); 866 if (NS_WARN_IF(!workerRef)) { 867 return false; 868 } 869 870 mWorkerRef = new ThreadSafeWorkerRef(workerRef); 871 } 872 873 mAgent = MakeRefPtr<DecoderAgent>(aId, std::move(aInfo)); 874 mActiveConfig = std::move(aConfig); 875 876 // ShutdownBlockingTicket requires an unique name to register its own 877 // nsIAsyncShutdownBlocker since each blocker needs a distinct name. 878 // To do that, we use DecoderAgent's unique id to create a unique name. 879 nsAutoString uniqueName; 880 uniqueName.AppendPrintf( 881 "Blocker for DecoderAgent #%d (codec: %s) @ %p", mAgent->mId, 882 NS_ConvertUTF16toUTF8(mActiveConfig->mCodec).get(), mAgent.get()); 883 884 mShutdownBlocker = media::ShutdownBlockingTicket::Create( 885 uniqueName, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__); 886 if (!mShutdownBlocker) { 887 LOGE("%s %p failed to create %s", DecoderType::Name.get(), this, 888 NS_ConvertUTF16toUTF8(uniqueName).get()); 889 return false; 890 } 891 892 // Clean up all the resources when xpcom-will-shutdown arrives since the page 893 // is going to be closed. 894 mShutdownBlocker->ShutdownPromise()->Then( 895 GetCurrentSerialEventTarget(), __func__, 896 [self = RefPtr{this}, id = mAgent->mId, 897 ref = mWorkerRef](bool /* aUnUsed*/) { 898 LOG("%s %p gets xpcom-will-shutdown notification for DecoderAgent #%d", 899 DecoderType::Name.get(), self.get(), id); 900 (void)self->ResetInternal(NS_ERROR_DOM_ABORT_ERR); 901 }, 902 [self = RefPtr{this}, id = mAgent->mId, 903 ref = mWorkerRef](bool /* aUnUsed*/) { 904 LOG("%s %p removes shutdown-blocker #%d before getting any " 905 "notification. DecoderAgent #%d should have been dropped", 906 DecoderType::Name.get(), self.get(), id, id); 907 MOZ_ASSERT(!self->mAgent || self->mAgent->mId != id); 908 }); 909 910 LOG("%s %p creates DecoderAgent #%d @ %p and its shutdown-blocker", 911 DecoderType::Name.get(), this, mAgent->mId, mAgent.get()); 912 913 resetOnFailure.release(); 914 return true; 915 } 916 917 template <typename DecoderType> 918 void DecoderTemplate<DecoderType>::DestroyDecoderAgentIfAny() { 919 AssertIsOnOwningThread(); 920 921 if (!mAgent) { 922 LOG("%s %p has no DecoderAgent to destroy", DecoderType::Name.get(), this); 923 return; 924 } 925 926 MOZ_ASSERT(mActiveConfig); 927 MOZ_ASSERT(mShutdownBlocker); 928 MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerRef); 929 930 LOG("%s %p destroys DecoderAgent #%d @ %p", DecoderType::Name.get(), this, 931 mAgent->mId, mAgent.get()); 932 mActiveConfig = nullptr; 933 RefPtr<DecoderAgent> agent = std::move(mAgent); 934 // mShutdownBlocker should be kept alive until the shutdown is done. 935 // mWorkerRef is used to ensure this task won't be discarded in worker. 936 agent->Shutdown()->Then( 937 GetCurrentSerialEventTarget(), __func__, 938 [self = RefPtr{this}, id = agent->mId, ref = std::move(mWorkerRef), 939 blocker = std::move(mShutdownBlocker)]( 940 const ShutdownPromise::ResolveOrRejectValue& aResult) { 941 LOG("%s %p, DecoderAgent #%d's shutdown has been %s. Drop its " 942 "shutdown-blocker now", 943 DecoderType::Name.get(), self.get(), id, 944 aResult.IsResolve() ? "resolved" : "rejected"); 945 }); 946 } 947 948 template class DecoderTemplate<VideoDecoderTraits>; 949 template class DecoderTemplate<AudioDecoderTraits>; 950 951 #undef LOG 952 #undef LOGW 953 #undef LOGE 954 #undef LOGV 955 #undef LOG_INTERNAL 956 957 } // namespace mozilla::dom