UNPKG

trtc-electron-sdk

Version:

trtc electron sdk

445 lines (444 loc) 21.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const trtc_define_1 = require("../trtc_define"); const utils_1 = require("../utils"); const logger_1 = __importDefault(require("../logger")); const Movable_1 = __importDefault(require("./Movable")); const Resizable_1 = __importDefault(require("./Resizable")); const MIN_MOVE_DISTANCE = 5; // minimum moving distance const initResolutionMap = () => { const map = new Map(); for (const key in trtc_define_1.TRTCVideoResolution) { if (isNaN(Number(key))) { const value = trtc_define_1.TRTCVideoResolution[key]; const tmp = key.split('_'); map.set(value, { width: parseInt(tmp[1]), height: parseInt(tmp[2]), }); } } return map; }; const resolutionMap = initResolutionMap(); class TRTCMediaMixingDesigner { constructor(options) { this.logPrefix = '[TRTCMediaMixingDesigner]'; this.mixingVideoWidth = 640; this.mixingVideoHeight = 360; this.canExceedContainer = false; this.movableHandler = null; this.resizableHandler = null; this.previewScale = 1; this.previewWidth = 0; this.previewHeight = 0; this.previewLeft = 0; this.previewTop = 0; this.newSelected = null; this.clickedMediaSources = []; this.oldSelectedIndex = -1; this.mousedownLeft = null; this.mousedownTop = null; this.eventButton = null; this.resizeObserver = null; this.container = options.view; this.container.style.position = 'absolute'; this.videoResolution = options.videoResolution; this.resMode = options.resMode; if (resolutionMap.has(options.videoResolution)) { const { width, height } = resolutionMap.get(options.videoResolution); if (this.resMode === trtc_define_1.TRTCVideoResolutionMode.TRTCVideoResolutionModeLandscape) { this.mixingVideoWidth = width; this.mixingVideoHeight = height; } else { this.mixingVideoWidth = height; this.mixingVideoHeight = width; } } this.updatePreviewProperty(); this.canExceedContainer = options.canExceedContainer || false; this.eventEmitter = new events_1.EventEmitter(); this.mediaList = []; this.selectedMediaIndex = -1; this.moveAndResizeOverlay = document.createElement('div'); this.moveAndResizeOverlay.style.position = 'relative'; this.container.appendChild(this.moveAndResizeOverlay); this.initMediaMovable(); this.initMediaResizable(); this.initContainerMouseEventHander(); this.initContainerResizeObserver(); } updateOptions(options) { if (!(0, utils_1.isNullOrUndefined)(options.videoResolution) || !(0, utils_1.isNullOrUndefined)(options.resMode)) { this.resMode = !(0, utils_1.isNullOrUndefined)(options.resMode) ? options.resMode : this.resMode; if (options.videoResolution) { if (resolutionMap.has(options.videoResolution)) { const { width, height } = resolutionMap.get(options.videoResolution); if (this.resMode === trtc_define_1.TRTCVideoResolutionMode.TRTCVideoResolutionModeLandscape) { this.mixingVideoWidth = width; this.mixingVideoHeight = height; } else { this.mixingVideoWidth = height; this.mixingVideoHeight = width; } } } this.updatePreviewProperty(); this.updateOverlay(); } } addMedia(media) { this.mediaList.unshift(media); if (media.isSelected) { this.selectedMediaIndex = 0; this.updateOverlay(); } } removeMedia(media) { const targetIndex = this.mediaList.findIndex(item => item.id === media.id); if (targetIndex !== -1) { this.mediaList.splice(targetIndex, 1); if (this.selectedMediaIndex === targetIndex || media.isSelected) { this.selectedMediaIndex = -1; this.updateOverlay(); } } } updateMedia(media) { const targetIndex = this.mediaList.findIndex(item => item.id === media.id); if (targetIndex !== -1) { this.mediaList[targetIndex] = Object.assign({}, this.mediaList[targetIndex], media); if (media.isSelected) { this.selectedMediaIndex = targetIndex; this.updateOverlay(); } } } on(event, func) { var _a; (_a = this.eventEmitter) === null || _a === void 0 ? void 0 : _a.on(event, func); } off(event, func) { var _a; (_a = this.eventEmitter) === null || _a === void 0 ? void 0 : _a.off(event, func); } destroy() { var _a, _b, _c, _d, _e; if (this.resizeObserver) { if (this.container) { this.resizeObserver.unobserve(this.container); } this.resizeObserver.disconnect(); this.resizeObserver = null; } (_a = this.movableHandler) === null || _a === void 0 ? void 0 : _a.destroy(); (_b = this.resizableHandler) === null || _b === void 0 ? void 0 : _b.destroy(); this.movableHandler = null; this.resizableHandler = null; (_c = this.container) === null || _c === void 0 ? void 0 : _c.removeEventListener('contextmenu', this.onRightButtonClicked, false); (_d = this.container) === null || _d === void 0 ? void 0 : _d.removeEventListener("mousedown", this.onContainerMousedown, false); if (this.moveAndResizeOverlay) { (_e = this.container) === null || _e === void 0 ? void 0 : _e.removeChild(this.moveAndResizeOverlay); this.moveAndResizeOverlay = null; } this.container = null; this.eventEmitter = null; this.mediaList = []; } initMediaMovable() { this.onMove = this.onMove.bind(this); this.movableHandler = new Movable_1.default(this.moveAndResizeOverlay, this.container, { canExceedContainer: this.canExceedContainer, calcPositionOnly: true, }); this.movableHandler.on('move', this.onMove); } initMediaResizable() { this.onResize = this.onResize.bind(this); this.resizableHandler = new Resizable_1.default(this.moveAndResizeOverlay, this.container, { keepRatio: true, stopPropagation: true, canExceedContainer: this.canExceedContainer, anchorMode: 0 }); this.resizableHandler.on('resize', this.onResize); } initContainerMouseEventHander() { if (this.container) { this.onContainerMousedown = this.onContainerMousedown.bind(this); this.onContainerMousemove = this.onContainerMousemove.bind(this); this.onContainerMouseup = this.onContainerMouseup.bind(this); this.container.addEventListener("mousedown", this.onContainerMousedown, false); this.onRightButtonClicked = this.onRightButtonClicked.bind(this); this.container.addEventListener('contextmenu', this.onRightButtonClicked, false); } else { logger_1.default.error(`${this.logPrefix}initContainerMouseEventHander failed, no container view`); } } initContainerResizeObserver() { if (this.container) { this.onPreviewAreaResize = this.onPreviewAreaResize.bind(this); this.resizeObserver = new ResizeObserver(this.onPreviewAreaResize); this.resizeObserver.observe(this.container); } } onPreviewAreaResize(entries) { logger_1.default.log(`${this.logPrefix}onPreviewAreaResize:`, entries); for (const entry of entries) { if (entry.target === this.container) { this.updatePreviewProperty(); this.updateOverlay(); break; } } } updatePreviewProperty() { if (this.container) { const containerWidth = this.container.offsetWidth; const containerHeight = this.container.offsetHeight; const widthScale = containerWidth / this.mixingVideoWidth; const heightScale = containerHeight / this.mixingVideoHeight; this.previewScale = widthScale > heightScale ? heightScale : widthScale; this.previewWidth = this.mixingVideoWidth * this.previewScale; this.previewHeight = this.mixingVideoHeight * this.previewScale; this.previewLeft = (containerWidth - this.previewWidth) / 2; this.previewTop = (containerHeight - this.previewHeight) / 2; } else { logger_1.default.error(`${this.logPrefix}calcPreviewScale failed, no HTML element to display`); } } updateOverlay() { if (this.moveAndResizeOverlay) { let left = `${this.previewLeft}px`; let top = `${this.previewTop}px`; let width = '0px'; let height = '0px'; if (this.selectedMediaIndex >= 0) { const selectedPreviewRect = { left: this.mediaList[this.selectedMediaIndex].rect.left * this.previewScale, top: this.mediaList[this.selectedMediaIndex].rect.top * this.previewScale, right: this.mediaList[this.selectedMediaIndex].rect.right * this.previewScale, bottom: this.mediaList[this.selectedMediaIndex].rect.bottom * this.previewScale }; left = `${selectedPreviewRect.left + this.previewLeft}px`; top = `${selectedPreviewRect.top + this.previewTop}px`; width = `${selectedPreviewRect.right - selectedPreviewRect.left}px`; height = `${selectedPreviewRect.bottom - selectedPreviewRect.top}px`; } this.moveAndResizeOverlay.style.left = left; this.moveAndResizeOverlay.style.top = top; this.moveAndResizeOverlay.style.width = width; this.moveAndResizeOverlay.style.height = height; } } onMove(left, top) { var _a; logger_1.default.log(`${this.logPrefix}onMove: ${left} ${top}`); const target = this.mediaList[this.selectedMediaIndex]; if (target && this.moveAndResizeOverlay) { // calc new preview rect const newPreviewRect = { left: left - this.previewLeft, top: top - this.previewTop, right: left - this.previewLeft + this.moveAndResizeOverlay.offsetWidth, bottom: top - this.previewTop + this.moveAndResizeOverlay.offsetHeight, }; // calc new mixing rect const newRectInMixing = { left: Math.round(newPreviewRect.left / this.previewScale), top: Math.round(newPreviewRect.top / this.previewScale), right: Math.round(newPreviewRect.right / this.previewScale), bottom: Math.round(newPreviewRect.bottom / this.previewScale), }; (_a = this.eventEmitter) === null || _a === void 0 ? void 0 : _a.emit('onSourceMoved', Object.assign({}, target), newRectInMixing); } else { logger_1.default.warn(`${this.logPrefix}onMove no selected media`); } } onResize(left, top, width, height) { var _a; logger_1.default.log(`${this.logPrefix}onResize: ${left} ${top} ${width} ${height}`); const target = this.mediaList[this.selectedMediaIndex]; if (target) { // calc new preview rect const newPreviewRect = { left: left - this.previewLeft, top: top - this.previewTop, right: left - this.previewLeft + width, bottom: top - this.previewTop + height, }; // calc new mixing rect const newRectInMixing = { left: Math.round(newPreviewRect.left / this.previewScale), top: Math.round(newPreviewRect.top / this.previewScale), right: Math.round(newPreviewRect.right / this.previewScale), bottom: Math.round(newPreviewRect.bottom / this.previewScale), }; (_a = this.eventEmitter) === null || _a === void 0 ? void 0 : _a.emit('onSourceResized', Object.assign({}, target), newRectInMixing); } else { logger_1.default.warn(`${this.logPrefix}onResize no selected media`); } } emitOnSelect(media) { var _a; if (media) { const length = this.mediaList.length; for (let i = 0; i < length; i++) { if (media.id === this.mediaList[i].id) { this.selectedMediaIndex = i; break; } } } else { this.selectedMediaIndex = -1; } this.updateOverlay(); (_a = this.eventEmitter) === null || _a === void 0 ? void 0 : _a.emit('onSourceSelected', media ? Object.assign({}, media) : null); } onContainerMousedown(event) { logger_1.default.log(`${this.logPrefix}onContainerMousedown event:`, event, event.target, event.currentTarget); const target = event.target; this.eventButton = event.button; if (target && this.container) { // calc click point coordinates in mix video image logger_1.default.log(`${this.logPrefix}onContainerMousedown mix video image clicked`); const containerBounds = this.container.getBoundingClientRect(); const xInPreviewImage = event.clientX - containerBounds.x - this.previewLeft; const yInPreviewImage = event.clientY - containerBounds.y - this.previewTop; const xInImage = xInPreviewImage / this.previewScale; const yInImage = yInPreviewImage / this.previewScale; logger_1.default.log(`${this.logPrefix}onContainerMousedown click point coordinates in mix video:`, xInPreviewImage, yInPreviewImage, xInImage, yInImage); for (let i = 0; i < this.mediaList.length; i++) { const item = this.mediaList[i]; if (item.rect && xInImage >= item.rect.left && xInImage <= item.rect.right && yInImage >= item.rect.top && yInImage <= item.rect.bottom) { this.clickedMediaSources.push(item); if (this.mediaList[this.selectedMediaIndex]) { if (item.id === this.mediaList[this.selectedMediaIndex].id) { this.oldSelectedIndex = this.clickedMediaSources.length - 1; } } } } this.mousedownLeft = event.screenX; this.mousedownTop = event.screenY; if (this.clickedMediaSources.length > 0) { if (this.eventButton === 2 && this.oldSelectedIndex === -1) { // select first media source this.newSelected = this.clickedMediaSources[0]; logger_1.default.log(`${this.logPrefix}onContainerMousedown find clicked media source:`, this.newSelected); // mediaSourcesStore.setSelectedMediaKey(newSelected); this.emitOnSelect(this.newSelected); this.clickedMediaSources.splice(0, this.clickedMediaSources.length); } else { document.addEventListener("mousemove", this.onContainerMousemove, false); document.addEventListener("mouseup", this.onContainerMouseup, false); } } else { this.newSelected = null; logger_1.default.log(`${this.logPrefix}onContainerMousedown find clicked media source:`, this.newSelected); this.emitOnSelect(null); this.mousedownLeft = null; this.mousedownTop = null; this.eventButton = null; } } } onContainerMousemove(event) { var _a; const target = event.target; if (target && this.container) { if (this.mousedownLeft !== null && this.mousedownTop !== null) { const leftMovedDistance = event.screenX - this.mousedownLeft; const topMovedDistance = event.screenY - this.mousedownTop; if (Math.abs(leftMovedDistance) >= MIN_MOVE_DISTANCE || Math.abs(topMovedDistance) >= MIN_MOVE_DISTANCE) { if (this.oldSelectedIndex >= 0) { // move or resize old selected media source logger_1.default.log(`${this.logPrefix}onContainerMousemove move or resize old selected media source, clear data:`, this.clickedMediaSources, this.oldSelectedIndex); this.clickedMediaSources.splice(0, this.clickedMediaSources.length); this.oldSelectedIndex = -1; } else if (this.clickedMediaSources.length > 0) { // select first media source this.newSelected = this.clickedMediaSources[0]; logger_1.default.log(`${this.logPrefix}onContainerMousemove find clicked media source:`, this.newSelected); this.emitOnSelect(this.newSelected); this.clickedMediaSources.splice(0, this.clickedMediaSources.length); // 支持 mousedown 选中媒体源和 mousemove 移动媒体源同时触发 (_a = this.moveAndResizeOverlay) === null || _a === void 0 ? void 0 : _a.dispatchEvent(new MouseEvent('mousedown', { screenX: this.mousedownLeft, screenY: this.mousedownTop, button: this.eventButton, })); } } } } } onContainerMouseup(event) { document.removeEventListener("mousemove", this.onContainerMousemove, false); document.removeEventListener("mouseup", this.onContainerMouseup, false); logger_1.default.log(`${this.logPrefix}onContainerMouseup data:`, this.clickedMediaSources, this.oldSelectedIndex); const target = event.target; if (target && this.container) { if (this.clickedMediaSources.length > 0) { if (this.oldSelectedIndex >= 0) { if (this.eventButton === 0) { // select next zOrder media source const newSelectedIndex = (this.oldSelectedIndex + 1) % this.clickedMediaSources.length; this.newSelected = this.clickedMediaSources[newSelectedIndex]; logger_1.default.log(`${this.logPrefix}onContainerMouseup find clicked media source:`, this.newSelected); this.emitOnSelect(this.newSelected); } else { // right click of selected media source, do not change selected media source // will show context menu } } else { // select first media source this.newSelected = this.clickedMediaSources[0]; logger_1.default.log(`${this.logPrefix}onContainerMouseup find clicked media source:`, this.newSelected); this.emitOnSelect(this.newSelected); } } else { // do nothing } } else { // un-select current media source logger_1.default.log(`${this.logPrefix}onContainerMouseup click outside of mixing video image`); this.emitOnSelect(null); } this.mousedownLeft = null; this.mousedownTop = null; this.clickedMediaSources.splice(0, this.clickedMediaSources.length); this.oldSelectedIndex = -1; this.newSelected = null; this.eventButton = null; } onRightButtonClicked(event) { var _a; logger_1.default.log(`${this.logPrefix}onRightButtonClicked:`, event.target, event.currentTarget, event.buttons); event.preventDefault(); (_a = this.eventEmitter) === null || _a === void 0 ? void 0 : _a.emit('onRightButtonClicked', Object.assign({}, this.mediaList[this.selectedMediaIndex])); } } exports.default = TRTCMediaMixingDesigner;