tor-browser

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

RTCEncodedVideoFrame.cpp (9383B)


      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 "jsapi/RTCEncodedVideoFrame.h"
      8 
      9 #include <stdint.h>
     10 
     11 #include <memory>
     12 #include <utility>
     13 
     14 #include "api/frame_transformer_factory.h"
     15 #include "api/frame_transformer_interface.h"
     16 #include "js/RootingAPI.h"
     17 #include "jsapi/RTCEncodedFrameBase.h"
     18 #include "mozilla/Maybe.h"
     19 #include "mozilla/RefPtr.h"
     20 #include "mozilla/dom/RTCEncodedVideoFrameBinding.h"
     21 #include "mozilla/dom/RTCRtpScriptTransformer.h"
     22 #include "mozilla/dom/StructuredCloneHolder.h"
     23 #include "mozilla/dom/StructuredCloneTags.h"
     24 #include "mozilla/fallible.h"
     25 #include "nsContentUtils.h"
     26 #include "nsCycleCollectionParticipant.h"
     27 #include "nsIGlobalObject.h"
     28 #include "nsISupports.h"
     29 #include "nsWrapperCache.h"
     30 
     31 namespace mozilla::dom {
     32 
     33 NS_IMPL_CYCLE_COLLECTION_CLASS(RTCEncodedVideoFrame)
     34 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(RTCEncodedVideoFrame,
     35                                                RTCEncodedFrameBase)
     36  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
     37  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     38 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     39 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(RTCEncodedVideoFrame,
     40                                                  RTCEncodedFrameBase)
     41  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
     42 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     43 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(RTCEncodedVideoFrame,
     44                                               RTCEncodedFrameBase)
     45  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
     46 NS_IMPL_CYCLE_COLLECTION_TRACE_END
     47 NS_IMPL_ADDREF_INHERITED(RTCEncodedVideoFrame, RTCEncodedFrameBase)
     48 NS_IMPL_RELEASE_INHERITED(RTCEncodedVideoFrame, RTCEncodedFrameBase)
     49 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCEncodedVideoFrame)
     50  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     51 NS_INTERFACE_MAP_END_INHERITING(RTCEncodedFrameBase)
     52 
     53 RTCEncodedVideoFrame::RTCEncodedVideoFrame(
     54    nsIGlobalObject* aGlobal,
     55    std::unique_ptr<webrtc::TransformableFrameInterface> aFrame,
     56    uint64_t aCounter, RTCRtpScriptTransformer* aOwner)
     57    : RTCEncodedVideoFrameData{RTCEncodedFrameState{std::move(aFrame), aCounter,
     58                                                    /*timestamp*/ 0}},
     59      RTCEncodedFrameBase(aGlobal, static_cast<RTCEncodedFrameState&>(*this)),
     60      mOwner(aOwner) {
     61  InitMetadata();
     62  // Base class needs this, but can't do it itself because of an assertion in
     63  // the cycle-collector.
     64  mozilla::HoldJSObjects(this);
     65 }
     66 
     67 RTCEncodedVideoFrame::RTCEncodedVideoFrame(nsIGlobalObject* aGlobal,
     68                                           RTCEncodedVideoFrameData&& aData)
     69    : RTCEncodedVideoFrameData{RTCEncodedFrameState{std::move(aData.mFrame),
     70                                                    aData.mCounter,
     71                                                    aData.mTimestamp},
     72                               aData.mType, std::move(aData.mMetadata),
     73                               aData.mRid},
     74      RTCEncodedFrameBase(aGlobal, static_cast<RTCEncodedFrameState&>(*this)),
     75      mOwner(nullptr) {
     76  // Base class needs this, but can't do it itself because of an assertion in
     77  // the cycle-collector.
     78  mozilla::HoldJSObjects(this);
     79 }
     80 
     81 void RTCEncodedVideoFrame::InitMetadata() {
     82  const auto& videoFrame(
     83      static_cast<webrtc::TransformableVideoFrameInterface&>(*mFrame));
     84  mType = videoFrame.IsKeyFrame() ? RTCEncodedVideoFrameType::Key
     85                                  : RTCEncodedVideoFrameType::Delta;
     86  auto metadata = videoFrame.Metadata();
     87 
     88  if (metadata.GetFrameId().has_value()) {
     89    mMetadata.mFrameId.Construct(*metadata.GetFrameId());
     90  }
     91  mMetadata.mDependencies.Construct();
     92  for (const auto dep : metadata.GetFrameDependencies()) {
     93    (void)mMetadata.mDependencies.Value().AppendElement(
     94        static_cast<unsigned long long>(dep), fallible);
     95  }
     96  mMetadata.mWidth.Construct(metadata.GetWidth());
     97  mMetadata.mHeight.Construct(metadata.GetHeight());
     98  if (metadata.GetSpatialIndex() >= 0) {
     99    mMetadata.mSpatialIndex.Construct(metadata.GetSpatialIndex());
    100  }
    101  if (metadata.GetTemporalIndex() >= 0) {
    102    mMetadata.mTemporalIndex.Construct(metadata.GetTemporalIndex());
    103  }
    104  mMetadata.mSynchronizationSource.Construct(videoFrame.GetSsrc());
    105  mMetadata.mPayloadType.Construct(videoFrame.GetPayloadType());
    106  mMetadata.mContributingSources.Construct();
    107  for (const auto csrc : metadata.GetCsrcs()) {
    108    (void)mMetadata.mContributingSources.Value().AppendElement(csrc, fallible);
    109  }
    110 
    111  // The metadata timestamp is different, and not presently present in the
    112  // libwebrtc types
    113  if (videoFrame.Rid().has_value() && !videoFrame.Rid()->empty()) {
    114    mRid = Some(videoFrame.Rid()->c_str());
    115  }
    116 }
    117 
    118 RTCEncodedVideoFrame::~RTCEncodedVideoFrame() {
    119  // Clear JS::Heap<> members before unregistering as a script holder,
    120  // so their destructors don't barrier against a finalized JS object.
    121  mData = nullptr;  // from RTCEncodedFrameBase (protected)
    122  // Base class needs this, but can't do it itself because of an assertion in
    123  // the cycle-collector.
    124  mozilla::DropJSObjects(this);
    125 }
    126 
    127 JSObject* RTCEncodedVideoFrame::WrapObject(JSContext* aCx,
    128                                           JS::Handle<JSObject*> aGivenProto) {
    129  return RTCEncodedVideoFrame_Binding::Wrap(aCx, this, aGivenProto);
    130 }
    131 
    132 // https://w3c.github.io/webrtc-encoded-transform/#RTCEncodedVideoFrame-constructor
    133 /* static */
    134 already_AddRefed<RTCEncodedVideoFrame> RTCEncodedVideoFrame::Constructor(
    135    const GlobalObject& aGlobal, const RTCEncodedVideoFrame& aOriginalFrame,
    136    const RTCEncodedVideoFrameOptions& aOptions, ErrorResult& aRv) {
    137  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
    138  if (!global) {
    139    aRv.Throw(NS_ERROR_FAILURE);
    140    return nullptr;
    141  }
    142  auto frame = MakeRefPtr<RTCEncodedVideoFrame>(global, aOriginalFrame.Clone());
    143 
    144  if (aOptions.mMetadata.WasPassed()) {
    145    const auto& src = aOptions.mMetadata.Value();
    146    auto& dst = frame->mMetadata;
    147 
    148    auto set_if = [](auto& dst, const auto& src) {
    149      if (src.WasPassed()) dst.Value() = src.Value();
    150    };
    151    set_if(dst.mFrameId, src.mFrameId);
    152    set_if(dst.mDependencies, src.mDependencies);
    153    set_if(dst.mWidth, src.mWidth);
    154    set_if(dst.mHeight, src.mHeight);
    155    set_if(dst.mSpatialIndex, src.mSpatialIndex);
    156    set_if(dst.mTemporalIndex, src.mTemporalIndex);
    157    set_if(dst.mSynchronizationSource, src.mSynchronizationSource);
    158    set_if(dst.mPayloadType, src.mPayloadType);
    159    set_if(dst.mContributingSources, src.mContributingSources);
    160    set_if(dst.mTimestamp, src.mTimestamp);
    161  }
    162  return frame.forget();
    163 }
    164 
    165 RTCEncodedVideoFrameData RTCEncodedVideoFrameData::Clone() const {
    166  return RTCEncodedVideoFrameData{
    167      RTCEncodedFrameState{
    168          webrtc::CloneVideoFrame(
    169              static_cast<webrtc::TransformableVideoFrameInterface*>(
    170                  mFrame.get())),
    171          mCounter, mTimestamp},
    172      mType, RTCEncodedVideoFrameMetadata(mMetadata), mRid};
    173 }
    174 
    175 nsIGlobalObject* RTCEncodedVideoFrame::GetParentObject() const {
    176  return mGlobal;
    177 }
    178 
    179 RTCEncodedVideoFrameType RTCEncodedVideoFrame::Type() const { return mType; }
    180 
    181 void RTCEncodedVideoFrame::GetMetadata(
    182    RTCEncodedVideoFrameMetadata& aMetadata) {
    183  aMetadata = mMetadata;
    184 }
    185 
    186 bool RTCEncodedVideoFrame::CheckOwner(RTCRtpScriptTransformer* aOwner) const {
    187  return aOwner == mOwner;
    188 }
    189 
    190 Maybe<nsCString> RTCEncodedVideoFrame::Rid() const { return mRid; }
    191 
    192 // https://www.w3.org/TR/webrtc-encoded-transform/#RTCEncodedVideoFrame-serialization
    193 /* static */
    194 JSObject* RTCEncodedVideoFrame::ReadStructuredClone(
    195    JSContext* aCx, nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader,
    196    RTCEncodedVideoFrameData& aData) {
    197  JS::Rooted<JS::Value> value(aCx, JS::NullValue());
    198  // To avoid a rooting hazard error from returning a raw JSObject* before
    199  // running the RefPtr destructor, RefPtr needs to be destructed before
    200  // returning the raw JSObject*, which is why the RefPtr<RTCEncodedVideoFrame>
    201  // is created in the scope below. Otherwise, the static analysis infers the
    202  // RefPtr cannot be safely destructed while the unrooted return JSObject* is
    203  // on the stack.
    204  {
    205    auto frame = MakeRefPtr<RTCEncodedVideoFrame>(aGlobal, std::move(aData));
    206    if (!GetOrCreateDOMReflector(aCx, frame, &value) || !value.isObject()) {
    207      return nullptr;
    208    }
    209  }
    210  return value.toObjectOrNull();
    211 }
    212 
    213 bool RTCEncodedVideoFrame::WriteStructuredClone(
    214    JSStructuredCloneWriter* aWriter, StructuredCloneHolder* aHolder) const {
    215  AssertIsOnOwningThread();
    216 
    217  // Indexing the chunk and send the index to the receiver.
    218  const uint32_t index =
    219      static_cast<uint32_t>(aHolder->RtcEncodedVideoFrames().Length());
    220  // The serialization is limited to the same process scope so it's ok to
    221  // hand over a (copy of a) webrtc internal object here.
    222  //
    223  // TODO: optimize later once encoded source API materializes
    224  // .AppendElement(aHolder->IsTransferred(mData) ? Take() : Clone())
    225  aHolder->RtcEncodedVideoFrames().AppendElement(Clone());
    226  return !NS_WARN_IF(
    227      !JS_WriteUint32Pair(aWriter, SCTAG_DOM_RTCENCODEDVIDEOFRAME, index));
    228 }
    229 
    230 }  // namespace mozilla::dom