tor-browser

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

RTCPeerConnection-insertable-streams-video.https.html (6825B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4 <title>RTCPeerConnection Insertable Streams - Video</title>
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 <script src=/resources/testdriver.js></script>
      8 <script src=/resources/testdriver-vendor.js></script>
      9 <script src='../../mediacapture-streams/permission-helper.js'></script>
     10 <script src="../../webrtc/RTCPeerConnection-helper.js"></script>
     11 <script src="./RTCPeerConnection-insertable-streams.js"></script>
     12 </head>
     13 <body>
     14 <script>
     15 async function testVideoFlow(t, negotiationFunction, setConstructorParam, frameCallback = () => {}) {
     16  const caller = new RTCPeerConnection(setConstructorParam ? {encodedInsertableStreams:true} : {});
     17  t.add_cleanup(() => caller.close());
     18  const callee = new RTCPeerConnection(setConstructorParam ? {encodedInsertableStreams:true} : {});
     19  t.add_cleanup(() => callee.close());
     20 
     21  await setMediaPermission("granted", ["camera"]);
     22  const stream = await navigator.mediaDevices.getUserMedia({video:true});
     23  const videoTrack = stream.getVideoTracks()[0];
     24  t.add_cleanup(() => videoTrack.stop());
     25 
     26  const videoSender = caller.addTrack(videoTrack)
     27  const senderStreams = videoSender.createEncodedStreams();
     28  const senderReader = senderStreams.readable.getReader();
     29  const senderWriter = senderStreams.writable.getWriter();
     30 
     31  const frameInfos = [];
     32  const numFramesPassthrough = 5;
     33  const numFramesReplaceData = 5;
     34  const numFramesModifyData = 5;
     35  const numFramesToSend = numFramesPassthrough + numFramesReplaceData + numFramesModifyData;
     36 
     37  let streamsCreatedAtNegotiation;
     38  const ontrackPromise = new Promise(resolve => {
     39    callee.ontrack = t.step_func(() => {
     40      const videoReceiver = callee.getReceivers().find(r => r.track.kind === 'video');
     41      assert_not_equals(videoReceiver, undefined);
     42 
     43      let receiverReader;
     44      let receiverWriter;
     45      if (streamsCreatedAtNegotiation) {
     46        const videoStreams = streamsCreatedAtNegotiation.find(r => r.kind === 'video');
     47        assert_true(!!videoStreams);
     48        receiverReader = videoStreams.streams.readable.getReader();
     49        receiverWriter = videoStreams.streams.writable.getWriter();
     50      } else {
     51        const receiverStreams =
     52            videoReceiver.createEncodedStreams();
     53        receiverReader = receiverStreams.readable.getReader();
     54        receiverWriter = receiverStreams.writable.getWriter();
     55      }
     56 
     57      const maxFramesToReceive = numFramesToSend;
     58      let numVerifiedFrames = 0;
     59      for (let i = 0; i < maxFramesToReceive; i++) {
     60        receiverReader.read().then(t.step_func(result => {
     61          verifyNonstandardAdditionalDataIfPresent(result.value);
     62          if (frameInfos[numVerifiedFrames] &&
     63              areFrameInfosEqual(result.value, frameInfos[numVerifiedFrames])) {
     64            numVerifiedFrames++;
     65          } else {
     66            // Receiving unexpected frames is an indication that
     67            // frames are not passed correctly between sender and receiver.
     68            assert_unreached("Incorrect frame received");
     69          }
     70 
     71          if (numVerifiedFrames == numFramesToSend)
     72            resolve();
     73        }));
     74      }
     75    });
     76  });
     77 
     78  exchangeIceCandidates(caller, callee);
     79  await negotiationFunction(caller, callee, (streams) => {streamsCreatedAtNegotiation = streams;});
     80 
     81  // Pass frames as they come from the encoder.
     82  for (let i = 0; i < numFramesPassthrough; i++) {
     83    const result = await senderReader.read();
     84    const frame = result.value;
     85    const metadata = frame.getMetadata();
     86    assert_true(containsVideoMetadata(metadata));
     87    verifyNonstandardAdditionalDataIfPresent(frame);
     88    frameInfos.push({
     89      timestamp: frame.timestamp,
     90      type: frame.type,
     91      data: frame.data,
     92      metadata: metadata,
     93      getMetadata() { return this.metadata; }
     94    });
     95    frameCallback(frame);
     96    senderWriter.write(frame);
     97  }
     98 
     99  // Replace frame data with arbitrary buffers.
    100  for (let i = 0; i < numFramesReplaceData; i++) {
    101    const result = await senderReader.read();
    102    const metadata = result.value.getMetadata();
    103    assert_true(containsVideoMetadata(metadata));
    104    const buffer = new ArrayBuffer(100);
    105    const int8View = new Int8Array(buffer);
    106    int8View.fill(i);
    107 
    108    result.value.data = buffer;
    109    frameInfos.push({
    110      timestamp: result.value.timestamp,
    111      type: result.value.type,
    112      data: result.value.data,
    113      metadata: metadata,
    114      getMetadata() { return this.metadata; }
    115    });
    116    senderWriter.write(result.value);
    117  }
    118 
    119  // Modify frame data.
    120  for (let i = 0; i < numFramesReplaceData; i++) {
    121    const result = await senderReader.read();
    122    const metadata = result.value.getMetadata();
    123    assert_true(containsVideoMetadata(metadata));
    124    const int8View = new Int8Array(result.value.data);
    125    int8View.fill(i);
    126 
    127    frameInfos.push({
    128      timestamp: result.value.timestamp,
    129      type: result.value.type,
    130      data: result.value.data,
    131      metadata: metadata,
    132      getMetadata() { return this.metadata; }
    133    });
    134    senderWriter.write(result.value);
    135  }
    136 
    137  return ontrackPromise;
    138 }
    139 
    140 for (const setConstructorParam of [false, true]) {
    141  promise_test(async t => {
    142    return testVideoFlow(t, exchangeOfferAnswer, setConstructorParam);
    143  }, 'Frames flow correctly using insertable streams' + (setConstructorParam ? ' with param' : ''));
    144 
    145  promise_test(async t => {
    146    return testVideoFlow(t, exchangeOfferAnswerReverse, setConstructorParam);
    147  }, 'Frames flow correctly using insertable streams when receiver starts negotiation' + (setConstructorParam ? ' with param' : ''));
    148 }
    149 
    150 promise_test(async t => {
    151  const caller = new RTCPeerConnection();
    152  t.add_cleanup(() => caller.close());
    153  const stream = await navigator.mediaDevices.getUserMedia({video:true});
    154  const track = stream.getTracks()[0];
    155  t.add_cleanup(() => track.stop());
    156  const sender = caller.addTrack(track)
    157  sender.createEncodedStreams();
    158  assert_throws_dom("InvalidStateError", () => sender.createEncodedStreams());
    159 }, 'Creating streams twice throws');
    160 
    161 promise_test(async t => {
    162  let clonedFrames = [];
    163  function verifyFramesSerializeAndDeserialize(frame) {
    164    // Clone encoded frames using structedClone (ie serialize + deserialize) and
    165    // keep a reference.
    166    const clone = structuredClone(frame);
    167    clonedFrames.push(clone);
    168  };
    169 
    170  await testVideoFlow(t, exchangeOfferAnswer, /*setConstructorParam=*/false, verifyFramesSerializeAndDeserialize);
    171 
    172  // Ensure all of our cloned frames are still alive and well, despite the
    173  // originals having been sent through the PeerConnection.
    174  clonedFrames.forEach((clonedFrame) => {
    175    assert_not_equals(clonedFrame.data.size, 0);
    176    assert_not_equals(clonedFrame.type, "empty")
    177  });
    178 }, 'Encoded frames serialize and deserialize into a deep clone');
    179 
    180 </script>
    181 </body>
    182 </html>