tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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