Skip to content

Instantly share code, notes, and snippets.

@sxd1140
Created June 3, 2020 06:49
Show Gist options
  • Save sxd1140/11117d36baf83f1f621a3661bbc6e5b2 to your computer and use it in GitHub Desktop.
Save sxd1140/11117d36baf83f1f621a3661bbc6e5b2 to your computer and use it in GitHub Desktop.
DEFAULT_PORT = 9910;
RECONNECT_INTERVAL = 5000;
COMMAND_CONNECT_HELLO = [0x10, 0x14, 0x53, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
COMMAND_CONNECT_HELLO_ANSWER = [0x80, 0x0C, 0x53, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00];
AUDIO_GAIN_RATE = 65381;
ATEM_maxInitPackageCount = 40;
ATEM.Model = {
'TVS': 0x01,
'1ME': 0x02,
'2ME': 0x03,
'PS4K': 0x04,
'1ME4K': 0x05,
'2ME4K': 0x06,
'2MEBS4K': 0x07
};
};
ATEM.prototype.connectionState = ATEM.ConnectionState.Closed;
ATEM.prototype.localPackedId = 1;
ATEM.prototype.sessionId = [];
ATEM.prototype.remotePacketId = [];
ATEM.prototype.missedInitializationPackages = [];
ATEM.prototype.waitingForIncoming = false;
ATEM.prototype.initPayloadSent = false;
ATEM.prototype.hasInitialized = false;
ATEM.prototype.initPayloadSentAtPacketId = 0;
function ATEM(options) {
if (options == null) {
options = {};
}
this._receivePacket = __bind(this._receivePacket, this);
this.forceOldStyle = options.forceOldStyle || false;
this.localPort = options.localPort || 1024 + Math.floor(Math.random() * 64511);
this.event = new EventEmitter;
this.commandEvent = new EventEmitter;
this.event.on('ping', (function(_this) {
return function(err) {
console.log('record lastconnectat')
return _this.lastConnectAt = new Date().getTime();
};
})(this));
this.socket = dgram.createSocket('udp4');
this.socket.on('message', this._receivePacket);
this.socket.bind(this.localPort);
setInterval((function(_this) {
return function() {
if (_this.lastConnectAt + RECONNECT_INTERVAL > new Date().getTime()) {
return;
}
if (_this.connectionState === ATEM.ConnectionState.Established) {
_this.connectionState = ATEM.ConnectionState.Closed;
_this.event.emit('disconnect', null, null);
}
// _this.localPackedId = 1;
// _this.sessionId = [];
return _this.connect(_this.address, _this.port);
};
})(this), RECONNECT_INTERVAL);
}
ATEM.prototype.connect = function (address, port, local_port) {
this.address = address;
this.port = port != null ? port : DEFAULT_PORT;
if (local_port == null) {
local_port = 0;
}
this.localPackedId = 0; // Init localPacketIDCounter to 0;
this.iRemotePacketID = 0;
this.remotePacketId = [];
this.initPayloadSent = false; // Will be true after initial payload of data is delivered (regular 12-byte ping packages are transmitted.)
this.hasInitialized = false; // Will be true after initial payload of data is resent and received well
this.isConnected = false; // Will be true after the initial hello-package handshakes.
this.sessionId = [0x53, 0xAB]; // Temporary session ID - a new will be given back from ATEM.
this.lastConnectAt = new Date().getTime();
for (var i = 0; i < ATEM_maxInitPackageCount + 7 / 8; i++) {
this.missedInitializationPackages[i] = 0xFF;
}
this.initPayloadSentAtPacketId = ATEM_maxInitPackageCount;
this._sendPacket(COMMAND_CONNECT_HELLO);
return this.connectionState = ATEM.ConnectionState.SynSent;
};
ATEM.prototype.on = function (name, callback) {
return this.event.on(name, callback);
};
buffer[1] = 0x0C;
buffer[2] = this.sessionId[0];
buffer[3] = this.sessionId[1];
buffer[4] = this.remotePacketId[0];
buffer[5] = this.remotePacketId[1];
buffer[9] = 0x41;
return this._sendPacket(buffer);
};
ATEM.prototype._sendCommand = function (command, payload) {
if (!this.hasInitialized) return;
var buffer;
if (!Buffer.isBuffer(payload)) {
payload = new Buffer(payload);
}
buffer = new Buffer(20 + payload.length);
buffer.fill(0);
buffer[0] = (20 + payload.length) / 256 | 0x08;
buffer[1] = (20 + payload.length) % 256;
buffer[2] = this.sessionId[0];
buffer[3] = this.sessionId[1];
buffer[19] = command.charCodeAt(3);
payload.copy(buffer, 20);
this._sendPacket(buffer);
return this.localPackedId++;
};
ATEM.prototype._sendPacket = function (buffer) {
if (!Buffer.isBuffer(buffer)) {
buffer = new Buffer(buffer);
}
// if (DEBUG) {
// console.log('SEND', buffer);
// }
// console.log('local id ' , buffer[10]<<8 |buffer[11]&255);
// console.log(String.fromCharCode(buffer[16],buffer[17],buffer[18],buffer[19]));
// if (this.connectionState == ATEM.ConnectionState.Closed) return;
return this.socket.send(buffer, 0, buffer.length, this.port, this.address);
};
ATEM.prototype._receivePacket = function (message, remote) {
var flags, length;
length = ((message[0] & 0x07) << 8) | message[1];
if (length !== remote.size) {
return;
}
// if (DEBUG) {
// console.log('RECV', message);
// }
flags = message[0] >> 3;
this.sessionId = [message[2], message[3]];
this.remotePacketId[0] = message[10];
this.remotePacketId[1] = message[11];
this.iRemotePacketID = message[10] << 8 | message[11];
this.lastConnectAt = new Date().getTime();
this.waitingForIncoming = false;
if (this.iRemotePacketID < ATEM_maxInitPackageCount) {
this.missedInitializationPackages[this.iRemotePacketID >> 3] &= ~(1 << (this.iRemotePacketID & 0x07));
}
if (flags & 0x2 && !(flags & 0x4)) {
this.isConnected = true;
COMMAND_CONNECT_HELLO_ANSWER[2] = this.sessionId[0];
COMMAND_CONNECT_HELLO_ANSWER[3] = this.sessionId[1];
this._sendPacket(COMMAND_CONNECT_HELLO_ANSWER);
}
if (!this.initPayloadSent && length == 12 && this.iRemotePacketID > 0) {
this.initPayloadSent = true;
this.initPayloadSentAtPacketId = this.iRemotePacketID;
}
if (this.initPayloadSent && (flags & 0x1) && (this.hasInitialized || !(flags & 0x4))) {
var buffer;
buffer = new Buffer(12);
buffer.fill(0);
buffer[0] = 0x10 << 3 | 12 >> 8 & 0x7;
buffer[1] = 12 & 0xFF;
buffer[2] = this.sessionId[0];
buffer[3] = this.sessionId[1];
buffer[4] = this.remotePacketId[0];
buffer[5] = this.remotePacketId[1];
// this.localPackedId++;
buffer[10] = this.localPackedId >> 8;
buffer[11] = this.localPackedId & 0xFF;
this._sendPacket(buffer);
// this._sendAck();
}
else if (this.initPayloadSent && (flags & 0x8) && this.hasInitialized) {
var b1 = message[6];
var b2 = message[7];
var buffer;
buffer = new Buffer(12);
buffer.fill(0);
buffer[0] = 0x1 << 3 | 12 >> 8 & 0x7;
buffer[1] = 12 & 0xFF;
buffer[2] = this.sessionId[0];
buffer[3] = this.sessionId[1];
buffer[4] = this.remotePacketId[0];
buffer[5] = this.remotePacketId[1];
buffer[10] = b1;
buffer[11] = b2;
this._sendPacket(buffer);
}
if ((flags & 0x1) && length > 12) {
this._parseCommand(message.slice(12));
this.event.emit('stateChanged', null, this.state);
}
if (!this.hasInitialized && this.initPayloadSent && !this.waitingForIncoming) {
for (var i = 1; i < this.initPayloadSentAtPacketId; i++) {
if (i <= ATEM_maxInitPackageCount) {
if (this.missedInitializationPackages[i >> 3] & (1 << (i & 0x7))) {
var buffer;
buffer = new Buffer(12);
buffer.fill(0);
buffer[0] = 0x8 << 3 | 12 >> 8 & 0x7;
buffer[1] = 12 & 0xFF;
buffer[2] = this.sessionId[0];
buffer[3] = this.sessionId[1];
buffer[4] = this.remotePacketId[0];
buffer[5] = this.remotePacketId[1];
buffer[6] = (i - 1) >> 8;
buffer[7] = (i - 1) & 0xFF;
buffer[8] = 1;
// buffer[10] = this.localPackedId >> 8;
// buffer[11] = this.localPackedId & 0xFF;
this._sendPacket(buffer);
this.waitingForIncoming = true;
break;
}
} else {
break;
}
}
if (!this.waitingForIncoming) {
this.hasInitialized = true;
this.event.emit('connect', null, null);
}
}
/*
if (flags & ATEM.PacketFlag.Connect && !(flags & ATEM.PacketFlag.Repeat)) {
console.log('hello ack');
console.log('new sessionid', this.sessionId)
this._sendPacket(COMMAND_CONNECT_HELLO_ANSWER);
}
if (flags & ATEM.PacketFlag.Sync && length > 12) {
this._parseCommand(message.slice(12));
this.event.emit('stateChanged', null, this.state);
}
if (flags & ATEM.PacketFlag.Sync && length === 12 && this.connectionState === ATEM.ConnectionState.SynSent) {
this.connectionState = ATEM.ConnectionState.Established;
console.log('connect first');
this.event.emit('connect', null, null);
}
if (flags & ATEM.PacketFlag.Sync && this.connectionState === ATEM.ConnectionState.Established) {
// if (a) {
this._sendAck();
// a = false;
// setTimeout(()=>{
// b = true;
// },10000)
// }
console.log('get ping');
return this.event.emit('ping', null, null);
}
*/
};
ATEM.prototype._parseCommand = function(buffer) {
var key, length, name, value, _ref, _results;
length = this._parseNumber(buffer.slice(0, 2));
name = this._parseString(buffer.slice(4, 8));
if (DEBUG) {
console.log('COMMAND', "" + name + "(" + length + ")", buffer.slice(0, length));
}
this._setStatus(name, buffer.slice(0, length).slice(8));
if (me == null) {
me = 0;
}
return this._sendCommand('DCut', [me, 0xef, 0xbf, 0x5f]);
};
ATEM.prototype.changeTransitionPosition = function(position, me) {
if (me == null) {
me = 0;
}
return this._sendCommand('CTPs', [me, 0x00, position / 256, position % 256]);
if (position >= 10000) {
return this._sendCommand('CTPs', [me, 0x00, 0x00, 0x00]);
}
};
ATEM.prototype.changeTransitionPreview = function(state, me) {
if (me == null) {
me = 0;
}
return this._sendCommand('CTPr', [me, state, 0x00, 0x00]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment