| <style> |
| canvas { |
| width: 300px; |
| height: 300px; |
| } |
| </style> |
| </head> |
| <script id="vertexShaderSource" type="text/glsl"> |
| attribute vec4 a_position; |
| varying vec2 v_texturePosition; |
| |
| void main() { |
| v_texturePosition = vec2((a_position.x + 1.0) / 2.0, (a_position.y + 1.0) / 2.0); |
| gl_Position = a_position; |
| } |
| </script> |
| <script id="fragmentShaderSource" type="text/glsl"> |
| precision mediump float; |
| |
| varying vec2 v_texturePosition; |
| |
| uniform sampler2D texture; |
| |
| void main() { |
| gl_FragColor = texture2D(texture, v_texturePosition); |
| } |
| </script> |
| <script> |
| |
| if (window.testRunner) { |
| testRunner.waitUntilDone(); |
| testRunner.dumpAsText(); |
| } |
| |
| const width = 300; |
| const height = 300; |
| let canvas; |
| let gl; |
| let animationFrame = null; |
| let tested = false; |
| let drawFunction = null; |
| let video; |
| |
| function output(msg) { |
| const div = document.getElementById("output"); |
| div.innerHTML += `${msg}<br>`; |
| } |
| |
| function isMostlyRed(buffer, x, y) { |
| let offset = (y * width + x) * 4; |
| return buffer[offset] > 240 && buffer[offset+1] < 25 && buffer[offset+2] < 25 && buffer[offset+3] > 240; |
| } |
| |
| function isMostlyBlue(buffer, x, y) { |
| let offset = (y * width + x) * 4; |
| return buffer[offset] < 25 && buffer[offset+1] < 25 && buffer[offset+2] > 240 && buffer[offset+3] > 240; |
| } |
| |
| function runTest() { |
| output("Checking the canvas pixels."); |
| |
| drawFunction(); |
| let pixels = new Uint8Array(width * height * 4); |
| gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); |
| |
| if (isMostlyRed(pixels, 2, 2)) |
| output("PASS: Bottom edge is red."); |
| else |
| output("FAIL: Bottom edge is not red."); |
| |
| if (isMostlyBlue(pixels, 2, height - 2)) |
| output("PASS: Top edge is blue."); |
| else |
| output("FAIL: Top edge is not blue."); |
| |
| cancelAnimationFrame(animationFrame); |
| video.pause(); |
| |
| if (window.testRunner) |
| testRunner.notifyDone(); |
| } |
| |
| function init() { |
| |
| canvas = document.querySelector("canvas"); |
| canvas.width = width; |
| canvas.height = height; |
| |
| gl = canvas.getContext("webgl"); |
| |
| gl.clearColor(0, 0, 0, 1); |
| |
| let vertexShader = gl.createShader(gl.VERTEX_SHADER); |
| gl.shaderSource(vertexShader, document.getElementById("vertexShaderSource").textContent); |
| gl.compileShader(vertexShader); |
| if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { |
| console.error("Vertex Shader failed to compile."); |
| console.log(gl.getShaderInfoLog(vertexShader)); |
| return; |
| } |
| |
| let fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); |
| gl.shaderSource(fragmentShader, document.getElementById("fragmentShaderSource").textContent); |
| gl.compileShader(fragmentShader); |
| if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { |
| console.error("Fragment Shader failed to compile."); |
| console.log(gl.getShaderInfoLog(fragmentShader)); |
| return; |
| } |
| |
| let program = gl.createProgram(); |
| gl.attachShader(program, vertexShader); |
| gl.attachShader(program, fragmentShader); |
| gl.linkProgram(program); |
| |
| if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { |
| console.error("Unable to link shaders into program."); |
| return; |
| } |
| |
| gl.useProgram(program); |
| let textureUniform = gl.getUniformLocation(program, "texture"); |
| let positionAttribute = gl.getAttribLocation(program, "a_position"); |
| gl.enableVertexAttribArray(positionAttribute); |
| |
| // Unlike the corresponding texImage2D-video-flipY-false test, draw a quad using |
| // four points in a triangle strip, with 3 floats per point. This way we're doing |
| // something different from the WebCore code that renders the video into a texture, |
| // ensuring that state restoration is correctly restored. |
| let vertices = new Float32Array([-1.0, -1.0, 0.0, 1.0, -1.0, 0.0, -1.0, 1.0, 0.0, 1.0, 1.0, 0.0]); |
| |
| let buffer = gl.createBuffer(); |
| gl.bindBuffer(gl.ARRAY_BUFFER, buffer); |
| gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); |
| gl.vertexAttribPointer(positionAttribute, 3, gl.FLOAT, false, 0, 0); |
| |
| let updateTexture = function (texture, data) { |
| gl.bindTexture(gl.TEXTURE_2D, texture); |
| gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); |
| gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, data); |
| gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); |
| gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); |
| gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); |
| gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); |
| |
| gl.bindTexture(gl.TEXTURE_2D, null); |
| } |
| |
| let texture = gl.createTexture(); |
| |
| video = document.createElement("video"); |
| video.loop = true; |
| video.playsInline = true; |
| |
| video.addEventListener("canplay", function () { |
| output("Video can play."); |
| }, false); |
| |
| video.addEventListener("timeupdate", function () { |
| // If we've just started, jump forward to 3 seconds. If we've played a bit after |
| // that, check the pixels. |
| if (video.currentTime < 1) { |
| video.currentTime = 3; |
| return; |
| } else if (video.currentTime > 3.1) { |
| if (!tested) |
| runTest(); |
| tested = true; |
| } |
| }, false); |
| |
| output("Setting video src."); |
| window.source = new MediaSource(); |
| source.addEventListener('sourceopen', async event => { |
| var buffer = source.addSourceBuffer('video/mp4'); |
| var response = await fetch('resources/orientation-normal-fragmented.mp4'); |
| buffer.appendBuffer(await response.arrayBuffer()); |
| }); |
| video.srcObject = source; |
| video.play(); |
| |
| drawFunction = function () { |
| gl.clear(gl.COLOR_BUFFER_BIT); |
| |
| if (video.currentTime > 0) { |
| updateTexture(texture, video); |
| } |
| |
| gl.activeTexture(gl.TEXTURE0); |
| gl.bindTexture(gl.TEXTURE_2D, texture); |
| gl.uniform1i(textureUniform, 0); |
| |
| gl.bindBuffer(gl.ARRAY_BUFFER, buffer); |
| |
| gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); |
| |
| animationFrame = requestAnimationFrame(drawFunction); |
| }; |
| |
| drawFunction(); |
| } |
| |
| window.addEventListener("load", init, false); |
| </script> |
| <body> |
| <canvas></canvas> |
| <div id="output"> |
| </div> |
| </body> |