| <!-- |
| 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 processor = new MediaStreamTrackProcessor(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); |
| |
| const frame_reader = processor.readable.getReader(); |
| frame_reader.read().then(function processFrame({done, value}) { |
| if (done) |
| return; |
| |
| if (pending_outputs > 30) { |
| console.log("drop this frame"); |
| // Too many frames in flight, encoder is overwhelmed |
| // let's drop this frame. |
| value.close(); |
| frame_reader.read().then(processFrame); |
| return; |
| } |
| |
| if(frame_counter == kMaxFrame) { |
| frame_reader.releaseLock(); |
| processor.readable.cancel(); |
| value.close(); |
| return; |
| } |
| |
| frame_counter++; |
| pending_outputs++; |
| const insert_keyframe = (frame_counter % 150) == 0; |
| encoder.encode(value, { keyFrame: insert_keyframe }); |
| |
| frame_reader.read().then(processFrame); |
| }); |
| } |
| |
| 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> |