tor-browser

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

videoFrame-copyTo.any.js (10917B)


      1 // META: global=window,dedicatedworker
      2 // META: script=/webcodecs/videoFrame-utils.js
      3 
      4 function makeRGBA_2x2() {
      5  const data = new Uint8Array([
      6      1,2,3,4,    5,6,7,8,
      7      9,10,11,12, 13,14,15,16,
      8  ]);
      9  const init = {
     10      format: 'RGBA',
     11      timestamp: 0,
     12      codedWidth: 2,
     13      codedHeight: 2,
     14  };
     15  return new VideoFrame(data, init);
     16 }
     17 
     18 const NV12_DATA = new Uint8Array([
     19      1, 2, 3, 4,   // y
     20      5, 6, 7, 8,
     21      9, 10, 11, 12 // uv
     22  ]);
     23 
     24 function makeNV12_4x2() {
     25  const init = {
     26      format: 'NV12',
     27      timestamp: 0,
     28      codedWidth: 4,
     29      codedHeight: 2,
     30  };
     31  return new VideoFrame(NV12_DATA, init);
     32 }
     33 
     34 promise_test(async t => {
     35  const frame = makeI420_4x2();
     36  frame.close();
     37 
     38  assert_throws_dom('InvalidStateError', () => frame.allocationSize(), 'allocationSize()');
     39 
     40  let data = new Uint8Array(12);
     41  await promise_rejects_dom(t, 'InvalidStateError', frame.copyTo(data), 'copyTo()');
     42 }, 'Test closed frame.');
     43 
     44 promise_test(async t => {
     45  const destination = new ArrayBuffer(I420_DATA.length);
     46  await testI420_4x2_copyTo(destination);
     47 }, 'Test copying I420 frame to a non-shared ArrayBuffer');
     48 
     49 promise_test(async t => {
     50  const destination = new Uint8Array(I420_DATA.length);
     51  await testI420_4x2_copyTo(destination);
     52 }, 'Test copying I420 frame to a non-shared ArrayBufferView');
     53 
     54 promise_test(async t => {
     55  const frame = makeRGBA_2x2();
     56  const expectedLayout = [
     57      {offset: 0, stride: 8},
     58  ];
     59  const expectedData = new Uint8Array([
     60      1,2,3,4,    5,6,7,8,
     61      9,10,11,12, 13,14,15,16,
     62  ]);
     63  assert_equals(frame.allocationSize(), expectedData.length, 'allocationSize()');
     64  const data = new Uint8Array(expectedData.length);
     65  const layout = await frame.copyTo(data);
     66  assert_layout_equals(layout, expectedLayout);
     67  assert_buffer_equals(data, expectedData);
     68 }, 'Test RGBA frame.');
     69 
     70 promise_test(async t => {
     71  const frame = makeNV12_4x2();
     72  const expectedLayout = [
     73      {offset: 0, stride: 4},
     74      {offset: 8, stride: 4},
     75  ];
     76  const expectedData = new Uint8Array([
     77      1,2,3,4,
     78      5,6,7,8,
     79      9,10,11,12
     80  ]);
     81  assert_equals(frame.allocationSize(), expectedData.length, 'allocationSize()');
     82  const data = new Uint8Array(expectedData.length);
     83  const layout = await frame.copyTo(data);
     84  assert_layout_equals(layout, expectedLayout);
     85  assert_buffer_equals(data, expectedData);
     86 }, 'Test NV12 frame.');
     87 
     88 promise_test(async t => {
     89  const frame = makeI420_4x2();
     90  const data = new Uint8Array(11);
     91  await promise_rejects_js(t, TypeError, frame.copyTo(data));
     92 }, 'Test undersized buffer.');
     93 
     94 promise_test(async t => {
     95  const frame = makeI420_4x2();
     96  const options = {
     97    layout: [{offset: 0, stride: 4}],
     98  };
     99  assert_throws_js(TypeError, () => frame.allocationSize(options));
    100  const data = new Uint8Array(12);
    101  await promise_rejects_js(t, TypeError, frame.copyTo(data, options));
    102 }, 'Test incorrect plane count.');
    103 
    104 promise_test(async t => {
    105  const frame = makeI420_4x2();
    106  const options = {
    107      layout: [
    108          {offset: 4, stride: 4},
    109          {offset: 0, stride: 2},
    110          {offset: 2, stride: 2},
    111      ],
    112  };
    113  const expectedData = new Uint8Array([
    114      9, 10,       // u
    115      11, 12,      // v
    116      1, 2, 3, 4,  // y
    117      5, 6, 7, 8,
    118  ]);
    119  assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
    120  const data = new Uint8Array(expectedData.length);
    121  const layout = await frame.copyTo(data, options);
    122  assert_layout_equals(layout, options.layout);
    123  assert_buffer_equals(data, expectedData);
    124 }, 'Test I420 stride and offset work.');
    125 
    126 promise_test(async t => {
    127  const frame = makeI420_4x2();
    128  const options = {
    129      layout: [
    130          {offset: 9, stride: 5},
    131          {offset: 1, stride: 3},
    132          {offset: 5, stride: 3},
    133      ],
    134  };
    135  const expectedData = new Uint8Array([
    136      0,
    137      9, 10, 0,       // u
    138      0,
    139      11, 12, 0,      // v
    140      0,
    141      1, 2, 3, 4, 0,  // y
    142      5, 6, 7, 8, 0,
    143  ]);
    144  assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
    145  const data = new Uint8Array(expectedData.length);
    146  const layout = await frame.copyTo(data, options);
    147  assert_layout_equals(layout, options.layout);
    148  assert_buffer_equals(data, expectedData);
    149 }, 'Test I420 stride and offset with padding.');
    150 
    151 promise_test(async t => {
    152  const init = {
    153    format: 'I420A',
    154    timestamp: 0,
    155    codedWidth: 4,
    156    codedHeight: 2,
    157  };
    158  const buf = new Uint8Array([
    159    1, 2, 3, 4,     // y
    160    5, 6, 7, 8,
    161    9, 10,          // u
    162    11, 12,         // v
    163    13, 14, 15, 16, // a
    164    17, 18, 19, 20,
    165  ]);
    166  const frame = new VideoFrame(buf, init);
    167  const options = {
    168      layout: [
    169          {offset: 12, stride: 4},
    170          {offset: 8, stride: 2},
    171          {offset: 10, stride: 2},
    172          {offset: 0, stride: 4},
    173      ],
    174  };
    175  const expectedData = new Uint8Array([
    176      13, 14, 15, 16, // a
    177      17, 18, 19, 20,
    178      9, 10,          // u
    179      11, 12,         // v
    180      1, 2, 3, 4,     // y
    181      5, 6, 7, 8,
    182  ]);
    183  assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
    184  const data = new Uint8Array(expectedData.length);
    185  const layout = await frame.copyTo(data, options);
    186  assert_layout_equals(layout, options.layout);
    187  assert_buffer_equals(data, expectedData);
    188 }, 'Test I420A stride and offset work.');
    189 
    190 promise_test(async t => {
    191  const init = {
    192    format: 'NV12',
    193    timestamp: 0,
    194    codedWidth: 4,
    195    codedHeight: 2,
    196  };
    197  const buf = new Uint8Array([
    198    1, 2, 3, 4,   // y
    199    5, 6, 7, 8,
    200    9, 10, 11, 12 // uv
    201  ]);
    202  const frame = new VideoFrame(buf, init);
    203  const options = {
    204      layout: [
    205          {offset: 4, stride: 4},
    206          {offset: 0, stride: 4},
    207      ],
    208  };
    209  const expectedData = new Uint8Array([
    210      9, 10, 11, 12, // uv
    211      1, 2, 3, 4,    // y
    212      5, 6, 7, 8
    213  ]);
    214  assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
    215  const data = new Uint8Array(expectedData.length);
    216  const layout = await frame.copyTo(data, options);
    217  assert_layout_equals(layout, options.layout);
    218  assert_buffer_equals(data, expectedData);
    219 }, 'Test NV12 stride and offset work.');
    220 
    221 promise_test(async t => {
    222  const frame = makeI420_4x2();
    223  const options = {
    224      layout: [
    225          {offset: 0, stride: 1},
    226          {offset: 8, stride: 2},
    227          {offset: 10, stride: 2},
    228      ],
    229  };
    230  assert_throws_js(TypeError, () => frame.allocationSize(options));
    231  const data = new Uint8Array(12);
    232  await promise_rejects_js(t, TypeError, frame.copyTo(data, options));
    233 }, 'Test invalid stride.');
    234 
    235 promise_test(async t => {
    236  const frame = makeI420_4x2();
    237  const options = {
    238      layout: [
    239          {offset: 0, stride: 4},
    240          {offset: 8, stride: 2},
    241          {offset: 2 ** 32 - 2, stride: 2},
    242      ],
    243  };
    244  assert_throws_js(TypeError, () => frame.allocationSize(options));
    245  const data = new Uint8Array(12);
    246  await promise_rejects_js(t, TypeError, frame.copyTo(data, options));
    247 }, 'Test address overflow.');
    248 
    249 promise_test(async t => {
    250  const frame = makeI420_4x2();
    251  const options = {
    252      rect: frame.codedRect,
    253  };
    254  const expectedLayout = [
    255      {offset: 0, stride: 4},
    256      {offset: 8, stride: 2},
    257      {offset: 10, stride: 2},
    258  ];
    259  const expectedData = new Uint8Array([
    260      1, 2, 3, 4, 5, 6, 7, 8,  // y
    261      9, 10,                   // u
    262      11, 12                   // v
    263  ]);
    264  assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
    265  const data = new Uint8Array(expectedData.length);
    266  const layout = await frame.copyTo(data, options);
    267  assert_layout_equals(layout, expectedLayout);
    268  assert_buffer_equals(data, expectedData);
    269 }, 'Test codedRect.');
    270 
    271 promise_test(async t => {
    272  const frame = makeI420_4x2();
    273  const options = {
    274      rect: {x: 0, y: 0, width: 4, height: 0},
    275  };
    276  assert_throws_js(TypeError, () => frame.allocationSize(options));
    277  const data = new Uint8Array(12);
    278  await promise_rejects_js(t, TypeError, frame.copyTo(data, options));
    279 }, 'Test empty rect.');
    280 
    281 promise_test(async t => {
    282  const frame = makeI420_4x2();
    283  const options = {
    284      rect: {x: 2, y: 0, width: 2, height: 2},
    285  };
    286  const expectedLayout = [
    287      {offset: 0, stride: 2},
    288      {offset: 4, stride: 1},
    289      {offset: 5, stride: 1},
    290  ];
    291  const expectedData = new Uint8Array([
    292      3, 4,  // y
    293      7, 8,
    294      10,    // u
    295      12     // v
    296  ]);
    297  assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
    298  const data = new Uint8Array(expectedData.length);
    299  const layout = await frame.copyTo(data, options);
    300  assert_layout_equals(layout, expectedLayout);
    301  assert_buffer_equals(data, expectedData);
    302 }, 'Test left crop.');
    303 
    304 promise_test(async t => {
    305  const frame = makeI420_4x2();
    306  const options = {
    307      rect: {x: 0, y: 0, width: 4, height: 4},
    308  };
    309  assert_throws_js(TypeError, () => frame.allocationSize(options));
    310  const data = new Uint8Array(12);
    311  await promise_rejects_js(t, TypeError, frame.copyTo(data, options));
    312 }, 'Test invalid rect.');
    313 
    314 promise_test(async t => {
    315  let init = {
    316    format: 'I420',
    317    timestamp: 1234,
    318    codedWidth: 8,
    319    codedHeight: 16,
    320    visibleRect: {
    321      x: 2,
    322      y: 2,
    323      width: 4,
    324      height: 8,
    325    },
    326    colorSpace: {
    327      primaries: 'smpte170m',
    328      transfer: 'smpte170m',
    329      matrix: 'smpte170m',
    330      fullRange: false,
    331    }
    332  };
    333 
    334  // Define YUV values for BT.601 red.
    335  const redY = 76;
    336  const redU = 84;
    337  const redV = 255;
    338 
    339  const ySize = init.codedWidth * init.codedHeight;
    340  const uvSize = ySize / 4;
    341  let data = new Uint8Array(ySize + 2 * uvSize);
    342  fillYUV(data, init.codedWidth, init.codedHeight, init.visibleRect, redY, redU,
    343          redV);
    344 
    345  let frame = new VideoFrame(data, init);
    346  assert_equals(frame.codedWidth, init.visibleRect.width);
    347  assert_equals(frame.codedHeight, init.visibleRect.height);
    348  assert_equals(frame.visibleRect.x, 0);
    349  assert_equals(frame.visibleRect.y, 0);
    350  assert_equals(frame.visibleRect.width, init.visibleRect.width);
    351  assert_equals(frame.visibleRect.height, init.visibleRect.height);
    352  assert_equals(frame.displayWidth, init.visibleRect.width);
    353  assert_equals(frame.displayHeight, init.visibleRect.height);
    354 
    355  let options = {rect: frame.visibleRect};
    356  let copied_data = new Uint8Array(frame.allocationSize(options));
    357  await frame.copyTo(copied_data, options);
    358 
    359  // We could write a bunch of code to carefully slice out the visible region
    360  // of `data`, but it's simpler and works better with testharness.js operators
    361  // to just create a fresh packed version for direct comparison.
    362  let packed_data = new Uint8Array(frame.allocationSize(options));
    363  fillYUV(packed_data, frame.codedWidth, frame.codedHeight, frame.visibleRect,
    364          redY, redU, redV);
    365 
    366  assert_array_equals(copied_data, packed_data, `Copied frame data incorrect.`);
    367 }, 'copyTo from byte data with non-default visibleRect');