Skip to content

Commit

Permalink
Add webgl webcodecs video frame test (KhronosGroup#3207)
Browse files Browse the repository at this point in the history
  • Loading branch information
RRRichardLi authored Jan 29, 2021
1 parent ec93da1 commit 301473c
Show file tree
Hide file tree
Showing 2 changed files with 224 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ Use of this source code is governed by an MIT-style license that can be
found in the LICENSE.txt file.
*/

let webgl_webcodecs_test_context_ = null;

function setTestMode(webgl_webcodecs_test_context) {
webgl_webcodecs_test_context_ = webgl_webcodecs_test_context;
}

function requestWebGLVideoFrameHandler(canvas) {
let gl = canvas.getContext('webgl');
if (!gl) {
Expand Down Expand Up @@ -290,8 +296,15 @@ function requestWebGLVideoFrameHandler(canvas) {
let videoFrameHandle = null;
try {
videoFrameHandle = ext.importVideoFrame(frame);
if (webgl_webcodecs_test_context_ != null) {
webgl_webcodecs_test_context_.testPassed("Import frame successfully.");
}
}
catch (error) {
if (webgl_webcodecs_test_context_ != null) {
webgl_webcodecs_test_context_.testFailed("Failed to import Videoframe.");
webgl_webcodecs_test_context_.finishTest();
}
console.log(error.message);
return;
}
Expand All @@ -306,15 +319,27 @@ function requestWebGLVideoFrameHandler(canvas) {
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 6);

if (webgl_webcodecs_test_context_ != null) {
webgl_webcodecs_test_context_.displayed_frame++;
webgl_webcodecs_test_context_.isFramePixelMatched(gl);
}
unbindVideoFrame(gl, videoFrameHandle, gl.TEXTURE0);

// Immediately schedule rendering of the next frame
setTimeout(renderFrame, 0);
ext.releaseVideoFrame(videoFrameHandle);
frame.destroy();
if (webgl_webcodecs_test_context_ != null && webgl_webcodecs_test_context_.displayed_frame
== webgl_webcodecs_test_context_.maxFrameTested) {
webgl_webcodecs_test_context_.finishTest();
}
}

function handleFrame(frame) {
if (webgl_webcodecs_test_context_ != null) {
webgl_webcodecs_test_context_.testPassed("Decode frame successfully.");
}
ready_frames.push(frame);
if (underflow) {
underflow = false;
Expand Down
199 changes: 199 additions & 0 deletions sdk/tests/conformance/extensions/webgl-webcodecs-video-frame.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
<!--
Copyright (c) 2021 The Khronos Group Inc.
Use of this source code is governed by an MIT-style license that can be
found in the LICENSE.txt file.
-->

<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="../../resources/js-test-style.css" />
<script src="../../js/js-test-pre.js"></script>
<script src="../../js/webgl-test-utils.js"></script>
<script src="../../js/tests/out-of-bounds-test.js"></script>
<script src="../../../../extensions/proposals/WEBGL_webcodecs_video_frame/webgl_webcodecs_video_frame.js"></script>
<style>
canvas {
padding: 10px;
background: gold;
}

button {
background-color: #555555;
border: none;
color: white;
padding: 15px 32px;
width: 150px;
text-align: center;
display: block;
font-size: 16px;
}
</style>
</head>

<body>
<canvas id="src" width="640" height="480"></canvas>
<canvas id="dst" width="640" height="480"></canvas>
<p id="info"></p>
<div id="description"></div>
<div id="console"></div>
<script>
"use strict";
description("Test of importing Videoframe from Webcodecs to Webgl");

const kIsRunningTest = true;
const kMaxFrame = 10;
const kTestPixel = [255, 128, 0, 255];
// Sum of pixel difference of R/G/B channel. Use to decide whether a
// pixel is matched with another.
const codec_string = "vp09.00.51.08.00";

let wtu = WebGLTestUtils;
let cnv = document.getElementById("src");
let src_width = cnv.width;
let src_height = cnv.height;
let src_color = "rgba(" + kTestPixel[0].toString() + "," + kTestPixel[1].toString() + ","
+ kTestPixel[2].toString() + "," + kTestPixel[3].toString() + ")";
let frame_counter = 0;
let pixelCompareTolerance = 5;

function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
if (pair[0] == variable) { return pair[1]; }
}
return false;
}

let th = parseInt(getQueryVariable('threshold'));
if (!isNaN(th))
pixelCompareTolerance = th;

async function startDrawing() {
let cnv = document.getElementById("src");
var ctx = cnv.getContext('2d', { alpha: false });

ctx.fillStyle = src_color;
let drawOneFrame = function (time) {
ctx.fillStyle = src_color;
ctx.fillRect(0, 0, src_width, src_height);
window.requestAnimationFrame(drawOneFrame);
}
window.requestAnimationFrame(drawOneFrame);
}

function captureAndEncode(processChunk) {
let cnv = document.getElementById("src");
let fps = 60;
let pending_outputs = 0;
let stream = cnv.captureStream(fps);
let vtr = new VideoTrackReader(stream.getVideoTracks()[0]);

const init = {
output: (chunk) => {
testPassed("Encode frame successfully.");
pending_outputs--;
processChunk(chunk);
},
error: (e) => {
testFailed("Failed to encode frame.");
finishTest();
vtr.stop();
}
};

const config = {
codec: codec_string,
width: cnv.width,
height: cnv.height,
bitrate: 10e6,
framerate: fps,
};

let encoder = new VideoEncoder(init);
encoder.configure(config);

vtr.start((frame) => {
if (pending_outputs > 30) {
console.log("drop this frame");
// Too many frames in flight, encoder is overwhelmed
// let's drop this frame.
return;
}
if (frame_counter == kMaxFrame) {
return;
}

frame_counter++;
pending_outputs++;
const insert_keyframe = (frame_counter % 150) == 0;
encoder.encode(frame, { keyFrame: insert_keyframe });
});
}

function startDecodingAndRendering(cnv, handleFrame) {
const init = {
output: handleFrame,
error: (e) => {
testFailed("Failed to decode frame.");
finishTest();
}
};

const config = {
codec: codec_string,
codedWidth: cnv.width,
codedHeight: cnv.height,
acceleration: "deny",

};

let decoder = new VideoDecoder(init);
decoder.configure(config);
return decoder;
}

function isFramePixelMatched(gl, th_per_pixel = pixelCompareTolerance) {
WebGLTestUtils.checkCanvasRect(gl, 0, 0, src_width, src_width, kTestPixel, "should be orange", pixelCompareTolerance)
}

function main() {
if (!("VideoEncoder" in window)) {
testPassed("WebCodecs API is not supported.");
finishTest();
return;
}
let cnv = document.getElementById("dst");

let webgl_webcodecs_test_context = {
maxFrameTested: kMaxFrame,
displayed_frame: 0,
isFramePixelMatched: isFramePixelMatched,
testFailed: testFailed,
testPassed: testPassed,
finishTest: finishTest
};
setTestMode(webgl_webcodecs_test_context);
let handleFrame = requestWebGLVideoFrameHandler(cnv);
if (handleFrame === null) {
finishTest();
return;
}

startDrawing();
let decoder = startDecodingAndRendering(cnv, handleFrame);
captureAndEncode((chunk) => {
decoder.decode(chunk);
});
}

document.body.onload = main;
</script>

</body>

</html>

0 comments on commit 301473c

Please sign in to comment.