tor-browser

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

test_peerConnection_simulcastOffer_screenshare.html (6916B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <head>
      4  <script type="application/javascript" src="pc.js"></script>
      5  <script type="application/javascript" src="parser_rtp.js"></script>
      6  <script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
      7  <script type="application/javascript" src="helpers_from_wpt/sdp.js"></script>
      8  <script type="application/javascript" src="simulcast.js"></script>
      9  <script type="application/javascript" src="stats.js"></script>
     10 </head>
     11 <body>
     12 <pre id="test">
     13 <script type="application/javascript">
     14  createHTML({
     15    bug: "1692873",
     16    title: "Screensharing peer connection with Simulcast offer",
     17    visible: true
     18  });
     19 
     20  async function hasH264() {
     21    const pc = new RTCPeerConnection();
     22    const offer = await pc.createOffer({offerToReceiveVideo: true});
     23    return offer.sdp.match(/a=rtpmap:[1-9][0-9]* H264/);
     24  }
     25 
     26  async function doTest(codec) {
     27    const recvCodecs = RTCRtpReceiver.getCapabilities("video")?.codecs;
     28    isnot(recvCodecs, null, "Expected recv capabilities");
     29    isnot(recvCodecs.length, 0, "Expected some recv codecs");
     30    if (!recvCodecs || !recvCodecs.length) {
     31      return;
     32    }
     33 
     34    const filteredRecvCodecs = recvCodecs.filter(recvCodec => {
     35      if (recvCodec.mimeType != codec.mimeType) {
     36        return false;
     37      }
     38      if (codec.sdpFmtpLineRegex && !recvCodec.sdpFmtpLine.match(codec.sdpFmtpLineRegex)) {
     39        return false;
     40      }
     41      return true;
     42    });
     43    is(
     44      filteredRecvCodecs.length,
     45      1,
     46      `Should match a single recv codec\nOriginal recv codecs:\n${JSON.stringify(
     47        recvCodecs,
     48        null,
     49        2
     50      )}\nFiltered recv codecs:\n${JSON.stringify(
     51        filteredRecvCodecs,
     52        null,
     53        2
     54      )}\nRequired codec: ${JSON.stringify(codec)}`
     55    );
     56    if (!filteredRecvCodecs.length) {
     57      return;
     58 
     59    }
     60    const [recvCodec] = filteredRecvCodecs;
     61 
     62    const offerer = new RTCPeerConnection();
     63    const answerer = new RTCPeerConnection();
     64 
     65    const add = (pc, can, failed) => can && pc.addIceCandidate(can).catch(failed);
     66    offerer.onicecandidate = e => add(answerer, e.candidate, generateErrorCallback());
     67    answerer.onicecandidate = e => add(offerer, e.candidate, generateErrorCallback());
     68 
     69    const metadataToBeLoaded = [];
     70    answerer.ontrack = e => {
     71      metadataToBeLoaded.push(getPlaybackWithLoadedMetadata(e.track));
     72    };
     73 
     74    // One send transceiver, that will be used to send both simulcast streams
     75    SpecialPowers.wrap(document).notifyUserGestureActivation();
     76    const videoStream = await navigator.mediaDevices.getDisplayMedia({video: {width: {max: 640}}});
     77    const sendEncodings = [
     78            { rid: '0' },
     79            { rid: '1', maxBitrate: 100000, scaleResolutionDownBy: 2 },
     80            { rid: '2', maxBitrate: 40000, scaleResolutionDownBy: 2 }
     81          ];
     82    offerer.addTransceiver(videoStream.getVideoTracks()[0], {sendEncodings});
     83 
     84    const [sender] = offerer.getSenders();
     85 
     86    const offer = await offerer.createOffer();
     87 
     88    const mungedOffer = ridToMid(offer);
     89    info(`Transformed send simulcast offer to multiple m-sections: ${offer.sdp} to ${mungedOffer}`);
     90 
     91    await answerer.setRemoteDescription({type: 'offer', sdp: mungedOffer});
     92    await offerer.setLocalDescription(offer);
     93 
     94    const recvTransceivers = answerer.getTransceivers();
     95    const rids = recvTransceivers.map(({mid}) => mid);
     96    is(rids.length, 3, 'Should have 3 mids in offer');
     97    isnot(rids[0], '', 'First mid should be non-empty');
     98    isnot(rids[1], '', 'Second mid should be non-empty');
     99    isnot(rids[2], '', 'Third mid should be non-empty');
    100 
    101    for (const transceiver of recvTransceivers) {
    102      transceiver.setCodecPreferences([recvCodec]);
    103    }
    104 
    105    const answer = await answerer.createAnswer();
    106 
    107    let mungedAnswer = midToRid(answer);
    108    info(`Transformed recv answer to simulcast: ${answer.sdp} to ${mungedAnswer}`);
    109    await offerer.setRemoteDescription({type: 'answer', sdp: mungedAnswer});
    110    await answerer.setLocalDescription(answer);
    111 
    112    is(metadataToBeLoaded.length, 3, 'Offerer should have gotten 3 ontrack events');
    113    info('Waiting for 3 loadedmetadata events');
    114    const videoElems = await Promise.all(metadataToBeLoaded);
    115 
    116    const statsReady =
    117      Promise.all([waitForSyncedRtcp(offerer), waitForSyncedRtcp(answerer)]);
    118 
    119    const {width} = videoStream.getVideoTracks()[0].getSettings();
    120    const {height} = videoStream.getVideoTracks()[0].getSettings();
    121    is(videoElems[0].videoWidth, width,
    122       "sink is same width as source, modulo our cropping algorithm");
    123    is(videoElems[0].videoHeight, height,
    124       "sink is same height as source, modulo our cropping algorithm");
    125    is(videoElems[1].videoWidth, Math.trunc(width / 2),
    126       "sink is 1/2 width of source, modulo our cropping algorithm");
    127    is(videoElems[1].videoHeight, Math.trunc(height / 2),
    128       "sink is 1/2 height of source, modulo our cropping algorithm");
    129    is(videoElems[2].videoWidth, Math.trunc(width / 2),
    130       "sink is 1/2 width of source, modulo our cropping algorithm");
    131    is(videoElems[2].videoHeight, Math.trunc(height / 2),
    132       "sink is 1/2 height of source, modulo our cropping algorithm");
    133 
    134    await statsReady;
    135    const senderStats = await sender.getStats();
    136    checkSendCodecsMimeType(senderStats, codec.mimeType, recvCodec.sdpFmtpLine);
    137    checkSenderStats(senderStats, 3);
    138    checkExpectedFields(senderStats);
    139    pedanticChecks(senderStats);
    140 
    141    videoStream.getVideoTracks()[0].stop();
    142    offerer.close();
    143    answerer.close();
    144    for (const elem of videoElems) {
    145      elem.remove();
    146    }
    147  }
    148 
    149  runNetworkTest(async () => {
    150    await pushPrefs(
    151        // 400Kbps was determined empirically, set well-higher than
    152        // the 140Kbps+overhead needed for the two restricted simulcast streams.
    153        ['media.peerconnection.video.min_bitrate_estimate', 400*1000],
    154        ["media.navigator.permission.disabled", true],
    155        ["media.peerconnection.video.lock_scaling", true],
    156        ["media.webrtc.simulcast.vp9.enabled", true],
    157        ["media.webrtc.simulcast.av1.enabled", true],
    158        ["media.webrtc.codec.video.av1.enabled", true],
    159        ["media.navigator.video.disable_h264_baseline", false],
    160      );
    161 
    162    const codecs = [
    163      {mimeType: "video/VP8"},
    164      {mimeType: "video/H264", sdpFmtpLineRegex: /profile-level-id=42e01f.*packetization-mode=1/},
    165      {mimeType: "video/H264", sdpFmtpLineRegex: /profile-level-id=42e01f.*asymmetry-allowed=1$/},
    166      {mimeType: "video/H264", sdpFmtpLineRegex: /profile-level-id=42001f.*packetization-mode=1/},
    167      {mimeType: "video/H264", sdpFmtpLineRegex: /profile-level-id=42001f.*asymmetry-allowed=1$/},
    168      {mimeType: "video/VP9"},
    169      {mimeType: "video/AV1"},
    170    ];
    171 
    172    for (const codec of codecs) {
    173      info(`Testing codec ${codec.mimeType}`)
    174      await doTest(codec);
    175    }
    176  });
    177 </script>
    178 </pre>
    179 </body>
    180 </html>