Skip to content

Commit 4e31e67

Browse files
committed
Bug 1868223 - Implement custom RTCEncodedVideoFrame::Read/WriteStructuredClone for same process. r=bwc,webidl,smaug
Differential Revision: https://phabricator.services.mozilla.com/D267543
1 parent 4fda8f0 commit 4e31e67

12 files changed

Lines changed: 316 additions & 64 deletions

dom/base/StructuredCloneHolder.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
#include "mozilla/RefPtr.h"
2323
#include "mozilla/ScopeExit.h"
2424
#include "mozilla/StaticPrefs_dom.h"
25+
#ifdef MOZ_WEBRTC
26+
# include "mozilla/StaticPrefs_media.h"
27+
#endif
2528
#include "mozilla/dom/AudioData.h"
2629
#include "mozilla/dom/BindingDeclarations.h"
2730
#include "mozilla/dom/BindingUtils.h"
@@ -52,6 +55,10 @@
5255
#include "mozilla/dom/MessagePortBinding.h"
5356
#include "mozilla/dom/OffscreenCanvas.h"
5457
#include "mozilla/dom/OffscreenCanvasBinding.h"
58+
#ifdef MOZ_WEBRTC
59+
# include "mozilla/dom/RTCEncodedVideoFrame.h"
60+
# include "mozilla/dom/RTCEncodedVideoFrameBinding.h"
61+
#endif
5562
#include "mozilla/dom/ReadableStream.h"
5663
#include "mozilla/dom/ReadableStreamBinding.h"
5764
#include "mozilla/dom/ScriptSettings.h"
@@ -1155,6 +1162,18 @@ JSObject* StructuredCloneHolder::CustomReadHandler(
11551162
}
11561163
}
11571164

1165+
#ifdef MOZ_WEBRTC
1166+
if (StaticPrefs::media_peerconnection_enabled() &&
1167+
aTag == SCTAG_DOM_RTCENCODEDVIDEOFRAME &&
1168+
CloneScope() == StructuredCloneScope::SameProcess &&
1169+
aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) {
1170+
JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject());
1171+
if (RTCEncodedVideoFrame_Binding::ConstructorEnabled(aCx, global)) {
1172+
return RTCEncodedVideoFrame::ReadStructuredClone(
1173+
aCx, mGlobal, aReader, RtcEncodedVideoFrames()[aIndex]);
1174+
}
1175+
}
1176+
#endif
11581177
return ReadFullySerializableObjects(aCx, aReader, aTag, false);
11591178
}
11601179

@@ -1297,6 +1316,18 @@ bool StructuredCloneHolder::CustomWriteHandler(
12971316
}
12981317
}
12991318

