EncoderTemplate.cpp (45506B)
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 "EncoderTemplate.h" 8 9 #include <algorithm> 10 #include <type_traits> 11 12 #include "EncoderTypes.h" 13 #include "WebCodecsUtils.h" 14 #include "mozilla/ScopeExit.h" 15 #include "mozilla/StaticPrefs_dom.h" 16 #include "mozilla/StaticPrefs_media.h" 17 #include "mozilla/Try.h" 18 #include "mozilla/dom/BindingDeclarations.h" 19 #include "mozilla/dom/DOMException.h" 20 #include "mozilla/dom/Event.h" 21 #include "mozilla/dom/Promise.h" 22 #include "mozilla/dom/VideoFrame.h" 23 #include "mozilla/dom/WorkerCommon.h" 24 #include "nsGkAtoms.h" 25 #include "nsRFPService.h" 26 #include "nsString.h" 27 #include "nsThreadUtils.h" 28 29 extern mozilla::LazyLogModule gWebCodecsLog; 30 31 namespace mozilla::dom { 32 33 #ifdef LOG_INTERNAL 34 # undef LOG_INTERNAL 35 #endif // LOG_INTERNAL 36 #define LOG_INTERNAL(level, msg, ...) \ 37 MOZ_LOG(gWebCodecsLog, LogLevel::level, (msg, ##__VA_ARGS__)) 38 39 #ifdef LOG 40 # undef LOG 41 #endif // LOG 42 #define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__) 43 44 #ifdef LOGW 45 # undef LOGW 46 #endif // LOGW 47 #define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__) 48 49 #ifdef LOGE 50 # undef LOGE 51 #endif // LOGE 52 #define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__) 53 54 #ifdef LOGV 55 # undef LOGV 56 #endif // LOGV 57 #define LOGV(msg, ...) LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__) 58 59 #define AUTO_ENCODER_MARKER(var, postfix) \ 60 AutoWebCodecsMarker var(EncoderType::Name.get(), postfix); 61 62 /* 63 * Below are ControlMessage classes implementations 64 */ 65 66 template <typename EncoderType> 67 EncoderTemplate<EncoderType>::ControlMessage::ControlMessage( 68 WebCodecsId aConfigureId) 69 : mConfigureId(aConfigureId), mMessageId(sNextId++) {} 70 71 template <typename EncoderType> 72 EncoderTemplate<EncoderType>::ConfigureMessage::ConfigureMessage( 73 WebCodecsId aConfigureId, const RefPtr<ConfigTypeInternal>& aConfig) 74 : ControlMessage(aConfigureId), mConfig(aConfig) {} 75 76 template <typename EncoderType> 77 EncoderTemplate<EncoderType>::EncodeMessage::EncodeMessage( 78 WebCodecsId aConfigureId, already_AddRefed<InputTypeInternal> aData, 79 Maybe<VideoEncoderEncodeOptions>&& aOptions) 80 : ControlMessage(aConfigureId) { 81 PushData(std::move(aData), std::move(aOptions)); 82 } 83 84 template <typename EncoderType> 85 EncoderTemplate<EncoderType>::FlushMessage::FlushMessage( 86 WebCodecsId aConfigureId) 87 : ControlMessage(aConfigureId) {} 88 89 /* 90 * Below are EncoderTemplate implementation 91 */ 92 93 template <typename EncoderType> 94 EncoderTemplate<EncoderType>::EncoderTemplate( 95 nsIGlobalObject* aGlobalObject, 96 RefPtr<WebCodecsErrorCallback>&& aErrorCallback, 97 RefPtr<OutputCallbackType>&& aOutputCallback) 98 : DOMEventTargetHelper(aGlobalObject), 99 mErrorCallback(std::move(aErrorCallback)), 100 mOutputCallback(std::move(aOutputCallback)), 101 mState(CodecState::Unconfigured), 102 mMessageQueueBlocked(false), 103 mEncodeQueueSize(0), 104 mDequeueEventScheduled(false), 105 mLatestConfigureId(0), 106 mEncodeCounter(0), 107 mFlushCounter(0) {} 108 109 template <typename EncoderType> 110 void EncoderTemplate<EncoderType>::Configure(const ConfigType& aConfig, 111 ErrorResult& aRv) { 112 AssertIsOnOwningThread(); 113 114 LOG("%s::Configure %p codec %s", EncoderType::Name.get(), this, 115 NS_ConvertUTF16toUTF8(aConfig.mCodec).get()); 116 117 nsCString errorMessage; 118 if (!EncoderType::Validate(aConfig, errorMessage)) { 119 LOG("Configure: Validate error: %s", errorMessage.get()); 120 aRv.ThrowTypeError(errorMessage); 121 return; 122 } 123 124 if (mState == CodecState::Closed) { 125 LOG("Configure: CodecState::Closed, rejecting with InvalidState"); 126 aRv.ThrowInvalidStateError("The codec is no longer usable"); 127 return; 128 } 129 130 // Clone a ConfigType as the active Encode config. 131 RefPtr<ConfigTypeInternal> config = 132 EncoderType::CreateConfigInternal(aConfig); 133 if (!config) { 134 CloseInternal(NS_ERROR_DOM_NOT_SUPPORTED_ERR); 135 return; 136 } 137 138 // Audio encoders are all software, no need to do anything. 139 if constexpr (std::is_same_v<ConfigType, VideoEncoderConfig>) { 140 ApplyResistFingerprintingIfNeeded(config, GetOwnerGlobal()); 141 } 142 143 mState = CodecState::Configured; 144 mEncodeCounter = 0; 145 mFlushCounter = 0; 146 147 mControlMessageQueue.push(MakeRefPtr<ConfigureMessage>(sNextId++, config)); 148 mLatestConfigureId = mControlMessageQueue.back()->mMessageId; 149 LOG("%s %p enqueues %s", EncoderType::Name.get(), this, 150 mControlMessageQueue.back()->ToString().get()); 151 ProcessControlMessageQueue(); 152 } 153 154 template <typename EncoderType> 155 void EncoderTemplate<EncoderType>::EncodeAudioData(InputType& aInput, 156 ErrorResult& aRv) { 157 AssertIsOnOwningThread(); 158 159 LOG("%s %p, EncodeAudioData", EncoderType::Name.get(), this); 160 161 if (mState != CodecState::Configured) { 162 aRv.ThrowInvalidStateError("Encoder must be configured first"); 163 return; 164 } 165 166 if (aInput.IsClosed()) { 167 aRv.ThrowTypeError("input AudioData has been closed"); 168 return; 169 } 170 171 mAsyncDurationTracker.Start( 172 aInput.Timestamp(), 173 AutoWebCodecsMarker(EncoderType::Name.get(), ".encode-duration-a")); 174 // Dummy options here as a shortcut 175 PushEncodeRequest( 176 mLatestConfigureId, 177 EncoderType::CreateInputInternal(aInput, VideoEncoderEncodeOptions())); 178 ProcessControlMessageQueue(); 179 } 180 181 template <typename EncoderType> 182 void EncoderTemplate<EncoderType>::EncodeVideoFrame( 183 InputType& aInput, const VideoEncoderEncodeOptions& aOptions, 184 ErrorResult& aRv) { 185 AssertIsOnOwningThread(); 186 187 LOG("%s::Encode %p %s", EncoderType::Name.get(), this, 188 aInput.ToString().get()); 189 190 if (mState != CodecState::Configured) { 191 aRv.ThrowInvalidStateError("Encoder must be configured first"); 192 return; 193 } 194 195 if (aInput.IsClosed()) { 196 aRv.ThrowTypeError("input VideoFrame has been closed"); 197 return; 198 } 199 200 mAsyncDurationTracker.Start( 201 aInput.Timestamp(), 202 AutoWebCodecsMarker(EncoderType::Name.get(), ".encode-duration-v")); 203 PushEncodeRequest(mLatestConfigureId, 204 EncoderType::CreateInputInternal(aInput, aOptions), 205 Some(aOptions)); 206 ProcessControlMessageQueue(); 207 } 208 209 template <typename EncoderType> 210 already_AddRefed<Promise> EncoderTemplate<EncoderType>::Flush( 211 ErrorResult& aRv) { 212 AssertIsOnOwningThread(); 213 214 LOG("%s::Flush %p", EncoderType::Name.get(), this); 215 216 if (mState != CodecState::Configured) { 217 LOG("%s %p, wrong state!", EncoderType::Name.get(), this); 218 aRv.ThrowInvalidStateError("Encoder must be configured first"); 219 return nullptr; 220 } 221 222 RefPtr<Promise> p = Promise::Create(GetParentObject(), aRv); 223 if (NS_WARN_IF(aRv.Failed())) { 224 return p.forget(); 225 } 226 227 auto msg = MakeRefPtr<FlushMessage>(mLatestConfigureId); 228 const auto flushPromiseId = static_cast<int64_t>(msg->mMessageId); 229 MOZ_ASSERT(!mPendingFlushPromises.Contains(flushPromiseId)); 230 mPendingFlushPromises.Insert(flushPromiseId, p); 231 232 mControlMessageQueue.emplace(std::move(msg)); 233 234 LOG("%s %p enqueues %s", EncoderType::Name.get(), this, 235 mControlMessageQueue.back()->ToString().get()); 236 ProcessControlMessageQueue(); 237 return p.forget(); 238 } 239 240 template <typename EncoderType> 241 void EncoderTemplate<EncoderType>::Reset(ErrorResult& aRv) { 242 AssertIsOnOwningThread(); 243 244 LOG("%s %p, Reset", EncoderType::Name.get(), this); 245 246 if (auto r = ResetInternal(NS_ERROR_DOM_ABORT_ERR); r.isErr()) { 247 aRv.Throw(r.unwrapErr()); 248 } 249 } 250 251 template <typename EncoderType> 252 void EncoderTemplate<EncoderType>::Close(ErrorResult& aRv) { 253 AssertIsOnOwningThread(); 254 255 LOG("%s %p, Close", EncoderType::Name.get(), this); 256 257 if (auto r = CloseInternalWithAbort(); r.isErr()) { 258 aRv.Throw(r.unwrapErr()); 259 } 260 } 261 262 template <typename EncoderType> 263 Result<Ok, nsresult> EncoderTemplate<EncoderType>::ResetInternal( 264 const nsresult& aResult) { 265 AssertIsOnOwningThread(); 266 267 LOG("%s::Reset %p", EncoderType::Name.get(), this); 268 269 if (mState == CodecState::Closed) { 270 return Err(NS_ERROR_DOM_INVALID_STATE_ERR); 271 } 272 273 mState = CodecState::Unconfigured; 274 mEncodeCounter = 0; 275 mFlushCounter = 0; 276 277 CancelPendingControlMessagesAndFlushPromises(aResult); 278 DestroyEncoderAgentIfAny(); 279 280 if (mEncodeQueueSize > 0) { 281 mEncodeQueueSize = 0; 282 ScheduleDequeueEvent(); 283 } 284 285 StopBlockingMessageQueue(); 286 287 return Ok(); 288 } 289 290 template <typename EncoderType> 291 Result<Ok, nsresult> EncoderTemplate<EncoderType>::CloseInternalWithAbort() { 292 AssertIsOnOwningThread(); 293 294 MOZ_TRY(ResetInternal(NS_ERROR_DOM_ABORT_ERR)); 295 mState = CodecState::Closed; 296 return Ok(); 297 } 298 299 template <typename EncoderType> 300 void EncoderTemplate<EncoderType>::CloseInternal(const nsresult& aResult) { 301 AssertIsOnOwningThread(); 302 MOZ_ASSERT(aResult != NS_ERROR_DOM_ABORT_ERR, "Use CloseInternalWithAbort"); 303 304 auto r = ResetInternal(aResult); 305 if (r.isErr()) { 306 nsCString name; 307 GetErrorName(r.unwrapErr(), name); 308 LOGE("Error during ResetInternal during CloseInternal: %s", name.get()); 309 } 310 mState = CodecState::Closed; 311 nsCString error; 312 GetErrorName(aResult, error); 313 LOGE("%s %p Close on error: %s", EncoderType::Name.get(), this, error.get()); 314 ReportError(aResult); 315 } 316 317 template <typename EncoderType> 318 void EncoderTemplate<EncoderType>::ReportError(const nsresult& aResult) { 319 AssertIsOnOwningThread(); 320 321 RefPtr<DOMException> e = DOMException::Create(aResult); 322 RefPtr<WebCodecsErrorCallback> cb(mErrorCallback); 323 cb->Call(*e); 324 } 325 326 template <> 327 void EncoderTemplate<VideoEncoderTraits>::OutputEncodedVideoData( 328 const nsTArray<RefPtr<MediaRawData>>&& aData) { 329 AssertIsOnOwningThread(); 330 MOZ_ASSERT(mState == CodecState::Configured); 331 MOZ_ASSERT(mActiveConfig); 332 333 // Get JSContext for RootedDictionary. 334 // The EncoderType::MetadataType, VideoDecoderConfig, and VideoColorSpaceInit 335 // below are rooted to work around the JS hazard issues. 336 AutoJSAPI jsapi; 337 if (!jsapi.Init(GetParentObject())) { 338 LOGE("%s %p AutoJSAPI init failed", VideoEncoderTraits::Name.get(), this); 339 return; 340 } 341 JSContext* cx = jsapi.cx(); 342 343 RefPtr<EncodedVideoChunkOutputCallback> cb(mOutputCallback); 344 for (const auto& data : aData) { 345 // It's possible to have reset() called in between this task having been 346 // dispatched, and running -- no output callback should happen when that's 347 // the case. 348 // This is imprecise in the spec, but discussed in 349 // https://github.com/w3c/webcodecs/issues/755 and agreed upon. 350 if (!mActiveConfig) { 351 return; 352 } 353 RefPtr<EncodedVideoChunk> encodedData = 354 EncodedDataToOutputType(GetParentObject(), data); 355 356 RootedDictionary<EncodedVideoChunkMetadata> metadata(cx); 357 if (mOutputNewDecoderConfig) { 358 RootedDictionary<VideoDecoderConfig> decoderConfig(cx); 359 EncoderConfigToDecoderConfig(cx, data, *mActiveConfig, decoderConfig); 360 metadata.mDecoderConfig.Construct(std::move(decoderConfig)); 361 mOutputNewDecoderConfig = false; 362 LOG("New config passed to output callback"); 363 } 364 365 nsAutoCString metadataInfo; 366 367 if (data->mTemporalLayerId) { 368 RootedDictionary<SvcOutputMetadata> svc(cx); 369 svc.mTemporalLayerId.Construct(data->mTemporalLayerId.value()); 370 metadata.mSvc.Construct(std::move(svc)); 371 metadataInfo.Append( 372 nsPrintfCString(", temporal layer id %d", 373 metadata.mSvc.Value().mTemporalLayerId.Value())); 374 } 375 376 if (metadata.mDecoderConfig.WasPassed()) { 377 metadataInfo.Append(", new decoder config"); 378 } 379 380 LOG("EncoderTemplate:: output callback (ts: % " PRId64 ")%s", 381 encodedData->Timestamp(), metadataInfo.get()); 382 mAsyncDurationTracker.End(encodedData->Timestamp()); 383 cb->Call((EncodedVideoChunk&)(*encodedData), metadata); 384 } 385 } 386 387 template <> 388 void EncoderTemplate<AudioEncoderTraits>::OutputEncodedAudioData( 389 const nsTArray<RefPtr<MediaRawData>>&& aData) { 390 AssertIsOnOwningThread(); 391 MOZ_ASSERT(mState == CodecState::Configured); 392 MOZ_ASSERT(mActiveConfig); 393 394 // Get JSContext for RootedDictionary. 395 // The EncoderType::MetadataType, AudioDecoderConfig 396 // below are rooted to work around the JS hazard issues. 397 AutoJSAPI jsapi; 398 if (!jsapi.Init(GetParentObject())) { 399 LOGE("%s %p AutoJSAPI init failed", AudioEncoderTraits::Name.get(), this); 400 return; 401 } 402 JSContext* cx = jsapi.cx(); 403 404 RefPtr<EncodedAudioChunkOutputCallback> cb(mOutputCallback); 405 for (const auto& data : aData) { 406 // It's possible to have reset() called in between this task having been 407 // dispatched, and running -- no output callback should happen when that's 408 // the case. 409 // This is imprecise in the spec, but discussed in 410 // https://github.com/w3c/webcodecs/issues/755 and agreed upon. 411 if (!mActiveConfig) { 412 return; 413 } 414 RefPtr<EncodedAudioChunk> encodedData = 415 EncodedDataToOutputType(GetParentObject(), data); 416 417 RootedDictionary<EncodedAudioChunkMetadata> metadata(cx); 418 if (mOutputNewDecoderConfig) { 419 RootedDictionary<AudioDecoderConfig> decoderConfig(cx); 420 EncoderConfigToDecoderConfig(cx, data, *mActiveConfig, decoderConfig); 421 metadata.mDecoderConfig.Construct(std::move(decoderConfig)); 422 mOutputNewDecoderConfig = false; 423 LOG("New config passed to output callback"); 424 } 425 426 nsAutoCString metadataInfo; 427 428 if (metadata.mDecoderConfig.WasPassed()) { 429 metadataInfo.Append(", new decoder config"); 430 } 431 432 LOG("EncoderTemplate:: output callback (ts: % " PRId64 433 ", duration: % " PRId64 ", %zu bytes, %" PRIu64 " so far)", 434 encodedData->Timestamp(), 435 !encodedData->GetDuration().IsNull() 436 ? encodedData->GetDuration().Value() 437 : 0, 438 data->Size(), mPacketsOutput++); 439 mAsyncDurationTracker.End(encodedData->Timestamp()); 440 cb->Call((EncodedAudioChunk&)(*encodedData), metadata); 441 } 442 } 443 444 template <typename EncoderType> 445 void EncoderTemplate<EncoderType>::ScheduleDequeueEvent() { 446 AssertIsOnOwningThread(); 447 448 if (mDequeueEventScheduled) { 449 return; 450 } 451 mDequeueEventScheduled = true; 452 453 QueueATask("dequeue event task", [self = RefPtr{this}]() { 454 self->FireEvent(nsGkAtoms::ondequeue, u"dequeue"_ns); 455 self->mDequeueEventScheduled = false; 456 }); 457 } 458 459 template <typename EncoderType> 460 nsresult EncoderTemplate<EncoderType>::FireEvent(nsAtom* aTypeWithOn, 461 const nsAString& aEventType) { 462 if (aTypeWithOn && !HasListenersFor(aTypeWithOn)) { 463 return NS_ERROR_ABORT; 464 } 465 466 LOGV("Dispatching %s event to %s %p", NS_ConvertUTF16toUTF8(aEventType).get(), 467 EncoderType::Name.get(), this); 468 RefPtr<Event> event = new Event(this, nullptr, nullptr); 469 event->InitEvent(aEventType, true, true); 470 event->SetTrusted(true); 471 this->DispatchEvent(*event); 472 return NS_OK; 473 } 474 475 template <typename EncoderType> 476 void EncoderTemplate<EncoderType>::SchedulePromiseResolveOrReject( 477 already_AddRefed<Promise> aPromise, const nsresult& aResult) { 478 AssertIsOnOwningThread(); 479 480 RefPtr<Promise> p = aPromise; 481 auto resolver = [p, result = aResult] { 482 if (NS_FAILED(result)) { 483 p->MaybeReject(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); 484 return; 485 } 486 p->MaybeResolveWithUndefined(); 487 }; 488 nsISerialEventTarget* target = GetCurrentSerialEventTarget(); 489 490 if (NS_IsMainThread()) { 491 MOZ_ALWAYS_SUCCEEDS(target->Dispatch(NS_NewRunnableFunction( 492 "SchedulePromiseResolveOrReject Runnable (main)", resolver))); 493 return; 494 } 495 496 MOZ_ALWAYS_SUCCEEDS(target->Dispatch(NS_NewCancelableRunnableFunction( 497 "SchedulePromiseResolveOrReject Runnable (worker)", resolver))); 498 } 499 500 template <typename EncoderType> 501 void EncoderTemplate<EncoderType>::ProcessControlMessageQueue() { 502 AssertIsOnOwningThread(); 503 MOZ_ASSERT(mState == CodecState::Configured); 504 505 while (!mMessageQueueBlocked && !mControlMessageQueue.empty()) { 506 RefPtr<ControlMessage>& msg = mControlMessageQueue.front(); 507 if (msg->AsConfigureMessage()) { 508 if (ProcessConfigureMessage(msg->AsConfigureMessage()) == 509 MessageProcessedResult::NotProcessed) { 510 break; 511 } 512 } else if (msg->AsEncodeMessage()) { 513 if (ProcessEncodeMessage(msg->AsEncodeMessage()) == 514 MessageProcessedResult::NotProcessed) { 515 break; 516 } 517 } else { 518 MOZ_ASSERT(msg->AsFlushMessage()); 519 if (ProcessFlushMessage(msg->AsFlushMessage()) == 520 MessageProcessedResult::NotProcessed) { 521 break; 522 } 523 } 524 } 525 } 526 527 template <typename EncoderType> 528 void EncoderTemplate<EncoderType>::CancelPendingControlMessagesAndFlushPromises( 529 const nsresult& aResult) { 530 AssertIsOnOwningThread(); 531 532 // Cancel the message that is being processed. 533 if (mProcessingMessage) { 534 LOG("%s %p cancels current %s", EncoderType::Name.get(), this, 535 mProcessingMessage->ToString().get()); 536 mProcessingMessage->Cancel(); 537 mProcessingMessage = nullptr; 538 } 539 540 // Clear the message queue. 541 while (!mControlMessageQueue.empty()) { 542 LOG("%s %p cancels pending %s", EncoderType::Name.get(), this, 543 mControlMessageQueue.front()->ToString().get()); 544 545 MOZ_ASSERT(!mControlMessageQueue.front()->IsProcessing()); 546 mControlMessageQueue.pop(); 547 } 548 549 // If there are pending flush promises, reject them. 550 mPendingFlushPromises.ForEach( 551 [&](const int64_t& id, const RefPtr<Promise>& p) { 552 LOG("%s %p, reject the promise for flush %" PRId64, 553 EncoderType::Name.get(), this, id); 554 p->MaybeReject(aResult); 555 }); 556 mPendingFlushPromises.Clear(); 557 } 558 559 template <typename EncoderType> 560 template <typename Func> 561 void EncoderTemplate<EncoderType>::QueueATask(const char* aName, 562 Func&& aSteps) { 563 AssertIsOnOwningThread(); 564 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread( 565 NS_NewRunnableFunction(aName, std::forward<Func>(aSteps)))); 566 } 567 568 template <typename EncoderType> 569 MessageProcessedResult EncoderTemplate<EncoderType>::ProcessConfigureMessage( 570 RefPtr<ConfigureMessage> aMessage) { 571 AssertIsOnOwningThread(); 572 MOZ_ASSERT(mState == CodecState::Configured); 573 MOZ_ASSERT(aMessage->AsConfigureMessage()); 574 575 if (mProcessingMessage) { 576 return MessageProcessedResult::NotProcessed; 577 } 578 579 mProcessingMessage = aMessage; 580 mControlMessageQueue.pop(); 581 LOG("%s %p Configuring, message queue processing blocked(%s)", 582 EncoderType::Name.get(), this, aMessage->ToString().get()); 583 StartBlockingMessageQueue(); 584 585 bool supported = EncoderType::IsSupported(*aMessage->Config()); 586 587 if (!supported) { 588 LOGE("%s %p ProcessConfigureMessage error (sync): Not supported", 589 EncoderType::Name.get(), this); 590 mProcessingMessage = nullptr; 591 QueueATask( 592 "Error while configuring encoder", 593 [self = RefPtr(this)]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { 594 LOGE("%s %p ProcessConfigureMessage (async close): Not supported", 595 EncoderType::Name.get(), self.get()); 596 self->CloseInternal(NS_ERROR_DOM_NOT_SUPPORTED_ERR); 597 }); 598 return MessageProcessedResult::Processed; 599 } 600 601 if (mAgent) { 602 Reconfigure(aMessage); 603 } else { 604 Configure(aMessage); 605 } 606 607 return MessageProcessedResult::Processed; 608 } 609 610 template <typename EncoderType> 611 void EncoderTemplate<EncoderType>::StartBlockingMessageQueue() { 612 LOG("=== Message queue blocked"); 613 mMessageQueueBlocked = true; 614 } 615 616 template <typename EncoderType> 617 void EncoderTemplate<EncoderType>::StopBlockingMessageQueue() { 618 LOG("=== Message queue unblocked"); 619 mMessageQueueBlocked = false; 620 } 621 622 template <typename EncoderType> 623 void EncoderTemplate<EncoderType>::OutputEncodedData( 624 const nsTArray<RefPtr<MediaRawData>>&& aData) { 625 if constexpr (std::is_same_v<EncoderType, VideoEncoderTraits>) { 626 OutputEncodedVideoData(std::move(aData)); 627 } else { 628 OutputEncodedAudioData(std::move(aData)); 629 } 630 } 631 632 template <typename EncoderType> 633 void EncoderTemplate<EncoderType>::Reconfigure( 634 RefPtr<ConfigureMessage> aMessage) { 635 MOZ_ASSERT(mAgent); 636 637 LOG("Reconfiguring encoder: %s", aMessage->Config()->ToString().get()); 638 639 RefPtr<ConfigTypeInternal> config = aMessage->Config(); 640 RefPtr<WebCodecsConfigurationChangeList> configDiff = 641 config->Diff(*mActiveConfig); 642 643 // Nothing to do, return now, but per spec the config 644 // must be output next time a packet is output. 645 if (configDiff->Empty()) { 646 mOutputNewDecoderConfig = true; 647 LOG("Reconfigure with identical config, returning."); 648 mProcessingMessage = nullptr; 649 StopBlockingMessageQueue(); 650 return; 651 } 652 653 LOG("Attempting to reconfigure encoder: old: %s new: %s, diff: %s", 654 mActiveConfig->ToString().get(), config->ToString().get(), 655 configDiff->ToString().get()); 656 657 RefPtr<EncoderConfigurationChangeList> changeList = 658 configDiff->ToPEMChangeList(); 659 660 // Attempt to reconfigure the encoder, if the config is similar enough. 661 // Otherwise, or if reconfiguring on the fly didn't work, flush the encoder 662 // and recreate a new one. 663 664 mAgent->Reconfigure(changeList) 665 ->Then( 666 GetCurrentSerialEventTarget(), __func__, 667 [self = RefPtr{this}, id = mAgent->mId, 668 message = std::move(aMessage)]( 669 const EncoderAgent::ReconfigurationPromise::ResolveOrRejectValue& 670 aResult) { 671 MOZ_ASSERT(self->mProcessingMessage); 672 MOZ_ASSERT(self->mProcessingMessage->AsConfigureMessage()); 673 MOZ_ASSERT(self->mState == CodecState::Configured); 674 MOZ_ASSERT(self->mAgent); 675 MOZ_ASSERT(id == self->mAgent->mId); 676 MOZ_ASSERT(self->mActiveConfig); 677 678 if (aResult.IsReject()) { 679 LOGE( 680 "Reconfiguring on the fly didn't succeed, flushing and " 681 "configuring a new encoder"); 682 self->mAgent->Drain()->Then( 683 GetCurrentSerialEventTarget(), __func__, 684 [self, id, 685 message](EncoderAgent::EncodePromise::ResolveOrRejectValue&& 686 aResult) { 687 if (aResult.IsReject()) { 688 // The spec asks to close the encoder with an 689 // NotSupportedError so we log the exact error here. 690 const MediaResult& error = aResult.RejectValue(); 691 LOGE("%s %p, EncoderAgent #%zu failed to configure: %s", 692 EncoderType::Name.get(), self.get(), id, 693 error.Description().get()); 694 695 self->QueueATask( 696 "Error during drain during reconfigure", 697 [self = RefPtr{self}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { 698 MOZ_ASSERT(self->mState != CodecState::Closed); 699 self->CloseInternal( 700 NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); 701 }); 702 return; 703 } 704 705 LOG("%s %p flush during reconfiguration succeeded.", 706 EncoderType::Name.get(), self.get()); 707 708 // If flush succeeded, schedule to output encoded data 709 // first, destroy the current encoder, and proceed to create 710 // a new one. 711 MOZ_ASSERT(aResult.IsResolve()); 712 nsTArray<RefPtr<MediaRawData>> data = 713 std::move(aResult.ResolveValue()); 714 715 if (data.IsEmpty()) { 716 LOG("%s %p no data during flush for reconfiguration with " 717 "encoder destruction", 718 EncoderType::Name.get(), self.get()); 719 } else { 720 LOG("%s %p Outputing %zu frames during flush " 721 " for reconfiguration with encoder destruction", 722 EncoderType::Name.get(), self.get(), data.Length()); 723 self->QueueATask( 724 "Output encoded Data", 725 [self = RefPtr{self}, data = std::move(data)]() 726 MOZ_CAN_RUN_SCRIPT_BOUNDARY { 727 self->OutputEncodedData(std::move(data)); 728 }); 729 } 730 731 self->QueueATask( 732 "Destroy + recreate encoder after failed reconfigure", 733 [self = RefPtr(self), message]() 734 MOZ_CAN_RUN_SCRIPT_BOUNDARY { 735 // Destroy the agent, and finally create a fresh 736 // encoder with the new configuration. 737 self->DestroyEncoderAgentIfAny(); 738 self->Configure(message); 739 }); 740 }); 741 return; 742 } 743 744 LOG("%s %p, EncoderAgent #%zu has been reconfigured on the fly to " 745 "%s", 746 EncoderType::Name.get(), self.get(), id, 747 message->ToString().get()); 748 749 self->mOutputNewDecoderConfig = true; 750 self->mActiveConfig = message->Config(); 751 self->mProcessingMessage = nullptr; 752 self->StopBlockingMessageQueue(); 753 self->ProcessControlMessageQueue(); 754 }); 755 } 756 757 template <typename EncoderType> 758 void EncoderTemplate<EncoderType>::Configure( 759 RefPtr<ConfigureMessage> aMessage) { 760 MOZ_ASSERT(!mAgent); 761 762 AUTO_ENCODER_MARKER(marker, ".configure"); 763 764 LOG("Configuring encoder: %s", aMessage->Config()->ToString().get()); 765 766 mOutputNewDecoderConfig = true; 767 mActiveConfig = aMessage->Config(); 768 769 bool encoderAgentCreated = 770 CreateEncoderAgent(aMessage->mMessageId, aMessage->Config()); 771 if (!encoderAgentCreated) { 772 LOGE( 773 "%s %p ProcessConfigureMessage error (sync): encoder agent " 774 "creation " 775 "failed", 776 EncoderType::Name.get(), this); 777 mProcessingMessage = nullptr; 778 QueueATask( 779 "Error when configuring encoder (encoder agent creation failed)", 780 [self = RefPtr(this)]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { 781 MOZ_ASSERT(self->mState != CodecState::Closed); 782 LOGE( 783 "%s %p ProcessConfigureMessage (async close): encoder agent " 784 "creation failed", 785 EncoderType::Name.get(), self.get()); 786 self->CloseInternal(NS_ERROR_DOM_NOT_SUPPORTED_ERR); 787 }); 788 return; 789 } 790 791 MOZ_ASSERT(mAgent); 792 MOZ_ASSERT(mActiveConfig); 793 794 LOG("Real configuration with fresh config: %s", 795 mActiveConfig->ToString().get()); 796 797 EncoderConfig config = mActiveConfig->ToEncoderConfig(); 798 mAgent->Configure(config) 799 ->Then(GetCurrentSerialEventTarget(), __func__, 800 [self = RefPtr{this}, id = mAgent->mId, aMessage, 801 m = std::move(marker)]( 802 const EncoderAgent::ConfigurePromise::ResolveOrRejectValue& 803 aResult) mutable { 804 MOZ_ASSERT(self->mProcessingMessage); 805 MOZ_ASSERT(self->mProcessingMessage->AsConfigureMessage()); 806 MOZ_ASSERT(self->mState == CodecState::Configured); 807 MOZ_ASSERT(self->mAgent); 808 MOZ_ASSERT(id == self->mAgent->mId); 809 MOZ_ASSERT(self->mActiveConfig); 810 811 LOG("%s %p, EncoderAgent #%zu %s has been %s. now unblocks " 812 "message-queue-processing", 813 EncoderType::Name.get(), self.get(), id, 814 aMessage->ToString().get(), 815 aResult.IsResolve() ? "resolved" : "rejected"); 816 817 aMessage->Complete(); 818 self->mProcessingMessage = nullptr; 819 820 if (aResult.IsReject()) { 821 // The spec asks to close the decoder with an 822 // NotSupportedError so we log the exact error here. 823 const MediaResult& error = aResult.RejectValue(); 824 LOGE("%s %p, EncoderAgent #%zu failed to configure: %s", 825 EncoderType::Name.get(), self.get(), id, 826 error.Description().get()); 827 828 self->QueueATask( 829 "Error during configure", 830 [self = RefPtr{self}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { 831 MOZ_ASSERT(self->mState != CodecState::Closed); 832 self->CloseInternal( 833 NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); 834 }); 835 return; 836 } 837 838 LOG("%s %p, EncoderAgent #%zu configured successfully. %u " 839 "encode requests are pending", 840 EncoderType::Name.get(), self.get(), id, 841 self->mEncodeQueueSize); 842 843 self->StopBlockingMessageQueue(); 844 self->ProcessControlMessageQueue(); 845 }) 846 ->Track(aMessage->Request()); 847 } 848 849 template <typename EncoderType> 850 MessageProcessedResult EncoderTemplate<EncoderType>::ProcessEncodeMessage( 851 RefPtr<EncodeMessage> aMessage) { 852 AssertIsOnOwningThread(); 853 MOZ_ASSERT(mState == CodecState::Configured); 854 MOZ_ASSERT(aMessage->AsEncodeMessage()); 855 MOZ_ASSERT(mEncodeQueueSize > 0); 856 857 AUTO_ENCODER_MARKER(marker, ".encode-process"); 858 859 if (mProcessingMessage) { 860 return MessageProcessedResult::NotProcessed; 861 } 862 863 mProcessingMessage = aMessage; 864 mControlMessageQueue.pop(); 865 866 LOGV("%s %p processing %s", EncoderType::Name.get(), this, 867 aMessage->ToString().get()); 868 869 MOZ_ASSERT(AssertedCast<uint32_t>(aMessage->BatchSize()) <= mEncodeQueueSize); 870 mEncodeQueueSize -= AssertedCast<uint32_t>(aMessage->BatchSize()); 871 ScheduleDequeueEvent(); 872 873 // Treat it like decode error if no EncoderAgent is available or the encoded 874 // data is invalid. 875 auto closeOnError = [&]() { 876 mProcessingMessage = nullptr; 877 QueueATask("Error during encode", 878 [self = RefPtr{this}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { 879 MOZ_ASSERT(self->mState != CodecState::Closed); 880 self->CloseInternal(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); 881 }); 882 return MessageProcessedResult::Processed; 883 }; 884 885 if (!mAgent) { 886 LOGE("%s %p is not configured", EncoderType::Name.get(), this); 887 return closeOnError(); 888 } 889 890 MOZ_ASSERT(mActiveConfig); 891 if (!aMessage->IsValid()) { 892 LOGE("%s %p, %s has empty data", EncoderType::Name.get(), this, 893 aMessage->ToString().get()); 894 return closeOnError(); 895 } 896 897 mAgent->Encode(aMessage->TakeData()) 898 ->Then(GetCurrentSerialEventTarget(), __func__, 899 [self = RefPtr{this}, id = mAgent->mId, m = std::move(marker)]( 900 EncoderAgent::EncodePromise::ResolveOrRejectValue&& 901 aResult) mutable { 902 MOZ_ASSERT(self->mProcessingMessage); 903 MOZ_ASSERT(self->mProcessingMessage->AsEncodeMessage()); 904 MOZ_ASSERT(self->mState == CodecState::Configured); 905 MOZ_ASSERT(self->mAgent); 906 MOZ_ASSERT(id == self->mAgent->mId); 907 MOZ_ASSERT(self->mActiveConfig); 908 909 RefPtr<EncodeMessage> msg = 910 self->mProcessingMessage->AsEncodeMessage(); 911 nsCString msgStr = msg->ToString(); 912 913 msg->Complete(); 914 self->mProcessingMessage = nullptr; 915 916 if (aResult.IsReject()) { 917 // The spec asks to queue a task to run close the decoder 918 // with an EncodingError so we log the exact error here. 919 const MediaResult& error = aResult.RejectValue(); 920 LOGE("%s %p, EncoderAgent #%zu %s failed: %s", 921 EncoderType::Name.get(), self.get(), id, msgStr.get(), 922 error.Description().get()); 923 self->QueueATask( 924 "Error during encode runnable", 925 [self = RefPtr{self}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { 926 MOZ_ASSERT(self->mState != CodecState::Closed); 927 self->CloseInternal( 928 NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); 929 }); 930 return; 931 } 932 933 MOZ_ASSERT(aResult.IsResolve()); 934 nsTArray<RefPtr<MediaRawData>> data = 935 std::move(aResult.ResolveValue()); 936 if (data.IsEmpty()) { 937 LOGV("%s %p got no data for %s", EncoderType::Name.get(), 938 self.get(), msgStr.get()); 939 } else { 940 LOGV("%s %p, schedule %zu encoded data output for %s", 941 EncoderType::Name.get(), self.get(), data.Length(), 942 msgStr.get()); 943 944 m.End(); 945 AUTO_ENCODER_MARKER(outMarker, ".encode-output"); 946 947 self->QueueATask( 948 "Output encoded Data", 949 [self = RefPtr{self}, data2 = std::move(data), 950 om = std::move(outMarker)]() 951 MOZ_CAN_RUN_SCRIPT_BOUNDARY mutable { 952 self->OutputEncodedData(std::move(data2)); 953 }); 954 } 955 self->ProcessControlMessageQueue(); 956 }) 957 ->Track(aMessage->Request()); 958 959 return MessageProcessedResult::Processed; 960 } 961 962 template <typename EncoderType> 963 MessageProcessedResult EncoderTemplate<EncoderType>::ProcessFlushMessage( 964 RefPtr<FlushMessage> aMessage) { 965 AssertIsOnOwningThread(); 966 MOZ_ASSERT(mState == CodecState::Configured); 967 MOZ_ASSERT(aMessage->AsFlushMessage()); 968 969 AUTO_ENCODER_MARKER(marker, ".flush"); 970 971 if (mProcessingMessage) { 972 return MessageProcessedResult::NotProcessed; 973 } 974 975 mProcessingMessage = aMessage; 976 mControlMessageQueue.pop(); 977 978 LOG("%s %p starts processing %s", EncoderType::Name.get(), this, 979 aMessage->ToString().get()); 980 981 // No agent, no thing to do. The promise has been rejected with the 982 // appropriate error in ResetInternal already. 983 if (!mAgent) { 984 LOGE("%s %p no agent, nothing to do", EncoderType::Name.get(), this); 985 mProcessingMessage = nullptr; 986 return MessageProcessedResult::Processed; 987 } 988 989 mAgent->Drain() 990 ->Then(GetCurrentSerialEventTarget(), __func__, 991 [self = RefPtr{this}, id = mAgent->mId, aMessage, 992 m = std::move(marker), 993 this](EncoderAgent::EncodePromise::ResolveOrRejectValue&& 994 aResult) mutable { 995 MOZ_ASSERT(self->mProcessingMessage); 996 MOZ_ASSERT(self->mProcessingMessage->AsFlushMessage()); 997 MOZ_ASSERT(self->mState == CodecState::Configured); 998 MOZ_ASSERT(self->mAgent); 999 MOZ_ASSERT(id == self->mAgent->mId); 1000 MOZ_ASSERT(self->mActiveConfig); 1001 1002 LOG("%s %p, EncoderAgent #%zu %s has been %s", 1003 EncoderType::Name.get(), self.get(), id, 1004 aMessage->ToString().get(), 1005 aResult.IsResolve() ? "resolved" : "rejected"); 1006 1007 nsCString msgStr = aMessage->ToString(); 1008 1009 aMessage->Complete(); 1010 1011 // If flush failed, it means encoder fails to encode the data 1012 // sent before, so we treat it like an encode error. We reject 1013 // the promise first and then queue a task to close VideoEncoder 1014 // with an EncodingError. 1015 if (aResult.IsReject()) { 1016 const MediaResult& error = aResult.RejectValue(); 1017 LOGE("%s %p, EncoderAgent #%zu failed to flush: %s", 1018 EncoderType::Name.get(), self.get(), id, 1019 error.Description().get()); 1020 // Reject with an EncodingError instead of the error we got 1021 // above. 1022 self->QueueATask( 1023 "Error during flush runnable", 1024 [self = RefPtr{this}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { 1025 // If Reset() was invoked before this task executes, the 1026 // promise in mPendingFlushPromises is handled there. 1027 // Otherwise, the promise is going to be rejected by 1028 // CloseInternal() below. 1029 self->mProcessingMessage = nullptr; 1030 MOZ_ASSERT(self->mState != CodecState::Closed); 1031 self->CloseInternal( 1032 NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); 1033 }); 1034 return; 1035 } 1036 1037 // If flush succeeded, schedule to output encoded data first 1038 // and then resolve the promise, then keep processing the 1039 // control messages. 1040 MOZ_ASSERT(aResult.IsResolve()); 1041 nsTArray<RefPtr<MediaRawData>> data = 1042 std::move(aResult.ResolveValue()); 1043 1044 if (data.IsEmpty()) { 1045 LOG("%s %p gets no data for %s", EncoderType::Name.get(), 1046 self.get(), msgStr.get()); 1047 } else { 1048 LOG("%s %p, schedule %zu encoded data output for %s", 1049 EncoderType::Name.get(), self.get(), data.Length(), 1050 msgStr.get()); 1051 } 1052 1053 const auto flushPromiseId = 1054 static_cast<int64_t>(aMessage->mMessageId); 1055 1056 m.End(); 1057 AUTO_ENCODER_MARKER(outMarker, ".flush-output"); 1058 1059 self->QueueATask( 1060 "Flush: output encoded data task", 1061 [self = RefPtr{self}, data = std::move(data), flushPromiseId, 1062 om = std::move( 1063 outMarker)]() MOZ_CAN_RUN_SCRIPT_BOUNDARY mutable { 1064 self->OutputEncodedData(std::move(data)); 1065 // If Reset() was invoked before this task executes, or 1066 // during the output callback above in the execution of 1067 // this task, the promise in mPendingFlushPromises is 1068 // handled there. Otherwise, the promise is resolved here. 1069 if (Maybe<RefPtr<Promise>> p = 1070 self->mPendingFlushPromises.Take(flushPromiseId)) { 1071 LOG("%s %p, resolving the promise for flush %" PRId64, 1072 EncoderType::Name.get(), self.get(), flushPromiseId); 1073 p.value()->MaybeResolveWithUndefined(); 1074 } 1075 }); 1076 self->mProcessingMessage = nullptr; 1077 self->ProcessControlMessageQueue(); 1078 }) 1079 ->Track(aMessage->Request()); 1080 1081 return MessageProcessedResult::Processed; 1082 } 1083 1084 // CreateEncoderAgent will create an EncoderAgent paired with a xpcom-shutdown 1085 // blocker and a worker-reference. Besides the needs mentioned in the header 1086 // file, the blocker and the worker-reference also provides an entry point for 1087 // us to clean up the resources. Other than the encoder dtor, Reset(), or 1088 // Close(), the resources should be cleaned up in the following situations: 1089 // 1. Encoder on window, closing document 1090 // 2. Encoder on worker, closing document 1091 // 3. Encoder on worker, terminating worker 1092 // 1093 // In case 1, the entry point to clean up is in the mShutdownBlocker's 1094 // ShutdownpPomise-resolver. In case 2, the entry point is in mWorkerRef's 1095 // shutting down callback. In case 3, the entry point is in mWorkerRef's 1096 // shutting down callback. 1097 1098 template <typename EncoderType> 1099 bool EncoderTemplate<EncoderType>::CreateEncoderAgent( 1100 WebCodecsId aId, RefPtr<ConfigTypeInternal> aConfig) { 1101 AssertIsOnOwningThread(); 1102 MOZ_ASSERT(mState == CodecState::Configured); 1103 MOZ_ASSERT(!mAgent); 1104 MOZ_ASSERT(!mShutdownBlocker); 1105 MOZ_ASSERT_IF(!NS_IsMainThread(), !mWorkerRef); 1106 1107 auto resetOnFailure = MakeScopeExit([&]() { 1108 mAgent = nullptr; 1109 mActiveConfig = nullptr; 1110 mShutdownBlocker = nullptr; 1111 mWorkerRef = nullptr; 1112 }); 1113 1114 // If the encoder is on worker, get a worker reference. 1115 if (!NS_IsMainThread()) { 1116 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1117 if (NS_WARN_IF(!workerPrivate)) { 1118 return false; 1119 } 1120 1121 // Clean up all the resources when worker is going away. 1122 RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create( 1123 workerPrivate, "EncoderTemplate::CreateEncoderAgent", 1124 [self = RefPtr{this}]() { 1125 LOG("%s %p, worker is going away", EncoderType::Name.get(), 1126 self.get()); 1127 (void)self->ResetInternal(NS_ERROR_DOM_ABORT_ERR); 1128 }); 1129 if (NS_WARN_IF(!workerRef)) { 1130 return false; 1131 } 1132 1133 mWorkerRef = new ThreadSafeWorkerRef(workerRef); 1134 } 1135 1136 mAgent = MakeRefPtr<EncoderAgent>(aId); 1137 1138 // ShutdownBlockingTicket requires an unique name to register its own 1139 // nsIAsyncShutdownBlocker since each blocker needs a distinct name. 1140 // To do that, we use EncoderAgent's unique id to create a unique name. 1141 nsAutoString uniqueName; 1142 uniqueName.AppendPrintf( 1143 "Blocker for EncoderAgent #%zu (codec: %s) @ %p", mAgent->mId, 1144 NS_ConvertUTF16toUTF8(mActiveConfig->mCodec).get(), mAgent.get()); 1145 1146 mShutdownBlocker = media::ShutdownBlockingTicket::Create( 1147 uniqueName, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__); 1148 if (!mShutdownBlocker) { 1149 LOGE("%s %p failed to create %s", EncoderType::Name.get(), this, 1150 NS_ConvertUTF16toUTF8(uniqueName).get()); 1151 return false; 1152 } 1153 1154 // Clean up all the resources when xpcom-will-shutdown arrives since the 1155 // page is going to be closed. 1156 mShutdownBlocker->ShutdownPromise()->Then( 1157 GetCurrentSerialEventTarget(), __func__, 1158 [self = RefPtr{this}, id = mAgent->mId, 1159 ref = mWorkerRef](bool /* aUnUsed*/) { 1160 LOG("%s %p gets xpcom-will-shutdown notification for EncoderAgent " 1161 "#%zu", 1162 EncoderType::Name.get(), self.get(), id); 1163 (void)self->ResetInternal(NS_ERROR_DOM_ABORT_ERR); 1164 }, 1165 [self = RefPtr{this}, id = mAgent->mId, 1166 ref = mWorkerRef](bool /* aUnUsed*/) { 1167 LOG("%s %p removes shutdown-blocker #%zu before getting any " 1168 "notification. EncoderAgent should have been dropped", 1169 EncoderType::Name.get(), self.get(), id); 1170 MOZ_ASSERT(!self->mAgent || self->mAgent->mId != id); 1171 }); 1172 1173 LOG("%s %p creates EncoderAgent #%zu @ %p and its shutdown-blocker", 1174 EncoderType::Name.get(), this, mAgent->mId, mAgent.get()); 1175 1176 resetOnFailure.release(); 1177 return true; 1178 } 1179 1180 template <typename EncoderType> 1181 void EncoderTemplate<EncoderType>::DestroyEncoderAgentIfAny() { 1182 AssertIsOnOwningThread(); 1183 1184 if (!mAgent) { 1185 LOG("%s %p has no EncoderAgent to destroy", EncoderType::Name.get(), this); 1186 return; 1187 } 1188 1189 MOZ_ASSERT(mActiveConfig); 1190 MOZ_ASSERT(mShutdownBlocker); 1191 MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerRef); 1192 1193 LOG("%s %p destroys EncoderAgent #%zu @ %p", EncoderType::Name.get(), this, 1194 mAgent->mId, mAgent.get()); 1195 mActiveConfig = nullptr; 1196 RefPtr<EncoderAgent> agent = std::move(mAgent); 1197 // mShutdownBlocker should be kept alive until the shutdown is done. 1198 // mWorkerRef is used to ensure this task won't be discarded in worker. 1199 agent->Shutdown()->Then( 1200 GetCurrentSerialEventTarget(), __func__, 1201 [self = RefPtr{this}, id = agent->mId, ref = std::move(mWorkerRef), 1202 blocker = std::move(mShutdownBlocker)]( 1203 const ShutdownPromise::ResolveOrRejectValue& aResult) { 1204 LOG("%s %p, EncoderAgent #%zu's shutdown has been %s. Drop its " 1205 "shutdown-blocker now", 1206 EncoderType::Name.get(), self.get(), id, 1207 aResult.IsResolve() ? "resolved" : "rejected"); 1208 }); 1209 } 1210 1211 template <typename EncoderType> 1212 void EncoderTemplate<EncoderType>::PushEncodeRequest( 1213 WebCodecsId aConfigureId, RefPtr<InputTypeInternal>&& aData, 1214 Maybe<VideoEncoderEncodeOptions>&& aOptions) { 1215 AssertIsOnOwningThread(); 1216 MOZ_ASSERT(mState == CodecState::Configured); 1217 1218 // TODO(Bug 1984936): Enable batch encoding for selected encoders now. 1219 const size_t batchSize = 1220 (StaticPrefs::media_use_remote_encoder_video() && mActiveConfig && 1221 IsH264CodecString(mActiveConfig->mCodec)) 1222 ? std::max<size_t>( 1223 StaticPrefs::dom_media_webcodecs_batch_encoding_size(), 1) 1224 : 1; 1225 1226 RefPtr<EncodeMessage> msg; 1227 if (!mControlMessageQueue.empty()) { 1228 msg = mControlMessageQueue.back()->AsEncodeMessage(); 1229 if (msg && 1230 (msg->mConfigureId != aConfigureId || msg->BatchSize() >= batchSize)) { 1231 msg = nullptr; 1232 } 1233 } 1234 1235 const bool isNewMessage = !msg; 1236 if (isNewMessage) { 1237 msg = MakeRefPtr<EncodeMessage>(aConfigureId, aData.forget(), 1238 std::move(aOptions)); 1239 mControlMessageQueue.push(msg); 1240 } else { 1241 msg->PushData(aData.forget(), std::move(aOptions)); 1242 } 1243 1244 mEncodeQueueSize += 1; 1245 LOGV("%s %p %s %s, encode queue size: %u", EncoderType::Name.get(), this, 1246 isNewMessage ? "queued a new" : "appended data to", 1247 msg->ToString().get(), mEncodeQueueSize); 1248 } 1249 1250 template class EncoderTemplate<VideoEncoderTraits>; 1251 template class EncoderTemplate<AudioEncoderTraits>; 1252 1253 #undef LOG 1254 #undef LOGW 1255 #undef LOGE 1256 #undef LOGV 1257 #undef LOG_INTERNAL 1258 1259 } // namespace mozilla::dom