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