1319+
#ifdef MOZ_WEBRTC
1320+
// See if this is an RTCEncodedVideoFrame object.
1321+
if (StaticPrefs::media_peerconnection_enabled()) {
1322+
RTCEncodedVideoFrame* rtcFrame = nullptr;
1323+
if (NS_SUCCEEDED(UNWRAP_OBJECT(RTCEncodedVideoFrame, &obj, rtcFrame))) {
1324+
SameProcessScopeRequired(aSameProcessScopeRequired);
1325+
return CloneScope() == StructuredCloneScope::SameProcess
1326+
? rtcFrame->WriteStructuredClone(aWriter, this)
1327+
: false;
1328+
}
1329+
}
1330+
#endif
13001331
{
13011332
// We only care about streams, so ReflectorToISupportsStatic is fine.
13021333
nsCOMPtr<nsISupports> base = xpc::ReflectorToISupportsStatic(aObj);

dom/base/StructuredCloneHolder.h

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ class MessagePort;
172172
class MessagePortIdentifier;
173173
struct VideoFrameSerializedData;
174174
struct AudioDataSerializedData;
175+
#ifdef MOZ_WEBRTC
176+
struct RTCEncodedVideoFrameData;
177+
#endif
175178

176179
class StructuredCloneHolder : public StructuredCloneHolderBase {
177180
public:
@@ -211,18 +214,25 @@ class StructuredCloneHolder : public StructuredCloneHolderBase {
211214
JS::MutableHandle<JS::Value> aValue,
212215
const JS::CloneDataPolicy& aCloneDataPolicy, ErrorResult& aRv);
213216

214-
// Create a statement for each of the side DOM-ish data members.
215-
// mTransferredPorts is not included because it is part of the
216-
// deserialized state.
217-
#define CLONED_DATA_MEMBERS \
218-
STMT(mBlobImplArray); \
219-
STMT(mWasmModuleArray); \
220-
STMT(mInputStreamArray); \
221-
STMT(mClonedSurfaces); \
222-
STMT(mVideoFrames); \
223-
STMT(mAudioData); \
224-
STMT(mEncodedVideoChunks); \
225-
STMT(mEncodedAudioChunks); \
217+
#ifdef MOZ_WEBRTC
218+
# define IF_WEBRTC(x) x
219+
#else
220+
# define IF_WEBRTC(x)
221+
#endif
222+
223+
// Create a statement for each of the side DOM-ish data members.
224+
// mTransferredPorts is not included because it is part of the
225+
// deserialized state.
226+
#define CLONED_DATA_MEMBERS \
227+
STMT(mBlobImplArray); \
228+
STMT(mWasmModuleArray); \
229+
STMT(mInputStreamArray); \
230+
STMT(mClonedSurfaces); \
231+
STMT(mVideoFrames); \
232+
STMT(mAudioData); \
233+
STMT(mEncodedVideoChunks); \
234+
STMT(mEncodedAudioChunks); \
235+
IF_WEBRTC(STMT(mRtcEncodedVideoFrames);) \
226236
STMT(mPortIdentifiers);
227237

228238
// Call this method to know if this object is keeping some DOM object alive.
@@ -302,6 +312,12 @@ class StructuredCloneHolder : public StructuredCloneHolderBase {
302312
return mEncodedAudioChunks;
303313
}
304314

315+
#ifdef MOZ_WEBRTC
316+
nsTArray<RTCEncodedVideoFrameData>& RtcEncodedVideoFrames() {
317+
return mRtcEncodedVideoFrames;
318+
}
319+
#endif
320+
305321
// Implementations of the virtual methods to allow cloning of objects which
306322
// JS engine itself doesn't clone.
307323

@@ -416,6 +432,11 @@ class StructuredCloneHolder : public StructuredCloneHolderBase {
416432
// Used for cloning EncodedAudioChunk in the structured cloning algorithm.
417433
nsTArray<EncodedAudioChunkData> mEncodedAudioChunks;
418434

435+
#ifdef MOZ_WEBRTC
436+
// Used for cloning RTCEncodedVideoFrame in the structured cloning algorithm.
437+
nsTArray<RTCEncodedVideoFrameData> mRtcEncodedVideoFrames;
438+
#endif
439+
419440
// This raw pointer is only set within ::Read() and is unset by the end.
420441
nsIGlobalObject* MOZ_NON_OWNING_REF mGlobal;
421442

dom/base/StructuredCloneTags.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ enum StructuredCloneTags : uint32_t {
163163

164164
SCTAG_DOM_RTCDATACHANNEL,
165165

166+
SCTAG_DOM_RTCENCODEDVIDEOFRAME,
167+
168+
SCTAG_DOM_RTCENCODEDAUDIOFRAME,
169+
166170
// IMPORTANT: If you plan to add an new IDB tag, it _must_ be add before the
167171
// "less stable" tags!
168172
};

dom/media/webrtc/jsapi/RTCEncodedAudioFrame.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,13 @@ RTCEncodedAudioFrame::RTCEncodedAudioFrame(
4242
nsIGlobalObject* aGlobal,
4343
std::unique_ptr<webrtc::TransformableFrameInterface> aFrame,
4444
uint64_t aCounter, RTCRtpScriptTransformer* aOwner)
45-
: RTCEncodedFrameBase(aGlobal, std::move(aFrame), aCounter),
45+
: RTCEncodedAudioFrameData{{std::move(aFrame), aCounter, /*timestamp*/ 0}},
46+
RTCEncodedFrameBase(aGlobal, static_cast<RTCEncodedFrameState&>(*this)),
4647
mOwner(aOwner) {
4748
mMetadata.mSynchronizationSource.Construct(mFrame->GetSsrc());
4849
mMetadata.mPayloadType.Construct(mFrame->GetPayloadType());
4950
const auto& audioFrame(
50-
static_cast<webrtc::TransformableAudioFrameInterface&>(*mFrame));
51+
static_cast<webrtc::TransformableAudioFrameInterface&>(*mState.mFrame));
5152
mMetadata.mContributingSources.Construct();
5253
for (const auto csrc : audioFrame.GetContributingSources()) {
5354
Unused << mMetadata.mContributingSources.Value().AppendElement(csrc,
@@ -85,4 +86,20 @@ void RTCEncodedAudioFrame::GetMetadata(
8586
bool RTCEncodedAudioFrame::CheckOwner(RTCRtpScriptTransformer* aOwner) const {
8687
return aOwner == mOwner;
8788
}
89+
90+
// https://www.w3.org/TR/webrtc-encoded-transform/#RTCEncodedAudioFrame-serialization
91+
/* static */
92+
already_AddRefed<RTCEncodedAudioFrame>
93+
RTCEncodedAudioFrame::ReadStructuredClone(JSContext* aCx,
94+
nsIGlobalObject* aGlobal,
95+
JSStructuredCloneReader* aReader) {
96+
// TBD implementation to follow in followup patch
97+
return nullptr;
98+
}
99+
100+
bool RTCEncodedAudioFrame::WriteStructuredClone(
101+
JSContext* aCx, JSStructuredCloneWriter* aWriter) const {
102+
return false;
103+
}
104+
88105
} // namespace mozilla::dom

dom/media/webrtc/jsapi/RTCEncodedAudioFrame.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@
1414

1515
namespace mozilla::dom {
1616

17+
struct RTCEncodedAudioFrameData : RTCEncodedFrameState {
18+
RTCEncodedAudioFrameMetadata mMetadata;
19+
};
20+
1721
// Wraps a libwebrtc frame, allowing the frame buffer to be modified, and
1822
// providing read-only access to various metadata. After the libwebrtc frame is
1923
// extracted (with RTCEncodedFrameBase::TakeFrame), the frame buffer is
2024
// detached, but the metadata remains accessible.
21-
class RTCEncodedAudioFrame final : public RTCEncodedFrameBase {
25+
class RTCEncodedAudioFrame final : public RTCEncodedAudioFrameData,
26+
public RTCEncodedFrameBase {
2227
public:
2328
explicit RTCEncodedAudioFrame(
2429
nsIGlobalObject* aGlobal,
@@ -42,10 +47,23 @@ class RTCEncodedAudioFrame final : public RTCEncodedFrameBase {
4247

4348
bool IsVideo() const override { return false; }
4449

50+
// [Serializable] implementations: {Read, Write}StructuredClone
51+
static already_AddRefed<RTCEncodedAudioFrame> ReadStructuredClone(
52+
JSContext* aCx, nsIGlobalObject* aGlobal,
53+
JSStructuredCloneReader* aReader);
54+
bool WriteStructuredClone(JSContext* aCx,
55+
JSStructuredCloneWriter* aWriter) const;
56+
4557
private:
4658
virtual ~RTCEncodedAudioFrame();
59+
60+
// forbid copy/move to keep mState member in base valid
61+
RTCEncodedAudioFrame(const RTCEncodedAudioFrame&) = delete;
62+
RTCEncodedAudioFrame& operator=(const RTCEncodedAudioFrame&) = delete;
63+
RTCEncodedAudioFrame(RTCEncodedAudioFrame&&) = delete;
64+
RTCEncodedAudioFrame& operator=(RTCEncodedAudioFrame&&) = delete;
65+
4766
RefPtr<RTCRtpScriptTransformer> mOwner;
48-
RTCEncodedAudioFrameMetadata mMetadata;
4967
};
5068

5169
} // namespace mozilla::dom

dom/media/webrtc/jsapi/RTCEncodedFrameBase.cpp

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include "jsapi/RTCEncodedFrameBase.h"
88

9+
#include "api/frame_transformer_interface.h"
910
#include "js/ArrayBuffer.h"
1011
#include "js/GCAPI.h"
1112
#include "mozilla/dom/ScriptSettings.h"
@@ -21,53 +22,63 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCEncodedFrameBase)
2122
NS_INTERFACE_MAP_ENTRY(nsISupports)
2223
NS_INTERFACE_MAP_END
2324

24-
RTCEncodedFrameBase::RTCEncodedFrameBase(
25-
nsIGlobalObject* aGlobal,
26-
std::unique_ptr<webrtc::TransformableFrameInterface> aFrame,
27-
uint64_t aCounter)
28-
: mGlobal(aGlobal),
29-
mFrame(std::move(aFrame)),
30-
mCounter(aCounter),
31-
mTimestamp(mFrame->GetTimestamp()) {
25+
RTCEncodedFrameBase::RTCEncodedFrameBase(nsIGlobalObject* aGlobal,
26+
RTCEncodedFrameState& aState)
27+
: mGlobal(aGlobal), mState(aState), mData(nullptr) {
28+
mState.mTimestamp = mState.mFrame->GetTimestamp();
3229
AutoJSAPI jsapi;
3330
if (NS_WARN_IF(!jsapi.Init(mGlobal))) {
3431
return;
3532
}
3633

3734
// Avoid a copy
3835
mData = JS::NewArrayBufferWithUserOwnedContents(
39-
jsapi.cx(), mFrame->GetData().size(), (void*)(mFrame->GetData().data()));
36+
jsapi.cx(), mState.mFrame->GetData().size(),
37+
(void*)(mState.mFrame->GetData().data()));
4038
}
4139

4240
RTCEncodedFrameBase::~RTCEncodedFrameBase() = default;
4341

44-
unsigned long RTCEncodedFrameBase::Timestamp() const { return mTimestamp; }
42+
unsigned long RTCEncodedFrameBase::Timestamp() const {
43+
return mState.mTimestamp;
44+
}
4545

4646
void RTCEncodedFrameBase::SetData(const ArrayBuffer& aData) {
4747
mData.set(aData.Obj());
48-
if (mFrame) {
48+
if (mState.mFrame) {
4949
aData.ProcessData([&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) {
50-
mFrame->SetData(
50+
mState.mFrame->SetData(
5151
webrtc::ArrayView<const uint8_t>(aData.Elements(), aData.Length()));
5252
});
5353
}
5454
}
5555

56-
void RTCEncodedFrameBase::GetData(JSContext* aCx, JS::Rooted<JSObject*>* aObj) {
56+
void RTCEncodedFrameBase::GetData(JSContext* aCx,
57+
JS::Rooted<JSObject*>* aObj) const {
5758
aObj->set(mData);
5859
}
5960

60-
uint64_t RTCEncodedFrameBase::GetCounter() const { return mCounter; }
61+
uint64_t RTCEncodedFrameBase::GetCounter() const { return mState.mCounter; }
6162

6263
std::unique_ptr<webrtc::TransformableFrameInterface>
6364
RTCEncodedFrameBase::TakeFrame() {
64-
AutoJSAPI jsapi;
65-
if (!jsapi.Init(mGlobal)) {
66-
MOZ_CRASH("Could not init JSAPI!");
65+
if (mState.mFrame) {
66+
AutoJSAPI jsapi;
67+
if (!jsapi.Init(mGlobal)) {
68+
MOZ_CRASH("Could not init JSAPI!");
69+
}
70+
// If the JS buffer was transferred (or otherwise detached), neuter native.
71+
JS::Rooted<JSObject*> rootedData(jsapi.cx(), mData);
72+
if (rootedData && JS::IsDetachedArrayBufferObject(rootedData)) {
73+
mState.mFrame.reset();
74+
return nullptr;
75+
}
76+
// Still attached: detach now since we're consuming the frame.
77+
JS::DetachArrayBuffer(jsapi.cx(), rootedData);
6778
}
68-
JS::Rooted<JSObject*> rootedData(jsapi.cx(), mData);
69-
JS::DetachArrayBuffer(jsapi.cx(), rootedData);
70-
return std::move(mFrame);
79+
return std::move(mState.mFrame);
7180
}
7281

82+
RTCEncodedFrameState::~RTCEncodedFrameState() = default;
83+
7384
} // namespace mozilla::dom

dom/media/webrtc/jsapi/RTCEncodedFrameBase.h

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,40 @@
99

1010
#include <memory>
1111

12-
#include "api/frame_transformer_interface.h"
1312
#include "js/TypeDecls.h"
1413
#include "mozilla/Assertions.h"
1514
#include "mozilla/dom/TypedArray.h" // ArrayBuffer
1615

1716
class nsIGlobalObject;
1817

18+
namespace webrtc {
19+
class TransformableFrameInterface;
20+
}
21+
1922
namespace mozilla::dom {
2023

24+
struct RTCEncodedFrameState {
25+
std::unique_ptr<webrtc::TransformableFrameInterface> mFrame;
26+
uint64_t mCounter = 0;
27+
unsigned long mTimestamp = 0;
28+
29+
// work around only having forward-declared TransformableFrameInterface
30+
~RTCEncodedFrameState();
31+
32+
// avoid "move got disabled by a user-declared destructor” trap
33+
RTCEncodedFrameState() = default;
34+
RTCEncodedFrameState(RTCEncodedFrameState&&) noexcept = default;
35+
RTCEncodedFrameState& operator=(RTCEncodedFrameState&&) noexcept = default;
36+
RTCEncodedFrameState(const RTCEncodedFrameState&) = delete;
37+
RTCEncodedFrameState& operator=(const RTCEncodedFrameState&) = delete;
38+
};
39+
2140
class RTCRtpScriptTransformer;
2241

2342
class RTCEncodedFrameBase : public nsISupports, public nsWrapperCache {
2443
public:
25-
explicit RTCEncodedFrameBase(
26-
nsIGlobalObject* aGlobal,
27-
std::unique_ptr<webrtc::TransformableFrameInterface> aFrame,
28-
uint64_t aCounter);
44+
explicit RTCEncodedFrameBase(nsIGlobalObject* aGlobal,
45+
RTCEncodedFrameState& aState);
2946

3047
// nsISupports
3148
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@@ -36,7 +53,7 @@ class RTCEncodedFrameBase : public nsISupports, public nsWrapperCache {
3653

3754
void SetData(const ArrayBuffer& aData);
3855

39-
void GetData(JSContext* aCx, JS::Rooted<JSObject*>* aObj);
56+
void GetData(JSContext* aCx, JS::Rooted<JSObject*>* aObj) const;
4057

4158
uint64_t GetCounter() const;
4259

@@ -48,10 +65,20 @@ class RTCEncodedFrameBase : public nsISupports, public nsWrapperCache {
4865

4966
protected:
5067
virtual ~RTCEncodedFrameBase();
68+
69+
// forbid copy/move to protect mState
70+
RTCEncodedFrameBase(const RTCEncodedFrameBase&) = delete;
71+
RTCEncodedFrameBase& operator=(const RTCEncodedFrameBase&) = delete;
72+
RTCEncodedFrameBase(RTCEncodedFrameBase&&) = delete;
73+
RTCEncodedFrameBase& operator=(RTCEncodedFrameBase&&) = delete;
74+
5175
RefPtr<nsIGlobalObject> mGlobal;
52-
std::unique_ptr<webrtc::TransformableFrameInterface> mFrame;
53-
const uint64_t mCounter = 0;
54-
const unsigned long mTimestamp = 0;
76+
77+
// Keep serializable state separate in this base and its subclasses
78+
// in a manner that avoids diamond inheritance. Subclasses must pass
79+
// in *this, to ensure it's constructed before and destroyed after
80+
// this base; copy and move are deleted.
81+
RTCEncodedFrameState& mState;
5582
JS::Heap<JSObject*> mData;
5683
};
5784

0 commit comments

Comments
 (0)