Files
tk-ws-scrcpy/vendor/h264-live-player/YUVWebGLCanvas.ts
2025-07-30 13:39:32 +08:00

121 lines
3.9 KiB
TypeScript

import WebGLCanvas from './WebGLCanvas';
import Size from './utils/Size';
import Program from './Program';
import Shader from './Shader';
import Script from './Script';
import Texture from './Texture';
export default class YUVWebGLCanvas extends WebGLCanvas {
protected static vertexShaderScript: Script = Script.createFromSource('x-shader/x-vertex', `
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
varying highp vec2 vTextureCoord;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vTextureCoord = aTextureCoord;
}
`);
protected static fragmentShaderScript: Script = Script.createFromSource('x-shader/x-fragment', `
precision highp float;
varying highp 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;
}`);
private YTexture?: Texture;
private UTexture?: Texture;
private VTexture?: Texture;
constructor(readonly canvas: HTMLCanvasElement, readonly size: Size) {
super(canvas, size, false);
}
protected onInitShaders(): void {
if (!this.gl) {
return;
}
this.program = new Program(this.gl);
this.program.attach(new Shader(this.gl, YUVWebGLCanvas.vertexShaderScript));
this.program.attach(new Shader(this.gl, YUVWebGLCanvas.fragmentShaderScript));
this.program.link();
this.program.use();
this.vertexPositionAttribute = this.program.getAttributeLocation('aVertexPosition');
this.gl.enableVertexAttribArray(this.vertexPositionAttribute as number);
this.textureCoordAttribute = this.program.getAttributeLocation('aTextureCoord');
this.gl.enableVertexAttribArray(this.textureCoordAttribute as number);
}
protected onInitTextures(): void {
if (!this.gl) {
return;
}
this.YTexture = new Texture(this.gl, this.size);
this.UTexture = new Texture(this.gl, this.size.getHalfSize());
this.VTexture = new Texture(this.gl, this.size.getHalfSize());
}
protected onInitSceneTextures(): void {
if (!this.program) {
return;
}
if (!this.YTexture || !this.UTexture || !this.VTexture) {
return;
}
this.YTexture.bind(0, this.program, 'YTexture');
this.UTexture.bind(1, this.program, 'UTexture');
this.VTexture.bind(2, this.program, 'VTexture');
}
protected fillYUVTextures(y: Uint8Array, u: Uint8Array, v: Uint8Array): void {
if (!this.YTexture || !this.UTexture || !this.VTexture) {
return;
}
this.YTexture.fill(y);
this.UTexture.fill(u);
this.VTexture.fill(v);
}
public decode(buffer: Uint8Array, width: number, height: number): void {
if (!buffer) {
return;
}
if (!this.YTexture || !this.UTexture || !this.VTexture) {
return;
}
const lumaSize = width * height;
const chromaSize = lumaSize >> 2;
this.fillYUVTextures(
buffer.subarray(0, lumaSize),
buffer.subarray(lumaSize, lumaSize + chromaSize),
buffer.subarray(lumaSize + chromaSize, lumaSize + 2 * chromaSize)
);
this.drawScene();
}
public toString(): string {
return 'YUVCanvas Size: ' + this.size;
}
}