tor-browser

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

EncoderAgent.cpp (16040B)


      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 "EncoderAgent.h"
      8 
      9 #include "PDMFactory.h"
     10 #include "mozilla/DebugOnly.h"
     11 #include "mozilla/Logging.h"
     12 #include "nsThreadUtils.h"
     13 
     14 extern mozilla::LazyLogModule gWebCodecsLog;
     15 
     16 namespace mozilla {
     17 
     18 #ifdef LOG_INTERNAL
     19 #  undef LOG_INTERNAL
     20 #endif  // LOG_INTERNAL
     21 #define LOG_INTERNAL(level, msg, ...) \
     22  MOZ_LOG(gWebCodecsLog, LogLevel::level, (msg, ##__VA_ARGS__))
     23 
     24 #ifdef LOG
     25 #  undef LOG
     26 #endif  // LOG
     27 #define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__)
     28 
     29 #ifdef LOGW
     30 #  undef LOGW
     31 #endif  // LOGE
     32 #define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__)
     33 
     34 #ifdef LOGE
     35 #  undef LOGE
     36 #endif  // LOGE
     37 #define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__)
     38 
     39 #ifdef LOGV
     40 #  undef LOGV
     41 #endif  // LOGV
     42 #define LOGV(msg, ...) LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__)
     43 
     44 EncoderAgent::EncoderAgent(WebCodecsId aId)
     45    : mId(aId),
     46      mOwnerThread(GetCurrentSerialEventTarget()),
     47      mPEMFactory(MakeRefPtr<PEMFactory>()),
     48      mEncoder(nullptr),
     49      mState(State::Unconfigured) {
     50  MOZ_ASSERT(mOwnerThread);
     51  MOZ_ASSERT(mPEMFactory);
     52  LOG("EncoderAgent #%zu (%p) ctor", mId, this);
     53 }
     54 
     55 EncoderAgent::~EncoderAgent() {
     56  LOG("EncoderAgent #%zu (%p) dtor", mId, this);
     57  MOZ_ASSERT(mState == State::Unconfigured, "encoder released in wrong state");
     58  MOZ_ASSERT(!mEncoder, "encoder must be shutdown");
     59 }
     60 
     61 RefPtr<EncoderAgent::ConfigurePromise> EncoderAgent::Configure(
     62    const EncoderConfig& aConfig) {
     63  MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
     64  MOZ_ASSERT(mState == State::Unconfigured || mState == State::Error);
     65  MOZ_ASSERT(mConfigurePromise.IsEmpty());
     66  MOZ_ASSERT(!mCreateRequest.Exists());
     67  MOZ_ASSERT(!mInitRequest.Exists());
     68 
     69  if (mState == State::Error) {
     70    LOGE("EncoderAgent #%zu (%p) tried to configure in error state", mId, this);
     71    return ConfigurePromise::CreateAndReject(
     72        MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
     73                    "Cannot configure in error state"),
     74        __func__);
     75  }
     76 
     77  MOZ_ASSERT(mState == State::Unconfigured);
     78  MOZ_ASSERT(!mEncoder);
     79  SetState(State::Configuring);
     80 
     81  LOG("EncoderAgent #%zu (%p) is creating an encoder (%s)", mId, this,
     82      mozilla::EnumValueToString(aConfig.mCodec));
     83 
     84  RefPtr<ConfigurePromise> p = mConfigurePromise.Ensure(__func__);
     85 
     86  mPEMFactory->CreateEncoderAsync(aConfig, dom::GetWebCodecsEncoderTaskQueue())
     87      ->Then(
     88          mOwnerThread, __func__,
     89          [self = RefPtr{this}](RefPtr<MediaDataEncoder>&& aEncoder) {
     90            self->mCreateRequest.Complete();
     91 
     92            // If EncoderAgent has been shut down, shut the created encoder down
     93            // and return.
     94            if (!self->mShutdownWhileCreationPromise.IsEmpty()) {
     95              MOZ_ASSERT(self->mState == State::ShuttingDown);
     96              MOZ_ASSERT(self->mConfigurePromise.IsEmpty(),
     97                         "configuration should have been rejected");
     98 
     99              LOGW(
    100                  "EncoderAgent #%zu (%p) has been shut down. We need to shut "
    101                  "the newly created encoder down",
    102                  self->mId, self.get());
    103              aEncoder->Shutdown()->Then(
    104                  self->mOwnerThread, __func__,
    105                  [self](const ShutdownPromise::ResolveOrRejectValue& aValue) {
    106                    MOZ_ASSERT(self->mState == State::ShuttingDown);
    107 
    108                    LOGW(
    109                        "EncoderAgent #%zu (%p), newly created encoder "
    110                        "shutdown "
    111                        "has been %s",
    112                        self->mId, self.get(),
    113                        aValue.IsResolve() ? "resolved" : "rejected");
    114 
    115                    self->SetState(State::Unconfigured);
    116 
    117                    self->mShutdownWhileCreationPromise.ResolveOrReject(
    118                        aValue, __func__);
    119                  });
    120              return;
    121            }
    122 
    123            self->mEncoder = aEncoder.forget();
    124            LOG("EncoderAgent #%zu (%p) has created a encoder, now initialize "
    125                "it",
    126                self->mId, self.get());
    127            self->mEncoder->Init()
    128                ->Then(
    129                    self->mOwnerThread, __func__,
    130                    [self]() {
    131                      self->mInitRequest.Complete();
    132                      LOG("EncoderAgent #%zu (%p) has initialized the encoder",
    133                          self->mId, self.get());
    134                      self->SetState(State::Configured);
    135                      self->mConfigurePromise.Resolve(true, __func__);
    136                    },
    137                    [self](const MediaResult& aError) {
    138                      self->mInitRequest.Complete();
    139                      LOGE(
    140                          "EncoderAgent #%zu (%p) failed to initialize the "
    141                          "encoder",
    142                          self->mId, self.get());
    143                      self->SetState(State::Error);
    144                      self->mConfigurePromise.Reject(aError, __func__);
    145                    })
    146                ->Track(self->mInitRequest);
    147          },
    148          [self = RefPtr{this}](const MediaResult& aError) {
    149            self->mCreateRequest.Complete();
    150            LOGE("EncoderAgent #%zu (%p) failed to create a encoder", self->mId,
    151                 self.get());
    152 
    153            // If EncoderAgent has been shut down, we need to resolve the
    154            // shutdown promise.
    155            if (!self->mShutdownWhileCreationPromise.IsEmpty()) {
    156              MOZ_ASSERT(self->mState == State::ShuttingDown);
    157              MOZ_ASSERT(self->mConfigurePromise.IsEmpty(),
    158                         "configuration should have been rejected");
    159 
    160              LOGW(
    161                  "EncoderAgent #%zu (%p) has been shut down. Resolve the "
    162                  "shutdown promise right away since encoder creation failed",
    163                  self->mId, self.get());
    164 
    165              self->SetState(State::Unconfigured);
    166              self->mShutdownWhileCreationPromise.Resolve(true, __func__);
    167              return;
    168            }
    169 
    170            self->SetState(State::Error);
    171            self->mConfigurePromise.Reject(aError, __func__);
    172          })
    173      ->Track(mCreateRequest);
    174 
    175  return p;
    176 }
    177 
    178 RefPtr<EncoderAgent::ReconfigurationPromise> EncoderAgent::Reconfigure(
    179    const RefPtr<const EncoderConfigurationChangeList>& aConfigChanges) {
    180  MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
    181  MOZ_ASSERT(mState == State::Configured || mState == State::Error);
    182  MOZ_ASSERT(mReconfigurationPromise.IsEmpty());
    183 
    184  if (mState == State::Error) {
    185    LOGE("EncoderAgent #%zu (%p) tried to reconfigure in error state", mId,
    186         this);
    187    return ReconfigurationPromise::CreateAndReject(
    188        MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
    189                    "Cannot reconfigure in error state"),
    190        __func__);
    191  }
    192 
    193  MOZ_ASSERT(mEncoder);
    194  SetState(State::Configuring);
    195 
    196  LOG("EncoderAgent #%zu (%p) is reconfiguring its encoder (%s)", mId, this,
    197      NS_ConvertUTF16toUTF8(aConfigChanges->ToString().get()).get());
    198 
    199  RefPtr<ReconfigurationPromise> p = mReconfigurationPromise.Ensure(__func__);
    200 
    201  mEncoder->Reconfigure(aConfigChanges)
    202      ->Then(
    203          mOwnerThread, __func__,
    204          [self = RefPtr{this}](bool) {
    205            self->mReconfigurationRequest.Complete();
    206            LOGE("EncoderAgent #%zu (%p) reconfigure success", self->mId,
    207                 self.get());
    208            self->SetState(State::Configured);
    209            self->mReconfigurationPromise.Resolve(true, __func__);
    210          },
    211          [self = RefPtr{this}](const MediaResult& aError) {
    212            self->mReconfigurationRequest.Complete();
    213            LOGE("EncoderAgent #%zu (%p) reconfigure failure", self->mId,
    214                 self.get());
    215            // Not a a fatal error per se, the owner will deal with it.
    216            self->mReconfigurationPromise.Reject(aError, __func__);
    217          })
    218      ->Track(mReconfigurationRequest);
    219 
    220  return p;
    221 }
    222 
    223 RefPtr<ShutdownPromise> EncoderAgent::Shutdown() {
    224  MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
    225 
    226  LOG("EncoderAgent #%zu (%p) shutdown in %s state", mId, this,
    227      EncoderAgent::EnumValueToString(mState));
    228 
    229  MOZ_ASSERT(mShutdownWhileCreationPromise.IsEmpty(),
    230             "Shutdown while shutting down is prohibited");
    231 
    232  auto r =
    233      MediaResult(NS_ERROR_DOM_MEDIA_CANCELED, "Canceled by encoder shutdown");
    234 
    235  // If the encoder creation has not been completed yet, wait until the encoder
    236  // being created has been shut down.
    237  if (mCreateRequest.Exists()) {
    238    MOZ_ASSERT(!mInitRequest.Exists());
    239    MOZ_ASSERT(!mConfigurePromise.IsEmpty());
    240    MOZ_ASSERT(!mEncoder);
    241    MOZ_ASSERT(mState == State::Configuring);
    242 
    243    LOGW(
    244        "EncoderAgent #%zu (%p) shutdown while the encoder creation for "
    245        "configuration is in flight. Reject the configuration now and defer "
    246        "the shutdown until the created encoder has been shut down",
    247        mId, this);
    248 
    249    // Reject the configuration in flight.
    250    mConfigurePromise.Reject(r, __func__);
    251 
    252    // Get the promise that will be resolved when the encoder being created has
    253    // been destroyed.
    254    SetState(State::ShuttingDown);
    255    return mShutdownWhileCreationPromise.Ensure(__func__);
    256  }
    257 
    258  // If encoder creation has been completed but failed, no encoder is set.
    259  if (!mEncoder) {
    260    LOG("EncoderAgent #%zu (%p) shutdown without an active encoder", mId, this);
    261    MOZ_ASSERT(mState == State::Error);
    262    MOZ_ASSERT(!mInitRequest.Exists());
    263    MOZ_ASSERT(mConfigurePromise.IsEmpty());
    264    MOZ_ASSERT(!mReconfigurationRequest.Exists());
    265    MOZ_ASSERT(mReconfigurationPromise.IsEmpty());
    266    MOZ_ASSERT(!mEncodeRequest.Exists());
    267    MOZ_ASSERT(mEncodePromise.IsEmpty());
    268    MOZ_ASSERT(!mDrainRequest.Exists());
    269    MOZ_ASSERT(mDrainPromise.IsEmpty());
    270    // ~EncoderAgent() will ensure that the encoder is shutdown.
    271    SetState(State::Unconfigured);
    272    return ShutdownPromise::CreateAndResolve(true, __func__);
    273  }
    274 
    275  // If encoder creation has succeeded, we must have the encoder now.
    276 
    277  // Cancel pending initialization for configuration in flight if any.
    278  mInitRequest.DisconnectIfExists();
    279  mConfigurePromise.RejectIfExists(r, __func__);
    280 
    281  mReconfigurationRequest.DisconnectIfExists();
    282  mReconfigurationPromise.RejectIfExists(r, __func__);
    283 
    284  // Cancel encode in flight if any.
    285  mEncodeRequest.DisconnectIfExists();
    286  mEncodePromise.RejectIfExists(r, __func__);
    287 
    288  // Cancel drain in flight if any.
    289  mDrainRequest.DisconnectIfExists();
    290  mDrainPromise.RejectIfExists(r, __func__);
    291 
    292  SetState(State::Unconfigured);
    293 
    294  RefPtr<MediaDataEncoder> encoder = std::move(mEncoder);
    295  return encoder->Shutdown();
    296 }
    297 
    298 RefPtr<EncoderAgent::EncodePromise> EncoderAgent::Encode(
    299    nsTArray<RefPtr<MediaData>>&& aInputs) {
    300  MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
    301  MOZ_ASSERT(!aInputs.IsEmpty());
    302  MOZ_ASSERT(mState == State::Configured || mState == State::Error);
    303  MOZ_ASSERT(mEncodePromise.IsEmpty());
    304  MOZ_ASSERT(!mEncodeRequest.Exists());
    305 
    306  if (mState == State::Error) {
    307    LOGE("EncoderAgent #%zu (%p) tried to encode in error state", mId, this);
    308    return EncodePromise::CreateAndReject(
    309        MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
    310                    "Cannot encode in error state"),
    311        __func__);
    312  }
    313 
    314  MOZ_ASSERT(mState == State::Configured);
    315  MOZ_ASSERT(mEncoder);
    316  SetState(State::Encoding);
    317 
    318  RefPtr<EncodePromise> p = mEncodePromise.Ensure(__func__);
    319 
    320  LOGV("EncoderAgent #%zu (%p) is encoding %zu samples", mId, this,
    321       aInputs.Length());
    322  mEncoder->Encode(std::move(aInputs))
    323      ->Then(
    324          mOwnerThread, __func__,
    325          [self = RefPtr{this}](MediaDataEncoder::EncodedData&& aData) {
    326            self->mEncodeRequest.Complete();
    327            LOGV("EncoderAgent #%zu (%p) encode a batch successful", self->mId,
    328                 self.get());
    329            self->SetState(State::Configured);
    330            self->mEncodePromise.Resolve(std::move(aData), __func__);
    331          },
    332          [self = RefPtr{this}](const MediaResult& aError) {
    333            self->mEncodeRequest.Complete();
    334            LOGV("EncoderAgent #%zu (%p) failed to encode a batch", self->mId,
    335                 self.get());
    336            self->SetState(State::Error);
    337            self->mEncodePromise.Reject(aError, __func__);
    338          })
    339      ->Track(mEncodeRequest);
    340 
    341  return p;
    342 }
    343 
    344 RefPtr<EncoderAgent::EncodePromise> EncoderAgent::Drain() {
    345  MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
    346  // This can be called when reconfiguring the encoder.
    347  MOZ_ASSERT(mState == State::Configured || mState == State::Configuring);
    348  MOZ_ASSERT(mDrainPromise.IsEmpty());
    349  MOZ_ASSERT(mEncoder);
    350 
    351  SetState(State::Draining);
    352 
    353  RefPtr<EncodePromise> p = mDrainPromise.Ensure(__func__);
    354  Dry(MediaDataEncoder::EncodedData());
    355  return p;
    356 }
    357 
    358 void EncoderAgent::Dry(MediaDataEncoder::EncodedData&& aPendingOutputs) {
    359  MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
    360  MOZ_ASSERT(mState == State::Draining);
    361  MOZ_ASSERT(!mDrainPromise.IsEmpty());
    362  MOZ_ASSERT(!mDrainRequest.Exists());
    363  MOZ_ASSERT(mEncoder);
    364 
    365  LOG("EncoderAgent #%zu (%p) is draining the encoder", mId, this);
    366  mEncoder->Drain()
    367      ->Then(
    368          mOwnerThread, __func__,
    369          [self = RefPtr{this}, outputs = std::move(aPendingOutputs)](
    370              MediaDataEncoder::EncodedData&& aData) mutable {
    371            self->mDrainRequest.Complete();
    372 
    373            if (aData.IsEmpty()) {
    374              LOG("EncoderAgent #%zu (%p) is dry now", self->mId, self.get());
    375              self->SetState(State::Configured);
    376              self->mDrainPromise.Resolve(std::move(outputs), __func__);
    377              return;
    378            }
    379 
    380            LOG("EncoderAgent #%zu (%p) drained %zu encoder data. Keep "
    381                "draining until dry",
    382                self->mId, self.get(), aData.Length());
    383            outputs.AppendElements(std::move(aData));
    384            self->Dry(std::move(outputs));
    385          },
    386          [self = RefPtr{this}](const MediaResult& aError) {
    387            self->mDrainRequest.Complete();
    388 
    389            LOGE("EncoderAgent %p failed to drain encoder", self.get());
    390            self->mDrainPromise.Reject(aError, __func__);
    391          })
    392      ->Track(mDrainRequest);
    393 }
    394 
    395 void EncoderAgent::SetState(State aState) {
    396  MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
    397 
    398  auto validateStateTransition = [](State aOldState, State aNewState) {
    399    switch (aOldState) {
    400      case State::Unconfigured:
    401        return aNewState == State::Configuring;
    402      case State::Configuring:
    403        return aNewState == State::Configured || aNewState == State::Error ||
    404               aNewState == State::Draining ||
    405               aNewState == State::Unconfigured ||
    406               aNewState == State::ShuttingDown;
    407      case State::Configured:
    408        return aNewState == State::Unconfigured ||
    409               aNewState == State::Configuring ||
    410               aNewState == State::Encoding || aNewState == State::Draining;
    411      case State::Encoding:
    412      case State::Draining:
    413        return aNewState == State::Configured || aNewState == State::Error ||
    414               aNewState == State::Unconfigured;
    415      case State::ShuttingDown:
    416        return aNewState == State::Unconfigured;
    417      case State::Error:
    418        return aNewState == State::Unconfigured;
    419      default:
    420        break;
    421    }
    422    MOZ_ASSERT_UNREACHABLE("Unhandled state transition");
    423    return false;
    424  };
    425 
    426  DebugOnly<bool> isValid = validateStateTransition(mState, aState);
    427  LOGV("EncoderAgent #%zu (%p) state change: %s -> %s", mId, this,
    428       EncoderAgent::EnumValueToString(mState),
    429       EncoderAgent::EnumValueToString(aState));
    430  MOZ_ASSERT(isValid);
    431  mState = aState;
    432 }
    433 
    434 #undef LOG
    435 #undef LOGW
    436 #undef LOGE
    437 #undef LOGV
    438 #undef LOG_INTERNAL
    439 
    440 }  // namespace mozilla