@rtn263net/cloudhub-rts-electron-sdk
Version:
cloudhub-rts-electron-sdk
418 lines (417 loc) • 16 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const log_1 = __importDefault(require("../../helpers/log"));
const { createProgramFromSources } = require('./webgl-utils');
const { EventEmitter } = require('events');
const CloudHubRender = function () {
log_1.default.info('CloudHubRender');
let gl;
let program;
let positionLocation;
let texCoordLocation;
let yTexture;
let uTexture;
let vTexture;
let texCoordBuffer;
let surfaceBuffer;
const that = {
view: undefined,
mirrorView: false,
container: undefined,
canvas: undefined,
renderImageCount: 0,
initWidth: 0,
initHeight: 0,
initRotation: 0,
clientWidth: 0,
clientHeight: 0,
contentMode: 0,
event: new EventEmitter(),
firstFrameRender: false,
lastImageWidth: 0,
lastImageHeight: 0,
lastImageRotation: 0
};
that.setContentMode = function (mode) {
that.contentMode = mode;
};
that.setMirror = function (mirror) {
that.mirrorView = mirror;
that.canvas.style.transform = mirror ? 'rotateY(180deg)' : 'rotateY(0deg)';
};
that.bind = function (view) {
initCanvas(view, view.clientWidth, view.clientHeight, that.initRotation, log_1.default.warn);
};
that.unbind = function () {
log_1.default.info('unbind');
try {
gl.getExtension('WEBGL_lose_context').loseContext();
}
catch (err) {
log_1.default.warn(err);
}
program = undefined;
positionLocation = undefined;
texCoordLocation = undefined;
deleteTexture(yTexture);
deleteTexture(uTexture);
deleteTexture(vTexture);
yTexture = undefined;
uTexture = undefined;
vTexture = undefined;
deleteBuffer(texCoordBuffer);
deleteBuffer(surfaceBuffer);
texCoordBuffer = undefined;
surfaceBuffer = undefined;
gl = undefined;
try {
that.container && that.container.removeChild(that.canvas);
that.view && that.view.removeChild(that.container);
}
catch (e) {
log_1.default.warn(e);
}
that.canvas = undefined;
that.container = undefined;
that.view = undefined;
};
that.refreshCanvas = function () {
if (that.lastImageWidth) {
updateViewZoomLevel(that.lastImageRotation, that.lastImageWidth, that.lastImageHeight);
}
};
that.renderImage = function (image) {
if (!gl) {
log_1.default.info('!gl');
return;
}
if (image.width != that.initWidth ||
image.height != that.initHeight ||
image.rotation != that.initRotation) {
const view = that.view;
that.unbind();
initCanvas(view, image.width, image.height, image.rotation, e => {
log_1.default.error(`init canvas ${image.width}*${image.height} rotation ${image.rotation} failed. ${e}`);
});
}
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
const xWidth = image.width + image.left + image.right;
const xHeight = image.height + image.top + image.bottom;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
image.left / xWidth,
image.bottom / xHeight,
1 - image.right / xWidth,
image.bottom / xHeight,
image.left / xWidth,
1 - image.top / xHeight,
image.left / xWidth,
1 - image.top / xHeight,
1 - image.right / xWidth,
image.bottom / xHeight,
1 - image.right / xWidth,
1 - image.top / xHeight
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
uploadYuv(xWidth, xHeight, image.yplane, image.uplane, image.vplane);
updateCanvas(image.rotation, image.width, image.height);
gl.drawArrays(gl.TRIANGLES, 0, 6);
that.renderImageCount += 1;
if (!that.firstFrameRender) {
that.firstFrameRender = true;
that.event.emit('ready');
}
};
that.drawFrame = function ({ header, yUint8Array, uUint8Array, vUint8Array }) {
var headerLength = 20;
var dv = new DataView(header);
var format = dv.getUint8(0);
var mirror = dv.getUint8(1);
var width = dv.getUint16(2);
var height = dv.getUint16(4);
var left = dv.getUint16(6);
var top = dv.getUint16(8);
var right = dv.getUint16(10);
var bottom = dv.getUint16(12);
var rotation = dv.getUint16(14);
var ts = dv.getUint32(16);
var xWidth = width + left + right;
var xHeight = height + top + bottom;
var yLength = xWidth * xHeight;
var yBegin = headerLength;
var yEnd = yBegin + yLength;
var uLength = yLength / 4;
var uBegin = yEnd;
var uEnd = uBegin + uLength;
var vLength = yLength / 4;
var vBegin = uEnd;
var vEnd = vBegin + vLength;
that.renderImage({
mirror: mirror,
width,
height,
left,
top,
right,
bottom,
rotation: rotation,
yplane: new Uint8Array(yUint8Array),
uplane: new Uint8Array(uUint8Array),
vplane: new Uint8Array(vUint8Array)
});
var now32 = (Date.now() & 0xffffffff) >>> 0;
var latency = now32 - ts;
};
function uploadYuv(width, height, yplane, uplane, vplane) {
var e;
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, yTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width, height, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, yplane);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, uTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width / 2, height / 2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, uplane);
gl.activeTexture(gl.TEXTURE2);
gl.bindTexture(gl.TEXTURE_2D, vTexture);
('');
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width / 2, height / 2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, vplane);
}
function deleteBuffer(buffer) {
if (buffer && gl) {
gl.deleteBuffer(buffer);
}
}
function deleteTexture(texture) {
if (texture && gl) {
gl.deleteTexture(texture);
}
}
const vertexShaderSource = 'attribute vec2 a_position;' +
'attribute vec2 a_texCoord;' +
'uniform vec2 u_resolution;' +
'varying vec2 v_texCoord;' +
'void main() {' +
'vec2 zeroToOne = a_position / u_resolution;' +
' vec2 zeroToTwo = zeroToOne * 2.0;' +
' vec2 clipSpace = zeroToTwo - 1.0;' +
' gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);' +
'v_texCoord = a_texCoord;' +
'}';
const yuvShaderSource = 'precision mediump float;' +
'uniform sampler2D Ytex;' +
'uniform sampler2D Utex,Vtex;' +
'varying vec2 v_texCoord;' +
'void main(void) {' +
' float nx,ny,r,g,b,y,u,v;' +
' mediump vec4 txl,ux,vx;' +
' nx=v_texCoord[0];' +
' ny=v_texCoord[1];' +
' y=texture2D(Ytex,vec2(nx,ny)).r;' +
' u=texture2D(Utex,vec2(nx,ny)).r;' +
' v=texture2D(Vtex,vec2(nx,ny)).r;' +
' y=1.1643*(y-0.0625);' +
' u=u-0.5;' +
' v=v-0.5;' +
' r=y+1.5958*v;' +
' g=y-0.39173*u-0.81290*v;' +
' b=y+2.017*u;' +
' gl_FragColor=vec4(r,g,b,1.0);' +
'}';
function initCanvas(view, width, height, rotation, onFailure) {
log_1.default.info('initCanvas');
that.clientWidth = view.clientWidth;
that.clientHeight = view.clientHeight;
that.view = view;
that.container = document.createElement('div');
that.container.style.width = '100%';
that.container.style.height = '100%';
that.container.style.display = 'flex';
that.container.style.justifyContent = 'center';
that.container.style.alignItems = 'center';
that.container.style.background = '#000000';
that.container.style.overflow = 'hidden';
that.view.appendChild(that.container);
that.canvas = document.createElement('canvas');
if (rotation == 0 || rotation == 180) {
that.canvas.width = width;
that.canvas.height = height;
}
else {
that.canvas.width = height;
that.canvas.height = width;
}
that.initWidth = width;
that.initHeight = height;
that.initRotation = rotation;
if (that.mirrorView) {
that.canvas.style.transform = 'rotateY(180deg)';
}
that.container.appendChild(that.canvas);
try {
gl =
that.canvas.getContext('webgl', { preserveDrawingBuffer: true }) ||
that.canvas.getContext('experimental-webgl');
}
catch (e) {
log_1.default.error(e);
}
if (!gl) {
gl = undefined;
log_1.default.error('Browser not support! No WebGL detected.');
return;
}
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
program = createProgramFromSources(gl, [vertexShaderSource, yuvShaderSource]);
gl.useProgram(program);
initTextures();
}
function initTextures() {
positionLocation = gl.getAttribLocation(program, 'a_position');
texCoordLocation = gl.getAttribLocation(program, 'a_texCoord');
surfaceBuffer = gl.createBuffer();
texCoordBuffer = gl.createBuffer();
gl.activeTexture(gl.TEXTURE0);
yTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, yTexture);
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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.activeTexture(gl.TEXTURE1);
uTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, uTexture);
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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.activeTexture(gl.TEXTURE2);
vTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, vTexture);
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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
const y = gl.getUniformLocation(program, 'Ytex');
gl.uniform1i(y, 0);
const u = gl.getUniformLocation(program, 'Utex');
gl.uniform1i(u, 1);
const v = gl.getUniformLocation(program, 'Vtex');
gl.uniform1i(v, 2);
}
function updateViewZoomLevel(rotation, width, height) {
that.clientWidth = that.view.clientWidth;
that.clientHeight = that.view.clientHeight;
try {
if (that.contentMode === 0) {
if (rotation === 0 || rotation === 180) {
if (that.clientWidth / that.clientHeight > width / height) {
that.canvas.style.zoom = that.clientWidth / width;
}
else {
that.canvas.style.zoom = that.clientHeight / height;
}
}
else {
if (that.clientHeight / that.clientWidth > width / height) {
that.canvas.style.zoom = that.clientHeight / width;
}
else {
that.canvas.style.zoom = that.clientWidth / height;
}
}
}
else if (rotation === 0 || rotation === 180) {
if (that.clientWidth / that.clientHeight > width / height) {
that.canvas.style.zoom = that.clientHeight / height;
}
else {
that.canvas.style.zoom = that.clientWidth / width;
}
}
else {
if (that.clientHeight / that.clientWidth > width / height) {
that.canvas.style.zoom = that.clientWidth / height;
}
else {
that.canvas.style.zoom = that.clientHeight / width;
}
}
}
catch (e) {
log_1.default.info(`updateCanvas 00001 gone ${that.canvas}`);
log_1.default.info(that);
log_1.default.error(e);
return false;
}
return true;
}
function updateCanvas(rotation, width, height) {
if (width || height) {
that.lastImageWidth = width;
that.lastImageHeight = height;
that.lastImageRotation = rotation;
}
else {
width = that.lastImageWidth;
height = that.lastImageHeight;
rotation = that.lastImageRotation;
}
if (!updateViewZoomLevel(rotation, width, height)) {
return;
}
gl.bindBuffer(gl.ARRAY_BUFFER, surfaceBuffer);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
const p1 = { x: 0, y: 0 };
const p2 = { x: width, y: 0 };
const p3 = { x: width, y: height };
const p4 = { x: 0, y: height };
let pp1 = p1, pp2 = p2, pp3 = p3, pp4 = p4;
switch (rotation) {
case 0:
break;
case 90:
pp1 = p2;
pp2 = p3;
pp3 = p4;
pp4 = p1;
break;
case 180:
pp1 = p3;
pp2 = p4;
pp3 = p1;
pp4 = p2;
break;
case 270:
pp1 = p4;
pp2 = p1;
pp3 = p2;
pp4 = p3;
break;
default:
}
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
pp1.x,
pp1.y,
pp2.x,
pp2.y,
pp4.x,
pp4.y,
pp4.x,
pp4.y,
pp2.x,
pp2.y,
pp3.x,
pp3.y
]), gl.STATIC_DRAW);
const resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
gl.uniform2f(resolutionLocation, width, height);
}
return that;
};
exports.default = CloudHubRender;