commit 660104461e7f0660c8e6e85182c1687be93197d3
parent f3c88f78b20ca3d4226533b497504bec6a02016e
Author: Jan-Ivar Bruaroey <jib@mozilla.com>
Date: Tue, 21 Oct 2025 20:02:35 +0000
Bug 1993997 - Test RTCEncodedVideoFrame/RTCEncodedAudioFrame copy construction on main thread. r=bwc
Differential Revision: https://phabricator.services.mozilla.com/D268397
Diffstat:
4 files changed, 112 insertions(+), 18 deletions(-)
diff --git a/testing/web-platform/tests/webrtc-encoded-transform/RTCEncodedFrame-copy-construction.https.html b/testing/web-platform/tests/webrtc-encoded-transform/RTCEncodedFrame-copy-construction.https.html
@@ -0,0 +1,101 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<script src="../webrtc/RTCPeerConnection-helper.js"></script>
+<script src="helper.js"></script>
+</head>
+<body>
+<script>
+
+const assert_json_equals = (a, b) => assert_equals(JSON.stringify(a), JSON.stringify(b));
+const assert_json_not_equals = (a, b) => assert_not_equals(JSON.stringify(a), JSON.stringify(b));
+
+promise_test(async t => {
+ const frame = await createRTCEncodedFrameFromScratch("video");
+ assert_true(frame instanceof RTCEncodedVideoFrame);
+ assert_equals(frame.type, "key"); // first frame is key
+
+ const clone = new RTCEncodedVideoFrame(frame);
+ assert_true(clone instanceof RTCEncodedVideoFrame);
+ assert_equals(clone.type, frame.type);
+ assert_true(areArrayBuffersEqual(clone.data, frame.data));
+ assert_json_equals(clone.getMetadata(), frame.getMetadata());
+}, "RTCEncodedVideoFrame copy construction on main thread.");
+
+promise_test(async t => {
+ const frame = await createRTCEncodedFrameFromScratch("audio");
+ assert_true(frame instanceof RTCEncodedAudioFrame);
+
+ const clone = new RTCEncodedAudioFrame(frame);
+ assert_true(clone instanceof RTCEncodedAudioFrame);
+ assert_equals(clone.type, frame.type);
+ assert_true(areArrayBuffersEqual(clone.data, frame.data));
+ assert_json_equals(clone.getMetadata(), frame.getMetadata());
+}, "RTCEncodedAudioFrame copy construction on main thread.");
+
+function different(value) {
+ switch (typeof value) {
+ case "number": return value + 1;
+ case "string": return value + "2";
+ case "object": return Array.isArray(value) ? value.concat([2]) : {};
+ default: assert_unreached(`unexpected type ${typeof value}`);
+ }
+}
+
+["RTCEncodedVideoFrame", "RTCEncodedAudioFrame"].forEach(constr => {
+ const kind = constr.includes("Video")? "video" : "audio";
+
+ promise_test(async t => {
+ const frame = await createRTCEncodedFrameFromScratch(kind);
+ const oldData = frame.getMetadata();
+ // test single key replacement
+ for (const key of Object.keys(oldData)) {
+ const metadata = {[key]: different(oldData[key])};
+ // Spec says "The new frame’s [[metadata]] is a deep copy
+ // of originalFrame.[[metadata]], with fields replaced with
+ // deep copies of the fields present in options.[metadata]."
+ // This compares well to how Object.assign() works
+ const expected = Object.assign(structuredClone(oldData), metadata);
+ const clone = new window[constr](frame, {metadata});
+ assert_json_equals(clone.getMetadata(), expected);
+ assert_json_not_equals(clone.getMetadata(), oldData);
+ }
+ // test cumulative key replacement
+ let metadata = {};
+ for (const key of Object.keys(oldData)) {
+ Object.assign(metadata, {[key]: different(oldData[key])});
+ const expected = Object.assign(structuredClone(oldData), metadata);
+ const clone = new window[constr](frame, {metadata});
+ assert_json_equals(clone.getMetadata(), expected);
+ assert_json_not_equals(clone.getMetadata(), oldData);
+ }
+ }, `${constr} copy construction metadata override on main thread.`);
+
+ promise_test(async t => {
+ const frame = await createRTCEncodedFrameFromScratch(kind);
+ assert_greater_than(frame.data.byteLength, 0);
+ const length = frame.data.byteLength;
+ const clone = structuredClone(frame);
+ assert_equals(frame.data.byteLength, length, "not transferred");
+ assert_true(areArrayBuffersEqual(clone.data, frame.data));
+ assert_json_equals(clone.getMetadata(), frame.getMetadata());
+ }, `${constr} structuredClone on main thread.`);
+
+ promise_test(async t => {
+ const frame = await createRTCEncodedFrameFromScratch(kind);
+ assert_greater_than(frame.data.byteLength, 0);
+ const length = frame.data.byteLength;
+ const clone = structuredClone(frame, {transfer: [frame.data]});
+ assert_equals(frame.data.byteLength, 0, "was transferred");
+ assert_equals(clone.data.byteLength, length);
+ }, `${constr} structuredClone transfer on main thread.`);
+
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/webrtc-encoded-transform/RTCRtpScriptTransform-sender-worker-single-frame.https.html b/testing/web-platform/tests/webrtc-encoded-transform/RTCRtpScriptTransform-sender-worker-single-frame.https.html
@@ -8,26 +8,11 @@
<script src=/resources/testdriver-vendor.js></script>
<script src='../../mediacapture-streams/permission-helper.js'></script>
<script src="../../webrtc/RTCPeerConnection-helper.js"></script>
+<script src="helper.js"></script>
</head>
<body>
<script>
-function areArrayBuffersEqual(buffer1, buffer2)
-{
- if (buffer1.byteLength !== buffer2.byteLength) {
- return false;
- }
- let array1 = new Int8Array(buffer1);
- var array2 = new Int8Array(buffer2);
- for (let i = 0 ; i < buffer1.byteLength ; ++i) {
- if (array1[i] !== array2[i]) {
- return false;
- }
- }
- return true;
-}
-
-
promise_test(async t => {
const caller = new RTCPeerConnection();
t.add_cleanup(() => caller.close());
@@ -130,4 +115,4 @@ promise_test(async t => {
</script>
</body>
-</html>
-\ No newline at end of file
+</html>
diff --git a/testing/web-platform/tests/webrtc-encoded-transform/helper.js b/testing/web-platform/tests/webrtc-encoded-transform/helper.js
@@ -31,3 +31,11 @@ async function createRTCEncodedFrameFromScratch(kind) {
const {data} = await new Promise(r => worker.onmessage = r);
return data;
}
+
+function areArrayBuffersEqual(a, b) {
+ if (a.byteLength != b.byteLength) {
+ return false;
+ }
+ const ui8b = new Uint8Array(b);
+ return new Uint8Array(a).every((val, i) => val === ui8b[i]);
+}
diff --git a/testing/web-platform/tests/webrtc-encoded-transform/tentative/RTCPeerConnection-insertable-streams-worker.https.html b/testing/web-platform/tests/webrtc-encoded-transform/tentative/RTCPeerConnection-insertable-streams-worker.https.html
@@ -9,6 +9,7 @@
<script src='../../mediacapture-streams/permission-helper.js'></script>
<script src="../../webrtc/RTCPeerConnection-helper.js"></script>
<script src="./RTCPeerConnection-insertable-streams.js"></script>
+<script src="./helper.js"></script>
</head>
<body>
<script>