UNPKG

atem-connection

Version:

Typescript Node.js library for connecting with an ATEM switcher.

211 lines 12 kB
"use strict"; var _DataTransferManager_nextTransferIdInner, _DataTransferManager_nextTransferId, _DataTransferManager_sendLockCommand, _DataTransferManager_stillsLock, _DataTransferManager_clipLocks, _DataTransferManager_labelsLock, _DataTransferManager_macroLock, _DataTransferManager_rawSendCommands; Object.defineProperty(exports, "__esModule", { value: true }); exports.DataTransferManager = void 0; const tslib_1 = require("tslib"); const exitHook = require("exit-hook"); const dataTransferQueue_1 = require("./dataTransferQueue"); const dataTransferUploadStill_1 = require("./dataTransferUploadStill"); const dataTransferUploadClip_1 = require("./dataTransferUploadClip"); const dataTransferUploadAudio_1 = require("./dataTransferUploadAudio"); const dataTransferUploadMultiViewerLabel_1 = require("./dataTransferUploadMultiViewerLabel"); const dataTransferDownloadMacro_1 = require("./dataTransferDownloadMacro"); const dataTransferUploadMacro_1 = require("./dataTransferUploadMacro"); const DataTransfer_1 = require("../commands/DataTransfer"); const debug_1 = require("debug"); const dataTransferDownloadStill_1 = require("./dataTransferDownloadStill"); const MAX_PACKETS_TO_SEND_PER_TICK = 50; const MAX_TRANSFER_INDEX = (1 << 16) - 1; // Inclusive maximum const debug = (0, debug_1.default)('atem-connection:data-transfer:manager'); class DataTransferManager { constructor(rawSendCommands) { _DataTransferManager_nextTransferIdInner.set(this, 0); _DataTransferManager_nextTransferId.set(this, () => { var _a, _b; const index = (tslib_1.__classPrivateFieldSet(this, _DataTransferManager_nextTransferIdInner, (_b = tslib_1.__classPrivateFieldGet(this, _DataTransferManager_nextTransferIdInner, "f"), _a = _b++, _b), "f"), _a); if (tslib_1.__classPrivateFieldGet(this, _DataTransferManager_nextTransferIdInner, "f") > MAX_TRANSFER_INDEX) tslib_1.__classPrivateFieldSet(this, _DataTransferManager_nextTransferIdInner, 0, "f"); return index; }); _DataTransferManager_sendLockCommand.set(this, (/*lock: DataTransferLockingQueue,*/ cmd) => { tslib_1.__classPrivateFieldGet(this, _DataTransferManager_rawSendCommands, "f").call(this, [cmd]).catch((e) => { debug(`Failed to send lock command: ${e}`); }); }); _DataTransferManager_stillsLock.set(this, new dataTransferQueue_1.DataTransferLockingQueue(0, tslib_1.__classPrivateFieldGet(this, _DataTransferManager_sendLockCommand, "f"), tslib_1.__classPrivateFieldGet(this, _DataTransferManager_nextTransferId, "f"))); _DataTransferManager_clipLocks.set(this, new Map()); // clipLocks get dynamically allocated _DataTransferManager_labelsLock.set(this, new dataTransferQueue_1.DataTransferSimpleQueue(tslib_1.__classPrivateFieldGet(this, _DataTransferManager_nextTransferId, "f"))); _DataTransferManager_macroLock.set(this, new dataTransferQueue_1.DataTransferSimpleQueue(tslib_1.__classPrivateFieldGet(this, _DataTransferManager_nextTransferId, "f"))); _DataTransferManager_rawSendCommands.set(this, void 0); tslib_1.__classPrivateFieldSet(this, _DataTransferManager_rawSendCommands, rawSendCommands, "f"); } get allLocks() { return [tslib_1.__classPrivateFieldGet(this, _DataTransferManager_stillsLock, "f"), ...tslib_1.__classPrivateFieldGet(this, _DataTransferManager_clipLocks, "f").values(), tslib_1.__classPrivateFieldGet(this, _DataTransferManager_labelsLock, "f"), tslib_1.__classPrivateFieldGet(this, _DataTransferManager_macroLock, "f")]; } /** * Start sending of commands * This is called once the connection has received the initial state data */ startCommandSending(skipUnlockAll) { // TODO - abort any active transfers if (!this.interval) { // New connection means a new queue if (!skipUnlockAll) { debug(`Clearing all held locks`); for (const lock of this.allLocks) { lock.clearQueueAndAbort(new Error('Restarting connection')); } } this.interval = setInterval(() => { for (const lock of this.allLocks) { const commandsToSend = lock.popQueuedCommands(MAX_PACKETS_TO_SEND_PER_TICK); // Take some, it is unlikely that multiple will run at once if (commandsToSend && commandsToSend.length > 0) { // debug(`Sending ${commandsToSend.length} commands `) tslib_1.__classPrivateFieldGet(this, _DataTransferManager_rawSendCommands, "f").call(this, commandsToSend).catch((e) => { // Failed to send/queue something, so abort it lock.tryAbortTransfer(new Error(`Command send failed: ${e}`)); }); } } }, 2); // TODO - refine this. perhaps we can stop and restart the interval? } if (!this.exitUnsubscribe) { this.exitUnsubscribe = exitHook(() => { debug(`Exit auto-cleanup`); // TODO - replace this with a WeakRef to the parent class? this.stopCommandSending(); }); } } /** * Stop sending of commands * This is called once the connection is disconnected */ stopCommandSending() { debug('Stopping command sending'); for (const lock of this.allLocks) { lock.clearQueueAndAbort(new Error('Stopping connection')); } if (this.exitUnsubscribe) { this.exitUnsubscribe(); this.exitUnsubscribe = undefined; } if (this.interval) { clearInterval(this.interval); this.interval = undefined; } } /** * Queue the handling of a received command * We do it via a queue as some of the handlers need to be async, and we don't want to block state updates from happening in parallel */ queueHandleCommand(command) { debug(`Received command ${command.constructor.name}: ${JSON.stringify(command)}`); if (command instanceof DataTransfer_1.LockObtainedCommand || command instanceof DataTransfer_1.LockStateUpdateCommand) { let lock; if (command.properties.index === 0) { lock = tslib_1.__classPrivateFieldGet(this, _DataTransferManager_stillsLock, "f"); } else if (command.properties.index >= 100) { // Looks like a special lock that we arent expecting // Ignore it for now return; } else { lock = tslib_1.__classPrivateFieldGet(this, _DataTransferManager_clipLocks, "f").get(command.properties.index - 1); } // Must be a clip that we aren't expecting if (!lock) lock = new dataTransferQueue_1.DataTransferLockingQueue(command.properties.index, tslib_1.__classPrivateFieldGet(this, _DataTransferManager_sendLockCommand, "f"), tslib_1.__classPrivateFieldGet(this, _DataTransferManager_nextTransferId, "f")); // handle actual command if (command instanceof DataTransfer_1.LockObtainedCommand) { lock.lockObtained(); } else if (command instanceof DataTransfer_1.LockStateUpdateCommand) { lock.updateLock(command.properties.locked); } return; } // If this command is for a transfer if (command.properties.transferId !== undefined) { // try to establish the associated DataLock: let lock; for (const _lock of this.allLocks) { if (_lock.currentTransferId === command.properties.transferId) { lock = _lock; } } // console.log('CMD', command.constructor.name) // Doesn't appear to be for a known lock // TODO - should we fire an abort back just in case? if (!lock) return; lock.handleCommand(command); // } else { // // debugging: // console.log('UNKNOWN COMMAND:', command) } } async downloadClipFrame(clipIndex, frameIndex) { if (clipIndex < 0 || clipIndex >= 2) { throw new Error('Invalid clip index'); } const transfer = new dataTransferDownloadStill_1.DataTransferDownloadStill(clipIndex + 1, frameIndex); return this.getClipLock(clipIndex).enqueue(transfer); } async downloadStill(index) { const transfer = new dataTransferDownloadStill_1.DataTransferDownloadStill(0, index); return tslib_1.__classPrivateFieldGet(this, _DataTransferManager_stillsLock, "f").enqueue(transfer); } async uploadStill(index, data, name, description) { const transfer = new dataTransferUploadStill_1.default(index, data, name, description); return tslib_1.__classPrivateFieldGet(this, _DataTransferManager_stillsLock, "f").enqueue(transfer); } async uploadClip(index, data, name) { const provideFrame = async function* () { let id = -1; for await (const frame of data) { id++; yield new dataTransferUploadClip_1.DataTransferUploadClipFrame(index, id, frame); } return undefined; }; const transfer = new dataTransferUploadClip_1.DataTransferUploadClip(index, name, provideFrame(), tslib_1.__classPrivateFieldGet(this, _DataTransferManager_nextTransferId, "f")); const lock = this.getClipLock(index); return lock.enqueue(transfer); } async uploadAudio(index, data, name) { const transfer = new dataTransferUploadAudio_1.default(index, data, name); const lock = this.getClipLock(index); return lock.enqueue(transfer); } async downloadMacro(index) { const transfer = new dataTransferDownloadMacro_1.DataTransferDownloadMacro(index); return tslib_1.__classPrivateFieldGet(this, _DataTransferManager_macroLock, "f").enqueue(transfer); } async uploadMacro(index, data, name) { const transfer = new dataTransferUploadMacro_1.DataTransferUploadMacro(index, data, name); return tslib_1.__classPrivateFieldGet(this, _DataTransferManager_macroLock, "f").enqueue(transfer); } async uploadMultiViewerLabel(index, data) { const transfer = new dataTransferUploadMultiViewerLabel_1.default(index, data); return tslib_1.__classPrivateFieldGet(this, _DataTransferManager_labelsLock, "f").enqueue(transfer); } getClipLock(index) { const lock = tslib_1.__classPrivateFieldGet(this, _DataTransferManager_clipLocks, "f").get(index); if (lock) { return lock; } else if (index >= 0 && index < 20) { const lock = new dataTransferQueue_1.DataTransferLockingQueue(index + 1, tslib_1.__classPrivateFieldGet(this, _DataTransferManager_sendLockCommand, "f"), tslib_1.__classPrivateFieldGet(this, _DataTransferManager_nextTransferId, "f")); tslib_1.__classPrivateFieldGet(this, _DataTransferManager_clipLocks, "f").set(index, lock); return lock; } else { throw new Error('Invalid clip index'); } } } exports.DataTransferManager = DataTransferManager; _DataTransferManager_nextTransferIdInner = new WeakMap(), _DataTransferManager_nextTransferId = new WeakMap(), _DataTransferManager_sendLockCommand = new WeakMap(), _DataTransferManager_stillsLock = new WeakMap(), _DataTransferManager_clipLocks = new WeakMap(), _DataTransferManager_labelsLock = new WeakMap(), _DataTransferManager_macroLock = new WeakMap(), _DataTransferManager_rawSendCommands = new WeakMap(); //# sourceMappingURL=index.js.map