tor-browser

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

RTCEncodedAudioFrame-metadata.https.html (8665B)


      1 <!DOCTYPE html>
      2 <meta charset="utf-8">
      3 <title>RTCEncodedAudioFrame can be cloned and distributed</title>
      4 <script src="/resources/testharness.js"></script>
      5 <script src="/resources/testharnessreport.js"></script>
      6 <script src=/resources/testdriver.js></script>
      7 <script src=/resources/testdriver-vendor.js></script>
      8 <script src='../../mediacapture-streams/permission-helper.js'></script>
      9 <script src="../../webrtc/RTCPeerConnection-helper.js"></script>
     10 <script src="../../service-workers/service-worker/resources/test-helpers.sub.js"></script>
     11 <script src='RTCEncodedFrame-timestamps-helper.js'></script>
     12 
     13 <script>
     14 "use strict";
     15 promise_test(async t => {
     16  const caller1 = new RTCPeerConnection();
     17  t.add_cleanup(() => caller1.close());
     18  const callee1 = new RTCPeerConnection({encodedInsertableStreams:true});
     19  t.add_cleanup(() => callee1.close());
     20  await setMediaPermission("granted", ["microphone"]);
     21  const inputStream = await navigator.mediaDevices.getUserMedia({audio:true});
     22  const inputTrack = inputStream.getAudioTracks()[0];
     23  t.add_cleanup(() => inputTrack.stop());
     24  caller1.addTrack(inputTrack)
     25  exchangeIceCandidates(caller1, callee1);
     26 
     27  const caller2 = new RTCPeerConnection({encodedInsertableStreams:true});
     28  t.add_cleanup(() => caller2.close());
     29  const sender2 = caller2.addTransceiver("audio").sender;
     30  const writer2 = sender2.createEncodedStreams().writable.getWriter();
     31  sender2.replaceTrack(new MediaStreamTrackGenerator({ kind: 'audio' }));
     32 
     33  const framesReceivedCorrectly = new Promise((resolve, reject) => {
     34    callee1.ontrack = async e => {
     35      const receiverStreams = e.receiver.createEncodedStreams();
     36      const receiverReader = receiverStreams.readable.getReader();
     37      const result = await receiverReader.read();
     38      const original = result.value;
     39      let newFrame = new RTCEncodedAudioFrame(original);
     40      assert_true(original.getMetadata().hasOwnProperty('receiveTime'));
     41      assert_true(original.getMetadata().receiveTime > 0);
     42      assert_equals(original.getMetadata().rtpTimestamp, newFrame.getMetadata().rtpTimestamp);
     43      assert_equals(original.getMetadata().captureTime, newFrame.getMetadata().captureTime);
     44      assert_equals(original.getMetadata().receiveTime, newFrame.getMetadata().receiveTime);
     45      assert_true(original.getMetadata().hasOwnProperty('audioLevel'));
     46      assert_equals(original.getMetadata().audioLevel, newFrame.getMetadata().audioLevel);
     47      assert_array_equals(Array.from(original.data), Array.from(newFrame.data));
     48      await writer2.write(newFrame);
     49      resolve();
     50    }
     51  });
     52 
     53  await exchangeOfferAnswer(caller1, callee1);
     54 
     55  return framesReceivedCorrectly;
     56 }, "Constructing audio frame before sending works");
     57 
     58 promise_test(async t => {
     59  const caller1 = new RTCPeerConnection();
     60 
     61  t.add_cleanup(() => caller1.close());
     62  const callee1 = new RTCPeerConnection({encodedInsertableStreams:true});
     63  t.add_cleanup(() => callee1.close());
     64  await setMediaPermission("granted", ["microphone"]);
     65  const inputStream = await navigator.mediaDevices.getUserMedia({audio:true});
     66  const inputTrack = inputStream.getAudioTracks()[0];
     67  t.add_cleanup(() => inputTrack.stop());
     68  caller1.addTrack(inputTrack)
     69  exchangeIceCandidates(caller1, callee1);
     70 
     71  const caller2 = new RTCPeerConnection({encodedInsertableStreams:true});
     72  t.add_cleanup(() => caller2.close());
     73  const sender2 = caller2.addTransceiver("audio").sender;
     74  const writer2 = sender2.createEncodedStreams().writable.getWriter();
     75  sender2.replaceTrack(new MediaStreamTrackGenerator({ kind: 'audio' }));
     76 
     77  const framesReceivedCorrectly = new Promise((resolve, reject) => {
     78    callee1.ontrack = async e => {
     79      const receiverStreams = e.receiver.createEncodedStreams();
     80      const receiverReader = receiverStreams.readable.getReader();
     81      const result = await receiverReader.read();
     82      const original = result.value;
     83      let newMetadata = original.getMetadata();
     84      newMetadata.rtpTimestamp = newMetadata.rtpTimestamp + 1;
     85      let newFrame = new RTCEncodedAudioFrame(original, {metadata: newMetadata});
     86      assert_not_equals(original.getMetadata().rtpTimestamp, newFrame.getMetadata().rtpTimestamp);
     87      assert_equals(newMetadata.rtpTimestamp, newFrame.getMetadata().rtpTimestamp);
     88      assert_equals(original.getMetadata().receiveTime, newFrame.getMetadata().receiveTime);
     89      assert_equals(original.getMetadata().captureTime, newFrame.getMetadata().captureTime);
     90      assert_equals(original.getMetadata().audioLevel, newFrame.getMetadata().audioLevel);
     91      assert_array_equals(Array.from(original.data), Array.from(newFrame.data));
     92      await writer2.write(newFrame);
     93      resolve();
     94    }
     95  });
     96 
     97  await exchangeOfferAnswer(caller1, callee1);
     98 
     99  return framesReceivedCorrectly;
    100 }, "Constructing audio frame with metadata argument before sending works");
    101 
    102 promise_test(async t => {
    103  const caller1 = new RTCPeerConnection();
    104  t.add_cleanup(() => caller1.close());
    105  const callee1 = new RTCPeerConnection({encodedInsertableStreams:true});
    106  t.add_cleanup(() => callee1.close());
    107  await setMediaPermission("granted", ["microphone"]);
    108  const inputStream = await navigator.mediaDevices.getUserMedia({audio:true});
    109  const inputTrack = inputStream.getAudioTracks()[0];
    110  t.add_cleanup(() => inputTrack.stop());
    111  caller1.addTrack(inputTrack)
    112  exchangeIceCandidates(caller1, callee1);
    113 
    114  const caller2 = new RTCPeerConnection({encodedInsertableStreams:true});
    115  t.add_cleanup(() => caller2.close());
    116  const sender2 = caller2.addTransceiver("audio").sender;
    117  const writer2 = sender2.createEncodedStreams().writable.getWriter();
    118  sender2.replaceTrack(new MediaStreamTrackGenerator({ kind: 'audio' }));
    119 
    120  const framesReceivedCorrectly = new Promise((resolve, reject) => {
    121    callee1.ontrack = async e => {
    122      const receiverStreams = e.receiver.createEncodedStreams();
    123      const receiverReader = receiverStreams.readable.getReader();
    124      const result = await receiverReader.read();
    125      const original = result.value;
    126      let newMetadata = original.getMetadata();
    127      newMetadata.synchronizationSource = newMetadata.synchronizationSource + 1;
    128  assert_throws_dom("InvalidModificationError", () => new RTCEncodedAudioFrame(original, {metadata: newMetadata}));
    129      resolve();
    130    }
    131  });
    132 
    133  await exchangeOfferAnswer(caller1, callee1);
    134 
    135  return framesReceivedCorrectly;
    136 }, "Constructing audio frame with bad metadata argument before sending does not work");
    137 
    138 promise_test(async t => {
    139  const kAudioLevel = 0.5;
    140  const kCaptureTime = 12345;
    141  const pc1 = new RTCPeerConnection({encodedInsertableStreams:true});
    142  t.add_cleanup(() => pc1.close());
    143  const pc2 = new RTCPeerConnection({encodedInsertableStreams:true});
    144  t.add_cleanup(() => pc2.close());
    145 
    146  exchangeIceCandidates(pc1, pc2);
    147 
    148  let numFrames = 0;
    149  let audioLevelRead = new Promise((resolve, reject) => {
    150    pc2.ontrack = t.step_func(e => {
    151      const receiverTransformer = new TransformStream({
    152        async transform(encodedFrame, controller) {
    153        const metadata = encodedFrame.getMetadata();
    154        if (metadata.audioLevel === undefined) {
    155          reject("No audioLevel");
    156        } else if (metadata.audioLevel < kAudioLevel - 0.1 || metadata.audioLevel > kAudioLevel + 0.1) {
    157          reject("Unexpected audioLevel");
    158        }
    159        if (metadata.captureTime < kCaptureTime - 1 || metadata.captureTime > kCaptureTime + 1) {
    160          reject("Unexpected captureTime");
    161        }
    162        controller.enqueue(encodedFrame);
    163        if (++numFrames == 10)
    164          resolve();
    165        }
    166      });
    167      const receiverStreams = e.receiver.createEncodedStreams();
    168      receiverStreams.readable
    169          .pipeThrough(receiverTransformer)
    170          .pipeTo(receiverStreams.writable);
    171    });
    172  });
    173 
    174  const stream = await navigator.mediaDevices.getUserMedia({audio:true});
    175  t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    176  const sender = pc1.addTrack(stream.getAudioTracks()[0]);
    177  const senderStreams = sender.createEncodedStreams();
    178  const senderTransformer = new TransformStream({
    179    async transform(encodedFrame, controller) {
    180      let metadata = encodedFrame.getMetadata();
    181      metadata.audioLevel = kAudioLevel;
    182      metadata.captureTime = kCaptureTime;
    183      controller.enqueue(new RTCEncodedAudioFrame(encodedFrame, {metadata}));
    184    }
    185  });
    186  senderStreams.readable
    187      .pipeThrough(senderTransformer)
    188      .pipeTo(senderStreams.writable);
    189 
    190  await addAbsCaptureTimeAndExchangeOffer(pc1, pc2);
    191  await checkAbsCaptureTimeAndExchangeAnswer(pc1, pc2, true);
    192 
    193  await audioLevelRead;
    194 }, 'Basic simulcast setup with three spatial layers');
    195 
    196 </script>