tor-browser

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

RemoteMediaDataEncoderChild.cpp (19770B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      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 "RemoteMediaDataEncoderChild.h"
      8 
      9 #include "RemoteDecodeUtils.h"
     10 #include "RemoteMediaManagerChild.h"
     11 #include "mozilla/dom/WebCodecsUtils.h"
     12 
     13 namespace mozilla {
     14 
     15 extern LazyLogModule sPEMLog;
     16 
     17 #define AUTO_MARKER(var, postfix) \
     18  AutoWebCodecsMarker var("RemoteMediaDataEncoderChild", postfix);
     19 
     20 #define LOGE(fmt, ...)                           \
     21  MOZ_LOG_FMT(sPEMLog, mozilla::LogLevel::Error, \
     22              "[RemoteMediaDataEncoderChild] {}: " fmt, __func__, __VA_ARGS__)
     23 #define LOGW(fmt, ...)                             \
     24  MOZ_LOG_FMT(sPEMLog, mozilla::LogLevel::Warning, \
     25              "[RemoteMediaDataEncoderChild] {}: " fmt, __func__, __VA_ARGS__)
     26 #define LOGD(fmt, ...)                           \
     27  MOZ_LOG_FMT(sPEMLog, mozilla::LogLevel::Debug, \
     28              "[RemoteMediaDataEncoderChild] {}: " fmt, __func__, __VA_ARGS__)
     29 #define LOGV(fmt, ...)                             \
     30  MOZ_LOG_FMT(sPEMLog, mozilla::LogLevel::Verbose, \
     31              "[RemoteMediaDataEncoderChild] {}: " fmt, __func__, __VA_ARGS__)
     32 
     33 RemoteMediaDataEncoderChild::RemoteMediaDataEncoderChild(
     34    nsCOMPtr<nsISerialEventTarget>&& aThread, RemoteMediaIn aLocation)
     35    : ShmemRecycleAllocator(this),
     36      mThread(std::move(aThread)),
     37      mLocation(aLocation) {
     38  LOGV("[{}]", fmt::ptr(this));
     39 }
     40 
     41 RemoteMediaDataEncoderChild::~RemoteMediaDataEncoderChild() {
     42  LOGV("[{}]", fmt::ptr(this));
     43 }
     44 
     45 void RemoteMediaDataEncoderChild::MaybeDestroyActor() {
     46  // If this is the last reference, and we still have an actor, then we know
     47  // that the last reference is solely due to the IPDL reference. Dispatch to
     48  // the owning thread to delete that so that we can clean up.
     49  MutexAutoLock lock(mMutex);
     50  if (mNeedsShutdown) {
     51    mNeedsShutdown = false;
     52    mThread->Dispatch(NS_NewRunnableFunction(__func__, [self = RefPtr{this}]() {
     53      if (self->CanSend()) {
     54        LOGD("[{}] destroying final self reference", fmt::ptr(self.get()));
     55        self->Send__delete__(self);
     56      }
     57    }));
     58  }
     59 }
     60 
     61 void RemoteMediaDataEncoderChild::ActorDestroy(ActorDestroyReason aWhy) {
     62  LOGD("[{}]", fmt::ptr(this));
     63 
     64  {
     65    MutexAutoLock lock(mMutex);
     66    mNeedsShutdown = false;
     67  }
     68 
     69  mRemoteCrashed = aWhy == ActorDestroyReason::AbnormalShutdown;
     70  CleanupShmemRecycleAllocator();
     71 }
     72 
     73 RefPtr<PlatformEncoderModule::CreateEncoderPromise>
     74 RemoteMediaDataEncoderChild::Construct() {
     75  {
     76    MutexAutoLock lock(mMutex);
     77    mNeedsShutdown = CanSend();
     78  }
     79 
     80  LOGD("[{}] send", fmt::ptr(this));
     81  SendConstruct()->Then(
     82      mThread, __func__,
     83      [self = RefPtr{this}](MediaResult aResult) {
     84        LOGD("[{}] Construct resolved code={}", fmt::ptr(self.get()),
     85             aResult.Description());
     86        self->mHasConstructed = true;
     87        self->mConstructPromise.Resolve(self, __func__);
     88        if (!self->mInitPromise.IsEmpty()) {
     89          self->DoSendInit();
     90        }
     91      },
     92      [self = RefPtr{this}](const mozilla::ipc::ResponseRejectReason& aReason) {
     93        LOGE("[{}] Construct ipc failed", fmt::ptr(self.get()));
     94        RemoteMediaManagerChild::HandleRejectionError(
     95            self->GetManager(), self->mLocation, aReason,
     96            [self](const MediaResult& aError) {
     97              self->mConstructPromise.RejectIfExists(aError, __func__);
     98              self->mInitPromise.RejectIfExists(aError, __func__);
     99            });
    100      });
    101  return mConstructPromise.Ensure(__func__);
    102 }
    103 
    104 void RemoteMediaDataEncoderChild::DoSendInit() {
    105  MOZ_ASSERT(mHasConstructed);
    106 
    107  LOGD("[{}] Init send", fmt::ptr(this));
    108  SendInit()->Then(
    109      mThread, __func__,
    110      [self = RefPtr{this}](EncodeInitResultIPDL&& aResponse) {
    111        if (aResponse.type() == EncodeInitResultIPDL::TMediaResult) {
    112          LOGE("[{}] Init resolved code={}", fmt::ptr(self.get()),
    113               aResponse.get_MediaResult().Description());
    114          self->mInitPromise.Reject(aResponse.get_MediaResult(), __func__);
    115          return;
    116        }
    117 
    118        const auto& initResponse = aResponse.get_EncodeInitCompletionIPDL();
    119 
    120        LOGD("[{}] Init resolved hwAccel={} desc=\"{}\"", fmt::ptr(self.get()),
    121             initResponse.hardware(), initResponse.description().get());
    122        MutexAutoLock lock(self->mMutex);
    123        self->mDescription = initResponse.description();
    124        self->mDescription.AppendFmt(
    125            " ({})", RemoteMediaInToStr(self->GetManager()->Location()));
    126 
    127        self->mIsHardwareAccelerated = initResponse.hardware();
    128        self->mHardwareAcceleratedReason = initResponse.hardwareReason();
    129        self->mInitPromise.ResolveIfExists(true, __func__);
    130      },
    131      [self = RefPtr{this}](const mozilla::ipc::ResponseRejectReason& aReason) {
    132        LOGE("[{}] Init ipc failed", fmt::ptr(self.get()));
    133        RemoteMediaManagerChild::HandleRejectionError(
    134            self->GetManager(), self->mLocation, aReason,
    135            [self](const MediaResult& aError) {
    136              self->mInitPromise.RejectIfExists(aError, __func__);
    137            });
    138      });
    139 }
    140 
    141 RefPtr<MediaDataEncoder::InitPromise> RemoteMediaDataEncoderChild::Init() {
    142  return InvokeAsync(
    143      mThread, __func__,
    144      [self = RefPtr{this}]() -> RefPtr<MediaDataEncoder::InitPromise> {
    145        // If the owner called Init before the Construct response, then just
    146        // create promise and wait for that first. This can happen if the owner
    147        // created the encoder via RemoteEncoderModule's CreateAudioEncoder or
    148        // CreateVideoEncoder instead of AsyncCreateEncoder.
    149        //
    150        // mConstructPromise might not have been created yet either because we
    151        // may have delayed dispatching related to the process launching.
    152        // mHasConstructed will be set when the construct IPDL call returns.
    153        if (self->mHasConstructed) {
    154          self->DoSendInit();
    155        } else {
    156          LOGD("[{}] Init deferred, still constructing", fmt::ptr(self.get()));
    157        }
    158        return self->mInitPromise.Ensure(__func__);
    159      });
    160 }
    161 
    162 RefPtr<PRemoteEncoderChild::EncodePromise>
    163 RemoteMediaDataEncoderChild::DoSendEncode(
    164    const nsTArray<RefPtr<MediaData>>& aSamples, ShmemRecycleTicket* aTicket) {
    165  if (mRemoteCrashed) {
    166    LOGE("[{}] remote crashed", fmt::ptr(this));
    167    nsresult err = NS_ERROR_DOM_MEDIA_REMOTE_CRASHED_UTILITY_ERR;
    168    if (mLocation == RemoteMediaIn::GpuProcess ||
    169        mLocation == RemoteMediaIn::RddProcess) {
    170      err = NS_ERROR_DOM_MEDIA_REMOTE_CRASHED_RDD_OR_GPU_ERR;
    171    } else if (mLocation == RemoteMediaIn::UtilityProcess_MFMediaEngineCDM) {
    172      err = NS_ERROR_DOM_MEDIA_REMOTE_CRASHED_MF_CDM_ERR;
    173    }
    174    return PRemoteEncoderChild::EncodePromise::CreateAndResolve(
    175        MediaResult(err, "Remote process crashed"), __func__);
    176  }
    177 
    178  if (aSamples.IsEmpty()) {
    179    LOGE("[{}] no samples to encode", fmt::ptr(this));
    180    return PRemoteEncoderChild::EncodePromise::CreateAndResolve(
    181        MediaResult(NS_ERROR_INVALID_ARG), __func__);
    182  }
    183 
    184  MediaData::Type type = aSamples[0]->mType;
    185 
    186  if (type == MediaData::Type::AUDIO_DATA) {
    187    nsTArray<RefPtr<AudioData>> audioSamples;
    188    for (auto& sample : aSamples) {
    189      audioSamples.AppendElement(sample->As<AudioData>());
    190    }
    191 
    192    auto samples = MakeRefPtr<ArrayOfRemoteAudioData>();
    193    if (!samples->Fill(audioSamples, [&](size_t aSize) {
    194          return AllocateBuffer(aSize, aTicket);
    195        })) {
    196      LOGE("[{}] buffer audio failed", fmt::ptr(this));
    197      return PRemoteEncoderChild::EncodePromise::CreateAndResolve(
    198          MediaResult(NS_ERROR_OUT_OF_MEMORY), __func__);
    199    }
    200    LOGD("[{}] send {} audio samples", fmt::ptr(this), audioSamples.Length());
    201    return SendEncode(std::move(samples));
    202  }
    203 
    204  if (type == MediaData::Type::VIDEO_DATA) {
    205    nsTArray<RefPtr<VideoData>> videoSamples;
    206    for (auto& sample : aSamples) {
    207      videoSamples.AppendElement(sample->As<VideoData>());
    208    }
    209 
    210    auto samples = MakeRefPtr<ArrayOfRemoteVideoData>();
    211    for (const auto& videoSample : videoSamples) {
    212      if (layers::Image* videoImage = videoSample->mImage) {
    213        // We don't need to supply a working deallocator because the ticket is
    214        // responsible for that cleanup.
    215        layers::SurfaceDescriptor sd;
    216        nsresult rv = videoImage->BuildSurfaceDescriptorGPUVideoOrBuffer(
    217            sd, layers::Image::BuildSdbFlags::Default,
    218            Some(GetVideoBridgeSourceFromRemoteMediaIn(mLocation)),
    219            [&](uint32_t aBufferSize) {
    220              ShmemBuffer buffer = AllocateBuffer(aBufferSize, aTicket);
    221              if (buffer.Valid()) {
    222                return layers::MemoryOrShmem(std::move(buffer.Get()));
    223              }
    224              return layers::MemoryOrShmem();
    225            },
    226            [&](layers::MemoryOrShmem&&) {});
    227 
    228        if (NS_WARN_IF(NS_FAILED(rv))) {
    229          LOGE("[{}] buffer video failed, code={}", fmt::ptr(this),
    230               fmt::enums::format_as(rv));
    231          return PRemoteEncoderChild::EncodePromise::CreateAndResolve(
    232              MediaResult(rv), __func__);
    233        }
    234 
    235        samples->Append(RemoteVideoData(
    236            MediaDataIPDL(videoSample->mOffset, videoSample->mTime,
    237                          videoSample->mTimecode, videoSample->mDuration,
    238                          videoSample->mKeyframe),
    239            videoSample->mDisplay, RemoteImageHolder(std::move(sd)),
    240            videoSample->mFrameID));
    241      }
    242    }
    243    LOGD("[{}] send {} video samples", fmt::ptr(this), videoSamples.Length());
    244    return SendEncode(std::move(samples));
    245  }
    246 
    247  return PRemoteEncoderChild::EncodePromise::CreateAndResolve(
    248      MediaResult(NS_ERROR_INVALID_ARG), __func__);
    249 }
    250 
    251 RefPtr<MediaDataEncoder::EncodePromise> RemoteMediaDataEncoderChild::Encode(
    252    const MediaData* aSample) {
    253  return Encode(nsTArray<RefPtr<MediaData>>{const_cast<MediaData*>(aSample)});
    254 }
    255 
    256 RefPtr<MediaDataEncoder::EncodePromise> RemoteMediaDataEncoderChild::Encode(
    257    nsTArray<RefPtr<MediaData>>&& aSamples) {
    258  return InvokeAsync(
    259      mThread, __func__,
    260      [self = RefPtr{this}, samples = std::move(aSamples)]()
    261          -> RefPtr<MediaDataEncoder::EncodePromise> {
    262        auto promise =
    263            MakeRefPtr<MediaDataEncoder::EncodePromise::Private>(__func__);
    264        auto ticket = MakeRefPtr<ShmemRecycleTicket>();
    265        self->DoSendEncode(samples, ticket)
    266            ->Then(
    267                self->mThread, __func__,
    268                [self, promise, ticket](EncodeResultIPDL&& aResponse) {
    269                  self->ReleaseTicket(ticket);
    270 
    271                  if (aResponse.type() == EncodeResultIPDL::TMediaResult) {
    272                    LOGD("[{}] Encode resolved, code={}", fmt::ptr(self.get()),
    273                         aResponse.get_MediaResult().Description());
    274                    promise->Reject(aResponse.get_MediaResult(), __func__);
    275                    return;
    276                  }
    277 
    278                  const auto& encodeResponse =
    279                      aResponse.get_EncodeCompletionIPDL();
    280 
    281                  nsTArray<RefPtr<MediaRawData>> samples;
    282                  if (auto remoteSamples = encodeResponse.samples()) {
    283                    size_t count = remoteSamples->Count();
    284                    samples.SetCapacity(count);
    285                    for (size_t i = 0; i < count; ++i) {
    286                      AUTO_MARKER(marker, ".Encode.ElementAt");
    287                      if (RefPtr<MediaRawData> sample =
    288                              remoteSamples->ElementAt(i)) {
    289                        marker.End();
    290                        samples.AppendElement(std::move(sample));
    291                      } else {
    292                        LOGE(
    293                            "[{}] Encode resolved, failed to buffer "
    294                            "samples",
    295                            fmt::ptr(self.get()));
    296                        promise->Reject(MediaResult(NS_ERROR_OUT_OF_MEMORY),
    297                                        __func__);
    298                        return;
    299                      }
    300                    }
    301                  }
    302 
    303                  LOGV("[{}] Encode resolved, {} samples", fmt::ptr(self.get()),
    304                       samples.Length());
    305                  promise->Resolve(std::move(samples), __func__);
    306                  self->SendReleaseTicket(encodeResponse.ticketId());
    307                },
    308                [self, promise,
    309                 ticket](const mozilla::ipc::ResponseRejectReason& aReason) {
    310                  LOGE("[{}] Encode ipc failed", fmt::ptr(self.get()));
    311                  self->ReleaseTicket(ticket);
    312                  RemoteMediaManagerChild::HandleRejectionError(
    313                      self->GetManager(), self->mLocation, aReason,
    314                      [promise](const MediaResult& aError) {
    315                        promise->Reject(aError, __func__);
    316                      });
    317                });
    318        return promise;
    319      });
    320 }
    321 
    322 RefPtr<MediaDataEncoder::EncodePromise> RemoteMediaDataEncoderChild::Drain() {
    323  return InvokeAsync(
    324      mThread, __func__,
    325      [self = RefPtr{this}]() -> RefPtr<MediaDataEncoder::EncodePromise> {
    326        LOGD("[{}] Drain send", fmt::ptr(self.get()));
    327        self->SendDrain()->Then(
    328            self->mThread, __func__,
    329            [self](EncodeResultIPDL&& aResponse) {
    330              if (aResponse.type() == EncodeResultIPDL::TMediaResult) {
    331                LOGE("[{}] Drain resolved, code={}", fmt::ptr(self.get()),
    332                     aResponse.get_MediaResult().Description());
    333                self->mDrainPromise.Reject(aResponse.get_MediaResult(),
    334                                           __func__);
    335                return;
    336              }
    337 
    338              const auto& encodeResponse = aResponse.get_EncodeCompletionIPDL();
    339 
    340              nsTArray<RefPtr<MediaRawData>> samples;
    341              if (auto remoteSamples = encodeResponse.samples()) {
    342                size_t count = remoteSamples->Count();
    343                samples.SetCapacity(count);
    344                for (size_t i = 0; i < count; ++i) {
    345                  if (RefPtr<MediaRawData> sample =
    346                          remoteSamples->ElementAt(i)) {
    347                    samples.AppendElement(std::move(sample));
    348                  } else {
    349                    LOGE("[{}] Drain resolved, failed to buffer samples",
    350                         fmt::ptr(self.get()));
    351                    self->mDrainPromise.Reject(
    352                        MediaResult(NS_ERROR_OUT_OF_MEMORY), __func__);
    353                    return;
    354                  }
    355                }
    356              }
    357 
    358              LOGD("[{}] Drain resolved, {} samples", fmt::ptr(self.get()),
    359                   samples.Length());
    360              self->mDrainPromise.Resolve(std::move(samples), __func__);
    361              self->SendReleaseTicket(encodeResponse.ticketId());
    362            },
    363            [self](const mozilla::ipc::ResponseRejectReason& aReason) {
    364              LOGE("[{}] Drain ipc failed", fmt::ptr(self.get()));
    365              RemoteMediaManagerChild::HandleRejectionError(
    366                  self->GetManager(), self->mLocation, aReason,
    367                  [self](const MediaResult& aError) {
    368                    self->mDrainPromise.RejectIfExists(aError, __func__);
    369                  });
    370            });
    371        return self->mDrainPromise.Ensure(__func__);
    372      });
    373 }
    374 
    375 RefPtr<MediaDataEncoder::ReconfigurationPromise>
    376 RemoteMediaDataEncoderChild::Reconfigure(
    377    const RefPtr<const EncoderConfigurationChangeList>& aConfigurationChanges) {
    378  return InvokeAsync(
    379      mThread, __func__,
    380      [self = RefPtr{this}, changes = RefPtr{aConfigurationChanges}]()
    381          -> RefPtr<MediaDataEncoder::ReconfigurationPromise> {
    382        LOGD("[{}] Reconfigure send", fmt::ptr(self.get()));
    383        self->SendReconfigure(
    384                const_cast<EncoderConfigurationChangeList*>(changes.get()))
    385            ->Then(
    386                self->mThread, __func__,
    387                [self](const MediaResult& aResult) {
    388                  if (NS_SUCCEEDED(aResult)) {
    389                    LOGD("[{}] Reconfigure resolved", fmt::ptr(self.get()));
    390                    self->mReconfigurePromise.ResolveIfExists(true, __func__);
    391                  } else {
    392                    LOGD("[{}] Reconfigure resolved, code={}",
    393                         fmt::ptr(self.get()), aResult.Description());
    394                    self->mReconfigurePromise.RejectIfExists(aResult, __func__);
    395                  }
    396                },
    397                [self](const mozilla::ipc::ResponseRejectReason& aReason) {
    398                  LOGE("[{}] Reconfigure ipc failed", fmt::ptr(self.get()));
    399                  RemoteMediaManagerChild::HandleRejectionError(
    400                      self->GetManager(), self->mLocation, aReason,
    401                      [self](const MediaResult& aError) {
    402                        self->mReconfigurePromise.RejectIfExists(aError,
    403                                                                 __func__);
    404                      });
    405                });
    406        return self->mReconfigurePromise.Ensure(__func__);
    407      });
    408 }
    409 
    410 RefPtr<mozilla::ShutdownPromise> RemoteMediaDataEncoderChild::Shutdown() {
    411  {
    412    MutexAutoLock lock(mMutex);
    413    mNeedsShutdown = false;
    414  }
    415 
    416  return InvokeAsync(
    417      mThread, __func__,
    418      [self = RefPtr{this}]() -> RefPtr<mozilla::ShutdownPromise> {
    419        LOGD("[{}] Shutdown send", fmt::ptr(self.get()));
    420        self->SendShutdown()->Then(
    421            self->mThread, __func__,
    422            [self](PRemoteEncoderChild::ShutdownPromise::ResolveOrRejectValue&&
    423                       aValue) {
    424              LOGD("[{}] Shutdown resolved", fmt::ptr(self.get()));
    425              if (self->CanSend()) {
    426                self->Send__delete__(self);
    427              }
    428              self->mShutdownPromise.Resolve(aValue.IsResolve(), __func__);
    429            });
    430        return self->mShutdownPromise.Ensure(__func__);
    431      });
    432 }
    433 
    434 RefPtr<GenericPromise> RemoteMediaDataEncoderChild::SetBitrate(
    435    uint32_t aBitsPerSec) {
    436  return InvokeAsync(
    437      mThread, __func__,
    438      [self = RefPtr{this}, aBitsPerSec]() -> RefPtr<GenericPromise> {
    439        auto promise = MakeRefPtr<GenericPromise::Private>(__func__);
    440        self->SendSetBitrate(aBitsPerSec)
    441            ->Then(
    442                self->mThread, __func__,
    443                [promise](const nsresult& aRv) {
    444                  if (NS_SUCCEEDED(aRv)) {
    445                    promise->Resolve(true, __func__);
    446                  } else {
    447                    promise->Reject(aRv, __func__);
    448                  }
    449                },
    450                [self,
    451                 promise](const mozilla::ipc::ResponseRejectReason& aReason) {
    452                  LOGE("[{}] SetBitrate ipc failed", fmt::ptr(self.get()));
    453                  RemoteMediaManagerChild::HandleRejectionError(
    454                      self->GetManager(), self->mLocation, aReason,
    455                      [promise](const MediaResult& aError) {
    456                        promise->Reject(aError.Code(), __func__);
    457                      });
    458                });
    459        return promise.forget();
    460      });
    461 }
    462 
    463 RemoteMediaManagerChild* RemoteMediaDataEncoderChild::GetManager() {
    464  if (!CanSend()) {
    465    return nullptr;
    466  }
    467  return static_cast<RemoteMediaManagerChild*>(Manager());
    468 }
    469 
    470 bool RemoteMediaDataEncoderChild::IsHardwareAccelerated(
    471    nsACString& aFailureReason) const {
    472  MutexAutoLock lock(mMutex);
    473  aFailureReason = mHardwareAcceleratedReason;
    474  return mIsHardwareAccelerated;
    475 }
    476 
    477 nsCString RemoteMediaDataEncoderChild::GetDescriptionName() const {
    478  MutexAutoLock lock(mMutex);
    479  return mDescription;
    480 }
    481 
    482 #undef AUTO_MARKER
    483 
    484 }  // namespace mozilla