tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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>