<!DOCTYPE html>
<!--
The most basic GPU smoke test for WebCodecs: Accelerated VideoEncoder encodes
several frames and accelerated VideoDecoder needs to successfully decode them.
-->
<html>
<head>
<title>Encode, decode, render test</title>
<script src="webcodecs_common.js"></script>
<script type="text/javascript">
'use strict';
function showFrameForDebug(frame) {
const cnv = document.getElementById('debug_cnv');
var ctx = cnv.getContext('2d');
ctx.drawImage(frame, 0, 0);
}
async function main(arg) {
const width = 640;
const height = 480;
const frames_to_encode = 32;
let frames_encoded = 0;
let frames_decoded = 0;
let errors = 0;
const encoder_config = {
codec: arg.codec,
hardwareAcceleration: arg.acceleration,
width: width,
height: height,
bitrate: 5000000,
framerate: 24
};
if (arg.codec.startsWith('avc1')) {
encoder_config.avc = { format: 'annexb' };
} else if (arg.codec.startsWith('hvc1')) {
encoder_config.hevc = { format: 'annexb' };
}
TEST.log('Starting test with arguments: ' + JSON.stringify(arg));
let supported = false;
try {
supported = (await VideoEncoder.isConfigSupported(encoder_config)).supported;
} catch (e) {}
if (!supported) {
TEST.skip('Unsupported codec: ' + arg.codec);
return;
}
let decoder = new VideoDecoder({
output(frame) {
let dots = frames_decoded;
// Check that we have intended number of dots and no more.
// Completely black frame shouldn't pass the test.
// Only count dots for sources that can actually pain them.
if (['offscreen', 'arraybuffer'].includes(arg.source_type)) {
if(!validateBlackDots(frame, dots) ||
validateBlackDots(frame, dots + 1)) {
showFrameForDebug(frame);
TEST.reportFailure(
`Unexpected dot count ts:${frame.timestamp} dots:${dots}`);
}
}
frames_decoded++;
frame.close();
},
error(e) {
errors++;
TEST.log(e);
}
});
let timestamps = [];
const encoder_init = {
output(chunk, metadata) {
let config = metadata.decoderConfig;
if (config) {
decoder.configure(config);
}
decoder.decode(chunk);
frames_encoded++;
let expected_timestamp = timestamps.shift();
TEST.assert(
chunk.timestamp == expected_timestamp,
`EncodedVideoChunk timestamp mismatch. Expected: ${
expected_timestamp} got ${chunk.timestamp}`);
},
error(e) {
errors++;
TEST.log(e);
}
};
let encoder = new VideoEncoder(encoder_init);
encoder.configure(encoder_config);
let source = await createFrameSource(arg.source_type, width, height);
if (!source) {
TEST.skip('Unsupported source: ' + arg.source_type);
return;
}
for (let i = 0; i < frames_to_encode; i++) {
let frame = await source.getNextFrame();
let keyframe = (i % 10 == 0);
timestamps.push(frame.timestamp);
encoder.encode(frame, { keyFrame: keyframe });
frame.close();
await waitForNextFrame();
}
await encoder.flush();
await decoder.flush();
encoder.close();
decoder.close();
source.close();
TEST.assert(
frames_encoded == frames_to_encode,
'frames_encoded mismatch: ' + frames_encoded);
TEST.assert(
frames_decoded == frames_to_encode,
'frames_decoded mismatch: ' + frames_decoded);
TEST.assert(
errors == 0, 'Decoding or encoding errors occurred during the test');
TEST.log('Test completed');
}
addManualTestButton([{
'source_type': 'offscreen',
'codec': 'avc1.42001E',
'acceleration':'prefer-software'
},
{
'source_type': 'camera',
'codec': 'avc1.42001E',
'acceleration':'prefer-software'
},
{
'source_type': 'offscreen',
'codec': 'avc1.42001E',
'acceleration':'prefer-hardware'
}]);
</script>
</head>
<body>
<canvas id="debug_cnv" width="640" height="480"></canvas>
</body>
</html>