/** * Created by delian on 3/12/14. * This module provides encoding and decoding of the AMF0 and AMF3 format */ const Logger = require('./logger'); const amf3dRules = { 0x00: amf3decUndefined, 0x01: amf3decNull, 0x02: amf3decFalse, 0x03: amf3decTrue, 0x04: amf3decInteger, 0x05: amf3decDouble, 0x06: amf3decString, 0x07: amf3decXmlDoc, 0x08: amf3decDate, 0x09: amf3decArray, 0x0A: amf3decObject, 0x0B: amf3decXml, 0x0C: amf3decByteArray //, // 0x0D: amf3decVecInt, // 0x0E: amf3decVecUInt, // 0x0F: amf3decVecDouble, // 0x10: amf3decVecObject, // 0x11: amf3decDict // No dictionary support for the moment! }; const amf3eRules = { 'string': amf3encString, 'integer': amf3encInteger, 'double': amf3encDouble, 'xml': amf3encXmlDoc, 'object': amf3encObject, 'array': amf3encArray, 'sarray': amf3encArray, 'binary': amf3encByteArray, 'true': amf3encTrue, 'false': amf3encFalse, 'undefined': amf3encUndefined, 'null': amf3encNull }; const amf0dRules = { 0x00: amf0decNumber, 0x01: amf0decBool, 0x02: amf0decString, 0x03: amf0decObject, // 0x04: amf0decMovie, // Reserved 0x05: amf0decNull, 0x06: amf0decUndefined, 0x07: amf0decRef, 0x08: amf0decArray, // 0x09: amf0decObjEnd, // Should never happen normally 0x0A: amf0decSArray, 0x0B: amf0decDate, 0x0C: amf0decLongString, // 0x0D: amf0decUnsupported, // Has been never originally implemented by Adobe! // 0x0E: amf0decRecSet, // Has been never originally implemented by Adobe! 0x0F: amf0decXmlDoc, 0x10: amf0decTypedObj, 0x11: amf0decSwitchAmf3 }; const amf0eRules = { 'string': amf0encString, 'integer': amf0encNumber, 'double': amf0encNumber, 'xml': amf0encXmlDoc, 'object': amf0encObject, 'array': amf0encArray, 'sarray': amf0encSArray, 'binary': amf0encString, 'true': amf0encBool, 'false': amf0encBool, 'undefined': amf0encUndefined, 'null': amf0encNull }; function amfType(o) { let jsType = typeof o; if (o === null) return 'null'; if (jsType == 'undefined') return 'undefined'; if (jsType == 'number') { if (parseInt(o) == o) return 'integer'; return 'double'; } if (jsType == 'boolean') return o ? 'true' : 'false'; if (jsType == 'string') return 'string'; if (jsType == 'object') { if (o instanceof Array) { if (o.sarray) return 'sarray'; return 'array'; } return 'object'; } throw new Error('Unsupported type!') } // AMF3 implementation /** * AMF3 Decode undefined value * @returns {{len: number, value: undefined}} */ function amf3decUndefined() { return { len: 1, value: undefined } } /** * AMF3 Encode undefined value * @returns {Buffer} */ function amf3encUndefined() { let buf = Buffer.alloc(1); buf.writeUInt8(0x00); return buf; } /** * AMF3 Decode null * @returns {{len: number, value: null}} */ function amf3decNull() { return { len: 1, value: null } } /** * AMF3 Encode null * @returns {Buffer} */ function amf3encNull() { let buf = Buffer.alloc(1); buf.writeUInt8(0x01); return buf; } /** * AMF3 Decode false * @returns {{len: number, value: boolean}} */ function amf3decFalse() { return { len: 1, value: false } } /** * AMF3 Encode false * @returns {Buffer} */ function amf3encFalse() { let buf = Buffer.alloc(1); buf.writeUInt8(0x02); return buf; } /** * AMF3 Decode true * @returns {{len: number, value: boolean}} */ function amf3decTrue() { return { len: 1, value: true } } /** * AMF3 Encode true * @returns {Buffer} */ function amf3encTrue() { let buf = Buffer.alloc(1); buf.writeUInt8(0x03); return buf; } /** * Generic decode of AMF3 UInt29 values * @param buf * @returns {{len: number, value: number}} */ function amf3decUI29(buf) { let val = 0; let len = 1; let b; do { b = buf.readUInt8(len++); val = (val << 7) + (b & 0x7F); } while (len < 5 || b > 0x7F); if (len == 5) val = val | b; // Preserve the major bit of the last byte return { len: len, value: val } } /** * Generic encode of AMF3 UInt29 value * @param num * @returns {Buffer} */ function amf3encUI29(num) { let len = 0; if (num < 0x80) len = 1; if (num < 0x4000) len = 2; if (num < 0x200000) len = 3; if (num >= 0x200000) len = 4; let buf = Buffer.alloc(len); switch (len) { case 1: buf.writeUInt8(num, 0); break; case 2: buf.writeUInt8(num & 0x7F, 0); buf.writeUInt8((num >> 7) | 0x80, 1); break; case 3: buf.writeUInt8(num & 0x7F, 0); buf.writeUInt8((num >> 7) & 0x7F, 1); buf.writeUInt8((num >> 14) | 0x80, 2); break; case 4: buf.writeUInt8(num & 0xFF, 0); buf.writeUInt8((num >> 8) & 0x7F, 1); buf.writeUInt8((num >> 15) | 0x7F, 2); buf.writeUInt8((num >> 22) | 0x7F, 3); break; } return buf; } /** * AMF3 Decode an integer * @param buf * @returns {{len: number, value: number}} */ function amf3decInteger(buf) { // Invert the integer let resp = amf3decUI29(buf); if (resp.value > 0x0FFFFFFF) resp.value = (resp.value & 0x0FFFFFFF) - 0x10000000; return resp; } /** * AMF3 Encode an integer * @param num * @returns {Buffer} */ function amf3encInteger(num) { let buf = Buffer.alloc(1); buf.writeUInt8(0x4, 0); return Buffer.concat([buf, amf3encUI29(num & 0x3FFFFFFF)]); // This AND will auto convert the sign bit! } /** * AMF3 Decode String * @param buf * @returns {{len: *, value: (*|String)}} */ function amf3decString(buf) { let sLen = amf3decUI29(buf); let s = sLen & 1; sLen = sLen >> 1; // The real length without the lowest bit if (s) return { len: sLen.value + 5, value: buf.slice(5, sLen.value + 5).toString('utf8') }; throw new Error("Error, we have a need to decode a String that is a Reference"); // TODO: Implement references! } /** * AMF3 Encode String * @param str * @returns {Buffer} */ function amf3encString(str) { let sLen = amf3encUI29(str.length << 1); let buf = Buffer.alloc(1); buf.writeUInt8(0x6, 0); return Buffer.concat([buf, sLen, Buffer.from(str, 'utf8')]); } /** * AMF3 Decode XMLDoc * @param buf * @returns {{len: *, value: (*|String)}} */ function amf3decXmlDoc(buf) { let sLen = amf3decUI29(buf); let s = sLen & 1; sLen = sLen >> 1; // The real length without the lowest bit if (s) return { len: sLen.value + 5, value: buf.slice(5, sLen.value + 5).toString('utf8') }; throw new Error("Error, we have a need to decode a String that is a Reference"); // TODO: Implement references! } /** * AMF3 Encode XMLDoc * @param str * @returns {Buffer} */ function amf3encXmlDoc(str) { let sLen = amf3encUI29(str.length << 1); let buf = Buffer.alloc(1); buf.writeUInt8(0x7, 0); return Buffer.concat([buf, sLen, Buffer.from(str, 'utf8')]); } /** * AMF3 Decode Generic XML * @param buf * @returns {{len: *, value: (*|String)}} */ function amf3decXml(buf) { let sLen = amf3decUI29(buf); let s = sLen & 1; sLen = sLen >> 1; // The real length without the lowest bit if (s) return { len: sLen.value + 5, value: buf.slice(5, sLen.value + 5).toString('utf8') }; throw new Error("Error, we have a need to decode a String that is a Reference"); // TODO: Implement references! } /** * AMF3 Encode Generic XML * @param str * @returns {Buffer} */ function amf3encXml(str) { let sLen = amf3encUI29(str.length << 1); let buf = Buffer.alloc(1); buf.writeUInt8(0x0B, 0); return Buffer.concat([buf, sLen, Buffer.from(str, 'utf8')]); } /** * AMF3 Decide Byte Array * @param buf * @returns {{len: *, value: (Array|string|*|Buffer|Blob)}} */ function amf3decByteArray(buf) { let sLen = amf3decUI29(buf); let s = sLen & 1; // TODO: Check if we follow the same rule! sLen = sLen >> 1; // The real length without the lowest bit if (s) return { len: sLen.value + 5, value: buf.slice(5, sLen.value + 5) }; throw new Error("Error, we have a need to decode a String that is a Reference"); // TODO: Implement references! } /** * AMF3 Encode Byte Array * @param str * @returns {Buffer} */ function amf3encByteArray(str) { let sLen = amf3encUI29(str.length << 1); let buf = Buffer.alloc(1); buf.writeUInt8(0x0C, 0); return Buffer.concat([buf, sLen, (typeof str == 'string') ? Buffer.from(str, 'binary') : str]); } /** * AMF3 Decode Double * @param buf * @returns {{len: number, value: (*|Number)}} */ function amf3decDouble(buf) { return { len: 9, value: buf.readDoubleBE(1) } } /** * AMF3 Encode Double * @param num * @returns {Buffer} */ function amf3encDouble(num) { let buf = Buffer.alloc(9); buf.writeUInt8(0x05, 0); buf.writeDoubleBE(num, 1); return buf; } /** * AMF3 Decode Date * @param buf * @returns {{len: *, value: (*|Number)}} */ function amf3decDate(buf) { // The UI29 should be 1 let uTz = amf3decUI29(buf); let ts = buf.readDoubleBE(uTz.len); return { len: uTz.len + 8, value: ts } } /** * AMF3 Encode Date * @param ts * @returns {Buffer} */ function amf3encDate(ts) { let buf = Buffer.alloc(1); buf.writeUInt8(0x8, 0); let tsBuf = Buffer.alloc(8); tsBuf.writeDoubleBE(ts, 0); return Buffer.concat([buf, amf3encUI29(1), tsBuf]); // We always do 1 } /** * AMF3 Decode Array * @param buf * @returns {{len: *, value: *}} */ function amf3decArray(buf) { let count = amf3decUI29(buf.slice(1)); let obj = amf3decObject(buf.slice(count.len)); if (count.value % 2 == 1) throw new Error("This is a reference to another array, which currently we don't support!"); return { len: count.len + obj.len, value: obj.value } } /** * AMF3 Encode Array */ function amf3encArray() { throw new Error('Encoding arrays is not supported yet!'); // TODO: Implement encoding of arrays } /** * AMF3 Decode Object * @param buf */ function amf3decObject(buf) { let obj = {}; let pos = 0; return obj; } /** * AMF3 Encode Object * @param o */ function amf3encObject(o) { } // AMF0 Implementation /** * AMF0 Decode Number * @param buf * @returns {{len: number, value: (*|Number)}} */ function amf0decNumber(buf) { return { len: 9, value: buf.readDoubleBE(1) } } /** * AMF0 Encode Number * @param num * @returns {Buffer} */ function amf0encNumber(num) { let buf = Buffer.alloc(9); buf.writeUInt8(0x00, 0); buf.writeDoubleBE(num, 1); return buf; } /** * AMF0 Decode Boolean * @param buf * @returns {{len: number, value: boolean}} */ function amf0decBool(buf) { return { len: 2, value: (buf.readUInt8(1) != 0) } } /** * AMF0 Encode Boolean * @param num * @returns {Buffer} */ function amf0encBool(num) { let buf = Buffer.alloc(2); buf.writeUInt8(0x01, 0); buf.writeUInt8((num ? 1 : 0), 1); return buf; } /** * AMF0 Decode Null * @returns {{len: number, value: null}} */ function amf0decNull() { return { len: 1, value: null } } /** * AMF0 Encode Null * @returns {Buffer} */ function amf0encNull() { let buf = Buffer.alloc(1); buf.writeUInt8(0x05, 0); return buf; } /** * AMF0 Decode Undefined * @returns {{len: number, value: undefined}} */ function amf0decUndefined() { return { len: 1, value: undefined } } /** * AMF0 Encode Undefined * @returns {Buffer} */ function amf0encUndefined() { let buf = Buffer.alloc(1); buf.writeUInt8(0x06, 0); return buf; } /** * AMF0 Decode Date * @param buf * @returns {{len: number, value: (*|Number)}} */ function amf0decDate(buf) { // let s16 = buf.readInt16BE(1); let ts = buf.readDoubleBE(3); return { len: 11, value: ts } } /** * AMF0 Encode Date * @param ts * @returns {Buffer} */ function amf0encDate(ts) { let buf = Buffer.alloc(11); buf.writeUInt8(0x0B, 0); buf.writeInt16BE(0, 1); buf.writeDoubleBE(ts, 3); return buf; } /** * AMF0 Decode Object * @param buf * @returns {{len: number, value: {}}} */ function amf0decObject(buf) { // TODO: Implement references! let obj = {}; let iBuf = buf.slice(1); let len = 1; // Logger.debug('ODec',iBuf.readUInt8(0)); while (iBuf.readUInt8(0) != 0x09) { // Logger.debug('Field', iBuf.readUInt8(0), iBuf); let prop = amf0decUString(iBuf); // Logger.debug('Got field for property', prop); len += prop.len; if(iBuf.length < prop.len) { break; } if (iBuf.slice(prop.len).readUInt8(0) == 0x09) { len++; // Logger.debug('Found the end property'); break; } // END Object as value, we shall leave if (prop.value == '') break; let val = amf0DecodeOne(iBuf.slice(prop.len)); // Logger.debug('Got field for value', val); obj[prop.value] = val.value; len += val.len; iBuf = iBuf.slice(prop.len + val.len); } return { len: len, value: obj } } /** * AMF0 Encode Object */ function amf0encObject(o) { if (typeof o !== 'object') return; let data = Buffer.alloc(1); data.writeUInt8(0x03, 0); // Type object let k; for (k in o) { data = Buffer.concat([data, amf0encUString(k), amf0EncodeOne(o[k])]); } let termCode = Buffer.alloc(1); termCode.writeUInt8(0x09, 0); return Buffer.concat([data, amf0encUString(''), termCode]); } /** * AMF0 Decode Reference * @param buf * @returns {{len: number, value: string}} */ function amf0decRef(buf) { let index = buf.readUInt16BE(1); return { len: 3, value: 'ref' + index } } /** * AMF0 Encode Reference * @param index * @returns {Buffer} */ function amf0encRef(index) { let buf = Buffer.alloc(3); buf.writeUInt8(0x07, 0); buf.writeUInt16BE(index, 1); return buf; } /** * AMF0 Decode String * @param buf * @returns {{len: *, value: (*|string|String)}} */ function amf0decString(buf) { let sLen = buf.readUInt16BE(1); return { len: 3 + sLen, value: buf.toString('utf8', 3, 3 + sLen) } } /** * AMF0 Decode Untyped (without the type byte) String * @param buf * @returns {{len: *, value: (*|string|String)}} */ function amf0decUString(buf) { let sLen = buf.readUInt16BE(0); return { len: 2 + sLen, value: buf.toString('utf8', 2, 2 + sLen) } } /** * Do AMD0 Encode of Untyped String * @param s * @returns {Buffer} */ function amf0encUString(str) { let data = Buffer.from(str, 'utf8'); let sLen = Buffer.alloc(2); sLen.writeUInt16BE(data.length, 0); return Buffer.concat([sLen, data]); } /** * AMF0 Encode String * @param str * @returns {Buffer} */ function amf0encString(str) { let buf = Buffer.alloc(3); buf.writeUInt8(0x02, 0); buf.writeUInt16BE(str.length, 1); return Buffer.concat([buf, Buffer.from(str, 'utf8')]); } /** * AMF0 Decode Long String * @param buf * @returns {{len: *, value: (*|string|String)}} */ function amf0decLongString(buf) { let sLen = buf.readUInt32BE(1); return { len: 5 + sLen, value: buf.toString('utf8', 5, 5 + sLen) } } /** * AMF0 Encode Long String * @param str * @returns {Buffer} */ function amf0encLongString(str) { let buf = Buffer.alloc(5); buf.writeUInt8(0x0C, 0); buf.writeUInt32BE(str.length, 1); return Buffer.concat([buf, Buffer.from(str, 'utf8')]); } /** * AMF0 Decode Array * @param buf * @returns {{len: *, value: ({}|*)}} */ function amf0decArray(buf) { // let count = buf.readUInt32BE(1); let obj = amf0decObject(buf.slice(4)); return { len: 5 + obj.len, value: obj.value } } /** * AMF0 Encode Array */ function amf0encArray(a) { let l = 0; if (a instanceof Array) l = a.length; else l = Object.keys(a).length; Logger.debug('Array encode', l, a); let buf = Buffer.alloc(5); buf.writeUInt8(8, 0); buf.writeUInt32BE(l, 1); let data = amf0encObject(a); return Buffer.concat([buf, data.slice(1)]); } /** * AMF0 Encode Binary Array into binary Object * @param aData * @returns {Buffer} */ function amf0cnletray2Object(aData) { let buf = Buffer.alloc(1); buf.writeUInt8(0x3, 0); // Object id return Buffer.concat([buf, aData.slice(5)]); } /** * AMF0 Encode Binary Object into binary Array * @param oData * @returns {Buffer} */ function amf0cnvObject2Array(oData) { let buf = Buffer.alloc(5); let o = amf0decObject(oData); let l = Object.keys(o).length; buf.writeUInt32BE(l, 1); return Buffer.concat([buf, oData.slice(1)]); } /** * AMF0 Decode XMLDoc * @param buf * @returns {{len: *, value: (*|string|String)}} */ function amf0decXmlDoc(buf) { let sLen = buf.readUInt16BE(1); return { len: 3 + sLen, value: buf.toString('utf8', 3, 3 + sLen) } } /** * AMF0 Encode XMLDoc * @param str * @returns {Buffer} */ function amf0encXmlDoc(str) { // Essentially it is the same as string let buf = Buffer.alloc(3); buf.writeUInt8(0x0F, 0); buf.writeUInt16BE(str.length, 1); return Buffer.concat([buf, Buffer.from(str, 'utf8')]); } /** * AMF0 Decode Strict Array * @param buf * @returns {{len: number, value: Array}} */ function amf0decSArray(buf) { let a = []; let len = 5; let ret; for (let count = buf.readUInt32BE(1); count; count--) { ret = amf0DecodeOne(buf.slice(len)); a.push(ret.value); len += ret.len; } return { len: len, value: amf0markSArray(a) } } /** * AMF0 Encode Strict Array * @param a Array */ function amf0encSArray(a) { Logger.debug('Do strict array!'); let buf = Buffer.alloc(5); buf.writeUInt8(0x0A, 0); buf.writeUInt32BE(a.length, 1); let i; for (i = 0; i < a.length; i++) { buf = Buffer.concat([buf, amf0EncodeOne(a[i])]); } return buf; } function amf0markSArray(a) { Object.defineProperty(a, 'sarray', { value: true }); return a; } /** * AMF0 Decode Typed Object * @param buf * @returns {{len: number, value: ({}|*)}} */ function amf0decTypedObj(buf) { let className = amf0decString(buf); let obj = amf0decObject(buf.slice(className.len - 1)); obj.value.__className__ = className.value; return { len: className.len + obj.len - 1, value: obj.value } } /** * AMF0 Decode Switch AMF3 Object * @param buf * @returns {{len: number, value: ({}|*)}} */ function amf0decSwitchAmf3(buf) { let r = amf3DecodeOne(buf.slice(1)); return r; } /** * AMF0 Encode Typed Object */ function amf0encTypedObj() { throw new Error("Error: SArray encoding is not yet implemented!"); // TODO: Error } /** * Decode one value from the Buffer according to the applied rules * @param rules * @param buffer * @returns {*} */ function amfXDecodeOne(rules, buffer) { if (!rules[buffer.readUInt8(0)]) { Logger.error('Unknown field', buffer.readUInt8(0)); return null; } return rules[buffer.readUInt8(0)](buffer); } /** * Decode one AMF0 value * @param buffer * @returns {*} */ function amf0DecodeOne(buffer) { return amfXDecodeOne(amf0dRules, buffer); } /** * Decode one AMF3 value * @param buffer * @returns {*} */ function amf3DecodeOne(buffer) { return amfXDecodeOne(amf3dRules, buffer); } /** * Decode a whole buffer of AMF values according to rules and return in array * @param rules * @param buffer * @returns {Array} */ function amfXDecode(rules, buffer) { // We shall receive clean buffer and will respond with an array of values let resp = []; let res; for (let i = 0; i < buffer.length;) { res = amfXDecodeOne(rules, buffer.slice(i)); i += res.len; resp.push(res.value); // Add the response } return resp; } /** * Decode a buffer of AMF3 values * @param buffer * @returns {Array} */ function amf3Decode(buffer) { return amfXDecode(amf3dRules, buffer); } /** * Decode a buffer of AMF0 values * @param buffer * @returns {Array} */ function amf0Decode(buffer) { return amfXDecode(amf0dRules, buffer); } /** * Encode one AMF value according to rules * @param rules * @param o * @returns {*} */ function amfXEncodeOne(rules, o) { // Logger.debug('amfXEncodeOne type',o,amfType(o),rules[amfType(o)]); let f = rules[amfType(o)]; if (f) return f(o); throw new Error('Unsupported type for encoding!'); } /** * Encode one AMF0 value * @param o * @returns {*} */ function amf0EncodeOne(o) { return amfXEncodeOne(amf0eRules, o); } /** * Encode one AMF3 value * @param o * @returns {*} */ function amf3EncodeOne(o) { return amfXEncodeOne(amf3eRules, o); } /** * Encode an array of values into a buffer * @param a * @returns {Buffer} */ function amf3Encode(a) { let buf = Buffer.alloc(0); a.forEach(function (o) { buf = Buffer.concat([buf, amf3EncodeOne(o)]); }); return buf; } /** * Encode an array of values into a buffer * @param a * @returns {Buffer} */ function amf0Encode(a) { let buf = Buffer.alloc(0); a.forEach(function (o) { buf = Buffer.concat([buf, amf0EncodeOne(o)]); }); return buf; } const rtmpCmdCode = { "_result": ["transId", "cmdObj", "info"], "_error": ["transId", "cmdObj", "info", "streamId"], // Info / Streamid are optional "onStatus": ["transId", "cmdObj", "info"], "releaseStream": ["transId", "cmdObj", "streamName"], "getStreamLength": ["transId", "cmdObj", "streamId"], "getMovLen": ["transId", "cmdObj", "streamId"], "FCPublish": ["transId", "cmdObj", "streamName"], "FCUnpublish": ["transId", "cmdObj", "streamName"], "onFCPublish": ["transId", "cmdObj", "info"], "connect": ["transId", "cmdObj", "args"], "call": ["transId", "cmdObj", "args"], "createStream": ["transId", "cmdObj"], "close": ["transId", "cmdObj"], "play": ["transId", "cmdObj", "streamName", "start", "duration", "reset"], "play2": ["transId", "cmdObj", "params"], "deleteStream": ["transId", "cmdObj", "streamId"], "closeStream": ["transId", "cmdObj"], "receiveAudio": ["transId", "cmdObj", "bool"], "receiveVideo": ["transId", "cmdObj", "bool"], "publish": ["transId", "cmdObj", "streamName", "type"], "seek": ["transId", "cmdObj", "ms"], "pause": ["transId", "cmdObj", "pause", "ms"] }; const rtmpDataCode = { "@setDataFrame": ["method", "dataObj"], "onMetaData": ["dataObj"], "|RtmpSampleAccess": ["bool1", "bool2"], }; /** * Decode a data! * @param dbuf * @returns {{cmd: (*|string|String|*), value: *}} */ function decodeAmf0Data(dbuf) { let buffer = dbuf; let resp = {}; let cmd = amf0DecodeOne(buffer); if(cmd) { resp.cmd = cmd.value; buffer = buffer.slice(cmd.len); if (rtmpDataCode[cmd.value]) { rtmpDataCode[cmd.value].forEach(function (n) { if (buffer.length > 0) { let r = amf0DecodeOne(buffer); if(r) { buffer = buffer.slice(r.len); resp[n] = r.value; } } }); } else { Logger.error('Unknown command', resp); } } return resp } /** * Decode a command! * @param dbuf * @returns {{cmd: (*|string|String|*), value: *}} */ function decodeAMF0Cmd(dbuf) { let buffer = dbuf; let resp = {}; let cmd = amf0DecodeOne(buffer); resp.cmd = cmd.value; buffer = buffer.slice(cmd.len); if (rtmpCmdCode[cmd.value]) { rtmpCmdCode[cmd.value].forEach(function (n) { if (buffer.length > 0) { let r = amf0DecodeOne(buffer); buffer = buffer.slice(r.len); resp[n] = r.value; } }); } else { Logger.error('Unknown command', resp); } return resp } /** * Encode AMF0 Command * @param opt * @returns {*} */ function encodeAMF0Cmd(opt) { let data = amf0EncodeOne(opt.cmd); if (rtmpCmdCode[opt.cmd]) { rtmpCmdCode[opt.cmd].forEach(function (n) { if (opt.hasOwnProperty(n)) data = Buffer.concat([data, amf0EncodeOne(opt[n])]); }); } else { Logger.error('Unknown command', opt); } // Logger.debug('Encoded as',data.toString('hex')); return data } function encodeAMF0Data(opt) { let data = amf0EncodeOne(opt.cmd); if (rtmpDataCode[opt.cmd]) { rtmpDataCode[opt.cmd].forEach(function (n) { if (opt.hasOwnProperty(n)) data = Buffer.concat([data, amf0EncodeOne(opt[n])]); }); } else { Logger.error('Unknown data', opt); } // Logger.debug('Encoded as',data.toString('hex')); return data } /** * * @param dbuf * @returns {{}} */ function decodeAMF3Cmd(dbuf) { let buffer = dbuf; let resp = {}; let cmd = amf3DecodeOne(buffer); resp.cmd = cmd.value; buffer = buffer.slice(cmd.len); if (rtmpCmdCode[cmd.value]) { rtmpCmdCode[cmd.value].forEach(function (n) { if (buffer.length > 0) { let r = amf3DecodeOne(buffer); buffer = buffer.slice(r.len); resp[n] = r.value; } }); } else { Logger.error('Unknown command', resp); } return resp } /** * Encode AMF3 Command * @param opt * @returns {*} */ function encodeAMF3Cmd(opt) { let data = amf0EncodeOne(opt.cmd); if (rtmpCmdCode[opt.cmd]) { rtmpCmdCode[opt.cmd].forEach(function (n) { if (opt.hasOwnProperty(n)) data = Buffer.concat([data, amf3EncodeOne(opt[n])]); }); } else { Logger.error('Unknown command', opt); } return data } module.exports = { decodeAmf3Cmd: decodeAMF3Cmd, encodeAmf3Cmd: encodeAMF3Cmd, decodeAmf0Cmd: decodeAMF0Cmd, encodeAmf0Cmd: encodeAMF0Cmd, decodeAmf0Data: decodeAmf0Data, encodeAmf0Data: encodeAMF0Data, amfType: amfType, amf0Encode: amf0Encode, amf0EncodeOne: amf0EncodeOne, amf0Decode: amf0Decode, amf0DecodeOne: amf0DecodeOne, amf3Encode: amf3Encode, amf3EncodeOne: amf3EncodeOne, amf3Decode: amf3Decode, amf3DecodeOne: amf3DecodeOne, amf0cnvA2O: amf0cnletray2Object, amf0cnvO2A: amf0cnvObject2Array, amf0markSArray: amf0markSArray, amf0decArray: amf0decArray, amf0decBool: amf0decBool, amf0decDate: amf0decDate, amf0decLongString: amf0decLongString, amf0decNull: amf0decNull, amf0decNumber: amf0decNumber, amf0decObject: amf0decObject, amf0decRef: amf0decRef, amf0decSArray: amf0decSArray, amf0decString: amf0decString, amf0decTypedObj: amf0decTypedObj, amf0decUndefined: amf0decUndefined, amf0decXmlDoc: amf0decXmlDoc, amf0encArray: amf0encArray, amf0encBool: amf0encBool, amf0encDate: amf0encDate, amf0encLongString: amf0encLongString, amf0encNull: amf0encNull, amf0encNumber: amf0encNumber, amf0encObject: amf0encObject, amf0encRef: amf0encRef, amf0encSArray: amf0encSArray, amf0encString: amf0encString, amf0encTypedObj: amf0encTypedObj, amf0encUndefined: amf0encUndefined, amf0encXmlDoc: amf0encXmlDoc, amf3decArray: amf3decArray, amf3decByteArray: amf3decByteArray, amf3decDate: amf3decDate, amf3decDouble: amf3decDouble, amf3decFalse: amf3decFalse, amf3decInteger: amf3decInteger, amf3decNull: amf3decNull, amf3decObject: amf3decObject, amf3decString: amf3decString, amf3decTrue: amf3decTrue, amf3decUI29: amf3decUI29, amf3decUndefined: amf3decUndefined, amf3decXml: amf3decXml, amf3decXmlDoc: amf3decXmlDoc, amf3encArray: amf3encArray, amf3encByteArray: amf3encByteArray, amf3encDate: amf3encDate, amf3encDouble: amf3encDouble, amf3encFalse: amf3encFalse, amf3encInteger: amf3encInteger, amf3encNull: amf3encNull, amf3encObject: amf3encObject, amf3encString: amf3encString, amf3encTrue: amf3encTrue, amf3encUI29: amf3encUI29, amf3encUndefined: amf3encUndefined, amf3encXml: amf3encXml, amf3encXmlDoc: amf3encXmlDoc };