per-frame-qp-encoding.https.any.js (3699B)
1 // META: global=window,dedicatedworker 2 // META: script=/webcodecs/video-encoder-utils.js 3 // META: variant=?av1 4 // META: variant=?vp9_p0 5 // META: variant=?vp9_p2 6 7 function get_config() { 8 const config = { 9 '?av1': {codec: 'av01.0.04M.08'}, 10 '?vp8': {codec: 'vp8'}, 11 '?vp9_p0': {codec: 'vp09.00.10.08'}, 12 '?vp9_p2': {codec: 'vp09.02.10.10'}, 13 '?h264': {codec: 'avc1.42001E', avc: {format: 'annexb'}}, 14 '?h265': {codec: 'hev1.1.6.L93.90', hevc: {format: 'annexb'}} 15 }[location.search]; 16 config.width = 320; 17 config.height = 200; 18 config.bitrate = 1000000; 19 config.bitrateMode = 'quantizer'; 20 config.framerate = 30; 21 return config; 22 } 23 24 function get_qp_range() { 25 switch (location.search) { 26 case '?av1': 27 return {min: 1, max: 255}; 28 case '?vp9_p0': 29 return {min: 1, max: 255}; 30 case '?vp9_p2': 31 return {min: 1, max: 255}; 32 case '?h264': 33 return {min: 1, max: 51}; 34 case '?h265': 35 return {min: 1, max: 51}; 36 } 37 return null; 38 } 39 40 function set_qp(options, value) { 41 switch (location.search) { 42 case '?av1': 43 options.av1 = {quantizer: value}; 44 return; 45 case '?vp9_p0': 46 options.vp9 = {quantizer: value}; 47 return; 48 case '?vp9_p2': 49 options.vp9 = {quantizer: value}; 50 return; 51 case '?h264': 52 options.avc = {quantizer: value}; 53 return; 54 case '?h265': 55 options.hevc = {quantizer: value}; 56 } 57 } 58 59 async function per_frame_qp_test(t, encoder_config, qp_range, validate_result) { 60 const w = encoder_config.width; 61 const h = encoder_config.height; 62 await checkEncoderSupport(t, encoder_config); 63 64 const frames_to_encode = 12; 65 let frames_decoded = 0; 66 let frames_encoded = 0; 67 let chunks = []; 68 let corrupted_frames = []; 69 70 const encoder_init = { 71 output(chunk, metadata) { 72 frames_encoded++; 73 chunks.push(chunk); 74 }, 75 error(e) { 76 assert_unreached(e.message); 77 } 78 }; 79 80 let encoder = new VideoEncoder(encoder_init); 81 encoder.configure(encoder_config); 82 83 let qp = qp_range.min; 84 for (let i = 0; i < frames_to_encode; i++) { 85 let frame = createDottedFrame(w, h, i); 86 let encode_options = {keyFrame: false}; 87 set_qp(encode_options, qp); 88 encoder.encode(frame, encode_options); 89 frame.close(); 90 qp += 3; 91 if (qp > qp_range.max) { 92 qp = qp_range.min 93 } 94 } 95 await encoder.flush(); 96 97 let decoder = new VideoDecoder({ 98 output(frame) { 99 frames_decoded++; 100 // Check that we have intended number of dots and no more. 101 // Completely black frame shouldn't pass the test. 102 if (validate_result && !validateBlackDots(frame, frame.timestamp) || 103 validateBlackDots(frame, frame.timestamp + 1)) { 104 corrupted_frames.push(frame.timestamp) 105 } 106 frame.close(); 107 }, 108 error(e) { 109 assert_unreached(e.message); 110 } 111 }); 112 113 let decoder_config = { 114 codec: encoder_config.codec, 115 codedWidth: w, 116 codedHeight: h, 117 }; 118 decoder.configure(decoder_config); 119 120 for (let chunk of chunks) { 121 decoder.decode(chunk); 122 } 123 await decoder.flush(); 124 125 encoder.close(); 126 decoder.close(); 127 assert_equals(frames_encoded, frames_to_encode); 128 assert_equals(chunks.length, frames_to_encode); 129 assert_equals(frames_decoded, frames_to_encode); 130 assert_equals( 131 corrupted_frames.length, 0, `corrupted_frames: ${corrupted_frames}`); 132 } 133 134 promise_test(async t => { 135 let config = get_config(); 136 let range = get_qp_range(); 137 return per_frame_qp_test(t, config, range, false); 138 }, 'Frame QP encoding, full range'); 139 140 promise_test(async t => { 141 let config = get_config(); 142 return per_frame_qp_test(t, config, {min: 1, max: 20}, true); 143 }, 'Frame QP encoding, good range with validation');