From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../tests/webrtc/simulcast/basic.https.html | 23 + .../tests/webrtc/simulcast/getStats.https.html | 34 ++ .../tests/webrtc/simulcast/h264.https.html | 31 ++ .../simulcast/negotiation-encodings.https.html | 534 +++++++++++++++++++++ .../tests/webrtc/simulcast/rid-manipulation.html | 39 ++ .../simulcast/setParameters-active.https.html | 106 ++++ .../simulcast/setParameters-encodings.https.html | 463 ++++++++++++++++++ .../tests/webrtc/simulcast/simulcast.js | 280 +++++++++++ .../tests/webrtc/simulcast/vp8.https.html | 26 + .../simulcast/vp9-scalability-mode.https.html | 35 ++ .../tests/webrtc/simulcast/vp9.https.html | 26 + 11 files changed, 1597 insertions(+) create mode 100644 testing/web-platform/tests/webrtc/simulcast/basic.https.html create mode 100644 testing/web-platform/tests/webrtc/simulcast/getStats.https.html create mode 100644 testing/web-platform/tests/webrtc/simulcast/h264.https.html create mode 100644 testing/web-platform/tests/webrtc/simulcast/negotiation-encodings.https.html create mode 100644 testing/web-platform/tests/webrtc/simulcast/rid-manipulation.html create mode 100644 testing/web-platform/tests/webrtc/simulcast/setParameters-active.https.html create mode 100644 testing/web-platform/tests/webrtc/simulcast/setParameters-encodings.https.html create mode 100644 testing/web-platform/tests/webrtc/simulcast/simulcast.js create mode 100644 testing/web-platform/tests/webrtc/simulcast/vp8.https.html create mode 100644 testing/web-platform/tests/webrtc/simulcast/vp9-scalability-mode.https.html create mode 100644 testing/web-platform/tests/webrtc/simulcast/vp9.https.html (limited to 'testing/web-platform/tests/webrtc/simulcast') diff --git a/testing/web-platform/tests/webrtc/simulcast/basic.https.html b/testing/web-platform/tests/webrtc/simulcast/basic.https.html new file mode 100644 index 0000000000..f7b9def762 --- /dev/null +++ b/testing/web-platform/tests/webrtc/simulcast/basic.https.html @@ -0,0 +1,23 @@ + + +RTCPeerConnection Simulcast Tests + + + + + + + + + + diff --git a/testing/web-platform/tests/webrtc/simulcast/getStats.https.html b/testing/web-platform/tests/webrtc/simulcast/getStats.https.html new file mode 100644 index 0000000000..b5a9e6eb28 --- /dev/null +++ b/testing/web-platform/tests/webrtc/simulcast/getStats.https.html @@ -0,0 +1,34 @@ + + +RTCPeerConnection Simulcast Tests - getStats + + + + + + + + + + diff --git a/testing/web-platform/tests/webrtc/simulcast/h264.https.html b/testing/web-platform/tests/webrtc/simulcast/h264.https.html new file mode 100644 index 0000000000..038449aa6e --- /dev/null +++ b/testing/web-platform/tests/webrtc/simulcast/h264.https.html @@ -0,0 +1,31 @@ + + +RTCPeerConnection Simulcast Tests + + + + + + + + + + diff --git a/testing/web-platform/tests/webrtc/simulcast/negotiation-encodings.https.html b/testing/web-platform/tests/webrtc/simulcast/negotiation-encodings.https.html new file mode 100644 index 0000000000..c16e2674b0 --- /dev/null +++ b/testing/web-platform/tests/webrtc/simulcast/negotiation-encodings.https.html @@ -0,0 +1,534 @@ + + +RTCPeerConnection Simulcast Tests - negotiation/encodings + + + + + + + + + + diff --git a/testing/web-platform/tests/webrtc/simulcast/rid-manipulation.html b/testing/web-platform/tests/webrtc/simulcast/rid-manipulation.html new file mode 100644 index 0000000000..a88506305a --- /dev/null +++ b/testing/web-platform/tests/webrtc/simulcast/rid-manipulation.html @@ -0,0 +1,39 @@ + + +RTCPeerConnection Simulcast Tests - RID manipulation + + + + + + + + + + diff --git a/testing/web-platform/tests/webrtc/simulcast/setParameters-active.https.html b/testing/web-platform/tests/webrtc/simulcast/setParameters-active.https.html new file mode 100644 index 0000000000..54191059a0 --- /dev/null +++ b/testing/web-platform/tests/webrtc/simulcast/setParameters-active.https.html @@ -0,0 +1,106 @@ + + +RTCPeerConnection Simulcast Tests - setParameters/active + + + + + + + + + + diff --git a/testing/web-platform/tests/webrtc/simulcast/setParameters-encodings.https.html b/testing/web-platform/tests/webrtc/simulcast/setParameters-encodings.https.html new file mode 100644 index 0000000000..86274a0c5a --- /dev/null +++ b/testing/web-platform/tests/webrtc/simulcast/setParameters-encodings.https.html @@ -0,0 +1,463 @@ + + +RTCPeerConnection Simulcast Tests - setParameters/encodings + + + + + + + + + + diff --git a/testing/web-platform/tests/webrtc/simulcast/simulcast.js b/testing/web-platform/tests/webrtc/simulcast/simulcast.js new file mode 100644 index 0000000000..e0b90d8ac3 --- /dev/null +++ b/testing/web-platform/tests/webrtc/simulcast/simulcast.js @@ -0,0 +1,280 @@ +'use strict'; +/* Helper functions to munge SDP and split the sending track into + * separate tracks on the receiving end. This can be done in a number + * of ways, the one used here uses the fact that the MID and RID header + * extensions which are used for packet routing share the same wire + * format. The receiver interprets the rids from the sender as mids + * which allows receiving the different spatial resolutions on separate + * m-lines and tracks. + */ + +const ridExtensions = [ + 'urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id', + 'urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id', +]; + +function ridToMid(description, rids) { + const sections = SDPUtils.splitSections(description.sdp); + const dtls = SDPUtils.getDtlsParameters(sections[1], sections[0]); + const ice = SDPUtils.getIceParameters(sections[1], sections[0]); + const rtpParameters = SDPUtils.parseRtpParameters(sections[1]); + const setupValue = SDPUtils.matchPrefix(description.sdp, 'a=setup:')[0].substring(8); + const direction = SDPUtils.getDirection(sections[1]); + const mline = SDPUtils.parseMLine(sections[1]); + + // Skip mid extension; we are replacing it with the rid extmap + rtpParameters.headerExtensions = rtpParameters.headerExtensions.filter( + ext => ext.uri !== 'urn:ietf:params:rtp-hdrext:sdes:mid' + ); + + for (const ext of rtpParameters.headerExtensions) { + if (ext.uri === 'urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id') { + ext.uri = 'urn:ietf:params:rtp-hdrext:sdes:mid'; + } + } + + // Filter rtx as we have no way to (re)interpret rrid. + // Not doing this makes probing use RTX, it's not understood and ramp-up is slower. + rtpParameters.codecs = rtpParameters.codecs.filter(c => c.name.toUpperCase() !== 'RTX'); + if (!rids) { + rids = SDPUtils.matchPrefix(sections[1], 'a=rid:') + .filter(line => line.endsWith(' send')) + .map(line => line.substring(6).split(' ')[0]); + } + + let sdp = SDPUtils.writeSessionBoilerplate() + + SDPUtils.writeDtlsParameters(dtls, setupValue) + + SDPUtils.writeIceParameters(ice) + + 'a=group:BUNDLE ' + rids.join(' ') + '\r\n' + + 'a=msid-semantic: WMS *\r\n'; + const baseRtpDescription = SDPUtils.writeRtpDescription(mline.kind, rtpParameters); + for (const rid of rids) { + sdp += baseRtpDescription + + 'a=mid:' + rid + '\r\n' + + 'a=msid:rid-' + rid + ' rid-' + rid + '\r\n'; + sdp += 'a=' + direction + '\r\n'; + } + return sdp; +} + +function midToRid(description, localDescription, rids) { + const sections = SDPUtils.splitSections(description.sdp); + const dtls = SDPUtils.getDtlsParameters(sections[1], sections[0]); + const ice = SDPUtils.getIceParameters(sections[1], sections[0]); + const rtpParameters = SDPUtils.parseRtpParameters(sections[1]); + const setupValue = description.sdp.match(/a=setup:(.*)/)[1]; + const direction = SDPUtils.getDirection(sections[1]); + const mline = SDPUtils.parseMLine(sections[1]); + + // Skip rid extensions; we are replacing them with the mid extmap + rtpParameters.headerExtensions = rtpParameters.headerExtensions.filter( + ext => !ridExtensions.includes(ext.uri) + ); + + for (const ext of rtpParameters.headerExtensions) { + if (ext.uri === 'urn:ietf:params:rtp-hdrext:sdes:mid') { + ext.uri = 'urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id'; + } + } + + const localMid = localDescription ? SDPUtils.getMid(SDPUtils.splitSections(localDescription.sdp)[1]) : '0'; + if (localDescription) { + const localVideoSection = SDPUtils.splitSections(localDescription.sdp)[1]; + const localParameters = SDPUtils.parseRtpParameters(localVideoSection); + + const localMidExtension = localParameters.headerExtensions + .find(ext => ext.uri === 'urn:ietf:params:rtp-hdrext:sdes:mid'); + if (localMidExtension) { + rtpParameters.headerExtensions.push(localMidExtension); + } + } else { + // Find unused id in remote description to formally have a mid. + for (let id = 1; id < 15; id++) { + if (rtpParameters.headerExtensions.find(ext => ext.id === id) === undefined) { + rtpParameters.headerExtensions.push( + {id, uri: 'urn:ietf:params:rtp-hdrext:sdes:mid'}); + break; + } + } + } + + if (!rids) { + rids = []; + for (let i = 1; i < sections.length; i++) { + rids.push(SDPUtils.getMid(sections[i])); + } + } + + let sdp = SDPUtils.writeSessionBoilerplate() + + SDPUtils.writeDtlsParameters(dtls, setupValue) + + SDPUtils.writeIceParameters(ice) + + 'a=group:BUNDLE ' + localMid + '\r\n' + + 'a=msid-semantic: WMS *\r\n'; + sdp += SDPUtils.writeRtpDescription(mline.kind, rtpParameters); + // Although we are converting mids to rids, we still need a mid. + // The first one will be consistent with trickle ICE candidates. + sdp += 'a=mid:' + localMid + '\r\n'; + sdp += 'a=' + direction + '\r\n'; + + for (const rid of rids) { + const stringrid = String(rid); // allow integers + const choices = stringrid.split(','); + choices.forEach(choice => { + sdp += 'a=rid:' + choice + ' recv\r\n'; + }); + } + if (rids.length) { + sdp += 'a=simulcast:recv ' + rids.join(';') + '\r\n'; + } + + return sdp; +} + +async function doOfferToSendSimulcast(offerer, answerer) { + await offerer.setLocalDescription(); + + // Is this a renegotiation? If so, we cannot remove (or reorder!) any mids, + // even if some rids have been removed or reordered. + let mids = []; + if (answerer.localDescription) { + // Renegotiation. Mids must be the same as before, because renegotiation + // can never remove or reorder mids, nor can it expand the simulcast + // envelope. + const sections = SDPUtils.splitSections(answerer.localDescription.sdp); + sections.shift(); + mids = sections.map(section => SDPUtils.getMid(section)); + } else { + // First negotiation; the mids will be exactly the same as the rids + const simulcastAttr = SDPUtils.matchPrefix(offerer.localDescription.sdp, + 'a=simulcast:send ')[0]; + if (simulcastAttr) { + mids = simulcastAttr.split(' ')[1].split(';'); + } + } + + const nonSimulcastOffer = ridToMid(offerer.localDescription, mids); + await answerer.setRemoteDescription({ + type: 'offer', + sdp: nonSimulcastOffer, + }); +} + +async function doAnswerToRecvSimulcast(offerer, answerer, rids) { + await answerer.setLocalDescription(); + const simulcastAnswer = midToRid( + answerer.localDescription, + offerer.localDescription, + rids + ); + await offerer.setRemoteDescription({ type: 'answer', sdp: simulcastAnswer }); +} + +async function doOfferToRecvSimulcast(offerer, answerer, rids) { + await offerer.setLocalDescription(); + const simulcastOffer = midToRid( + offerer.localDescription, + answerer.localDescription, + rids + ); + await answerer.setRemoteDescription({ type: 'offer', sdp: simulcastOffer }); +} + +async function doAnswerToSendSimulcast(offerer, answerer) { + await answerer.setLocalDescription(); + + // See which mids the offerer had; it will barf if we remove or reorder them. + const sections = SDPUtils.splitSections(offerer.localDescription.sdp); + sections.shift(); + const mids = sections.map(section => SDPUtils.getMid(section)); + let nonSimulcastAnswer = ridToMid(answerer.localDescription, mids); + // Restore MID RTP header extension. + const localParameters = SDPUtils.parseRtpParameters(sections[0]); + + const localMidExtension = localParameters.headerExtensions + .find(ext => ext.uri === 'urn:ietf:params:rtp-hdrext:sdes:mid'); + if (localMidExtension) { + nonSimulcastAnswer += SDPUtils.writeExtmap(localMidExtension); + } + await offerer.setRemoteDescription({ + type: 'answer', + sdp: nonSimulcastAnswer, + }); +} + +async function doOfferToSendSimulcastAndAnswer(offerer, answerer, rids) { + await doOfferToSendSimulcast(offerer, answerer); + await doAnswerToRecvSimulcast(offerer, answerer, rids); +} + +async function doOfferToRecvSimulcastAndAnswer(offerer, answerer, rids) { + await doOfferToRecvSimulcast(offerer, answerer, rids); + await doAnswerToSendSimulcast(offerer, answerer); +} + +function swapRidAndMidExtensionsInSimulcastOffer(offer, rids) { + return ridToMid(offer, rids); +} + +function swapRidAndMidExtensionsInSimulcastAnswer(answer, localDescription, rids) { + return midToRid(answer, localDescription, rids); +} + +async function negotiateSimulcastAndWaitForVideo( + t, rids, pc1, pc2, codec, scalabilityMode = undefined) { + exchangeIceCandidates(pc1, pc2); + + const metadataToBeLoaded = []; + pc2.ontrack = (e) => { + const stream = e.streams[0]; + const v = document.createElement('video'); + v.autoplay = true; + v.srcObject = stream; + v.id = stream.id + metadataToBeLoaded.push(new Promise((resolve) => { + v.addEventListener('loadedmetadata', () => { + resolve(); + }); + })); + }; + + const sendEncodings = rids.map(rid => ({rid})); + // Use a 2X downscale factor between each layer. To improve ramp-up time, the + // top layer is scaled down by a factor 2. Smaller layer comes first. For + // example if MediaStreamTrack is 720p and we want to send three layers we'll + // get {90p, 180p, 360p}. + let scaleResolutionDownBy = 2; + for (let i = sendEncodings.length - 1; i >= 0; --i) { + if (scalabilityMode) { + sendEncodings[i].scalabilityMode = scalabilityMode; + } + sendEncodings[i].scaleResolutionDownBy = scaleResolutionDownBy; + scaleResolutionDownBy *= 2; + } + + // Use getUserMedia as getNoiseStream does not have enough entropy to ramp-up. + await setMediaPermission(); + const stream = await navigator.mediaDevices.getUserMedia({video: {width: 1280, height: 720}}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const transceiver = pc1.addTransceiver(stream.getVideoTracks()[0], { + streams: [stream], + sendEncodings: sendEncodings, + }); + if (codec) { + preferCodec(transceiver, codec.mimeType, codec.sdpFmtpLine); + } + + const offer = await pc1.createOffer(); + await pc1.setLocalDescription(offer), + await pc2.setRemoteDescription({ + type: 'offer', + sdp: swapRidAndMidExtensionsInSimulcastOffer(offer, rids), + }); + const answer = await pc2.createAnswer(); + await pc2.setLocalDescription(answer); + await pc1.setRemoteDescription({ + type: 'answer', + sdp: swapRidAndMidExtensionsInSimulcastAnswer(answer, pc1.localDescription, rids), + }); + assert_equals(metadataToBeLoaded.length, rids.length); + return Promise.all(metadataToBeLoaded); +} diff --git a/testing/web-platform/tests/webrtc/simulcast/vp8.https.html b/testing/web-platform/tests/webrtc/simulcast/vp8.https.html new file mode 100644 index 0000000000..3d04bc7172 --- /dev/null +++ b/testing/web-platform/tests/webrtc/simulcast/vp8.https.html @@ -0,0 +1,26 @@ + + +RTCPeerConnection Simulcast Tests + + + + + + + + + + diff --git a/testing/web-platform/tests/webrtc/simulcast/vp9-scalability-mode.https.html b/testing/web-platform/tests/webrtc/simulcast/vp9-scalability-mode.https.html new file mode 100644 index 0000000000..9dc8a3103d --- /dev/null +++ b/testing/web-platform/tests/webrtc/simulcast/vp9-scalability-mode.https.html @@ -0,0 +1,35 @@ + + +RTCPeerConnection Simulcast Tests + + + + + + + + + + diff --git a/testing/web-platform/tests/webrtc/simulcast/vp9.https.html b/testing/web-platform/tests/webrtc/simulcast/vp9.https.html new file mode 100644 index 0000000000..a033dab477 --- /dev/null +++ b/testing/web-platform/tests/webrtc/simulcast/vp9.https.html @@ -0,0 +1,26 @@ + + +RTCPeerConnection Simulcast Tests + + + + + + + + + + -- cgit v1.2.3