UNPKG

atem-connection

Version:

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

138 lines 7.01 kB
"use strict"; var _DataTransferUploadBuffer_bytesSent; Object.defineProperty(exports, "__esModule", { value: true }); exports.DataTransferUploadBuffer = exports.generateUploadBufferInfo = exports.generateHashForBuffer = void 0; const tslib_1 = require("tslib"); const DataTransfer_1 = require("../commands/DataTransfer"); const crypto = require("crypto"); const dataTransfer_1 = require("./dataTransfer"); const debug0 = require("debug"); const rgbaToYuv422_1 = require("../lib/converters/rgbaToYuv422"); const rle_1 = require("../lib/converters/rle"); const debug = debug0('atem-connection:data-transfer:upload-buffer'); function generateHashForBuffer(data) { return data ? crypto.createHash('md5').update(data).digest('base64') : ''; } exports.generateHashForBuffer = generateHashForBuffer; function generateUploadBufferInfo(data, resolution, shouldEncodeRLE) { const expectedLength = resolution.width * resolution.height * 4; if (Buffer.isBuffer(data)) { if (data.length !== expectedLength) throw new Error(`Pixel buffer has incorrect length. Received ${data.length} expected ${expectedLength}`); const encodedData = (0, rgbaToYuv422_1.convertRGBAToYUV422)(resolution.width, resolution.height, data); return { encodedData: shouldEncodeRLE ? (0, rle_1.encodeRLE)(encodedData) : encodedData, rawDataLength: encodedData.length, isRleEncoded: shouldEncodeRLE, hash: generateHashForBuffer(encodedData), }; } else { const result = { ...data }; if (data.rawDataLength !== expectedLength) throw new Error(`Pixel buffer has incorrect length. Received ${data.rawDataLength} expected ${expectedLength}`); if (shouldEncodeRLE && !data.isRleEncoded) { data.isRleEncoded = true; data.encodedData = (0, rle_1.encodeRLE)(data.encodedData); } return result; } } exports.generateUploadBufferInfo = generateUploadBufferInfo; class DataTransferUploadBuffer extends dataTransfer_1.DataTransfer { constructor(buffer) { super(); _DataTransferUploadBuffer_bytesSent.set(this, 0); this.hash = buffer.hash ?? generateHashForBuffer(buffer.encodedData); this.data = buffer.encodedData; } async handleCommand(command, oldState) { if (command instanceof DataTransfer_1.DataTransferErrorCommand) { switch (command.properties.errorCode) { case DataTransfer_1.ErrorCode.Retry: return this.restartTransfer(command.properties.transferId); case DataTransfer_1.ErrorCode.NotFound: this.abort(new Error('Invalid upload')); return { newState: dataTransfer_1.DataTransferState.Finished, commands: [], }; default: // Abort the transfer. this.abort(new Error(`Unknown error ${command.properties.errorCode}`)); return { newState: dataTransfer_1.DataTransferState.Finished, commands: [], }; } } else if (command instanceof DataTransfer_1.DataTransferUploadContinueCommand) { const result = { newState: oldState, commands: [], }; // Atem requested more packets of data if (oldState === dataTransfer_1.DataTransferState.Ready) { // First bunch of packets, also send the description result.newState = dataTransfer_1.DataTransferState.Transferring; result.commands.push(this.generateDescriptionCommand(command.properties.transferId)); } const nextChunks = this.getNextChunks(command.properties); result.commands.push(...nextChunks); // if (nextChunks.length === 0) this.abort(new Error('Ran out of data')) return result; } else if (command instanceof DataTransfer_1.DataTransferCompleteCommand) { // Atem reports that it recieved everything if (oldState === dataTransfer_1.DataTransferState.Transferring) { this.resolvePromise(); return { newState: dataTransfer_1.DataTransferState.Finished, commands: [], }; } else { return { newState: oldState, commands: [] }; } } else { // Unknown command return { newState: oldState, commands: [] }; } } getNextChunks(props) { const commands = []; // Take a little less because the atem does that? // const chunkSize = props.chunkSize - 4 const chunkSize = Math.floor(props.chunkSize / 8) * 8; for (let i = 0; i < props.chunkCount; i++) { // Make sure the packet isn't empty if (tslib_1.__classPrivateFieldGet(this, _DataTransferUploadBuffer_bytesSent, "f") >= this.data.length) break; // Make sure the packet doesn't end in the middle of a RLE block let shortenBy = 0; if (chunkSize + tslib_1.__classPrivateFieldGet(this, _DataTransferUploadBuffer_bytesSent, "f") > this.data.length) { // The last chunk can't end with a RLE header shortenBy = tslib_1.__classPrivateFieldGet(this, _DataTransferUploadBuffer_bytesSent, "f") + chunkSize - this.data.length; } else if (rle_1.RLE_HEADER === this.data.readBigUint64BE(tslib_1.__classPrivateFieldGet(this, _DataTransferUploadBuffer_bytesSent, "f") + chunkSize - 8)) { // RLE header starts 8 bytes before the end shortenBy = 8; } else if (rle_1.RLE_HEADER === this.data.readBigUint64BE(tslib_1.__classPrivateFieldGet(this, _DataTransferUploadBuffer_bytesSent, "f") + chunkSize - 16)) { // RLE header starts 16 bytes before the end shortenBy = 16; } commands.push(new DataTransfer_1.DataTransferDataCommand({ transferId: props.transferId, body: this.data.slice(tslib_1.__classPrivateFieldGet(this, _DataTransferUploadBuffer_bytesSent, "f"), tslib_1.__classPrivateFieldGet(this, _DataTransferUploadBuffer_bytesSent, "f") + chunkSize - shortenBy), })); tslib_1.__classPrivateFieldSet(this, _DataTransferUploadBuffer_bytesSent, tslib_1.__classPrivateFieldGet(this, _DataTransferUploadBuffer_bytesSent, "f") + (chunkSize - shortenBy), "f"); } debug(`Generated ${commands.length} chunks for size ${chunkSize}`); return commands; } } exports.DataTransferUploadBuffer = DataTransferUploadBuffer; _DataTransferUploadBuffer_bytesSent = new WeakMap(); //# sourceMappingURL=dataTransferUploadBuffer.js.map