videoDecoder-codec-specific-setup.js (8353B)
1 const AV1_DATA = { 2 src: 'av1.mp4', 3 config: { 4 codec: 'av01.0.04M.08', 5 codedWidth: 320, 6 codedHeight: 240, 7 visibleRect: {x: 0, y: 0, width: 320, height: 240}, 8 displayWidth: 320, 9 displayHeight: 240, 10 }, 11 chunks: [ 12 {offset: 48, size: 1938}, {offset: 1986, size: 848}, 13 {offset: 2834, size: 3}, {offset: 2837, size: 47}, {offset: 2884, size: 3}, 14 {offset: 2887, size: 116}, {offset: 3003, size: 3}, 15 {offset: 3006, size: 51}, {offset: 3057, size: 25}, 16 {offset: 3082, size: 105} 17 ] 18 }; 19 20 const VP8_DATA = { 21 src: 'vp8.webm', 22 config: { 23 codec: 'vp8', 24 codedWidth: 320, 25 codedHeight: 240, 26 visibleRect: {x: 0, y: 0, width: 320, height: 240}, 27 displayWidth: 320, 28 displayHeight: 240, 29 }, 30 chunks: [ 31 {offset: 522, size: 4826}, {offset: 5355, size: 394}, 32 {offset: 5756, size: 621}, {offset: 6384, size: 424}, 33 {offset: 6815, size: 532}, {offset: 7354, size: 655}, 34 {offset: 8016, size: 670}, {offset: 8693, size: 2413}, 35 {offset: 11113, size: 402}, {offset: 11522, size: 686} 36 ] 37 }; 38 39 const VP9_DATA = { 40 src: 'vp9.mp4', 41 // TODO(sandersd): Verify that the file is actually level 1. 42 config: { 43 codec: 'vp09.00.10.08', 44 codedWidth: 320, 45 codedHeight: 240, 46 displayAspectWidth: 320, 47 displayAspectHeight: 240, 48 }, 49 chunks: [ 50 {offset: 44, size: 3315}, {offset: 3359, size: 203}, 51 {offset: 3562, size: 245}, {offset: 3807, size: 172}, 52 {offset: 3979, size: 312}, {offset: 4291, size: 170}, 53 {offset: 4461, size: 195}, {offset: 4656, size: 181}, 54 {offset: 4837, size: 356}, {offset: 5193, size: 159} 55 ] 56 }; 57 58 const H264_AVC_DATA = { 59 src: 'h264.mp4', 60 config: { 61 codec: 'avc1.64000b', 62 description: {offset: 9490, size: 45}, 63 codedWidth: 320, 64 codedHeight: 240, 65 displayAspectWidth: 320, 66 displayAspectHeight: 240, 67 }, 68 chunks: [ 69 {offset: 48, size: 4140}, {offset: 4188, size: 604}, 70 {offset: 4792, size: 475}, {offset: 5267, size: 561}, 71 {offset: 5828, size: 587}, {offset: 6415, size: 519}, 72 {offset: 6934, size: 532}, {offset: 7466, size: 523}, 73 {offset: 7989, size: 454}, {offset: 8443, size: 528} 74 ] 75 }; 76 77 const H264_SEI_AVC_DATA = { 78 src: 'h264_sei.mp4', 79 config: { 80 codec: 'avc1.64000b', 81 description: {offset: 11989, size: 46}, 82 codedWidth: 320, 83 codedHeight: 240, 84 displayAspectWidth: 320, 85 displayAspectHeight: 240, 86 }, 87 chunks: [ 88 {offset: 48, size: 4229, key: true}, {offset: 4277, size: 1114}, 89 {offset: 5391, size: 320}, {offset: 5711, size: 188}, 90 {offset: 5899, size: 173}, {offset: 6072, size: 3694, key: true}, 91 {offset: 9766, size: 936}, {offset: 10702, size: 345}, 92 {offset: 11047, size: 213}, {offset: 11260, size: 210} 93 ] 94 }; 95 96 const H264_ANNEXB_DATA = { 97 src: 'h264.annexb', 98 config: { 99 codec: 'avc1.64000b', 100 codedWidth: 320, 101 codedHeight: 240, 102 displayAspectWidth: 320, 103 displayAspectHeight: 240, 104 }, 105 chunks: [ 106 {offset: 0, size: 4175}, {offset: 4175, size: 602}, 107 {offset: 4777, size: 473}, {offset: 5250, size: 559}, 108 {offset: 5809, size: 585}, {offset: 6394, size: 517}, 109 {offset: 6911, size: 530}, {offset: 7441, size: 521}, 110 {offset: 7962, size: 452}, {offset: 8414, size: 526} 111 ] 112 }; 113 114 const H264_SEI_ANNEXB_DATA = { 115 src: 'h264_sei.annexb', 116 config: { 117 codec: 'avc1.64000b', 118 codedWidth: 320, 119 codedHeight: 240, 120 displayAspectWidth: 320, 121 displayAspectHeight: 240, 122 }, 123 chunks: [ 124 {offset: 0, size: 4264, key: true}, {offset: 4264, size: 1112}, 125 {offset: 5376, size: 318}, {offset: 5694, size: 186}, 126 {offset: 5880, size: 171}, {offset: 6051, size: 3729, key: true}, 127 {offset: 9780, size: 934}, {offset: 10714, size: 343}, 128 {offset: 11057, size: 211}, {offset: 11268, size: 208} 129 ] 130 }; 131 132 const H265_HEVC_DATA = { 133 src: 'h265.mp4', 134 config: { 135 codec: 'hev1.1.6.L60.90', 136 description: {offset: 5821, size: 2406}, 137 codedWidth: 320, 138 codedHeight: 240, 139 displayAspectWidth: 320, 140 displayAspectHeight: 240, 141 }, 142 chunks: [ 143 {offset: 44, size: 2515}, {offset: 2559, size: 279}, 144 {offset: 2838, size: 327}, {offset: 3165, size: 329}, 145 {offset: 3494, size: 308}, {offset: 3802, size: 292}, 146 {offset: 4094, size: 352}, {offset: 4446, size: 296}, 147 {offset: 4742, size: 216}, {offset: 4958, size: 344} 148 ] 149 }; 150 151 const H265_ANNEXB_DATA = { 152 src: 'h265.annexb', 153 config: { 154 codec: 'hev1.1.6.L60.90', 155 codedWidth: 320, 156 codedHeight: 240, 157 displayAspectWidth: 320, 158 displayAspectHeight: 240, 159 }, 160 chunks: [ 161 {offset: 0, size: 4894}, {offset: 4894, size: 279}, 162 {offset: 5173, size: 327}, {offset: 5500, size: 329}, 163 {offset: 5829, size: 308}, {offset: 6137, size: 292}, 164 {offset: 6429, size: 352}, {offset: 6781, size: 296}, 165 {offset: 7077, size: 216}, {offset: 7293, size: 344} 166 ] 167 }; 168 169 // Note: We only test AVC format since with AnnexB there's not enough 170 // information ahead of the first decode() call to determine if the content is 171 // interlaced or not. 172 const H264_INTERLACED_AVC_DATA = { 173 src: 'h264_interlaced.mp4', 174 config: { 175 codec: 'avc1.64000b', 176 description: {offset: 7501, size: 47}, 177 codedWidth: 320, 178 codedHeight: 240, 179 displayAspectWidth: 320, 180 displayAspectHeight: 240, 181 }, 182 chunks: [ 183 {offset: 48, size: 4091}, {offset: 4139, size: 949}, 184 {offset: 5088, size: 260}, {offset: 5348, size: 134}, 185 {offset: 5482, size: 111}, {offset: 5593, size: 660}, 186 {offset: 6253, size: 197}, {offset: 6450, size: 96}, 187 {offset: 6546, size: 159}, {offset: 6705, size: 277} 188 ] 189 }; 190 191 // Allows mutating `callbacks` after constructing the VideoDecoder, wraps calls 192 // in t.step(). 193 function createVideoDecoder(t, callbacks) { 194 return new VideoDecoder({ 195 output(frame) { 196 if (callbacks && callbacks.output) { 197 t.step(() => callbacks.output(frame)); 198 } else { 199 t.unreached_func('unexpected output()'); 200 } 201 }, 202 error(e) { 203 if (callbacks && callbacks.error) { 204 t.step(() => callbacks.error(e)); 205 } else { 206 t.unreached_func('unexpected error()'); 207 } 208 } 209 }); 210 } 211 212 function createCorruptChunk(index) { 213 let bad_data = CHUNK_DATA[index]; 214 // AV1 may require more extensive corruption to trigger decoding errors with 215 // some software decoders (e.g., dav1d). 216 let skip = CONFIG.codec.indexOf('av01') >= 0 ? 2 : 4; 217 for (var i = 0; i < bad_data.byteLength; i += skip) { 218 bad_data[i] = 0xFF; 219 } 220 return new EncodedVideoChunk( 221 {type: 'delta', timestamp: index, data: bad_data}); 222 } 223 224 // Create a view of an ArrayBuffer. 225 function view(buffer, {offset, size}) { 226 return new Uint8Array(buffer, offset, size); 227 } 228 229 async function checkImplements() { 230 // Don't run any tests if the codec is not supported. 231 assert_equals('function', typeof VideoDecoder.isConfigSupported); 232 let supported = false; 233 try { 234 // TODO(sandersd): To properly support H.264 in AVC format, this should 235 // include the `description`. For now this test assumes that H.264 Annex B 236 // support is the same as H.264 AVC support. 237 const support = await VideoDecoder.isConfigSupported({codec: CONFIG.codec}); 238 supported = support.supported; 239 } catch (e) { 240 } 241 assert_implements_optional(supported, CONFIG.codec + ' unsupported'); 242 } 243 244 let CONFIG = null; 245 let CHUNK_DATA = null; 246 let CHUNKS = null; 247 promise_setup(async () => { 248 const data = { 249 '?av1': AV1_DATA, 250 '?vp8': VP8_DATA, 251 '?vp9': VP9_DATA, 252 '?h264_avc': H264_AVC_DATA, 253 '?h264_sei_avc': H264_SEI_AVC_DATA, 254 '?h264_interlaced_avc': H264_INTERLACED_AVC_DATA, 255 '?h264_annexb': H264_ANNEXB_DATA, 256 '?h264_sei_annexb': H264_SEI_ANNEXB_DATA, 257 '?h265_hevc': H265_HEVC_DATA, 258 '?h265_annexb': H265_ANNEXB_DATA 259 }[location.search]; 260 261 // Fetch the media data and prepare buffers. 262 const response = await fetch(data.src); 263 const buf = await response.arrayBuffer(); 264 265 CONFIG = {...data.config}; 266 if (data.config.description) { 267 CONFIG.description = view(buf, data.config.description); 268 } 269 270 CHUNK_DATA = data.chunks.map((chunk, i) => view(buf, chunk)); 271 272 CHUNKS = 273 CHUNK_DATA.map((encoded_data, i) => new EncodedVideoChunk({ 274 type: (i == 0 || data.chunks[i].key) ? 'key' : 'delta', 275 timestamp: i, 276 duration: 1, 277 data: encoded_data 278 })); 279 });