RTCEncodedAudioFrame.cpp (7950B)
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/RTCEncodedAudioFrame.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 "jsapi/RTCRtpScriptTransform.h" 19 #include "mozilla/HoldDropJSObjects.h" 20 #include "mozilla/RefPtr.h" 21 #include "mozilla/dom/RTCEncodedAudioFrameBinding.h" 22 #include "mozilla/dom/RTCRtpScriptTransformer.h" 23 #include "mozilla/dom/StructuredCloneHolder.h" 24 #include "mozilla/dom/StructuredCloneTags.h" 25 #include "mozilla/fallible.h" 26 #include "nsContentUtils.h" 27 #include "nsCycleCollectionParticipant.h" 28 #include "nsIGlobalObject.h" 29 #include "nsISupports.h" 30 #include "nsWrapperCache.h" 31 32 namespace mozilla::dom { 33 34 NS_IMPL_CYCLE_COLLECTION_CLASS(RTCEncodedAudioFrame) 35 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(RTCEncodedAudioFrame, 36 RTCEncodedFrameBase) 37 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner) 38 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 39 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 40 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(RTCEncodedAudioFrame, 41 RTCEncodedFrameBase) 42 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) 43 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 44 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(RTCEncodedAudioFrame, 45 RTCEncodedFrameBase) 46 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER 47 NS_IMPL_CYCLE_COLLECTION_TRACE_END 48 NS_IMPL_ADDREF_INHERITED(RTCEncodedAudioFrame, RTCEncodedFrameBase) 49 NS_IMPL_RELEASE_INHERITED(RTCEncodedAudioFrame, RTCEncodedFrameBase) 50 51 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCEncodedAudioFrame) 52 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 53 NS_INTERFACE_MAP_END_INHERITING(RTCEncodedFrameBase) 54 55 RTCEncodedAudioFrame::RTCEncodedAudioFrame( 56 nsIGlobalObject* aGlobal, 57 std::unique_ptr<webrtc::TransformableFrameInterface> aFrame, 58 uint64_t aCounter, RTCRtpScriptTransformer* aOwner) 59 : RTCEncodedAudioFrameData{RTCEncodedFrameState{std::move(aFrame), aCounter, 60 /*timestamp*/ 0}}, 61 RTCEncodedFrameBase(aGlobal, static_cast<RTCEncodedFrameState&>(*this)), 62 mOwner(aOwner) { 63 mMetadata.mSynchronizationSource.Construct(mFrame->GetSsrc()); 64 mMetadata.mPayloadType.Construct(mFrame->GetPayloadType()); 65 const auto& audioFrame( 66 static_cast<webrtc::TransformableAudioFrameInterface&>(*mFrame)); 67 mMetadata.mContributingSources.Construct(); 68 for (const auto csrc : audioFrame.GetContributingSources()) { 69 (void)mMetadata.mContributingSources.Value().AppendElement(csrc, fallible); 70 } 71 if (const auto optionalSeqNum = audioFrame.SequenceNumber()) { 72 mMetadata.mSequenceNumber.Construct(*optionalSeqNum); 73 } 74 75 // Base class needs this, but can't do it itself because of an assertion in 76 // the cycle-collector. 77 mozilla::HoldJSObjects(this); 78 } 79 80 RTCEncodedAudioFrame::RTCEncodedAudioFrame(nsIGlobalObject* aGlobal, 81 RTCEncodedAudioFrameData&& aData) 82 : RTCEncodedAudioFrameData{RTCEncodedFrameState{std::move(aData.mFrame), 83 aData.mCounter, 84 aData.mTimestamp}, 85 std::move(aData.mMetadata)}, 86 RTCEncodedFrameBase(aGlobal, static_cast<RTCEncodedFrameState&>(*this)), 87 mOwner(nullptr) { 88 // Base class needs this, but can't do it itself because of an assertion in 89 // the cycle-collector. 90 mozilla::HoldJSObjects(this); 91 } 92 93 RTCEncodedAudioFrame::~RTCEncodedAudioFrame() { 94 // Clear JS::Heap<> members before unregistering as a script holder, 95 // so their destructors don't barrier against a finalized JS object. 96 mData = nullptr; // from RTCEncodedFrameBase (protected) 97 // Base class needs this, but can't do it itself because of an assertion in 98 // the cycle-collector. 99 mozilla::DropJSObjects(this); 100 } 101 102 JSObject* RTCEncodedAudioFrame::WrapObject(JSContext* aCx, 103 JS::Handle<JSObject*> aGivenProto) { 104 return RTCEncodedAudioFrame_Binding::Wrap(aCx, this, aGivenProto); 105 } 106 107 // https://w3c.github.io/webrtc-encoded-transform/#RTCEncodedAudioFrame-constructor 108 /* static */ 109 already_AddRefed<RTCEncodedAudioFrame> RTCEncodedAudioFrame::Constructor( 110 const GlobalObject& aGlobal, const RTCEncodedAudioFrame& aOriginalFrame, 111 const RTCEncodedAudioFrameOptions& aOptions, ErrorResult& aRv) { 112 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 113 if (!global) { 114 aRv.Throw(NS_ERROR_FAILURE); 115 return nullptr; 116 } 117 auto frame = MakeRefPtr<RTCEncodedAudioFrame>(global, aOriginalFrame.Clone()); 118 119 if (aOptions.mMetadata.WasPassed()) { 120 const auto& src = aOptions.mMetadata.Value(); 121 auto& dst = frame->mMetadata; 122 123 auto set_if = [](auto& dst, const auto& src) { 124 if (src.WasPassed()) dst.Value() = src.Value(); 125 }; 126 set_if(dst.mSynchronizationSource, src.mSynchronizationSource); 127 set_if(dst.mPayloadType, src.mPayloadType); 128 set_if(dst.mContributingSources, src.mContributingSources); 129 set_if(dst.mSequenceNumber, src.mSequenceNumber); 130 } 131 return frame.forget(); 132 } 133 134 RTCEncodedAudioFrameData RTCEncodedAudioFrameData::Clone() const { 135 return RTCEncodedAudioFrameData{ 136 RTCEncodedFrameState{webrtc::CloneAudioFrame( 137 static_cast<webrtc::TransformableAudioFrameInterface*>( 138 mFrame.get()))}, 139 RTCEncodedAudioFrameMetadata(mMetadata)}; 140 } 141 142 nsIGlobalObject* RTCEncodedAudioFrame::GetParentObject() const { 143 return mGlobal; 144 } 145 146 void RTCEncodedAudioFrame::GetMetadata( 147 RTCEncodedAudioFrameMetadata& aMetadata) const { 148 aMetadata = mMetadata; 149 } 150 151 bool RTCEncodedAudioFrame::CheckOwner(RTCRtpScriptTransformer* aOwner) const { 152 return aOwner == mOwner; 153 } 154 155 // https://www.w3.org/TR/webrtc-encoded-transform/#RTCEncodedAudioFrame-serialization 156 /* static */ 157 JSObject* RTCEncodedAudioFrame::ReadStructuredClone( 158 JSContext* aCx, nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader, 159 RTCEncodedAudioFrameData& aData) { 160 JS::Rooted<JS::Value> value(aCx, JS::NullValue()); 161 // To avoid a rooting hazard error from returning a raw JSObject* before 162 // running the RefPtr destructor, RefPtr needs to be destructed before 163 // returning the raw JSObject*, which is why the RefPtr<RTCEncodedAudioFrame> 164 // is created in the scope below. Otherwise, the static analysis infers the 165 // RefPtr cannot be safely destructed while the unrooted return JSObject* is 166 // on the stack. 167 { 168 auto frame = MakeRefPtr<RTCEncodedAudioFrame>(aGlobal, std::move(aData)); 169 if (!GetOrCreateDOMReflector(aCx, frame, &value) || !value.isObject()) { 170 return nullptr; 171 } 172 } 173 return value.toObjectOrNull(); 174 } 175 176 bool RTCEncodedAudioFrame::WriteStructuredClone( 177 JSStructuredCloneWriter* aWriter, StructuredCloneHolder* aHolder) const { 178 AssertIsOnOwningThread(); 179 180 // Indexing the chunk and send the index to the receiver. 181 const uint32_t index = 182 static_cast<uint32_t>(aHolder->RtcEncodedAudioFrames().Length()); 183 // The serialization is limited to the same process scope so it's ok to 184 // hand over a (copy of a) webrtc internal object here. 185 // 186 // TODO: optimize later once encoded source API materializes 187 // .AppendElement(aHolder->IsTransferred(mData) ? Take() : Clone()) 188 aHolder->RtcEncodedAudioFrames().AppendElement(Clone()); 189 return !NS_WARN_IF( 190 !JS_WriteUint32Pair(aWriter, SCTAG_DOM_RTCENCODEDAUDIOFRAME, index)); 191 } 192 193 } // namespace mozilla::dom