diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/media/webrtc/tests/mochitests/test_peerConnection_setParameters.html | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/media/webrtc/tests/mochitests/test_peerConnection_setParameters.html')
-rw-r--r-- | dom/media/webrtc/tests/mochitests/test_peerConnection_setParameters.html | 470 |
1 files changed, 470 insertions, 0 deletions
diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_setParameters.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_setParameters.html new file mode 100644 index 0000000000..5df97e39f5 --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_setParameters.html @@ -0,0 +1,470 @@ +<!DOCTYPE HTML> +<html> +<head> + <script type="application/javascript" src="pc.js"></script> +</head> +<body> +<pre id="test"> +<script type="application/javascript"> +createHTML({ + bug: "1230184", + title: "Set parameters on sender", + visible: true +}); + +const simulcastOffer = `v=0 +o=- 3840232462471583827 0 IN IP4 127.0.0.1 +s=- +t=0 0 +a=group:BUNDLE 0 +a=msid-semantic: WMS +m=video 9 UDP/TLS/RTP/SAVPF 96 +c=IN IP4 0.0.0.0 +a=rtcp:9 IN IP4 0.0.0.0 +a=ice-ufrag:Li6+ +a=ice-pwd:3C05CTZBRQVmGCAq7hVasHlT +a=ice-options:trickle +a=fingerprint:sha-256 5B:D3:8E:66:0E:7D:D3:F3:8E:E6:80:28:19:FC:55:AD:58:5D:B9:3D:A8:DE:45:4A:E7:87:02:F8:3C:0B:3B:B3 +a=setup:actpass +a=mid:0 +a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id +a=recvonly +a=rtcp-mux +a=rtpmap:96 VP8/90000 +a=rtcp-fb:96 goog-remb +a=rtcp-fb:96 transport-cc +a=rtcp-fb:96 ccm fir +a=rid:foo recv +a=rid:bar recv +a=simulcast:recv foo;bar +`; + +function buildMaximumSendEncodings() { + const sendEncodings = []; + while (true) { + // isDeeply does not see identical string primitives and String objects + // as the same, so we make this a string primitive. + sendEncodings.push({rid: `${sendEncodings.length}`}); + const pc = new RTCPeerConnection(); + const {sender} = pc.addTransceiver('video', {sendEncodings}); + const {encodings} = sender.getParameters(); + if (encodings.length < sendEncodings.length) { + sendEncodings.pop(); + return sendEncodings; + } + } +} + +async function queueAWebrtcTask() { + const pc = new RTCPeerConnection(); + pc.addTransceiver('audio'); + await new Promise(r => pc.onnegotiationneeded = r); + pc.close(); +} + +// setParameters is mostly tested in wpt, but we test a few +// implementation-specific things here. Other mochitests check whether the +// set parameters actually have the desired effect on the media streams. +const tests = [ + + // wpt currently does not assume support for 3 encodings, which limits the + // effectiveness of its powers-of-2 test (since it can test only for 1 and 2) + async function checkScaleResolutionDownByAutoFillPowersOf2() { + const pc = new RTCPeerConnection(); + const {sender} = pc.addTransceiver('video', { + sendEncodings: [{rid: "0"},{rid: "1"},{rid: "2"}] + }); + const {encodings} = sender.getParameters(); + const scaleValues = encodings.map(({scaleResolutionDownBy}) => scaleResolutionDownBy); + isDeeply(scaleValues, [4, 2, 1]); + }, + + // wpt currently does not assume support for 3 encodings, which limits the + // effectiveness of its fill-with-1 test + async function checkScaleResolutionDownByAutoFillWith1() { + const pc = new RTCPeerConnection(); + const {sender} = pc.addTransceiver('video', { + sendEncodings: [ + {rid: "0"},{rid: "1", scaleResolutionDownBy: 3},{rid: "2"} + ] + }); + const {encodings} = sender.getParameters(); + const scaleValues = encodings.map(({scaleResolutionDownBy}) => scaleResolutionDownBy); + isDeeply(scaleValues, [1, 3, 1]); + }, + + async function checkVideoEncodingLimit() { + const pc = new RTCPeerConnection(); + const maxSendEncodings = buildMaximumSendEncodings(); + const sendEncodings = maxSendEncodings.concat({rid: "a"}); + const {sender} = pc.addTransceiver('video', {sendEncodings}); + const {encodings} = sender.getParameters(); + + const rids = encodings.map(({rid}) => rid); + const expectedRids = maxSendEncodings.map(({rid}) => rid); + isDeeply(rids, expectedRids); + + const scaleValues = encodings.map(({scaleResolutionDownBy}) => scaleResolutionDownBy); + const expectedScaleValues = []; + let scale = 1; + while (expectedScaleValues.length < maxSendEncodings.length) { + expectedScaleValues.push(scale); + scale *= 2; + } + isDeeply(scaleValues, expectedScaleValues.reverse()); + }, + + async function checkScaleDownByInTrimmedEncoding() { + const pc = new RTCPeerConnection(); + const maxSendEncodings = buildMaximumSendEncodings(); + const sendEncodings = maxSendEncodings.concat({rid: "a", scaleResolutionDownBy: 3}); + const {sender} = pc.addTransceiver('video', {sendEncodings}); + const {encodings} = sender.getParameters(); + const rids = encodings.map(({rid}) => rid); + const expectedRids = maxSendEncodings.map(({rid}) => rid); + isDeeply(rids, expectedRids); + const scaleValues = encodings.map(({scaleResolutionDownBy}) => scaleResolutionDownBy); + const expectedScaleValues = maxSendEncodings.map(() => 1); + isDeeply(scaleValues, expectedScaleValues); + }, + + async function checkLibwebrtcRidLengthLimit() { + const pc = new RTCPeerConnection(); + try { + pc.addTransceiver('video', { + sendEncodings: [{rid: "wibblywobblyjeremybearimy"}]} + ); + ok(false, "Rid should be too long for libwebrtc!"); + } catch (e) { + is(e.name, "TypeError", + "Rid that is too long for libwebrtc should result in a TypeError"); + } + }, + + async function checkErrorsInTrimmedEncodings() { + const pc = new RTCPeerConnection(); + const maxSendEncodings = buildMaximumSendEncodings(); + try { + const sendEncodings = maxSendEncodings.concat({rid: "foo-bar"}); + pc.addTransceiver('video', { sendEncodings }); + ok(false, "Should throw due to invalid rid characters"); + } catch (e) { + is(e.name, "TypeError") + } + try { + const sendEncodings = maxSendEncodings.concat({rid: "wibblywobblyjeremybearimy"}); + pc.addTransceiver('video', { sendEncodings }); + ok(false, "Should throw because rid too long"); + } catch (e) { + is(e.name, "TypeError") + } + try { + const sendEncodings = maxSendEncodings.concat({scaleResolutionDownBy: 2}); + pc.addTransceiver('video', { sendEncodings }); + ok(false, "Should throw due to missing rid"); + } catch (e) { + is(e.name, "TypeError") + } + try { + const sendEncodings = maxSendEncodings.concat(maxSendEncodings[0]); + pc.addTransceiver('video', { sendEncodings }); + ok(false, "Should throw due to duplicate rid"); + } catch (e) { + is(e.name, "TypeError") + } + try { + const sendEncodings = maxSendEncodings.concat({rid: maxSendEncodings.length, scaleResolutionDownBy: 0}); + pc.addTransceiver('video', { sendEncodings }); + ok(false, "Should throw due to invalid scaleResolutionDownBy"); + } catch (e) { + is(e.name, "RangeError") + } + try { + const sendEncodings = maxSendEncodings.concat({rid: maxSendEncodings.length, maxFramerate: -1}); + pc.addTransceiver('video', { sendEncodings }); + ok(false, "Should throw due to invalid maxFramerate"); + } catch (e) { + is(e.name, "RangeError") + } + }, + + async function checkCompatModeUnicastSetParametersAllowsSimulcastOffer() { + await pushPrefs( + ["media.peerconnection.allow_old_setParameters", true]); + const pc1 = new RTCPeerConnection(); + const stream = await navigator.mediaDevices.getUserMedia({video: true}); + const sender = pc1.addTrack(stream.getTracks()[0]); + const parameters = sender.getParameters(); + parameters.encodings[0].scaleResolutionDownBy = 3.0; + await sender.setParameters(parameters); + + await pc1.setRemoteDescription({type: "offer", sdp: simulcastOffer}); + + const {encodings} = sender.getParameters(); + const rids = encodings.map(({rid}) => rid); + isDeeply(rids, ["foo", "bar"]); + is(encodings[0].scaleResolutionDownBy, 2.0); + is(encodings[1].scaleResolutionDownBy, 1.0); + }, + + async function checkCompatModeUnicastSetParametersInterruptAllowsSimulcastOffer() { + await pushPrefs( + ["media.peerconnection.allow_old_setParameters", true]); + const pc1 = new RTCPeerConnection(); + const stream = await navigator.mediaDevices.getUserMedia({video: true}); + const sender = pc1.addTrack(stream.getTracks()[0]); + const parameters = sender.getParameters(); + parameters.encodings[0].scaleResolutionDownBy = 3.0; + + const offerDone = pc1.setRemoteDescription({type: "offer", sdp: simulcastOffer}); + await sender.setParameters(parameters); + await offerDone; + + const {encodings} = sender.getParameters(); + const rids = encodings.map(({rid}) => rid); + isDeeply(rids, ["foo", "bar"]); + is(encodings[0].scaleResolutionDownBy, 2.0); + is(encodings[1].scaleResolutionDownBy, 1.0); + }, + + async function checkCompatModeSimulcastSetParametersSetsSimulcastEnvelope() { + await pushPrefs( + ["media.peerconnection.allow_old_setParameters", true]); + const pc1 = new RTCPeerConnection(); + const stream = await navigator.mediaDevices.getUserMedia({video: true}); + const sender = pc1.addTrack(stream.getTracks()[0]); + const parameters = sender.getParameters(); + parameters.encodings[0].rid = "1"; + parameters.encodings.push({rid: "2"}); + await sender.setParameters(parameters); + + await pc1.setRemoteDescription({type: "offer", sdp: simulcastOffer}); + + const {encodings} = sender.getParameters(); + const rids = encodings.map(({rid}) => rid); + // No overlap in rids -> unicast + isDeeply(rids, [undefined]); + }, + + async function checkCompatModeSimulcastSetParametersRacesLocalUnicastOffer() { + await pushPrefs( + ["media.peerconnection.allow_old_setParameters", true]); + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + const stream = await navigator.mediaDevices.getUserMedia({video: true}); + const sender = pc1.addTrack(stream.getTracks()[0]); + // unicast offer + const offer = await pc1.createOffer(); + const aTask = queueAWebrtcTask(); + const sldPromise = pc1.setLocalDescription(offer); + + // Right now, we have aTask queued. The success task for sLD is not queued + // yet, because Firefox performs the initial steps on the microtask queue, + // which we have not allowed to run yet. Awaiting aTask will first clear + // the microtask queue, then run the task queue until aTask is finished. + // That _should_ result in the success task for sLD(offer) being queued. + await aTask; + + const parameters = sender.getParameters(); + parameters.encodings[0].rid = "foo"; + parameters.encodings.push({rid: "bar"}); + // simulcast setParameters; the task to update [[SendEncodings]] should be + // queued after the success task for sLD(offer) + await sender.setParameters(parameters); + await sldPromise; + + const {encodings} = sender.getParameters(); + const rids = encodings.map(({rid}) => rid); + // Compat mode lets this slide, but won't try to negotiate it since we've + // already applied a unicast local offer. + isDeeply(rids, ["foo", "bar"]); + + // Let negotiation finish, so we can generate a new offer + await pc2.setRemoteDescription(pc1.localDescription); + await pc2.setLocalDescription(); + await pc1.setRemoteDescription(pc2.localDescription); + + const reoffer = await pc1.createOffer(); + ok(!reoffer.sdp.includes("a=simulcast"), "reoffer should be unicast"); + }, + + async function checkCompatModeSimulcastSetParametersRacesRemoteOffer() { + await pushPrefs( + ["media.peerconnection.allow_old_setParameters", true]); + const pc1 = new RTCPeerConnection(); + const stream = await navigator.mediaDevices.getUserMedia({video: true}); + const sender = pc1.addTrack(stream.getTracks()[0]); + const parameters = sender.getParameters(); + parameters.encodings[0].rid = "foo"; + parameters.encodings.push({rid: "bar"}); + const p = sender.setParameters(parameters); + await pc1.setRemoteDescription({type: "offer", sdp: simulcastOffer}); + await p; + const answer = await pc1.createAnswer(); + + const {encodings} = sender.getParameters(); + const rids = encodings.map(({rid}) => rid); + isDeeply(rids, ["foo", "bar"]); + ok(answer.sdp.includes("a=simulcast:send foo;bar"), "answer should be simulcast"); + }, + + async function checkCompatModeSimulcastSetParametersRacesLocalAnswer() { + await pushPrefs( + ["media.peerconnection.allow_old_setParameters", true]); + // We do an initial negotiation, and while the local answer is pending, + // perform a setParameters on a not-yet-negotiated video sender. The intent + // here is to have the success task for sLD(answer) run while the + // setParameters is pending. + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + + const audioStream = await navigator.mediaDevices.getUserMedia({audio: true}); + // We use this later on, but set it up now so we don't inadvertently + // crank the event loop more than we intend below. + const videoStream = await navigator.mediaDevices.getUserMedia({video: true}); + pc2.addTrack(audioStream.getTracks()[0]); + await pc2.setLocalDescription(); + await pc1.setRemoteDescription(pc2.localDescription); + const answer = await pc1.createAnswer(); + const aTask = queueAWebrtcTask(); + const sldPromise = pc1.setLocalDescription(answer); + + // Right now, we have aTask queued. The success task for sLD is not queued + // yet, because Firefox performs the initial steps on the microtask queue, + // which we have not allowed to run yet. Awaiting aTask will first clear + // the microtask queue, then run the task queue until aTask is finished. + // That _should_ result in the success task for sLD(answer) being queued. + await aTask; + + // The success task for sLD(answer) should be queued now. Don't relinquish + // the event loop! + + // New sender that has nothing to do with the negotiation in progress. + const sender = pc1.addTrack(videoStream.getTracks()[0]); + const parameters = sender.getParameters(); + parameters.encodings[0].rid = "foo"; + parameters.encodings.push({rid: "bar"}); + + // We have not relinquished the event loop, so the sLD(answer) should still + // be queued. The task that updates [[SendEncodings]] (from setParameters) + // should be queued behind it. Let them both run. + await sender.setParameters(parameters); + await sldPromise; + + const offer = await pc1.createOffer(); + + const {encodings} = sender.getParameters(); + const rids = encodings.map(({rid}) => rid); + isDeeply(rids, ["foo", "bar"]); + ok(offer.sdp.includes("a=simulcast:send foo;bar"), "offer should be simulcast"); + }, + + async function checkCompatModeSimulcastSetParametersRacesRemoteAnswer() { + await pushPrefs( + ["media.peerconnection.allow_old_setParameters", true]); + // We do an initial negotiation, and while the remote answer is pending, + // perform a setParameters on a not-yet-negotiated video sender. The intent + // here is to have the success task for sRD(answer) run while the + // setParameters is pending. + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + + const audioStream = await navigator.mediaDevices.getUserMedia({audio: true}); + // We use this later on, but set it up now so we don't inadvertently + // crank the event loop more than we intend below. + const videoStream = await navigator.mediaDevices.getUserMedia({video: true}); + pc1.addTrack(audioStream.getTracks()[0]); + await pc1.setLocalDescription(); + await pc2.setRemoteDescription(pc1.localDescription); + await pc2.setLocalDescription(); + const aTask = queueAWebrtcTask(); + const srdPromise = pc1.setRemoteDescription(pc2.localDescription); + + // Right now, we have aTask queued. The success task for sRD is not queued + // yet, because Firefox performs the initial steps on the microtask queue, + // which we have not allowed to run yet. Awaiting aTask will first clear + // the microtask queue, then run the task queue until aTask is finished. + // That _should_ result in the success task for sRD(answer) being queued. + await aTask; + + // The success task for sRD(answer) should be queued now. Don't relinquish + // the event loop! + + const sender = pc1.addTrack(videoStream.getTracks()[0]); + const parameters = sender.getParameters(); + parameters.encodings[0].rid = "foo"; + parameters.encodings.push({rid: "bar"}); + + // We have not relinquished the event loop, so the sRD(answer) should still + // be queued. The task that updates [[SendEncodings]] (from setParameters) + // should be queued behind it. Let them both run. + await sender.setParameters(parameters); + await srdPromise; + + const offer = await pc1.createOffer(); + + const {encodings} = sender.getParameters(); + const rids = encodings.map(({rid}) => rid); + isDeeply(rids, ["foo", "bar"]); + ok(offer.sdp.includes("a=simulcast:send foo;bar"), "offer should be simulcast"); + }, + + async function checkCompatModeSimulcastRidlessSetParametersRacesLocalOffer() { + await pushPrefs( + ["media.peerconnection.allow_old_setParameters", true]); + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + const stream = await navigator.mediaDevices.getUserMedia({video: true}); + const sender = pc1.addTrack(stream.getTracks()[0]); + // unicast offer + const aTask = queueAWebrtcTask(); + const sldPromise = pc1.setLocalDescription(); + + // Right now, we have aTask queued. The success task for sLD is not queued + // yet, because Firefox performs the initial steps on the microtask queue, + // which we have not allowed to run yet. Awaiting aTask will first clear + // the microtask queue, then run the task queue until aTask is finished. + // That _should_ result in the success task for sLD(offer) being queued. + await aTask; + + // simulcast setParameters; the task to update [[SendEncodings]] should be + // queued after the success task for sLD(offer) + try { + await sender.setParameters({"encodings": [{}, {}]}); + ok(false, "setParameters with two ridless encodings should fail"); + } catch (e) { + ok(true, "setParameters with two ridless encodings should fail"); + } + await sldPromise; + + const {encodings} = sender.getParameters(); + const rids = encodings.map(({rid}) => rid); + // Compat mode lets this slide, but won't try to negotiate it since we've + // already applied a unicast local offer. + isDeeply(rids, [undefined]); + + // Let negotiation finish, so we can generate a new offer + await pc2.setRemoteDescription(pc1.localDescription); + await pc2.setLocalDescription(); + await pc1.setRemoteDescription(pc2.localDescription); + + const reoffer = await pc1.createOffer(); + ok(!reoffer.sdp.includes("a=simulcast"), "reoffer should be unicast"); + }, + +]; + +runNetworkTest(async () => { + await pushPrefs( + ["media.peerconnection.allow_old_setParameters", false]); + for (const test of tests) { + info(`Running test: ${test.name}`); + await test(); + info(`Done running test: ${test.name}`); + } +}); + +</script> +</pre> +</body> +</html> |