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 }