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