full-cycle-test.https.any.js (7246B)
1 // META: timeout=long 2 // META: global=window,dedicatedworker 3 // META: script=/webcodecs/video-encoder-utils.js 4 // META: variant=?av1 5 // META: variant=?av1_444_high 6 // META: variant=?vp8 7 // META: variant=?vp9_p0 8 // META: variant=?vp9_p2 9 // META: variant=?vp9_444_p1 10 // META: variant=?vp9_444_p3 11 // META: variant=?h264_avc 12 // META: variant=?h264_annexb 13 // META: variant=?h265_hevc 14 // META: variant=?h265_annexb 15 16 var ENCODER_CONFIG = null; 17 promise_setup(async () => { 18 const config = { 19 '?av1': { 20 codec: 'av01.0.04M.08', 21 hasEmbeddedColorSpace: true, 22 hardwareAcceleration: 'prefer-software', 23 }, 24 '?av1_444_high': { 25 codec: 'av01.1.04M.08.0.000', 26 hasEmbeddedColorSpace: true, 27 hardwareAcceleration: 'prefer-software', 28 outputPixelFormat: 'I444', 29 }, 30 '?vp8': { 31 codec: 'vp8', 32 hasEmbeddedColorSpace: false, 33 hardwareAcceleration: 'prefer-software', 34 }, 35 '?vp9_p0': { 36 codec: 'vp09.00.10.08', 37 hasEmbeddedColorSpace: true, 38 hardwareAcceleration: 'prefer-software', 39 }, 40 '?vp9_p2': { 41 codec: 'vp09.02.10.10', 42 hasEmbeddedColorSpace: true, 43 hardwareAcceleration: 'prefer-software', 44 // TODO(https://github.com/w3c/webcodecs/issues/384): 45 // outputPixelFormat should be 'I420P10' 46 }, 47 '?vp9_444_p1': { 48 codec: 'vp09.01.10.08.03', 49 hasEmbeddedColorSpace: true, 50 hardwareAcceleration: 'prefer-software', 51 outputPixelFormat: 'I444', 52 }, 53 '?vp9_444_p3': { 54 codec: 'vp09.03.10.10.03', 55 hasEmbeddedColorSpace: true, 56 hardwareAcceleration: 'prefer-software', 57 // TODO(https://github.com/w3c/webcodecs/issues/384): 58 // outputPixelFormat should be 'I444P10' 59 }, 60 '?h264_avc': { 61 codec: 'avc1.42001E', 62 avc: {format: 'avc'}, 63 hasEmbeddedColorSpace: true, 64 hardwareAcceleration: 'prefer-software', 65 }, 66 '?h264_annexb': { 67 codec: 'avc1.42001E', 68 avc: {format: 'annexb'}, 69 hasEmbeddedColorSpace: true, 70 hardwareAcceleration: 'prefer-software', 71 }, 72 '?h265_hevc': { 73 codec: 'hvc1.1.6.L123.00', 74 hevc: {format: 'hevc'}, 75 hasEmbeddedColorSpace: true, 76 hardwareAcceleration: 'prefer-hardware', 77 }, 78 '?h265_annexb': { 79 codec: 'hvc1.1.6.L123.00', 80 hevc: {format: 'annexb'}, 81 hasEmbeddedColorSpace: true, 82 hardwareAcceleration: 'prefer-hardware', 83 } 84 }[location.search]; 85 config.width = 320; 86 config.height = 200; 87 config.bitrate = 1000000; 88 config.bitrateMode = "constant"; 89 config.framerate = 30; 90 ENCODER_CONFIG = config; 91 }); 92 93 async function runFullCycleTest(t, options) { 94 let encoder_config = { ...ENCODER_CONFIG }; 95 if (options.realTimeLatencyMode) { 96 encoder_config.latencyMode = 'realtime'; 97 } 98 let encoder_color_space = {}; 99 const w = encoder_config.width; 100 const h = encoder_config.height; 101 let next_ts = 0 102 let frames_to_encode = 16; 103 let frames_encoded = 0; 104 let frames_decoded = 0; 105 106 await checkEncoderSupport(t, encoder_config); 107 let decoder = new VideoDecoder({ 108 output(frame) { 109 t.add_cleanup(() => { frame.close() }); 110 111 assert_equals(frame.visibleRect.width, w, "visibleRect.width"); 112 assert_equals(frame.visibleRect.height, h, "visibleRect.height"); 113 if (!options.realTimeLatencyMode) { 114 assert_equals(frame.timestamp, next_ts++, "decode timestamp"); 115 } 116 117 if (ENCODER_CONFIG.outputPixelFormat) { 118 assert_equals( 119 frame.format, ENCODER_CONFIG.outputPixelFormat, 120 "decoded pixel format"); 121 } 122 123 // The encoder is allowed to change the color space to satisfy the 124 // encoder when readback is needed to send the frame for encoding, but 125 // the decoder shouldn't change it after the fact. 126 assert_equals( 127 frame.colorSpace.primaries, encoder_color_space.primaries, 128 'colorSpace.primaries'); 129 assert_equals( 130 frame.colorSpace.transfer, encoder_color_space.transfer, 131 'colorSpace.transfer'); 132 assert_equals( 133 frame.colorSpace.matrix, encoder_color_space.matrix, 134 'colorSpace.matrix'); 135 assert_equals( 136 frame.colorSpace.fullRange, encoder_color_space.fullRange, 137 'colorSpace.fullRange'); 138 139 frames_decoded++; 140 assert_true(validateBlackDots(frame, frame.timestamp), 141 "frame doesn't match. ts: " + frame.timestamp); 142 }, 143 error(e) { 144 assert_unreached(e.message); 145 } 146 }); 147 148 let next_encode_ts = 0; 149 const encoder_init = { 150 output(chunk, metadata) { 151 let config = metadata.decoderConfig; 152 // Issue a configure if there's a new config, or on the 153 // first chunk if testing rate control 154 if (!options.rateControl && config || 155 options.rateControl && chunk.timestamp == 0) { 156 config.hardwareAcceleration = encoder_config.hardwareAcceleration; 157 encoder_color_space = config.colorSpace; 158 159 // Removes the color space provided by the encoder so that color space 160 // information in the underlying bitstream is exposed during decode. 161 if (options.stripDecoderConfigColorSpace) 162 config.colorSpace = {}; 163 164 decoder.configure(config); 165 } 166 decoder.decode(chunk); 167 frames_encoded++; 168 if (!options.realTimeLatencyMode) { 169 assert_equals(chunk.timestamp, next_encode_ts++, "encode timestamp"); 170 } 171 }, 172 error(e) { 173 assert_unreached(e.message); 174 } 175 }; 176 177 let encoder = new VideoEncoder(encoder_init); 178 encoder.configure(encoder_config); 179 180 for (let i = 0; i < frames_to_encode; i++) { 181 let frame = createDottedFrame(w, h, i); 182 183 // Frames should have a valid color space when created from canvas. 184 assert_not_equals(frame.colorSpace.primaries, null, 'colorSpace.primaries'); 185 assert_not_equals(frame.colorSpace.transfer, null, 'colorSpace.transfer'); 186 assert_not_equals(frame.colorSpace.matrix, null, 'colorSpace.matrix'); 187 assert_not_equals(frame.colorSpace.fullRange, null, 'colorSpace.fullRange'); 188 189 let keyframe = (i % 5 == 0); 190 encoder.encode(frame, { keyFrame: keyframe }); 191 if (i % 3 == 0 && options.rateControl) { 192 // reconfigure with a different rate 193 encoder_config.bitrate = encoder_config.bitrate * 0.9; 194 encoder.configure(encoder_config); 195 } 196 frame.close(); 197 } 198 await encoder.flush(); 199 await decoder.flush(); 200 encoder.close(); 201 decoder.close(); 202 if (options.realTimeLatencyMode) { 203 assert_greater_than(frames_encoded, 0, "frames_encoded"); 204 } else { 205 assert_equals(frames_encoded, frames_to_encode, "frames_encoded"); 206 } 207 assert_equals(frames_decoded, frames_encoded, "frames_decoded"); 208 } 209 210 promise_test(async t => { 211 return runFullCycleTest(t, {}); 212 }, 'Encoding and decoding cycle'); 213 214 promise_test(async t => { 215 return runFullCycleTest(t, {realTimeLatencyMode: true}); 216 }, 'Encoding and decoding cycle with realtime latency mode'); 217 218 promise_test(async t => { 219 if (ENCODER_CONFIG.hasEmbeddedColorSpace) 220 return runFullCycleTest(t, {stripDecoderConfigColorSpace: true}); 221 }, 'Encoding and decoding cycle w/ stripped color space'); 222 223 promise_test(async t => { 224 return runFullCycleTest(t, {rateControl: true}); 225 }, 'Encoding and decoding cycle w/ rate control');