trtc-electron-sdk
Version:
trtc electron sdk
229 lines (228 loc) • 10.4 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_isequal_1 = __importDefault(require("lodash.isequal"));
const yuv_canvas_1 = __importDefault(require("yuv-canvas"));
const yuv_buffer_1 = __importDefault(require("../yuv-buffer"));
const trtc_define_1 = require("../../trtc_define");
const util_1 = require("../util");
const logger_1 = __importDefault(require("../../logger"));
class Canvas2dRenderer {
constructor(pixelFormat, view) {
this.pixelFormat = pixelFormat;
this.viewContainer = view;
this.contentMode = trtc_define_1.TRTCVideoFillMode.TRTCVideoFillMode_Fit;
const viewWrapper = document.createElement('div');
viewWrapper.className = 'trtc-2d';
Object.assign(viewWrapper.style, {
width: '100%',
height: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
});
this.viewWrapper = viewWrapper;
this.canvas = document.createElement('canvas');
this.viewWrapper.appendChild(this.canvas);
this.cacheCanvasOptions = {};
this.bind(view);
}
setContentMode(mode = trtc_define_1.TRTCVideoFillMode.TRTCVideoFillMode_Fit) {
this.contentMode = mode;
}
bind(viewContainer) {
this.viewContainer = viewContainer;
if (this.viewWrapper) {
this.viewContainer.appendChild(this.viewWrapper);
if (this.pixelFormat === trtc_define_1.TRTCVideoPixelFormat.TRTCVideoPixelFormat_I420) {
this.yuv = yuv_canvas_1.default.attach(this.canvas, { webGL: false });
}
}
}
unbind() {
var _a;
if (this.viewWrapper && this.viewContainer && this.viewWrapper.parentElement === this.viewContainer) {
this.viewContainer.removeChild(this.viewWrapper);
}
else {
logger_1.default.warn("trtc: Canvas2DRenderer unbind error: remove viewWrapper error", this.viewWrapper, this.viewContainer, (_a = this.viewContainer) === null || _a === void 0 ? void 0 : _a.parentElement);
}
this.viewContainer = null;
this.yuv = null;
}
drawFrame(frameData) {
switch (this.pixelFormat) {
case trtc_define_1.TRTCVideoPixelFormat.TRTCVideoPixelFormat_I420:
this._drawFrameI420(frameData);
break;
case trtc_define_1.TRTCVideoPixelFormat.TRTCVideoPixelFormat_RGBA32:
this._drawFrameRGBA(frameData);
break;
case trtc_define_1.TRTCVideoPixelFormat.TRTCVideoPixelFormat_BGRA32:
this._drawFrameBGRA(frameData);
break;
default:
logger_1.default.warn('Canvas2dRenderer.drawFrame error. Not support video format:', this.pixelFormat);
}
}
_drawFrameI420(frameData) {
try {
const { data, width, height, timestamp, rotation, isNeedRotate } = frameData;
if (width <= 0 || height <= 0) {
throw new Error(`wrong video parameter width:${width} or height:${height}`);
}
const yLength = width * height;
const uLength = width * height / 4;
const yPlane = new Uint8Array(data.buffer || data, 0, yLength);
const uPlane = new Uint8Array(data.buffer || data, yLength, uLength);
const vPlane = new Uint8Array(data.buffer || data, yLength + uLength, uLength);
const newFrameData = this._correctI420Data({ width, height, yUint8Array: yPlane, uUint8Array: uPlane, vUint8Array: vPlane });
const { width: newWidth, yUint8Array, uUint8Array, vUint8Array } = newFrameData;
if (this.viewWrapper && this.yuv) {
this._updateCanvasStyle({
contentWidth: newWidth,
contentHeight: height,
containerWidth: this.viewWrapper.clientWidth,
containerHeight: this.viewWrapper.clientHeight,
contentMode: this.contentMode,
rotation,
isNeedRotate,
isNeedMirror: false,
});
const format = yuv_buffer_1.default.format({
width: newWidth,
height,
chromaWidth: newWidth / 2,
chromaHeight: height / 2
});
const y = yuv_buffer_1.default.lumaPlane(format, yUint8Array);
const u = yuv_buffer_1.default.chromaPlane(format, uUint8Array);
const v = yuv_buffer_1.default.chromaPlane(format, vUint8Array);
const frame = yuv_buffer_1.default.frame(format, y, u, v);
this.yuv.drawFrame(frame);
}
else {
throw new Error('renderer container element is null');
}
}
catch (err) {
logger_1.default.error('Canvas2dRenderer._drawFrameI420 error:', err);
}
}
_drawFrameRGBA(frameData) {
try {
const { data, width, height, timestamp, rotation, isNeedRotate } = frameData;
if (width <= 0 || height <= 0) {
throw new Error(`wrong video parameter width:${width} or height:${height}`);
}
if (this.viewWrapper && this.canvas) {
this._updateCanvasStyle({
contentWidth: width,
contentHeight: height,
containerWidth: this.viewWrapper.clientWidth,
containerHeight: this.viewWrapper.clientHeight,
contentMode: this.contentMode,
rotation,
isNeedRotate,
isNeedMirror: false,
});
const newImage = new ImageData(new Uint8ClampedArray(data.buffer || data), width, height);
const tempCanvas = document.createElement('canvas');
tempCanvas.width = width;
tempCanvas.height = height;
const tempCtx = tempCanvas.getContext('2d');
if (tempCtx) {
tempCtx.putImageData(newImage, 0, 0);
const ctx = this.canvas.getContext("2d");
if (ctx) {
ctx.drawImage(tempCanvas, 0, 0);
}
else {
throw new Error('draw video to canvas failed');
}
}
else {
throw new Error('draw video to temp canvas failed');
}
}
else {
throw new Error('renderer container element is null');
}
}
catch (err) {
logger_1.default.error('Canvas2dRenderer._drawFrameRGBA error:', err);
}
}
_drawFrameBGRA(frameData) {
this._drawFrameRGBA(Object.assign(Object.assign({}, frameData), { data: (0, util_1.transferBGRA2RGBA)(frameData.data, frameData.width, frameData.height) }));
}
_updateCanvasStyle(options) {
if (this.canvas) {
if ((0, lodash_isequal_1.default)(this.cacheCanvasOptions, options)) {
return;
}
this.cacheCanvasOptions = Object.assign({}, options);
const { width, height, zoom, transform } = (0, util_1.calcCanvasStyle)(options);
this.canvas.width = width;
this.canvas.height = height;
// @ts-ignore
this.canvas.style.zoom = zoom;
if (transform) {
this.canvas.style.transform = transform;
}
}
else {
throw new Error('canvas element not exist');
}
}
_correctI420Data(data) {
const { width, height, yUint8Array, uUint8Array, vUint8Array } = data;
const widthBase = 8;
const w8Modulo = width % widthBase;
if (w8Modulo !== 0) {
const newWidth = width - w8Modulo;
const newYSize = newWidth * height;
const newYArrayBuffer = new ArrayBuffer(newYSize);
const newYUint8Array = new Uint8Array(newYArrayBuffer);
let oldIndex = -1, newIndex = -1;
for (let h = 0; h < height; h += 1) {
for (let w = 0; w < newWidth; w += 1) {
oldIndex = width * h + w;
newIndex = newWidth * h + w;
newYUint8Array[newIndex] = yUint8Array[oldIndex];
}
}
const oldChromaWidth = width / 2;
const newChromaWidth = newWidth / 2;
const chromaHeight = height / 2;
const newChromaSize = newChromaWidth * chromaHeight;
const newUArrayBuffer = new ArrayBuffer(newChromaSize);
const newUInt8Array = new Uint8Array(newUArrayBuffer);
const newVArrayBuffer = new ArrayBuffer(newChromaSize);
const newVInt8Array = new Uint8Array(newVArrayBuffer);
for (let h = 0; h < chromaHeight; h += 1) {
for (let w = 0; w < newChromaWidth; w += 1) {
oldIndex = oldChromaWidth * h + w;
newIndex = newChromaWidth * h + w;
newUInt8Array[newIndex] = uUint8Array[oldIndex];
newVInt8Array[newIndex] = vUint8Array[oldIndex];
}
}
return Object.assign(Object.assign({}, data), { width: newWidth, height, yUint8Array: newYUint8Array, uUint8Array: newUInt8Array, vUint8Array: newVInt8Array });
}
return data;
}
isValid(viewContainer) {
var _a;
return viewContainer === this.viewContainer && this.viewContainer === ((_a = this.viewWrapper) === null || _a === void 0 ? void 0 : _a.parentElement);
}
destroy() {
this.unbind();
this.viewWrapper = null;
this.canvas = null;
this.cacheCanvasOptions = null;
}
}
exports.default = Canvas2dRenderer;