146 lines
4.8 KiB
TypeScript
146 lines
4.8 KiB
TypeScript
import ShaderProgram from './ShaderProgram';
|
|
import ShaderCompiler from './ShaderCompiler';
|
|
import { fragmentYUV, vertexQuad } from './ShaderSources';
|
|
import Texture from '../h264-live-player/Texture';
|
|
|
|
type ShaderArguments = {
|
|
yTexture: WebGLUniformLocation | null;
|
|
uTexture: WebGLUniformLocation | null;
|
|
vTexture: WebGLUniformLocation | null;
|
|
u_projection: WebGLUniformLocation | null;
|
|
a_position: number;
|
|
a_texCoord: number;
|
|
};
|
|
|
|
export default class YUVSurfaceShader {
|
|
/**
|
|
*
|
|
* @param {WebGLRenderingContext} gl
|
|
* @returns {YUVSurfaceShader}
|
|
*/
|
|
static create(gl: WebGLRenderingContext): YUVSurfaceShader {
|
|
const program = this._initShaders(gl);
|
|
const shaderArgs = this._initShaderArgs(gl, program);
|
|
const vertexBuffer = this._initBuffers(gl);
|
|
|
|
return new YUVSurfaceShader(gl, vertexBuffer as WebGLBuffer, shaderArgs, program);
|
|
}
|
|
|
|
static _initShaders(gl: WebGLRenderingContext): ShaderProgram {
|
|
const program = new ShaderProgram(gl);
|
|
program.attach(ShaderCompiler.compile(gl, vertexQuad) as WebGLShader);
|
|
program.attach(ShaderCompiler.compile(gl, fragmentYUV) as WebGLShader);
|
|
program.link();
|
|
program.use();
|
|
|
|
return program;
|
|
}
|
|
|
|
static _initShaderArgs(gl: WebGLRenderingContext, program: ShaderProgram): ShaderArguments {
|
|
// find shader arguments
|
|
const shaderArgs: ShaderArguments = {
|
|
yTexture: program.getUniformLocation('yTexture'),
|
|
uTexture: program.getUniformLocation('uTexture'),
|
|
vTexture: program.getUniformLocation('vTexture'),
|
|
u_projection: program.getUniformLocation('u_projection'),
|
|
a_position: program.getAttributeLocation('a_position'),
|
|
a_texCoord: program.getAttributeLocation('a_texCoord'),
|
|
};
|
|
|
|
gl.enableVertexAttribArray(shaderArgs.a_position);
|
|
gl.enableVertexAttribArray(shaderArgs.a_texCoord);
|
|
|
|
return shaderArgs;
|
|
}
|
|
|
|
static _initBuffers(gl: WebGLRenderingContext): WebGLBuffer | null {
|
|
// Create vertex buffer object.
|
|
return gl.createBuffer();
|
|
}
|
|
|
|
constructor(
|
|
private gl: WebGLRenderingContext,
|
|
private vertexBuffer: WebGLBuffer,
|
|
private shaderArgs: ShaderArguments,
|
|
private program: ShaderProgram,
|
|
) {}
|
|
|
|
/**
|
|
*
|
|
* @param {Texture} textureY
|
|
* @param {Texture} textureU
|
|
* @param {Texture} textureV
|
|
*/
|
|
setTexture(textureY: Texture, textureU: Texture, textureV: Texture): void {
|
|
const gl = this.gl;
|
|
|
|
gl.uniform1i(this.shaderArgs.yTexture, 0);
|
|
gl.uniform1i(this.shaderArgs.uTexture, 1);
|
|
gl.uniform1i(this.shaderArgs.vTexture, 2);
|
|
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
gl.bindTexture(gl.TEXTURE_2D, textureY.texture);
|
|
|
|
gl.activeTexture(gl.TEXTURE1);
|
|
gl.bindTexture(gl.TEXTURE_2D, textureU.texture);
|
|
|
|
gl.activeTexture(gl.TEXTURE2);
|
|
gl.bindTexture(gl.TEXTURE_2D, textureV.texture);
|
|
}
|
|
|
|
use(): void {
|
|
this.program.use();
|
|
}
|
|
|
|
release(): void {
|
|
this.gl.useProgram(null);
|
|
}
|
|
|
|
/**
|
|
* @param {{w:number, h:number}}encodedFrameSize
|
|
* @param {{maxXTexCoord:number, maxYTexCoord:number}} h264RenderState
|
|
*/
|
|
updateShaderData(
|
|
encodedFrameSize: { w: number; h: number },
|
|
h264RenderState: { maxXTexCoord: number; maxYTexCoord: number },
|
|
): void {
|
|
const { w, h } = encodedFrameSize;
|
|
this.gl.viewport(0, 0, w, h);
|
|
// prettier-ignore
|
|
this.program.setUniformM4(this.shaderArgs.u_projection as WebGLUniformLocation, [
|
|
2.0 / w, 0, 0, 0,
|
|
0, 2.0 / -h, 0, 0,
|
|
0, 0, 1, 0,
|
|
-1, 1, 0, 1
|
|
])
|
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer);
|
|
// prettier-ignore
|
|
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([
|
|
// First triangle
|
|
// top left:
|
|
0, 0, 0, 0,
|
|
// top right:
|
|
w, 0, h264RenderState.maxXTexCoord, 0,
|
|
// bottom right:
|
|
w, h, h264RenderState.maxXTexCoord, h264RenderState.maxYTexCoord,
|
|
|
|
// Second triangle
|
|
// bottom right:
|
|
w, h, h264RenderState.maxXTexCoord, h264RenderState.maxYTexCoord,
|
|
// bottom left:
|
|
0, h, 0, h264RenderState.maxYTexCoord,
|
|
// top left:
|
|
0, 0, 0, 0
|
|
]), this.gl.DYNAMIC_DRAW);
|
|
this.gl.vertexAttribPointer(this.shaderArgs.a_position, 2, this.gl.FLOAT, false, 16, 0);
|
|
this.gl.vertexAttribPointer(this.shaderArgs.a_texCoord, 2, this.gl.FLOAT, false, 16, 8);
|
|
}
|
|
|
|
draw(): void {
|
|
const gl = this.gl;
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
|
|
gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 6);
|
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
}
|
|
}
|