trtc-electron-sdk
Version:
trtc electron sdk
445 lines (444 loc) • 21.6 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 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;