RTCPeerConnection-active-inactive-transceivers-bundle-no-mid-no-ssrcs.https.html (3917B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <meta name="timeout" content="long"> 4 <title>Regression test for bug 1965831: That RTCPeerConnection media flows with two transceivers (one inactive, one active) of the same type, same payload types, BUNDLE, no MID RTP header extension and no a=ssrc lines.</title> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="/webrtc/RTCPeerConnection-helper.js"></script> 8 <script> 9 'use strict'; 10 11 // Munge away the MID RTP header extension to force packet filtering on PTs. 12 // Note this is in violation of RFC8843 9.1, but some sites rely on this. 13 const midExtmap = /\r\na=extmap:\d+ urn:ietf:params:rtp-hdrext:sdes:mid/g; 14 // Munge away a=ssrc (and a=ssrc-group) lines so conduits are willing to 15 // switch SSRC binding based on packets (bug 1756222). 16 const ssrc = /\r\na=ssrc:\d+ cname:\{[a-f0-9\-]+\}/g; 17 const fecBinding = /\r\na=ssrc-group:FID \d+ \d+/g; 18 function mungeSdp({sdp}) { 19 return sdp.replace(midExtmap, "") 20 .replace(ssrc, "") 21 .replace(fecBinding, ""); 22 } 23 24 async function doTest(t, kind) { 25 const pc1 = new RTCPeerConnection(); 26 const pc2 = new RTCPeerConnection(); 27 t.add_cleanup(() => pc1.close()); 28 t.add_cleanup(() => pc2.close()); 29 30 exchangeIceCandidates(pc1, pc2); 31 32 // Add a transceiver that will have unique payload types bound to it. 33 pc1.addTransceiver(kind, {direction: "recvonly"}); 34 35 // Negotiate to bind unique payload types to the first transceiver. 36 const offer1 = {sdp: mungeSdp(await pc1.createOffer()), type: "offer"}; 37 await pc1.setLocalDescription(offer1); 38 await pc2.setRemoteDescription(offer1); 39 // Munge the answer too, or a=ssrc lines will be inserted. 40 const answer1 = {sdp: mungeSdp(await pc2.createAnswer()), type: "answer"}; 41 await pc2.setLocalDescription(answer1); 42 await pc1.setRemoteDescription(answer1); 43 44 // Inactivate the transceiver, which will cache its unique payload types, 45 // to "steal" packets from the other transceiver. 46 const [transceiver1] = pc2.getTransceivers(); 47 transceiver1.direction = "inactive"; 48 49 // Add another transceiver that will send some packets. 50 const stream = await getNoiseStream({[kind]: true}); 51 const [track] = stream.getTracks(); 52 t.add_cleanup(() => track.stop()); 53 const transceiver2 = pc2.addTransceiver(track, {direction: "sendonly"}); 54 55 // Renegotiate. 56 const offer2 = {sdp: mungeSdp(await pc2.createOffer()), type: "offer"}; 57 await pc2.setLocalDescription(offer2); 58 await pc1.setRemoteDescription(offer2); 59 const answer2 = {sdp: mungeSdp(await pc1.createAnswer()), type: "answer"}; 60 await pc1.setLocalDescription(answer2); 61 await pc2.setRemoteDescription(answer2); 62 63 const {receiver} = pc1.getTransceivers().find(t => t.currentDirection == "recvonly"); 64 const inactiveTranceiver = pc1.getTransceivers().find(t => t.currentDirection == "inactive"); 65 assert_not_equals(inactiveTranceiver, undefined); 66 67 let timedout = false; 68 t.step_timeout(() => timedout = true, 10000); 69 const threshold = 10; 70 let inboundStats; 71 while (!timedout) { 72 const stats = await receiver.getStats(); 73 inboundStats = [...stats.values()].find(({type}) => type == "inbound-rtp"); 74 if (inboundStats?.packetsReceived > threshold) { 75 break; 76 } 77 await new Promise(r => t.step_timeout(r, 50)); 78 } 79 assert_greater_than( 80 inboundStats?.packetsReceived, 81 threshold, 82 "packets received indicates media flow" 83 ); 84 } 85 86 promise_test(async t => { 87 await doTest(t, "video"); 88 }, "Video flows to RTCPeerConnection's active transceiver, with BUNDLE but without MID extension"); 89 90 promise_test(async t => { 91 await doTest(t, "audio"); 92 }, "Audio flows to RTCPeerConnection's active transceiver, with BUNDLE but without MID extension"); 93 94 </script>