UNPKG

nertc-electron-sdk

Version:
190 lines (189 loc) 6.95 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const EventEmitter = require('events').EventEmitter; const isEqual = require('lodash.isequal'); const YUVBuffer = require('yuv-buffer'); const YUVCanvas = require('yuv-canvas'); class Renderer { constructor() { this.cacheCanvasOpts = {}; this.yuv = {}; this.event = new EventEmitter(); this.ready = false; this.contentMode = 0; this.container = {}; this.canvas = {}; this.element = {}; this.forceMirror = undefined; // JS层镜像覆盖:undefined=跟帧数据,bool=强制覆盖 this.backgroundColor = 'black'; // 背景色 } bind(element) { // record element this.element = element; // create container let container = document.createElement('div'); Object.assign(container.style, { width: '100%', height: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center', overflow: 'hidden', backgroundColor: this.backgroundColor || 'black', }); this.container = container; element.appendChild(this.container); // create canvas this.canvas = document.createElement('canvas'); this.container.appendChild(this.canvas); this.yuv = YUVCanvas.attach(this.canvas, { webGL: false }); } unbind() { this.container && this.container.removeChild(this.canvas); this.element && this.element.removeChild(this.container); this.yuv = null; this.element = null; this.canvas = null; this.view = null; } equalsElement(element) { return this.element === element; } refreshCanvas() { if (!this.container || !this.canvas) return; if (!this.cacheCanvasOpts || !Object.keys(this.cacheCanvasOpts).length) return; this.updateCanvas(Object.assign({}, this.cacheCanvasOpts, { contentMode: this.contentMode, clientWidth: this.container.clientWidth, clientHeight: this.container.clientHeight, })); } captureImage() { // Not implemented for software renderer return ''; } updateCanvas(options = { width: 0, height: 0, rotation: 0, mirrorView: false, contentMode: 0, clientWidth: 0, clientHeight: 0, }) { // check if display options changed if (isEqual(this.cacheCanvasOpts, options)) { return; } this.cacheCanvasOpts = Object.assign({}, options); // check for rotation if (options.rotation === 0 || options.rotation === 180) { this.canvas.width = options.width; this.canvas.height = options.height; } else if (options.rotation === 90 || options.rotation === 270) { this.canvas.height = options.width; this.canvas.width = options.height; } else { throw new Error('Invalid value for rotation. Only support 0, 90, 180, 270'); } let transformItems = []; transformItems.push(`rotateZ(${options.rotation}deg)`); this.canvas.style.width = "auto"; this.canvas.style.height = "auto"; this.canvas.style.zoom = ''; // 画布自然尺寸(已考虑 rotation 交换宽高) let canvasW, canvasH; if (options.rotation === 0 || options.rotation === 180) { canvasW = options.width; canvasH = options.height; } else { canvasW = options.height; canvasH = options.width; } const scaleX = options.clientWidth / canvasW; const scaleY = options.clientHeight / canvasH; if (options.contentMode === 0) { // Fit: 等比缩放,保证视频完整显示 const s = Math.min(scaleX, scaleY); transformItems.push(`scale(${s})`); } else if (options.contentMode === 1) { // FullFill: 非等比拉伸,填满容器 transformItems.push(`scale(${scaleX}, ${scaleY})`); } else { // CropFill: 等比缩放,填满容器,超出裁剪 const s = Math.max(scaleX, scaleY); transformItems.push(`scale(${s})`); } // check for mirror:forceMirror 优先于帧数据 mirrorView const effectiveMirror = this.forceMirror !== undefined ? this.forceMirror : options.mirrorView; if (effectiveMirror) { // this.canvas.style.transform = 'rotateY(180deg)'; transformItems.push('rotateY(180deg)'); } if (transformItems.length > 0) { let transform = `${transformItems.join(' ')}`; this.canvas.style.transformOrigin = 'center center'; this.canvas.style.transform = transform; } } drawFrame(imageData = { header, yUint8Array, uUint8Array, vUint8Array }) { if (!this.ready) { this.ready = true; this.event.emit('ready'); } let dv = new DataView(imageData.header); // let format = dv.getUint8(0); let mirror = dv.getUint8(1); let contentWidth = dv.getUint16(2); let contentHeight = dv.getUint16(4); let left = dv.getUint16(6); let top = dv.getUint16(8); let right = dv.getUint16(10); let bottom = dv.getUint16(12); let rotation = dv.getUint16(14); // let ts = dv.getUint32(16); let width = contentWidth + left + right; let height = contentHeight + top + bottom; this.updateCanvas({ width, height, rotation, mirrorView: mirror, contentMode: this.contentMode, clientWidth: this.container.clientWidth, clientHeight: this.container.clientHeight, }); let format = YUVBuffer.format({ width, height, chromaWidth: width / 2, chromaHeight: height / 2 }); let y = YUVBuffer.lumaPlane(format, imageData.yUint8Array); let u = YUVBuffer.chromaPlane(format, imageData.uUint8Array); let v = YUVBuffer.chromaPlane(format, imageData.vUint8Array); let frame = YUVBuffer.frame(format, y, u, v); this.yuv.drawFrame(frame); } setContentMode(mode = 0) { this.contentMode = mode; } // JS 层镜像覆盖(CSS transform) setMirrorMode(enabled) { this.forceMirror = enabled; this.refreshCanvas(); } // 设置视频背景色(letterbox 区域颜色) setBackgroundColor(color) { this.backgroundColor = color; if (this.container && this.container.style) { this.container.style.backgroundColor = color; } } } exports.default = Renderer;