tor-browser

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

GMPVideoEncoderChild.cpp (6821B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "GMPVideoEncoderChild.h"
      7 
      8 #include "GMPContentChild.h"
      9 #include "GMPPlatform.h"
     10 #include "GMPVideoEncodedFrameImpl.h"
     11 #include "mozilla/StaticPrefs_media.h"
     12 #include "runnable_utils.h"
     13 
     14 namespace mozilla::gmp {
     15 
     16 GMPVideoEncoderChild::GMPVideoEncoderChild(GMPContentChild* aPlugin)
     17    : mPlugin(aPlugin), mVideoEncoder(nullptr), mVideoHost(this) {
     18  MOZ_ASSERT(mPlugin);
     19 }
     20 
     21 GMPVideoEncoderChild::~GMPVideoEncoderChild() = default;
     22 
     23 bool GMPVideoEncoderChild::MgrIsOnOwningThread() const {
     24  return !mPlugin || mPlugin->GMPMessageLoop() == MessageLoop::current();
     25 }
     26 
     27 void GMPVideoEncoderChild::Init(GMPVideoEncoder* aEncoder) {
     28  MOZ_ASSERT(aEncoder,
     29             "Cannot initialize video encoder child without a video encoder!");
     30  mVideoEncoder = aEncoder;
     31 }
     32 
     33 GMPVideoHostImpl& GMPVideoEncoderChild::Host() { return mVideoHost; }
     34 
     35 void GMPVideoEncoderChild::Encoded(GMPVideoEncodedFrame* aEncodedFrame,
     36                                   const uint8_t* aCodecSpecificInfo,
     37                                   uint32_t aCodecSpecificInfoLength) {
     38  if (NS_WARN_IF(!mPlugin)) {
     39    aEncodedFrame->Destroy();
     40    return;
     41  }
     42 
     43  MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
     44 
     45  auto ef = static_cast<GMPVideoEncodedFrameImpl*>(aEncodedFrame);
     46 
     47  if (GMPSharedMemManager* memMgr = mVideoHost.SharedMemMgr()) {
     48    ipc::Shmem inputShmem;
     49    if (memMgr->MgrTakeShmem(GMPSharedMemClass::Decoded, &inputShmem)) {
     50      (void)SendReturnShmem(std::move(inputShmem));
     51    }
     52  }
     53 
     54  nsTArray<uint8_t> codecSpecific;
     55  codecSpecific.AppendElements(aCodecSpecificInfo, aCodecSpecificInfoLength);
     56 
     57  GMPVideoEncodedFrameData frameData;
     58  ipc::Shmem frameShmem;
     59  nsTArray<uint8_t> frameArray;
     60  if (ef->RelinquishFrameData(frameData, frameShmem)) {
     61    (void)SendEncodedShmem(frameData, std::move(frameShmem), codecSpecific);
     62  } else if (ef->RelinquishFrameData(frameData, frameArray)) {
     63    (void)SendEncodedData(frameData, std::move(frameArray), codecSpecific);
     64  } else {
     65    MOZ_CRASH("Encoded without any frame data!");
     66  }
     67 
     68  mLatestEncodedTimestamp = frameData.mTimestamp();
     69 
     70  aEncodedFrame->Destroy();
     71 }
     72 
     73 void GMPVideoEncoderChild::MgrDecodedFrameDestroyed(
     74    GMPVideoi420FrameImpl* aFrame) {
     75  if (NS_WARN_IF(!mPlugin)) {
     76    return;
     77  }
     78 
     79  // The OpenH264 encoder destroys the input frame if it has skipped encoding
     80  // it. When it has encoded it, it calls the Encoded() callback before
     81  // destroying the frame.
     82  MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
     83  if (aFrame->Timestamp() > mLatestEncodedTimestamp) {
     84    (void)SendDroppedFrame(aFrame->Timestamp());
     85  }
     86 }
     87 
     88 void GMPVideoEncoderChild::Error(GMPErr aError) {
     89  if (NS_WARN_IF(!mPlugin)) {
     90    return;
     91  }
     92 
     93  MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
     94 
     95  SendError(aError);
     96 }
     97 
     98 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvInitEncode(
     99    const GMPVideoCodec& aCodecSettings, nsTArray<uint8_t>&& aCodecSpecific,
    100    const int32_t& aNumberOfCores, const uint32_t& aMaxPayloadSize) {
    101  if (!mVideoEncoder) {
    102    return IPC_FAIL(this, "!mVideoDecoder");
    103  }
    104 
    105  // Ignore any return code. It is OK for this to fail without killing the
    106  // process.
    107  mVideoEncoder->InitEncode(aCodecSettings, aCodecSpecific.Elements(),
    108                            aCodecSpecific.Length(), this, aNumberOfCores,
    109                            aMaxPayloadSize);
    110 
    111  return IPC_OK();
    112 }
    113 
    114 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvGiveShmem(
    115    ipc::Shmem&& aOutputShmem) {
    116  if (GMPSharedMemManager* memMgr = mVideoHost.SharedMemMgr()) {
    117    memMgr->MgrGiveShmem(GMPSharedMemClass::Encoded, std::move(aOutputShmem));
    118  } else {
    119    DeallocShmem(aOutputShmem);
    120  }
    121 
    122  return IPC_OK();
    123 }
    124 
    125 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvEncode(
    126    const GMPVideoi420FrameData& aInputFrame, ipc::Shmem&& aInputShmem,
    127    nsTArray<uint8_t>&& aCodecSpecificInfo,
    128    nsTArray<GMPVideoFrameType>&& aFrameTypes) {
    129  if (!mVideoEncoder) {
    130    DeallocShmem(aInputShmem);
    131    return IPC_FAIL(this, "!mVideoDecoder");
    132  }
    133 
    134  // The `this` destroyed callback outlives the frame, because `mVideoEncoder`
    135  // is responsible for destroying the frame, and we outlive `mVideoEncoder`.
    136  auto* f = new GMPVideoi420FrameImpl(aInputFrame, std::move(aInputShmem),
    137                                      &mVideoHost, HostReportPolicy::Destroyed);
    138 
    139  // Ignore any return code. It is OK for this to fail without killing the
    140  // process.
    141  mVideoEncoder->Encode(f, aCodecSpecificInfo.Elements(),
    142                        aCodecSpecificInfo.Length(), aFrameTypes.Elements(),
    143                        aFrameTypes.Length());
    144 
    145  return IPC_OK();
    146 }
    147 
    148 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvSetChannelParameters(
    149    const uint32_t& aPacketLoss, const uint32_t& aRTT) {
    150  if (!mVideoEncoder) {
    151    return IPC_FAIL(this, "!mVideoDecoder");
    152  }
    153 
    154  // Ignore any return code. It is OK for this to fail without killing the
    155  // process.
    156  mVideoEncoder->SetChannelParameters(aPacketLoss, aRTT);
    157 
    158  return IPC_OK();
    159 }
    160 
    161 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvSetRates(
    162    const uint32_t& aNewBitRate, const uint32_t& aFrameRate) {
    163  if (!mVideoEncoder) {
    164    return IPC_FAIL(this, "!mVideoDecoder");
    165  }
    166 
    167  // Ignore any return code. It is OK for this to fail without killing the
    168  // process.
    169  mVideoEncoder->SetRates(aNewBitRate, aFrameRate);
    170 
    171  return IPC_OK();
    172 }
    173 
    174 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvSetPeriodicKeyFrames(
    175    const bool& aEnable) {
    176  if (!mVideoEncoder) {
    177    return IPC_FAIL(this, "!mVideoDecoder");
    178  }
    179 
    180  // Ignore any return code. It is OK for this to fail without killing the
    181  // process.
    182  mVideoEncoder->SetPeriodicKeyFrames(aEnable);
    183 
    184  return IPC_OK();
    185 }
    186 
    187 void GMPVideoEncoderChild::ActorDestroy(ActorDestroyReason why) {
    188  // If there are no decoded frames, then we know that OpenH264 has destroyed
    189  // any outstanding references to its pending encode frames. This means it
    190  // should be safe to destroy the encoder since there should not be any pending
    191  // sync callbacks.
    192  if (!SpinPendingGmpEventsUntil(
    193          [&]() -> bool { return mVideoHost.IsDecodedFramesEmpty(); },
    194          StaticPrefs::media_gmp_coder_shutdown_timeout_ms())) {
    195    NS_WARNING("Timed out waiting for synchronous events!");
    196  }
    197 
    198  if (mVideoEncoder) {
    199    // Ignore any return code. It is OK for this to fail without killing the
    200    // process.
    201    mVideoEncoder->EncodingComplete();
    202    mVideoEncoder = nullptr;
    203  }
    204 
    205  mVideoHost.DoneWithAPI();
    206 
    207  mPlugin = nullptr;
    208 }
    209 
    210 }  // namespace mozilla::gmp