function Texture(gl) { this.gl = gl this.texture = gl.createTexture() gl.bindTexture(gl.TEXTURE_2D, this.texture) 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) } Texture.prototype.bind = function(n, program, name) { var gl = this.gl gl.activeTexture([gl.TEXTURE0, gl.TEXTURE1, gl.TEXTURE2][n]) gl.bindTexture(gl.TEXTURE_2D, this.texture) gl.uniform1i(gl.getUniformLocation(program, name), n) } Texture.prototype.fill = function(width, height, data) { var gl = this.gl gl.bindTexture(gl.TEXTURE_2D, this.texture) gl.texImage2D( gl.TEXTURE_2D, 0, gl.LUMINANCE, width, height, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data ) } export function WebGLPlayer(canvas) { this.canvas = canvas this.gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl') this.initGL() } WebGLPlayer.prototype.initGL = function() { if (!this.gl) { console.log('[ER] WebGL not supported.') return } var gl = this.gl gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1) var program = gl.createProgram() var vertexShaderSource = [ 'attribute highp vec4 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'varying highp vec2 vTextureCoord;', 'void main(void) {', ' gl_Position = aVertexPosition;', ' vTextureCoord = aTextureCoord;', '}', ].join('\n') var vertexShader = gl.createShader(gl.VERTEX_SHADER) gl.shaderSource(vertexShader, vertexShaderSource) gl.compileShader(vertexShader) var fragmentShaderSource = [ 'precision highp float;', 'varying lowp vec2 vTextureCoord;', 'uniform sampler2D YTexture;', 'uniform sampler2D UTexture;', 'uniform sampler2D VTexture;', 'const mat4 YUV2RGB = mat4', '(', ' 1.1643828125, 0, 1.59602734375, -.87078515625,', ' 1.1643828125, -.39176171875, -.81296875, .52959375,', ' 1.1643828125, 2.017234375, 0, -1.081390625,', ' 0, 0, 0, 1', ');', 'void main(void) {', ' gl_FragColor = vec4( texture2D(YTexture, vTextureCoord).x, texture2D(UTexture, vTextureCoord).x, texture2D(VTexture, vTextureCoord).x, 1) * YUV2RGB;', '}', ].join('\n') var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER) gl.shaderSource(fragmentShader, fragmentShaderSource) gl.compileShader(fragmentShader) gl.attachShader(program, vertexShader) gl.attachShader(program, fragmentShader) gl.linkProgram(program) gl.useProgram(program) if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { console.log('[ER] Shader link failed.') } var vertexPositionAttribute = gl.getAttribLocation(program, 'aVertexPosition') gl.enableVertexAttribArray(vertexPositionAttribute) var textureCoordAttribute = gl.getAttribLocation(program, 'aTextureCoord') gl.enableVertexAttribArray(textureCoordAttribute) var verticesBuffer = gl.createBuffer() gl.bindBuffer(gl.ARRAY_BUFFER, verticesBuffer) gl.bufferData( gl.ARRAY_BUFFER, 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, ]), gl.STATIC_DRAW ) gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0) var texCoordBuffer = gl.createBuffer() gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer) gl.bufferData( gl.ARRAY_BUFFER, new Float32Array([1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0]), gl.STATIC_DRAW ) gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0) gl.y = new Texture(gl) gl.u = new Texture(gl) gl.v = new Texture(gl) gl.y.bind(0, program, 'YTexture') gl.u.bind(1, program, 'UTexture') gl.v.bind(2, program, 'VTexture') } WebGLPlayer.prototype.renderFrame = function(videoFrame, width, height) { if (!this.gl) { console.log('[ER] Render frame failed due to WebGL not supported.') return } var gl = this.gl gl.viewport(0, 0, gl.canvas.width, gl.canvas.height) gl.clearColor(0.0, 0.0, 0.0, 0.0) gl.clear(gl.COLOR_BUFFER_BIT) gl.y.fill(width, height, videoFrame.subarray(0, width * height)) gl.u.fill( width >> 1, height >> 1, videoFrame.subarray(width * height, (width * height * 5) / 4) ) gl.v.fill( width >> 1, height >> 1, videoFrame.subarray((width * height * 5) / 4, (width * height * 3) / 2) ) gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4) } WebGLPlayer.prototype.fullscreen = function() { let canvas = this.canvas.parentNode.parentNode.parentNode if (canvas.RequestFullScreen) { canvas.RequestFullScreen() } else if (canvas.webkitRequestFullScreen) { canvas.webkitRequestFullScreen() } else if (canvas.mozRequestFullScreen) { canvas.mozRequestFullScreen() } else if (canvas.msRequestFullscreen) { canvas.msRequestFullscreen() } else { alert("This browser doesn't supporter fullscreen") } } WebGLPlayer.prototype.exitfullscreen = function() { if (document.exitFullscreen) { document.exitFullscreen() } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen() } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen() } else if (document.msExitFullscreen) { document.msExitFullscreen() } else { alert("Exit fullscreen doesn't work") } }