tor-browser

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

VideoTrackGenerator.worker.js (7399B)


      1 // META: title=VideoTrackGenerator tests.
      2 
      3 importScripts("/resources/testharness.js");
      4 
      5 function make_audio_data(timestamp, channels, sampleRate, frames) {
      6  let data = new Float32Array(frames*channels);
      7 
      8  // This generates samples in a planar format.
      9  for (var channel = 0; channel < channels; channel++) {
     10    let hz = 100 + channel * 50; // sound frequency
     11    let base_index = channel * frames;
     12    for (var i = 0; i < frames; i++) {
     13      let t = (i / sampleRate) * hz * (Math.PI * 2);
     14      data[base_index + i] = Math.sin(t);
     15    }
     16  }
     17 
     18  return new AudioData({
     19    timestamp: timestamp,
     20    data: data,
     21    numberOfChannels: channels,
     22    numberOfFrames: frames,
     23    sampleRate: sampleRate,
     24    format: "f32-planar",
     25  });
     26 }
     27 
     28 const pixelColour = [50, 100, 150, 255];
     29 const height = 240;
     30 const width = 320;
     31 function makeVideoFrame(timestamp) {
     32  const canvas = new OffscreenCanvas(width, height);
     33 
     34  const ctx = canvas.getContext('2d', {alpha: false});
     35  ctx.fillStyle = `rgba(${pixelColour.join()})`;
     36  ctx.fillRect(0, 0, width, height);
     37 
     38  return new VideoFrame(canvas, {timestamp, alpha: 'discard'});
     39 }
     40 
     41 promise_test(async t => {
     42  const videoFrame = makeVideoFrame(1);
     43  const originalWidth = videoFrame.displayWidth;
     44  const originalHeight = videoFrame.displayHeight;
     45  const originalTimestamp = videoFrame.timestamp;
     46  const generator = new VideoTrackGenerator();
     47  t.add_cleanup(() => generator.track.stop());
     48 
     49  // Use a MediaStreamTrackProcessor as a sink for |generator| to verify
     50  // that |processor| actually forwards the frames written to its writable
     51  // field.
     52  const processor = new MediaStreamTrackProcessor(generator);
     53  const reader = processor.readable.getReader();
     54  const readerPromise = new Promise(async resolve => {
     55    const result = await reader.read();
     56    t.add_cleanup(() => result.value.close());
     57    t.step_func(() => {
     58      assert_equals(result.value.displayWidth, originalWidth);
     59      assert_equals(result.value.displayHeight, originalHeight);
     60      assert_equals(result.value.timestamp, originalTimestamp);
     61    })();
     62    resolve();
     63  });
     64 
     65  generator.writable.getWriter().write(videoFrame);
     66  return readerPromise;
     67 }, 'Tests that VideoTrackGenerator forwards frames to sink');
     68 
     69 promise_test(async t => {
     70  const generator = new VideoTrackGenerator();
     71  t.add_cleanup(() => generator.track.stop());
     72 
     73  const writer = generator.writable.getWriter();
     74  const frame = makeVideoFrame(1);
     75  await writer.write(frame);
     76 
     77  assert_equals(generator.track.kind, "video");
     78  assert_equals(generator.track.readyState, "live");
     79 }, "Tests that creating a VideoTrackGenerator works as expected");
     80 
     81 promise_test(async t => {
     82  const generator = new VideoTrackGenerator();
     83  t.add_cleanup(() => generator.track.stop());
     84 
     85  const writer = generator.writable.getWriter();
     86  const frame = makeVideoFrame(1);
     87  await writer.write(frame);
     88 
     89  assert_throws_dom("InvalidStateError", () => frame.clone(), "VideoFrame wasn't destroyed on write.");
     90 }, "Tests that VideoFrames are destroyed on write");
     91 
     92 promise_test(async t => {
     93  const generator = new VideoTrackGenerator();
     94  t.add_cleanup(() => generator.track.stop());
     95 
     96  const writer = generator.writable.getWriter();
     97 
     98  if (!self.AudioData)
     99    return;
    100 
    101  const defaultInit = {
    102      timestamp: 1234,
    103      channels: 2,
    104      sampleRate: 8000,
    105      frames: 100,
    106  };
    107  const audioData = make_audio_data(defaultInit.timestamp, defaultInit.channels, defaultInit.sampleRate,
    108      defaultInit.frames);
    109 
    110  await promise_rejects_js(t, TypeError, writer.write("test"));
    111 }, "Generator writer rejects on mismatched media input");
    112 
    113 promise_test(async t => {
    114  const generator = new VideoTrackGenerator();
    115  t.add_cleanup(() => generator.track.stop());
    116 
    117  const writer = generator.writable.getWriter();
    118  await promise_rejects_js(t, TypeError, writer.write("potatoe"));
    119 }, "Generator writer rejects on non media input");
    120 
    121 promise_test(async t => {
    122  const generator = new VideoTrackGenerator();
    123 
    124  const writer = generator.writable.getWriter();
    125  const frame1 = makeVideoFrame(1);
    126  t.add_cleanup(() => frame1.close());
    127  await writer.write(frame1);
    128  assert_equals(frame1.codedWidth, 0);
    129 
    130  generator.track.stop();
    131 
    132  await writer.closed;
    133 
    134  const frame2 = makeVideoFrame(1);
    135  t.add_cleanup(() => frame2.close());
    136  await promise_rejects_js(t, TypeError, writer.write(frame2));
    137 
    138  assert_equals(frame2.codedWidth, 320);
    139 }, "A writer rejects when generator's track is stopped");
    140 
    141 promise_test(async t => {
    142  const generator = new VideoTrackGenerator();
    143  generator.muted = true;
    144 
    145  const writer = generator.writable.getWriter();
    146  const frame1 = makeVideoFrame(1);
    147  t.add_cleanup(() => frame1.close());
    148  await writer.write(frame1);
    149  assert_equals(frame1.codedWidth, 0);
    150 
    151  generator.track.stop();
    152 
    153  await writer.closed;
    154 
    155  const frame2 = makeVideoFrame(1);
    156  t.add_cleanup(() => frame2.close());
    157  await promise_rejects_js(t, TypeError, writer.write(frame2));
    158 
    159  assert_equals(frame2.codedWidth, 320);
    160 }, "A muted writer rejects when generator's track is stopped");
    161 
    162 promise_test(async t => {
    163  const generator = new VideoTrackGenerator();
    164 
    165  const writer = generator.writable.getWriter();
    166  const frame1 = makeVideoFrame(1);
    167  t.add_cleanup(() => frame1.close());
    168  await writer.write(frame1);
    169  assert_equals(frame1.codedWidth, 0);
    170 
    171  const clonedTrack = generator.track.clone();
    172  generator.track.stop();
    173 
    174  await new Promise(resolve => t.step_timeout(resolve, 100));
    175 
    176  const frame2 = makeVideoFrame(1);
    177  t.add_cleanup(() => frame2.close());
    178  await writer.write(frame2);
    179  assert_equals(frame2.codedWidth, 0);
    180 
    181  clonedTrack.stop();
    182 
    183  await writer.closed;
    184 
    185  const frame3 = makeVideoFrame(1);
    186  t.add_cleanup(() => frame3.close());
    187  await promise_rejects_js(t, TypeError, writer.write(frame3));
    188 
    189  assert_equals(frame3.codedWidth, 320);
    190 }, "A writer rejects when generator's track and clones are stopped");
    191 
    192 promise_test(async t => {
    193  const generator = new VideoTrackGenerator();
    194  t.add_cleanup(() => generator.track.stop());
    195 
    196  // Use a MediaStreamTrackProcessor as a sink for |generator| to verify
    197  // that |processor| actually forwards the frames written to its writable
    198  // field.
    199  const processor = new MediaStreamTrackProcessor(generator);
    200  const reader = processor.readable.getReader();
    201  const videoFrame = makeVideoFrame(1);
    202 
    203  const writer = generator.writable.getWriter();
    204  const videoFrame1 = makeVideoFrame(1);
    205  writer.write(videoFrame1);
    206  const result1 = await reader.read();
    207  t.add_cleanup(() => result1.value.close());
    208  assert_equals(result1.value.timestamp, 1);
    209  generator.muted = true;
    210 
    211  // This frame is expected to be discarded.
    212  const videoFrame2 = makeVideoFrame(2);
    213  writer.write(videoFrame2);
    214  generator.muted = false;
    215 
    216  const videoFrame3 = makeVideoFrame(3);
    217  writer.write(videoFrame3);
    218  const result3 = await reader.read();
    219  t.add_cleanup(() => result3.value.close());
    220  assert_equals(result3.value.timestamp, 3);
    221 
    222  // Set up a read ahead of time, then mute, enqueue and unmute.
    223  const promise5 = reader.read();
    224  generator.muted = true;
    225  writer.write(makeVideoFrame(4)); // Expected to be discarded.
    226  generator.muted = false;
    227  writer.write(makeVideoFrame(5));
    228  const result5 = await promise5;
    229  t.add_cleanup(() => result5.value.close());
    230  assert_equals(result5.value.timestamp, 5);
    231 }, 'Tests that VideoTrackGenerator forwards frames only when unmuted');
    232 
    233 done();