tor-browser

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

video-encoder.https.any.js (9187B)


      1 // META: global=window,dedicatedworker
      2 // META: script=/common/media.js
      3 // META: script=/webcodecs/utils.js
      4 // META: script=/webcodecs/video-encoder-utils.js
      5 
      6 const defaultConfig = {
      7  codec: 'vp8',
      8  width: 640,
      9  height: 480
     10 };
     11 
     12 promise_test(t => {
     13  // VideoEncoderInit lacks required fields.
     14  assert_throws_js(TypeError, () => { new VideoEncoder({}); });
     15 
     16  // VideoEncoderInit has required fields.
     17  let encoder = new VideoEncoder(getDefaultCodecInit(t));
     18 
     19  assert_equals(encoder.state, "unconfigured");
     20 
     21  encoder.close();
     22 
     23  return endAfterEventLoopTurn();
     24 }, 'Test VideoEncoder construction');
     25 
     26 promise_test(async t => {
     27  let output_chunks = [];
     28  let codecInit = getDefaultCodecInit(t);
     29  let decoderConfig = null;
     30  let encoderConfig = {
     31    codec: 'vp8',
     32    width: 640,
     33    height: 480,
     34    displayWidth: 800,
     35    displayHeight: 600,
     36  };
     37 
     38  codecInit.output = (chunk, metadata) => {
     39    assert_not_equals(metadata, null);
     40    if (metadata.decoderConfig)
     41      decoderConfig = metadata.decoderConfig;
     42    output_chunks.push(chunk);
     43  }
     44 
     45  let encoder = new VideoEncoder(codecInit);
     46  encoder.configure(encoderConfig);
     47 
     48  let frame1 = createFrame(640, 480, 0);
     49  let frame2 = createFrame(640, 480, 33333);
     50  t.add_cleanup(() => {
     51    frame1.close();
     52    frame2.close();
     53  });
     54 
     55  encoder.encode(frame1);
     56  encoder.encode(frame2);
     57 
     58  await encoder.flush();
     59 
     60  // Decoder config should be given with the first chunk
     61  assert_not_equals(decoderConfig, null);
     62  assert_equals(decoderConfig.codec, encoderConfig.codec);
     63  assert_greater_than_equal(decoderConfig.codedHeight, encoderConfig.height);
     64  assert_greater_than_equal(decoderConfig.codedWidth, encoderConfig.width);
     65  assert_equals(decoderConfig.displayAspectHeight, encoderConfig.displayHeight);
     66  assert_equals(decoderConfig.displayAspectWidth, encoderConfig.displayWidth);
     67  assert_not_equals(decoderConfig.colorSpace.primaries, null);
     68  assert_not_equals(decoderConfig.colorSpace.transfer, null);
     69  assert_not_equals(decoderConfig.colorSpace.matrix, null);
     70  assert_not_equals(decoderConfig.colorSpace.fullRange, null);
     71 
     72  assert_equals(output_chunks.length, 2);
     73  assert_equals(output_chunks[0].timestamp, frame1.timestamp);
     74  assert_equals(output_chunks[0].duration, frame1.duration);
     75  assert_equals(output_chunks[1].timestamp, frame2.timestamp);
     76  assert_equals(output_chunks[1].duration, frame2.duration);
     77 }, 'Test successful configure(), encode(), and flush()');
     78 
     79 promise_test(async t => {
     80  let codecInit = getDefaultCodecInit(t);
     81  let encoderConfig = {
     82    codec: 'vp8',
     83    width: 320,
     84    height: 200
     85  };
     86 
     87  codecInit.output = (chunk, metadata) => {}
     88 
     89  let encoder = new VideoEncoder(codecInit);
     90 
     91  // No encodes yet.
     92  assert_equals(encoder.encodeQueueSize, 0);
     93 
     94  encoder.configure(encoderConfig);
     95 
     96  // Still no encodes.
     97  assert_equals(encoder.encodeQueueSize, 0);
     98 
     99  const frames_count = 100;
    100  let frames = [];
    101  for (let i = 0; i < frames_count; i++) {
    102    let frame = createFrame(320, 200, i * 16000);
    103    frames.push(frame);
    104  }
    105 
    106  let lastDequeueSize = Infinity;
    107  encoder.ondequeue = () => {
    108    assert_greater_than(lastDequeueSize, 0, "Dequeue event after queue empty");
    109    assert_greater_than(lastDequeueSize, encoder.encodeQueueSize,
    110                        "Dequeue event without decreased queue size");
    111    lastDequeueSize = encoder.encodeQueueSize;
    112  };
    113 
    114  for (let frame of frames)
    115    encoder.encode(frame);
    116 
    117  assert_greater_than_equal(encoder.encodeQueueSize, 0);
    118  assert_less_than_equal(encoder.encodeQueueSize, frames_count);
    119 
    120  await encoder.flush();
    121  // We can guarantee that all encodes are processed after a flush.
    122  assert_equals(encoder.encodeQueueSize, 0);
    123  // Last dequeue event should fire when the queue is empty.
    124  assert_equals(lastDequeueSize, 0);
    125 
    126  // Reset this to Infinity to track the decline of queue size for this next
    127  // batch of encodes.
    128  lastDequeueSize = Infinity;
    129 
    130  for (let frame of frames) {
    131    encoder.encode(frame);
    132    frame.close();
    133  }
    134 
    135  assert_greater_than_equal(encoder.encodeQueueSize, 0);
    136  encoder.reset();
    137  assert_equals(encoder.encodeQueueSize, 0);
    138 }, 'encodeQueueSize test');
    139 
    140 
    141 promise_test(async t => {
    142  let timestamp = 0;
    143  let callbacks_before_reset = 0;
    144  let callbacks_after_reset = 0;
    145  const timestamp_step = 40000;
    146  const expected_callbacks_before_reset = 3;
    147  let codecInit = getDefaultCodecInit(t);
    148  let original = createFrame(320, 200, 0);
    149  let encoder = null;
    150  let reset_completed = false;
    151  codecInit.output = (chunk, metadata) => {
    152    if (chunk.timestamp % 2 == 0) {
    153      // pre-reset frames have even timestamp
    154      callbacks_before_reset++;
    155      if (callbacks_before_reset == expected_callbacks_before_reset) {
    156        encoder.reset();
    157        reset_completed = true;
    158      }
    159    } else {
    160      // after-reset frames have odd timestamp
    161      callbacks_after_reset++;
    162    }
    163  }
    164 
    165  encoder = new VideoEncoder(codecInit);
    166  encoder.configure(defaultConfig);
    167  await encoder.flush();
    168 
    169  // Send 10x frames to the encoder, call reset() on it after x outputs,
    170  // and make sure no more chunks are emitted afterwards.
    171  let encodes_before_reset = expected_callbacks_before_reset * 10;
    172  for (let i = 0; i < encodes_before_reset; i++) {
    173    let frame = new VideoFrame(original, { timestamp: timestamp });
    174    timestamp += timestamp_step;
    175    encoder.encode(frame);
    176    frame.close();
    177  }
    178 
    179  await t.step_wait(() => reset_completed,
    180    "Reset() should be called by output callback", 10000, 1);
    181 
    182  assert_equals(callbacks_before_reset, expected_callbacks_before_reset);
    183  assert_true(reset_completed);
    184  assert_equals(encoder.encodeQueueSize, 0);
    185 
    186  let newConfig = { ...defaultConfig };
    187  newConfig.width = 800;
    188  newConfig.height = 600;
    189  encoder.configure(newConfig);
    190 
    191  const frames_after_reset = 5;
    192  for (let i = 0; i < frames_after_reset; i++) {
    193    let frame = createFrame(800, 600, timestamp + 1);
    194    timestamp += timestamp_step;
    195    encoder.encode(frame);
    196    frame.close();
    197  }
    198  await encoder.flush();
    199 
    200  assert_equals(callbacks_after_reset, frames_after_reset,
    201    "not all after-reset() outputs have been emitted");
    202  assert_equals(callbacks_before_reset, expected_callbacks_before_reset,
    203    "pre-reset() outputs were emitter after reset() and flush()");
    204  assert_equals(encoder.encodeQueueSize, 0);
    205 }, 'Test successful reset() and re-confiugre()');
    206 
    207 promise_test(async t => {
    208  let output_chunks = [];
    209  const codecInit = {
    210    output: chunk => output_chunks.push(chunk),
    211  };
    212  const error = new Promise(resolve => codecInit.error = e => {
    213    resolve(e);
    214  });
    215 
    216  let encoder = new VideoEncoder(codecInit);
    217 
    218  // No encodes yet.
    219  assert_equals(encoder.encodeQueueSize, 0);
    220 
    221  let config = defaultConfig;
    222 
    223  encoder.configure(config);
    224 
    225  let frame1 = createFrame(640, 480, 0);
    226  let frame2 = createFrame(640, 480, 33333);
    227 
    228  encoder.encode(frame1);
    229  encoder.configure(config);
    230 
    231  encoder.encode(frame2);
    232 
    233  await encoder.flush();
    234 
    235  // We can guarantee that all encodes are processed after a flush.
    236  assert_equals(encoder.encodeQueueSize, 0, "queue size after encode");
    237 
    238  assert_equals(output_chunks.length, 2, "number of chunks");
    239  assert_equals(output_chunks[0].timestamp, frame1.timestamp);
    240  assert_equals(output_chunks[1].timestamp, frame2.timestamp);
    241 
    242  output_chunks = [];
    243 
    244  let frame3 = createFrame(640, 480, 66666);
    245 
    246  encoder.encode(frame3);
    247 
    248  let badConfig = { ...defaultConfig };
    249  badConfig.codec = '';
    250  assert_throws_js(TypeError, () => encoder.configure(badConfig));
    251 
    252  badConfig.codec = 'bogus';
    253  encoder.configure(badConfig);
    254  let e = await error;
    255  assert_true(e instanceof DOMException);
    256  assert_equals(e.name, 'NotSupportedError');
    257  assert_equals(encoder.state, 'closed', 'state');
    258 
    259  // We may or may not have received frame3 before closing.
    260 }, 'Test successful encode() after re-configure().');
    261 
    262 promise_test(async t => {
    263  let encoder = new VideoEncoder(getDefaultCodecInit(t));
    264 
    265  let frame = createFrame(640, 480, 0);
    266 
    267  return testClosedCodec(t, encoder, defaultConfig, frame);
    268 }, 'Verify closed VideoEncoder operations');
    269 
    270 promise_test(async t => {
    271  let encoder = new VideoEncoder(getDefaultCodecInit(t));
    272 
    273  let frame = createFrame(640, 480, 0);
    274 
    275  return testUnconfiguredCodec(t, encoder, frame);
    276 }, 'Verify unconfigured VideoEncoder operations');
    277 
    278 promise_test(async t => {
    279  let encoder = new VideoEncoder(getDefaultCodecInit(t));
    280 
    281  let frame = createFrame(640, 480, 0);
    282  frame.close();
    283 
    284  encoder.configure(defaultConfig);
    285 
    286  assert_throws_js(TypeError, () => {
    287    encoder.encode(frame);
    288  });
    289 }, 'Verify encoding closed frames throws.');
    290 
    291 promise_test(async t => {
    292  let output_chunks = [];
    293  let codecInit = getDefaultCodecInit(t);
    294  codecInit.output = chunk => output_chunks.push(chunk);
    295 
    296  let encoder = new VideoEncoder(codecInit);
    297  let config = defaultConfig;
    298  encoder.configure(config);
    299 
    300  let frame = createFrame(640, 480, -10000);
    301  encoder.encode(frame);
    302  frame.close();
    303  await encoder.flush();
    304  encoder.close();
    305  assert_equals(output_chunks.length, 1);
    306  assert_equals(output_chunks[0].timestamp, -10000, "first chunk timestamp");
    307  assert_greater_than(output_chunks[0].byteLength, 0);
    308 }, 'Encode video with negative timestamp');