tor-browser

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

videoFrame-utils.js (4920B)


      1 const I420_DATA = new Uint8Array([
      2      1, 2, 3, 4,  // y
      3      5, 6, 7, 8,
      4      9, 10,       // u
      5      11, 12,      // v
      6  ]);
      7 
      8 function makeI420_4x2() {
      9  const init = {
     10      format: 'I420',
     11      timestamp: 0,
     12      codedWidth: 4,
     13      codedHeight: 2,
     14  };
     15  return new VideoFrame(I420_DATA, init);
     16 }
     17 
     18 function testBufferConstructedI420Frame(bufferType) {
     19  let fmt = 'I420';
     20  let vfInit = {format: fmt, timestamp: 1234, codedWidth: 4, codedHeight: 2};
     21 
     22  let buffer;
     23  if (bufferType == 'SharedArrayBuffer' ||
     24      bufferType == 'Uint8Array(SharedArrayBuffer)') {
     25    buffer = new SharedArrayBuffer(I420_DATA.length);
     26  } else {
     27    assert_true(bufferType == 'ArrayBuffer' ||
     28                bufferType == 'Uint8Array(ArrayBuffer)');
     29    buffer = new ArrayBuffer(I420_DATA.length);
     30  }
     31  let bufferView = new Uint8Array(buffer);
     32  bufferView.set(I420_DATA);
     33  let data = bufferType.startsWith('Uint8Array') ? bufferView : buffer;
     34 
     35  let frame = new VideoFrame(data, vfInit);
     36  assert_equals(frame.format, fmt, 'plane format');
     37  assert_equals(frame.colorSpace.primaries, 'bt709', 'color primaries');
     38  assert_equals(frame.colorSpace.transfer, 'bt709', 'color transfer');
     39  assert_equals(frame.colorSpace.matrix, 'bt709', 'color matrix');
     40  assert_false(frame.colorSpace.fullRange, 'color range');
     41  frame.close();
     42 
     43  let y = {offset: 0, stride: 4};
     44  let u = {offset: 8, stride: 2};
     45  let v = {offset: 10, stride: 2};
     46 
     47  assert_throws_js(TypeError, () => {
     48    let y = {offset: 0, stride: 1};
     49    let frame = new VideoFrame(data, {...vfInit, layout: [y, u, v]});
     50  }, 'y stride too small');
     51  assert_throws_js(TypeError, () => {
     52    let u = {offset: 8, stride: 1};
     53    let frame = new VideoFrame(data, {...vfInit, layout: [y, u, v]});
     54  }, 'u stride too small');
     55  assert_throws_js(TypeError, () => {
     56    let v = {offset: 10, stride: 1};
     57    let frame = new VideoFrame(data, {...vfInit, layout: [y, u, v]});
     58  }, 'v stride too small');
     59  assert_throws_js(TypeError, () => {
     60    let frame = new VideoFrame(data.slice(0, 8), vfInit);
     61  }, 'data too small');
     62 }
     63 
     64 function assert_buffer_equals(actual, expected) {
     65  assert_true(expected instanceof Uint8Array, 'actual instanceof Uint8Array');
     66 
     67  if (actual instanceof ArrayBuffer ||
     68      (typeof(SharedArrayBuffer) != 'undefined' &&
     69        actual instanceof SharedArrayBuffer)) {
     70    actual = new Uint8Array(actual);
     71  } else {
     72    assert_true(actual instanceof Uint8Array,
     73        'expected instanceof Uint8Array, ArrayBuffer, or SharedArrayBuffer');
     74  }
     75 
     76  assert_equals(actual.length, expected.length, 'buffer length');
     77  for (let i = 0; i < actual.length; i++) {
     78    if (actual[i] != expected[i]) {
     79      assert_equals(actual[i], expected[i], 'buffer contents at index ' + i);
     80    }
     81  }
     82 }
     83 
     84 function assert_layout_equals(actual, expected) {
     85  assert_equals(actual.length, expected.length, 'layout planes');
     86  for (let i = 0; i < actual.length; i++) {
     87    assert_object_equals(actual[i], expected[i], 'plane ' + i + ' layout');
     88  }
     89 }
     90 
     91 async function testI420_4x2_copyTo(destination) {
     92  const frame = makeI420_4x2();
     93  const expectedLayout = [
     94      {offset: 0, stride: 4},
     95      {offset: 8, stride: 2},
     96      {offset: 10, stride: 2},
     97  ];
     98  const expectedData = new Uint8Array([
     99      1, 2, 3, 4,  // y
    100      5, 6, 7, 8,
    101      9, 10,       // u
    102      11, 12       // v
    103  ]);
    104 
    105  assert_equals(frame.allocationSize(), expectedData.length, 'allocationSize()');
    106  const layout = await frame.copyTo(destination);
    107  assert_layout_equals(layout, expectedLayout);
    108  assert_buffer_equals(destination, expectedData);
    109 }
    110 
    111 function verifyTimestampRequiredToConstructFrame(imageSource) {
    112  assert_throws_js(
    113      TypeError,
    114      () => new VideoFrame(imageSource),
    115      'timestamp required to construct VideoFrame from this source');
    116  let validFrame = new VideoFrame(imageSource, {timestamp: 0});
    117  validFrame.close();
    118 }
    119 
    120 // Fills a visible region within `data` with the given YUV values.
    121 function fillYUV(data, codedWidth, codedHeight, visibleRect, v_y, v_u, v_v) {
    122  for (let y = 0; y < codedHeight; y++) {
    123    for (let x = 0; x < codedWidth; x++) {
    124      data[y * codedWidth + x] =
    125          (x >= visibleRect.x && y < visibleRect.y + visibleRect.height) ? v_y
    126                                                                         : 0;
    127    }
    128  }
    129  const uvWidth = codedWidth / 2;
    130  const uvHeight = codedHeight / 2;
    131  const uvPlaneSize = uvWidth * uvHeight;
    132  const uvOffset = codedWidth * codedHeight;
    133  const uvX = visibleRect.x / 2;
    134  const uvMaxY = (visibleRect.y + visibleRect.height) / 2;
    135  for (let y = 0; y < uvHeight; y++) {
    136    for (let x = 0; x < uvWidth; x++) {
    137      if (x >= uvX && y < uvMaxY) {
    138        data[uvOffset + y * uvWidth + x] = v_u;
    139        data[uvOffset + uvPlaneSize + y * uvWidth + x] = v_v;
    140      } else {
    141        data[uvOffset + y * uvWidth + x] =
    142            data[uvOffset + uvPlaneSize + y * uvWidth + x] = 128;
    143      }
    144    }
    145  }
    146 }