tor-browser

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

GMPVideoDecoderParent.cpp (13725B)


      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 "GMPVideoDecoderParent.h"
      7 
      8 #include "GMPContentParent.h"
      9 #include "GMPLog.h"
     10 #include "GMPMessageUtils.h"
     11 #include "GMPUtils.h"
     12 #include "GMPVideoEncodedFrameImpl.h"
     13 #include "GMPVideoi420FrameImpl.h"
     14 #include "mozilla/gmp/GMPTypes.h"
     15 #include "nsAutoRef.h"
     16 #include "nsPrintfCString.h"
     17 #include "nsThreadUtils.h"
     18 
     19 namespace mozilla::gmp {
     20 
     21 // States:
     22 // Initial: mIsOpen == false
     23 //    on InitDecode success -> Open
     24 //    on Shutdown -> Dead
     25 // Open: mIsOpen == true
     26 //    on Close -> Dead
     27 //    on ActorDestroy -> Dead
     28 //    on Shutdown -> Dead
     29 // Dead: mIsOpen == false
     30 
     31 GMPVideoDecoderParent::GMPVideoDecoderParent(GMPContentParent* aPlugin)
     32    : mIsOpen(false),
     33      mShuttingDown(false),
     34      mActorDestroyed(false),
     35      mIsAwaitingResetComplete(false),
     36      mIsAwaitingDrainComplete(false),
     37      mPlugin(aPlugin),
     38      mCallback(nullptr),
     39      mVideoHost(this),
     40      mPluginId(aPlugin->GetPluginId()),
     41      mPluginType(aPlugin->GetPluginType()),
     42      mFrameCount(0) {
     43  MOZ_ASSERT(mPlugin);
     44 }
     45 
     46 GMPVideoDecoderParent::~GMPVideoDecoderParent() = default;
     47 
     48 bool GMPVideoDecoderParent::MgrIsOnOwningThread() const {
     49  return !mPlugin || mPlugin->GMPEventTarget()->IsOnCurrentThread();
     50 }
     51 
     52 GMPVideoHostImpl& GMPVideoDecoderParent::Host() { return mVideoHost; }
     53 
     54 // Note: may be called via Terminated()
     55 void GMPVideoDecoderParent::Close() {
     56  GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::Close()", this);
     57  MOZ_ASSERT(!mPlugin || mPlugin->GMPEventTarget()->IsOnCurrentThread());
     58 
     59  // Ensure if we've received a Close while waiting for a ResetComplete
     60  // or DrainComplete notification, we'll unblock the caller before processing
     61  // the close. This seems unlikely to happen, but better to be careful.
     62  UnblockResetAndDrain();
     63 
     64  // Consumer is done with us; we can shut down.  No more callbacks should
     65  // be made to mCallback.  Note: do this before Shutdown()!
     66  mCallback = nullptr;
     67  // Let Shutdown mark us as dead so it knows if we had been alive
     68 
     69  // In case this is the last reference
     70  RefPtr<GMPVideoDecoderParent> kungfudeathgrip(this);
     71  Release();
     72  Shutdown();
     73 }
     74 
     75 nsresult GMPVideoDecoderParent::InitDecode(
     76    const GMPVideoCodec& aCodecSettings,
     77    const nsTArray<uint8_t>& aCodecSpecific,
     78    GMPVideoDecoderCallbackProxy* aCallback, int32_t aCoreCount) {
     79  GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::InitDecode()", this);
     80 
     81  if (mActorDestroyed) {
     82    NS_WARNING("Trying to use a destroyed GMP video decoder!");
     83    return NS_ERROR_FAILURE;
     84  }
     85  if (mIsOpen) {
     86    NS_WARNING("Trying to re-init an in-use GMP video decoder!");
     87    return NS_ERROR_FAILURE;
     88  }
     89 
     90  MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
     91 
     92  if (!aCallback) {
     93    return NS_ERROR_FAILURE;
     94  }
     95  mCallback = aCallback;
     96 
     97  if (!SendInitDecode(aCodecSettings, aCodecSpecific, aCoreCount)) {
     98    return NS_ERROR_FAILURE;
     99  }
    100  mIsOpen = true;
    101 
    102  // Async IPC, we don't have access to a return value.
    103  return NS_OK;
    104 }
    105 
    106 nsresult GMPVideoDecoderParent::Decode(
    107    GMPUniquePtr<GMPVideoEncodedFrame> aInputFrame, bool aMissingFrames,
    108    const nsTArray<uint8_t>& aCodecSpecificInfo, int64_t aRenderTimeMs) {
    109  GMP_LOG_VERBOSE(
    110      "GMPVideoDecoderParent[%p]::Decode() timestamp=%" PRId64 " keyframe=%d",
    111      this, aInputFrame->TimeStamp(), aInputFrame->FrameType() == kGMPKeyFrame);
    112 
    113  if (!mIsOpen) {
    114    GMP_LOG_ERROR(
    115        "GMPVideoDecoderParent[%p]::Decode() ERROR; dead GMPVideoDecoder",
    116        this);
    117    NS_WARNING("Trying to use an dead GMP video decoder");
    118    return NS_ERROR_FAILURE;
    119  }
    120 
    121  MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
    122 
    123  GMPUniquePtr<GMPVideoEncodedFrameImpl> inputFrameImpl(
    124      static_cast<GMPVideoEncodedFrameImpl*>(aInputFrame.release()));
    125 
    126  GMPVideoEncodedFrameData frameData;
    127  ipc::Shmem frameShmem;
    128  if (!inputFrameImpl->RelinquishFrameData(frameData, frameShmem)) {
    129    GMP_LOG_ERROR(
    130        "GMPVideoDecoderParent[%p]::Decode() ERROR; missing input shmem", this);
    131    return NS_ERROR_FAILURE;
    132  }
    133 
    134  if (mDecodedShmemSize > 0) {
    135    if (GMPSharedMemManager* memMgr = mVideoHost.SharedMemMgr()) {
    136      ipc::Shmem outputShmem;
    137      if (memMgr->MgrTakeShmem(GMPSharedMemClass::Decoded, mDecodedShmemSize,
    138                               &outputShmem)) {
    139        (void)SendGiveShmem(std::move(outputShmem));
    140      }
    141    }
    142  }
    143 
    144  if (!SendDecode(frameData, std::move(frameShmem), aMissingFrames,
    145                  aCodecSpecificInfo, aRenderTimeMs)) {
    146    GMP_LOG_ERROR(
    147        "GMPVideoDecoderParent[%p]::Decode() ERROR; SendDecode() failure.",
    148        this);
    149    return NS_ERROR_FAILURE;
    150  }
    151  mFrameCount++;
    152 
    153  // Async IPC, we don't have access to a return value.
    154  return NS_OK;
    155 }
    156 
    157 nsresult GMPVideoDecoderParent::Reset() {
    158  GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::Reset()", this);
    159 
    160  if (!mIsOpen) {
    161    NS_WARNING("Trying to use an dead GMP video decoder");
    162    return NS_ERROR_FAILURE;
    163  }
    164 
    165  MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
    166 
    167  if (!SendReset()) {
    168    return NS_ERROR_FAILURE;
    169  }
    170 
    171  mIsAwaitingResetComplete = true;
    172 
    173  RefPtr<GMPVideoDecoderParent> self(this);
    174  nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
    175      "gmp::GMPVideoDecoderParent::Reset", [self]() -> void {
    176        GMP_LOG_DEBUG(
    177            "GMPVideoDecoderParent[%p]::ResetCompleteTimeout() timed out "
    178            "waiting for ResetComplete",
    179            self.get());
    180        self->mResetCompleteTimeout = nullptr;
    181        LogToBrowserConsole(nsLiteralString(
    182            u"GMPVideoDecoderParent timed out waiting for ResetComplete()"));
    183      });
    184  CancelResetCompleteTimeout();
    185  nsCOMPtr<nsISerialEventTarget> target = mPlugin->GMPEventTarget();
    186  mResetCompleteTimeout = SimpleTimer::Create(task, 5000, target);
    187 
    188  // Async IPC, we don't have access to a return value.
    189  return NS_OK;
    190 }
    191 
    192 void GMPVideoDecoderParent::CancelResetCompleteTimeout() {
    193  if (mResetCompleteTimeout) {
    194    mResetCompleteTimeout->Cancel();
    195    mResetCompleteTimeout = nullptr;
    196  }
    197 }
    198 
    199 nsresult GMPVideoDecoderParent::Drain() {
    200  GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::Drain() frameCount=%d", this,
    201                mFrameCount);
    202 
    203  if (!mIsOpen) {
    204    NS_WARNING("Trying to use an dead GMP video decoder");
    205    return NS_ERROR_FAILURE;
    206  }
    207 
    208  MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
    209 
    210  if (!SendDrain()) {
    211    return NS_ERROR_FAILURE;
    212  }
    213 
    214  mIsAwaitingDrainComplete = true;
    215 
    216  // Async IPC, we don't have access to a return value.
    217  return NS_OK;
    218 }
    219 
    220 nsCString GMPVideoDecoderParent::GetDisplayName() const {
    221  if (NS_WARN_IF(!mIsOpen)) {
    222    return ""_ns;
    223  }
    224 
    225  MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
    226  return mPlugin->GetDisplayName();
    227 }
    228 
    229 // Note: Consider keeping ActorDestroy sync'd up when making changes here.
    230 nsresult GMPVideoDecoderParent::Shutdown() {
    231  GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::Shutdown()", this);
    232  MOZ_ASSERT(!mPlugin || mPlugin->GMPEventTarget()->IsOnCurrentThread());
    233 
    234  if (mShuttingDown) {
    235    return NS_OK;
    236  }
    237  mShuttingDown = true;
    238 
    239  // Ensure if we've received a shutdown while waiting for a ResetComplete
    240  // or DrainComplete notification, we'll unblock the caller before processing
    241  // the shutdown.
    242  UnblockResetAndDrain();
    243 
    244  // Notify client we're gone!  Won't occur after Close()
    245  if (mCallback) {
    246    mCallback->Terminated();
    247    mCallback = nullptr;
    248  }
    249 
    250  mIsOpen = false;
    251  if (!mActorDestroyed) {
    252    (void)Send__delete__(this);
    253  }
    254 
    255  return NS_OK;
    256 }
    257 
    258 // Note: Keep this sync'd up with Shutdown
    259 void GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy) {
    260  GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::ActorDestroy reason=%d", this,
    261                aWhy);
    262 
    263  mIsOpen = false;
    264  mActorDestroyed = true;
    265 
    266  // Ensure if we've received a destroy while waiting for a ResetComplete
    267  // or DrainComplete notification, we'll unblock the caller before processing
    268  // the error.
    269  UnblockResetAndDrain();
    270 
    271  if (mCallback) {
    272    // May call Close() (and Shutdown()) immediately or with a delay
    273    mCallback->Terminated();
    274    mCallback = nullptr;
    275  }
    276  if (mPlugin) {
    277    // Ignore any return code. It is OK for this to fail without killing the
    278    // process.
    279    mPlugin->VideoDecoderDestroyed(this);
    280    mPlugin = nullptr;
    281  }
    282  mVideoHost.ActorDestroyed();
    283  MaybeDisconnect(aWhy == AbnormalShutdown);
    284 }
    285 
    286 bool GMPVideoDecoderParent::HandleDecoded(
    287    const GMPVideoi420FrameData& aDecodedFrame, size_t aDecodedSize) {
    288  --mFrameCount;
    289  if (aDecodedFrame.mUpdatedTimestamp() &&
    290      aDecodedFrame.mUpdatedTimestamp().value() != aDecodedFrame.mTimestamp()) {
    291    GMP_LOG_VERBOSE(
    292        "GMPVideoDecoderParent[%p]::HandleDecoded() timestamp=[%" PRId64
    293        " -> %" PRId64 "] frameCount=%d",
    294        this, aDecodedFrame.mTimestamp(),
    295        aDecodedFrame.mUpdatedTimestamp().value(), mFrameCount);
    296  } else {
    297    GMP_LOG_VERBOSE(
    298        "GMPVideoDecoderParent[%p]::HandleDecoded() timestamp=%" PRId64
    299        " frameCount=%d",
    300        this, aDecodedFrame.mTimestamp(), mFrameCount);
    301  }
    302 
    303  if (mCallback) {
    304    if (GMPVideoi420FrameImpl::CheckFrameData(aDecodedFrame, aDecodedSize)) {
    305      return true;
    306    } else {
    307      GMP_LOG_ERROR(
    308          "GMPVideoDecoderParent[%p]::HandleDecoded() "
    309          "timestamp=%" PRId64 " decoded frame corrupt, ignoring",
    310          this, aDecodedFrame.mTimestamp());
    311      // TODO: Verify if we should take more serious the arrival of
    312      // a corrupted frame, see bug 1750506.
    313    }
    314  }
    315 
    316  return false;
    317 }
    318 
    319 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvReturnShmem(
    320    ipc::Shmem&& aInputShmem) {
    321  if (GMPSharedMemManager* memMgr = mVideoHost.SharedMemMgr()) {
    322    memMgr->MgrGiveShmem(GMPSharedMemClass::Encoded, std::move(aInputShmem));
    323  } else {
    324    DeallocShmem(aInputShmem);
    325  }
    326 
    327  return IPC_OK();
    328 }
    329 
    330 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvDecodedShmem(
    331    const GMPVideoi420FrameData& aDecodedFrame, ipc::Shmem&& aDecodedShmem) {
    332  if (HandleDecoded(aDecodedFrame, aDecodedShmem.Size<uint8_t>())) {
    333    auto* f = new GMPVideoi420FrameImpl(aDecodedFrame, std::move(aDecodedShmem),
    334                                        &mVideoHost);
    335    mCallback->Decoded(f);
    336  } else {
    337    DeallocShmem(aDecodedShmem);
    338  }
    339  return IPC_OK();
    340 }
    341 
    342 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvDecodedData(
    343    const GMPVideoi420FrameData& aDecodedFrame,
    344    nsTArray<uint8_t>&& aDecodedArray) {
    345  if (HandleDecoded(aDecodedFrame, aDecodedArray.Length())) {
    346    mDecodedShmemSize = std::max(mDecodedShmemSize, aDecodedArray.Length());
    347    auto* f = new GMPVideoi420FrameImpl(aDecodedFrame, std::move(aDecodedArray),
    348                                        &mVideoHost);
    349    mCallback->Decoded(f);
    350  }
    351 
    352  return IPC_OK();
    353 }
    354 
    355 mozilla::ipc::IPCResult
    356 GMPVideoDecoderParent::RecvReceivedDecodedReferenceFrame(
    357    const uint64_t& aPictureId) {
    358  if (mCallback) {
    359    mCallback->ReceivedDecodedReferenceFrame(aPictureId);
    360  }
    361 
    362  return IPC_OK();
    363 }
    364 
    365 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvReceivedDecodedFrame(
    366    const uint64_t& aPictureId) {
    367  if (mCallback) {
    368    mCallback->ReceivedDecodedFrame(aPictureId);
    369  }
    370 
    371  return IPC_OK();
    372 }
    373 
    374 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvInputDataExhausted() {
    375  GMP_LOG_VERBOSE("GMPVideoDecoderParent[%p]::RecvInputDataExhausted()", this);
    376 
    377  if (mCallback) {
    378    mCallback->InputDataExhausted();
    379  }
    380 
    381  return IPC_OK();
    382 }
    383 
    384 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvDrainComplete() {
    385  GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::RecvDrainComplete() frameCount=%d",
    386                this, mFrameCount);
    387  nsAutoString msg;
    388  msg.AppendLiteral(
    389      "GMPVideoDecoderParent::RecvDrainComplete() outstanding frames=");
    390  msg.AppendInt(mFrameCount);
    391  LogToBrowserConsole(msg);
    392 
    393  if (mCallback && mIsAwaitingDrainComplete) {
    394    mIsAwaitingDrainComplete = false;
    395 
    396    mCallback->DrainComplete();
    397  }
    398 
    399  return IPC_OK();
    400 }
    401 
    402 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvResetComplete() {
    403  GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::RecvResetComplete()", this);
    404 
    405  CancelResetCompleteTimeout();
    406 
    407  if (mCallback && mIsAwaitingResetComplete) {
    408    mIsAwaitingResetComplete = false;
    409    mFrameCount = 0;
    410 
    411    mCallback->ResetComplete();
    412  }
    413 
    414  return IPC_OK();
    415 }
    416 
    417 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvError(const GMPErr& aError) {
    418  GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::RecvError(error=%d)", this, aError);
    419 
    420  if (mCallback) {
    421    // Ensure if we've received an error while waiting for a ResetComplete
    422    // or DrainComplete notification, we'll unblock the caller before processing
    423    // the error.
    424    UnblockResetAndDrain();
    425 
    426    mCallback->Error(aError);
    427  }
    428 
    429  return IPC_OK();
    430 }
    431 
    432 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvShutdown() {
    433  GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::RecvShutdown()", this);
    434 
    435  Shutdown();
    436  return IPC_OK();
    437 }
    438 
    439 void GMPVideoDecoderParent::UnblockResetAndDrain() {
    440  GMP_LOG_DEBUG(
    441      "GMPVideoDecoderParent[%p]::UnblockResetAndDrain() "
    442      "awaitingResetComplete=%d awaitingDrainComplete=%d",
    443      this, mIsAwaitingResetComplete, mIsAwaitingDrainComplete);
    444 
    445  if (!mCallback) {
    446    MOZ_ASSERT(!mIsAwaitingResetComplete);
    447    MOZ_ASSERT(!mIsAwaitingDrainComplete);
    448    return;
    449  }
    450  if (mIsAwaitingResetComplete) {
    451    mIsAwaitingResetComplete = false;
    452    mCallback->ResetComplete();
    453  }
    454  if (mIsAwaitingDrainComplete) {
    455    mIsAwaitingDrainComplete = false;
    456    mCallback->DrainComplete();
    457  }
    458  CancelResetCompleteTimeout();
    459 }
    460 
    461 }  // namespace mozilla::gmp