tor-browser

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

FrameTransformerProxy.cpp (9267B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=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 https://mozilla.org/MPL/2.0/. */
      6 
      7 #include "libwebrtcglue/FrameTransformerProxy.h"
      8 
      9 #include <memory>
     10 #include <string>
     11 #include <utility>
     12 
     13 #include "ErrorList.h"
     14 #include "api/frame_transformer_interface.h"
     15 #include "jsapi/RTCRtpScriptTransformer.h"
     16 #include "libwebrtcglue/FrameTransformer.h"
     17 #include "mozilla/Assertions.h"
     18 #include "mozilla/Logging.h"
     19 #include "mozilla/Maybe.h"
     20 #include "mozilla/Mutex.h"
     21 #include "mozilla/RefPtr.h"
     22 #include "mozilla/dom/RTCRtpReceiver.h"
     23 #include "mozilla/dom/RTCRtpSender.h"
     24 #include "nsDebug.h"
     25 #include "nsIEventTarget.h"
     26 #include "nsIRunnable.h"
     27 #include "nsISupports.h"
     28 #include "nsThreadUtils.h"
     29 #include "nscore.h"
     30 
     31 namespace mozilla {
     32 
     33 LazyLogModule gFrameTransformerProxyLog("FrameTransformerProxy");
     34 
     35 FrameTransformerProxy::FrameTransformerProxy()
     36    : mMutex("FrameTransformerProxy::mMutex") {}
     37 
     38 FrameTransformerProxy::~FrameTransformerProxy() = default;
     39 
     40 void FrameTransformerProxy::SetScriptTransformer(
     41    dom::RTCRtpScriptTransformer& aTransformer) {
     42  MutexAutoLock lock(mMutex);
     43  if (mReleaseScriptTransformerCalled) {
     44    MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Warning,
     45            ("RTCRtpScriptTransformer is ready, but ReleaseScriptTransformer "
     46             "has already been called."));
     47    // The mainthread side has torn down while the worker init was pending.
     48    // Don't grab a reference to the worker thread, or the script transformer.
     49    // Also, let the script transformer know that we do not need it after all.
     50    aTransformer.NotifyReleased();
     51    return;
     52  }
     53 
     54  MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Info,
     55          ("RTCRtpScriptTransformer is ready!"));
     56  mWorkerThread = GetCurrentSerialEventTarget();
     57  MOZ_ASSERT(mWorkerThread);
     58 
     59  MOZ_ASSERT(!mScriptTransformer);
     60  mScriptTransformer = &aTransformer;
     61 
     62  if (!mQueue.empty()) {
     63    std::list<std::unique_ptr<webrtc::TransformableFrameInterface>> queue;
     64    std::swap(queue, mQueue);
     65    mWorkerThread->Dispatch(NS_NewRunnableFunction(
     66        __func__, [this, self = RefPtr<FrameTransformerProxy>(this),
     67                   queue = std::move(queue)]() mutable {
     68          if (NS_WARN_IF(!mScriptTransformer)) {
     69            // Could happen due to errors. Is there some
     70            // other processing we ought to do?
     71            return;
     72          }
     73          while (!queue.empty()) {
     74            mScriptTransformer->TransformFrame(std::move(queue.front()));
     75            queue.pop_front();
     76          }
     77        }));
     78  }
     79 }
     80 
     81 Maybe<bool> FrameTransformerProxy::IsVideo() const {
     82  MutexAutoLock lock(mMutex);
     83  return mVideo;
     84 }
     85 
     86 void FrameTransformerProxy::ReleaseScriptTransformer() {
     87  MutexAutoLock lock(mMutex);
     88  MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Debug, ("In %s", __FUNCTION__));
     89  if (mReleaseScriptTransformerCalled) {
     90    return;
     91  }
     92  mReleaseScriptTransformerCalled = true;
     93 
     94  if (mWorkerThread) {
     95    mWorkerThread->Dispatch(NS_NewRunnableFunction(
     96        __func__, [this, self = RefPtr<FrameTransformerProxy>(this)] {
     97          if (mScriptTransformer) {
     98            mScriptTransformer->NotifyReleased();
     99            mScriptTransformer = nullptr;
    100          }
    101 
    102          // Make sure cycles are broken; this unset might have been caused by
    103          // something other than the sender/receiver being unset.
    104          GetMainThreadSerialEventTarget()->Dispatch(
    105              NS_NewRunnableFunction(__func__, [this, self] {
    106                MutexAutoLock lock(mMutex);
    107                mSender = nullptr;
    108                mReceiver = nullptr;
    109              }));
    110        }));
    111    mWorkerThread = nullptr;
    112  }
    113 }
    114 
    115 void FrameTransformerProxy::SetLibwebrtcTransformer(
    116    FrameTransformer* aLibwebrtcTransformer) {
    117  MutexAutoLock lock(mMutex);
    118  mLibwebrtcTransformer = aLibwebrtcTransformer;
    119  if (mLibwebrtcTransformer) {
    120    MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Info,
    121            ("mLibwebrtcTransformer is now set!"));
    122    mVideo = Some(mLibwebrtcTransformer->IsVideo());
    123  }
    124 }
    125 
    126 void FrameTransformerProxy::Transform(
    127    std::unique_ptr<webrtc::TransformableFrameInterface> aFrame) {
    128  MutexAutoLock lock(mMutex);
    129  MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Debug, ("In %s", __FUNCTION__));
    130  if (!mWorkerThread && !mReleaseScriptTransformerCalled) {
    131    MOZ_LOG(
    132        gFrameTransformerProxyLog, LogLevel::Info,
    133        ("In %s, queueing frame because RTCRtpScriptTransformer is not ready",
    134         __FUNCTION__));
    135    // We are still waiting for the script transformer to be created on the
    136    // worker thread.
    137    mQueue.push_back(std::move(aFrame));
    138    return;
    139  }
    140 
    141  if (mWorkerThread) {
    142    MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Debug,
    143            ("Queueing call to RTCRtpScriptTransformer::TransformFrame"));
    144    mWorkerThread->Dispatch(NS_NewRunnableFunction(
    145        __func__, [this, self = RefPtr<FrameTransformerProxy>(this),
    146                   frame = std::move(aFrame)]() mutable {
    147          if (NS_WARN_IF(!mScriptTransformer)) {
    148            // Could happen due to errors. Is there some
    149            // other processing we ought to do?
    150            return;
    151          }
    152          mScriptTransformer->TransformFrame(std::move(frame));
    153        }));
    154  }
    155 }
    156 
    157 void FrameTransformerProxy::OnTransformedFrame(
    158    std::unique_ptr<webrtc::TransformableFrameInterface> aFrame) {
    159  MutexAutoLock lock(mMutex);
    160  // If the worker thread has changed, we drop the frame, to avoid frames
    161  // arriving out of order.
    162  if (mLibwebrtcTransformer) {
    163    // This will lock, lock order is mMutex, FrameTransformer::mLibwebrtcMutex
    164    mLibwebrtcTransformer->OnTransformedFrame(std::move(aFrame));
    165  }
    166 }
    167 
    168 void FrameTransformerProxy::SetSender(dom::RTCRtpSender* aSender) {
    169  {
    170    MutexAutoLock lock(mMutex);
    171    MOZ_ASSERT(!mReceiver);
    172    mSender = aSender;
    173  }
    174  if (!aSender) {
    175    MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Info, ("Sender set to null"));
    176    ReleaseScriptTransformer();
    177  }
    178 }
    179 
    180 void FrameTransformerProxy::SetReceiver(dom::RTCRtpReceiver* aReceiver) {
    181  {
    182    MutexAutoLock lock(mMutex);
    183    MOZ_ASSERT(!mSender);
    184    mReceiver = aReceiver;
    185  }
    186  if (!aReceiver) {
    187    MOZ_LOG(gFrameTransformerProxyLog, LogLevel::Info,
    188            ("Receiver set to null"));
    189    ReleaseScriptTransformer();
    190  }
    191 }
    192 
    193 bool FrameTransformerProxy::RequestKeyFrame() {
    194  {
    195    // Spec wants this to reject synchronously if the RTCRtpScriptTransformer
    196    // is not associated with a video receiver. This may change to an async
    197    // check?
    198    MutexAutoLock lock(mMutex);
    199    if (!mReceiver || !mVideo.isSome() || !*mVideo) {
    200      return false;
    201    }
    202  }
    203 
    204  // Thread hop to main, and then the conduit thread-hops to the call thread.
    205  GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
    206      __func__, [this, self = RefPtr<FrameTransformerProxy>(this)] {
    207        MutexAutoLock lock(mMutex);
    208        if (mReceiver && mVideo.isSome() && *mVideo) {
    209          mReceiver->RequestKeyFrame();
    210        }
    211      }));
    212  return true;
    213 }
    214 
    215 void FrameTransformerProxy::KeyFrameRequestDone() {
    216  MutexAutoLock lock(mMutex);
    217  if (mWorkerThread) {
    218    mWorkerThread->Dispatch(NS_NewRunnableFunction(
    219        __func__, [this, self = RefPtr<FrameTransformerProxy>(this)] {
    220          if (mScriptTransformer) {
    221            mScriptTransformer->KeyFrameRequestDone();
    222          }
    223        }));
    224  }
    225 }
    226 
    227 bool FrameTransformerProxy::GenerateKeyFrame(const Maybe<std::string>& aRid) {
    228  {
    229    // Spec wants this to reject synchronously if the RTCRtpScriptTransformer
    230    // is not associated with a video sender. This may change to an async
    231    // check?
    232    MutexAutoLock lock(mMutex);
    233    if (!mSender || !mVideo.isSome() || !*mVideo) {
    234      return false;
    235    }
    236  }
    237 
    238  // Thread hop to main, and then the conduit thread-hops to the call thread.
    239  GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
    240      __func__, [this, self = RefPtr<FrameTransformerProxy>(this), aRid] {
    241        MutexAutoLock lock(mMutex);
    242        if (!mSender || !mVideo.isSome() || !*mVideo ||
    243            !mSender->GenerateKeyFrame(aRid)) {
    244          CopyableErrorResult rv;
    245          rv.ThrowInvalidStateError("Not sending video");
    246          if (mWorkerThread) {
    247            mWorkerThread->Dispatch(NS_NewRunnableFunction(
    248                __func__,
    249                [this, self = RefPtr<FrameTransformerProxy>(this), aRid, rv] {
    250                  if (mScriptTransformer) {
    251                    mScriptTransformer->GenerateKeyFrameError(aRid, rv);
    252                  }
    253                }));
    254          }
    255        }
    256      }));
    257  return true;
    258 }
    259 
    260 void FrameTransformerProxy::GenerateKeyFrameError(
    261    const Maybe<std::string>& aRid, const CopyableErrorResult& aResult) {
    262  MutexAutoLock lock(mMutex);
    263  if (mWorkerThread) {
    264    mWorkerThread->Dispatch(NS_NewRunnableFunction(
    265        __func__,
    266        [this, self = RefPtr<FrameTransformerProxy>(this), aRid, aResult] {
    267          if (mScriptTransformer) {
    268            mScriptTransformer->GenerateKeyFrameError(aRid, aResult);
    269          }
    270        }));
    271  }
    272 }
    273 
    274 }  // namespace mozilla