UNPKG

trtc-electron-sdk

Version:

trtc electron sdk

229 lines (228 loc) 10.4 kB
"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;