Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
- This WPT test may be referenced by the following Test IDs:
            - /webrtc/RTCPeerConnection-active-inactive-transceivers-bundle-no-mid-no-ssrcs.https.html - WPT Dashboard Interop Dashboard
 
<!doctype html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<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>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webrtc/RTCPeerConnection-helper.js"></script>
<script>
  'use strict';
  // Munge away the MID RTP header extension to force packet filtering on PTs.
  // Note this is in violation of RFC8843 9.1, but some sites rely on this.
  const midExtmap = /\r\na=extmap:\d+ urn:ietf:params:rtp-hdrext:sdes:mid/g;
  // Munge away a=ssrc (and a=ssrc-group) lines so conduits are willing to
  const ssrc = /\r\na=ssrc:\d+ cname:\{[a-f0-9\-]+\}/g;
  const fecBinding = /\r\na=ssrc-group:FID \d+ \d+/g;
  function mungeSdp({sdp}) {
    return sdp.replace(midExtmap, "")
              .replace(ssrc, "")
              .replace(fecBinding, "");
  }
  async function doTest(t, kind) {
    const pc1 = new RTCPeerConnection();
    const pc2 = new RTCPeerConnection();
    t.add_cleanup(() => pc1.close());
    t.add_cleanup(() => pc2.close());
    exchangeIceCandidates(pc1, pc2);
    // Add a transceiver that will have unique payload types bound to it.
    pc1.addTransceiver(kind, {direction: "recvonly"});
    // Negotiate to bind unique payload types to the first transceiver.
    const offer1 = {sdp: mungeSdp(await pc1.createOffer()), type: "offer"};
    await pc1.setLocalDescription(offer1);
    await pc2.setRemoteDescription(offer1);
    // Munge the answer too, or a=ssrc lines will be inserted.
    const answer1 = {sdp: mungeSdp(await pc2.createAnswer()), type: "answer"};
    await pc2.setLocalDescription(answer1);
    await pc1.setRemoteDescription(answer1);
    // Inactivate the transceiver, which will cache its unique payload types,
    // to "steal" packets from the other transceiver.
    const [transceiver1] = pc2.getTransceivers();
    transceiver1.direction = "inactive";
    // Add another transceiver that will send some packets.
    const stream = await getNoiseStream({[kind]: true});
    const [track] = stream.getTracks();
    t.add_cleanup(() => track.stop());
    const transceiver2 = pc2.addTransceiver(track, {direction: "sendonly"});
    // Renegotiate.
    const offer2 = {sdp: mungeSdp(await pc2.createOffer()), type: "offer"};
    await pc2.setLocalDescription(offer2);
    await pc1.setRemoteDescription(offer2);
    const answer2 = {sdp: mungeSdp(await pc1.createAnswer()), type: "answer"};
    await pc1.setLocalDescription(answer2);
    await pc2.setRemoteDescription(answer2);
    const {receiver} = pc1.getTransceivers().find(t => t.currentDirection == "recvonly");
    const inactiveTranceiver = pc1.getTransceivers().find(t => t.currentDirection == "inactive");
    assert_not_equals(inactiveTranceiver, undefined);
    let timedout = false;
    t.step_timeout(() => timedout = true, 10000);
    const threshold = 10;
    let inboundStats;
    while (!timedout) {
      const stats = await receiver.getStats();
      inboundStats = [...stats.values()].find(({type}) => type == "inbound-rtp");
      if (inboundStats?.packetsReceived > threshold) {
        break;
      }
      await new Promise(r => t.step_timeout(r, 50));
    }
    assert_greater_than(
      inboundStats?.packetsReceived,
      threshold,
      "packets received indicates media flow"
    );
  }
  promise_test(async t => {
    await doTest(t, "video");
  }, "Video flows to RTCPeerConnection's active transceiver, with BUNDLE but without MID extension");
  promise_test(async t => {
    await doTest(t, "audio");
  }, "Audio flows to RTCPeerConnection's active transceiver, with BUNDLE but without MID extension");
</script>