No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

rtmp_client.js 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. //
  2. // Created by Mingliang Chen on 18/6/21.
  3. // illuspas[a]gmail.com
  4. // Copyright (c) 2018 Nodemedia. All rights reserved.
  5. //
  6. const EventEmitter = require('events');
  7. const Logger = require('./logger');
  8. const Crypto = require('crypto');
  9. const Url = require('url');
  10. const Net = require('net');
  11. const AMF = require('./amf');
  12. const FLASHVER = "LNX 9,0,124,2";
  13. const RTMP_OUT_CHUNK_SIZE = 60000;
  14. const RTMP_PORT = 1935;
  15. const RTMP_HANDSHAKE_SIZE = 1536;
  16. const RTMP_HANDSHAKE_UNINIT = 0;
  17. const RTMP_HANDSHAKE_0 = 1;
  18. const RTMP_HANDSHAKE_1 = 2;
  19. const RTMP_HANDSHAKE_2 = 3;
  20. const RTMP_PARSE_INIT = 0;
  21. const RTMP_PARSE_BASIC_HEADER = 1;
  22. const RTMP_PARSE_MESSAGE_HEADER = 2;
  23. const RTMP_PARSE_EXTENDED_TIMESTAMP = 3;
  24. const RTMP_PARSE_PAYLOAD = 4;
  25. const RTMP_CHUNK_HEADER_MAX = 18;
  26. const RTMP_CHUNK_TYPE_0 = 0; // 11-bytes: timestamp(3) + length(3) + stream type(1) + stream id(4)
  27. const RTMP_CHUNK_TYPE_1 = 1; // 7-bytes: delta(3) + length(3) + stream type(1)
  28. const RTMP_CHUNK_TYPE_2 = 2; // 3-bytes: delta(3)
  29. const RTMP_CHUNK_TYPE_3 = 3; // 0-byte
  30. const RTMP_CHANNEL_PROTOCOL = 2;
  31. const RTMP_CHANNEL_INVOKE = 3;
  32. const RTMP_CHANNEL_AUDIO = 4;
  33. const RTMP_CHANNEL_VIDEO = 5;
  34. const RTMP_CHANNEL_DATA = 6;
  35. const rtmpHeaderSize = [11, 7, 3, 0];
  36. /* Protocol Control Messages */
  37. const RTMP_TYPE_SET_CHUNK_SIZE = 1;
  38. const RTMP_TYPE_ABORT = 2;
  39. const RTMP_TYPE_ACKNOWLEDGEMENT = 3; // bytes read report
  40. const RTMP_TYPE_WINDOW_ACKNOWLEDGEMENT_SIZE = 5; // server bandwidth
  41. const RTMP_TYPE_SET_PEER_BANDWIDTH = 6; // client bandwidth
  42. /* User Control Messages Event (4) */
  43. const RTMP_TYPE_EVENT = 4;
  44. const RTMP_TYPE_AUDIO = 8;
  45. const RTMP_TYPE_VIDEO = 9;
  46. /* Data Message */
  47. const RTMP_TYPE_FLEX_STREAM = 15; // AMF3
  48. const RTMP_TYPE_DATA = 18; // AMF0
  49. /* Shared Object Message */
  50. const RTMP_TYPE_FLEX_OBJECT = 16; // AMF3
  51. const RTMP_TYPE_SHARED_OBJECT = 19; // AMF0
  52. /* Command Message */
  53. const RTMP_TYPE_FLEX_MESSAGE = 17; // AMF3
  54. const RTMP_TYPE_INVOKE = 20; // AMF0
  55. /* Aggregate Message */
  56. const RTMP_TYPE_METADATA = 22;
  57. const RTMP_CHUNK_SIZE = 128;
  58. const RTMP_PING_TIME = 60000;
  59. const RTMP_PING_TIMEOUT = 30000;
  60. const STREAM_BEGIN = 0x00;
  61. const STREAM_EOF = 0x01;
  62. const STREAM_DRY = 0x02;
  63. const STREAM_EMPTY = 0x1f;
  64. const STREAM_READY = 0x20;
  65. const RTMP_TRANSACTION_CONNECT = 1;
  66. const RTMP_TRANSACTION_CREATE_STREAM = 2;
  67. const RTMP_TRANSACTION_GET_STREAM_LENGTH = 3;
  68. const RtmpPacket = {
  69. create: (fmt = 0, cid = 0) => {
  70. return {
  71. header: {
  72. fmt: fmt,
  73. cid: cid,
  74. timestamp: 0,
  75. length: 0,
  76. type: 0,
  77. stream_id: 0
  78. },
  79. clock: 0,
  80. delta: 0,
  81. payload: null,
  82. capacity: 0,
  83. bytes: 0
  84. };
  85. }
  86. };
  87. class NodeRtmpClient {
  88. constructor(rtmpUrl) {
  89. this.url = rtmpUrl;
  90. this.info = this.rtmpUrlParser(rtmpUrl);
  91. this.isPublish = false;
  92. this.launcher = new EventEmitter();
  93. this.handshakePayload = Buffer.alloc(RTMP_HANDSHAKE_SIZE);
  94. this.handshakeState = RTMP_HANDSHAKE_UNINIT;
  95. this.handshakeBytes = 0;
  96. this.parserBuffer = Buffer.alloc(RTMP_CHUNK_HEADER_MAX);
  97. this.parserState = RTMP_PARSE_INIT;
  98. this.parserBytes = 0;
  99. this.parserBasicBytes = 0;
  100. this.parserPacket = null;
  101. this.inPackets = new Map();
  102. this.inChunkSize = RTMP_CHUNK_SIZE;
  103. this.outChunkSize = RTMP_CHUNK_SIZE;
  104. this.streamId = 0;
  105. this.isSocketOpen = false;
  106. }
  107. onSocketData(data) {
  108. let bytes = data.length;
  109. let p = 0;
  110. let n = 0;
  111. while (bytes > 0) {
  112. switch (this.handshakeState) {
  113. case RTMP_HANDSHAKE_UNINIT:
  114. // read s0
  115. // Logger.debug('[rtmp client] read s0');
  116. this.handshakeState = RTMP_HANDSHAKE_0;
  117. this.handshakeBytes = 0;
  118. bytes -= 1;
  119. p += 1;
  120. break;
  121. case RTMP_HANDSHAKE_0:
  122. // read s1
  123. n = RTMP_HANDSHAKE_SIZE - this.handshakeBytes;
  124. n = n <= bytes ? n : bytes;
  125. data.copy(this.handshakePayload, this.handshakeBytes, p, p + n);
  126. this.handshakeBytes += n;
  127. bytes -= n;
  128. p += n;
  129. if (this.handshakeBytes === RTMP_HANDSHAKE_SIZE) {
  130. // Logger.debug('[rtmp client] read s1');
  131. this.handshakeState = RTMP_HANDSHAKE_1;
  132. this.handshakeBytes = 0;
  133. this.socket.write(this.handshakePayload);// write c2;
  134. // Logger.debug('[rtmp client] write c2');
  135. }
  136. break;
  137. case RTMP_HANDSHAKE_1:
  138. //read s2
  139. n = RTMP_HANDSHAKE_SIZE - this.handshakeBytes;
  140. n = n <= bytes ? n : bytes;
  141. data.copy(this.handshakePayload, this.handshakeBytes, p, n);
  142. this.handshakeBytes += n;
  143. bytes -= n;
  144. p += n;
  145. if (this.handshakeBytes === RTMP_HANDSHAKE_SIZE) {
  146. // Logger.debug('[rtmp client] read s2');
  147. this.handshakeState = RTMP_HANDSHAKE_2;
  148. this.handshakeBytes = 0;
  149. this.handshakePayload = null;
  150. this.rtmpSendConnect();
  151. }
  152. break;
  153. case RTMP_HANDSHAKE_2:
  154. return this.rtmpChunkRead(data, p, bytes);
  155. }
  156. }
  157. }
  158. onSocketError(e) {
  159. Logger.error('rtmp_client', "onSocketError", e);
  160. this.isSocketOpen = false;
  161. this.stop();
  162. }
  163. onSocketClose() {
  164. // Logger.debug('rtmp_client', "onSocketClose");
  165. this.isSocketOpen = false;
  166. this.stop();
  167. }
  168. onSocketTimeout() {
  169. // Logger.debug('rtmp_client', "onSocketTimeout");
  170. this.isSocketOpen = false;
  171. this.stop();
  172. }
  173. on(event, callback) {
  174. this.launcher.on(event, callback);
  175. }
  176. startPull() {
  177. this._start();
  178. }
  179. startPush() {
  180. this.isPublish = true;
  181. this._start();
  182. }
  183. _start() {
  184. this.socket = Net.createConnection(this.info.port, this.info.hostname, () => {
  185. //rtmp handshark c0c1
  186. let c0c1 = Crypto.randomBytes(1537);
  187. c0c1.writeUInt8(3);
  188. c0c1.writeUInt32BE(Date.now() / 1000, 1);
  189. c0c1.writeUInt32BE(0, 5);
  190. this.socket.write(c0c1);
  191. // Logger.debug('[rtmp client] write c0c1');
  192. });
  193. this.socket.on('data', this.onSocketData.bind(this));
  194. this.socket.on('error', this.onSocketError.bind(this));
  195. this.socket.on('close', this.onSocketClose.bind(this));
  196. this.socket.on('timeout', this.onSocketTimeout.bind(this));
  197. this.socket.setTimeout(60000);
  198. }
  199. stop() {
  200. if (this.streamId > 0) {
  201. if(!this.socket.destroyed) {
  202. if (this.isPublish) {
  203. this.rtmpSendFCUnpublish();
  204. }
  205. this.rtmpSendDeleteStream();
  206. this.socket.destroy();
  207. }
  208. this.streamId = 0;
  209. this.launcher.emit('close');
  210. }
  211. }
  212. pushAudio(audioData, timestamp) {
  213. if (this.streamId == 0) return;
  214. let packet = RtmpPacket.create();
  215. packet.header.fmt = RTMP_CHUNK_TYPE_0;
  216. packet.header.cid = RTMP_CHANNEL_AUDIO;
  217. packet.header.type = RTMP_TYPE_AUDIO;
  218. packet.payload = audioData;
  219. packet.header.length = packet.payload.length;
  220. packet.header.timestamp = timestamp;
  221. let rtmpChunks = this.rtmpChunksCreate(packet);
  222. this.socket.write(rtmpChunks);
  223. }
  224. pushVideo(videoData, timestamp) {
  225. if (this.streamId == 0) return;
  226. let packet = RtmpPacket.create();
  227. packet.header.fmt = RTMP_CHUNK_TYPE_0;
  228. packet.header.cid = RTMP_CHANNEL_VIDEO;
  229. packet.header.type = RTMP_TYPE_VIDEO;
  230. packet.payload = videoData;
  231. packet.header.length = packet.payload.length;
  232. packet.header.timestamp = timestamp;
  233. let rtmpChunks = this.rtmpChunksCreate(packet);
  234. this.socket.write(rtmpChunks);
  235. }
  236. pushScript(scriptData, timestamp) {
  237. if (this.streamId == 0) return;
  238. let packet = RtmpPacket.create();
  239. packet.header.fmt = RTMP_CHUNK_TYPE_0;
  240. packet.header.cid = RTMP_CHANNEL_DATA;
  241. packet.header.type = RTMP_TYPE_DATA;
  242. packet.payload = scriptData;
  243. packet.header.length = packet.payload.length;
  244. packet.header.timestamp = timestamp;
  245. let rtmpChunks = this.rtmpChunksCreate(packet);
  246. this.socket.write(rtmpChunks);
  247. }
  248. rtmpUrlParser(url) {
  249. let urlInfo = Url.parse(url, true);
  250. urlInfo.app = urlInfo.path.split('/')[1];
  251. urlInfo.port = !!urlInfo.port ? urlInfo.port : RTMP_PORT;
  252. urlInfo.tcurl = urlInfo.href.match(/rtmp:\/\/([^\/]+)\/([^\/]+)/)[0];
  253. urlInfo.stream = urlInfo.path.slice(urlInfo.app.length + 2);
  254. return urlInfo;
  255. }
  256. rtmpChunkBasicHeaderCreate(fmt, cid) {
  257. let out;
  258. if (cid >= 64 + 255) {
  259. out = Buffer.alloc(3);
  260. out[0] = (fmt << 6) | 1;
  261. out[1] = (cid - 64) & 0xFF;
  262. out[2] = ((cid - 64) >> 8) & 0xFF;
  263. } else if (cid >= 64) {
  264. out = Buffer.alloc(2);
  265. out[0] = (fmt << 6) | 0;
  266. out[1] = (cid - 64) & 0xFF;
  267. } else {
  268. out = Buffer.alloc(1);
  269. out[0] = (fmt << 6) | cid;
  270. }
  271. return out;
  272. }
  273. rtmpChunkMessageHeaderCreate(header) {
  274. let out = Buffer.alloc(rtmpHeaderSize[header.fmt % 4]);
  275. if (header.fmt <= RTMP_CHUNK_TYPE_2) {
  276. out.writeUIntBE(header.timestamp >= 0xffffff ? 0xffffff : header.timestamp, 0, 3);
  277. }
  278. if (header.fmt <= RTMP_CHUNK_TYPE_1) {
  279. out.writeUIntBE(header.length, 3, 3);
  280. out.writeUInt8(header.type, 6);
  281. }
  282. if (header.fmt === RTMP_CHUNK_TYPE_0) {
  283. out.writeUInt32LE(header.stream_id, 7);
  284. }
  285. return out;
  286. }
  287. rtmpChunksCreate(packet) {
  288. let header = packet.header;
  289. let payload = packet.payload;
  290. let payloadSize = header.length;
  291. let chunkSize = this.outChunkSize;
  292. let chunksOffset = 0;
  293. let payloadOffset = 0;
  294. let chunkBasicHeader = this.rtmpChunkBasicHeaderCreate(header.fmt, header.cid);
  295. let chunkBasicHeader3 = this.rtmpChunkBasicHeaderCreate(RTMP_CHUNK_TYPE_3, header.cid);
  296. let chunkMessageHeader = this.rtmpChunkMessageHeaderCreate(header);
  297. let useExtendedTimestamp = header.timestamp >= 0xffffff;
  298. let headerSize = chunkBasicHeader.length + chunkMessageHeader.length + (useExtendedTimestamp ? 4 : 0);
  299. let n = headerSize + payloadSize + Math.floor(payloadSize / chunkSize);
  300. if (useExtendedTimestamp) {
  301. n += Math.floor(payloadSize / chunkSize) * 4;
  302. }
  303. if (!(payloadSize % chunkSize)) {
  304. n -= 1;
  305. if (useExtendedTimestamp) { //TODO CHECK
  306. n -= 4;
  307. }
  308. }
  309. let chunks = Buffer.alloc(n);
  310. chunkBasicHeader.copy(chunks, chunksOffset);
  311. chunksOffset += chunkBasicHeader.length;
  312. chunkMessageHeader.copy(chunks, chunksOffset);
  313. chunksOffset += chunkMessageHeader.length;
  314. if (useExtendedTimestamp) {
  315. chunks.writeUInt32BE(header.timestamp, chunksOffset);
  316. chunksOffset += 4;
  317. }
  318. while (payloadSize > 0) {
  319. if (payloadSize > chunkSize) {
  320. payload.copy(chunks, chunksOffset, payloadOffset, payloadOffset + chunkSize);
  321. payloadSize -= chunkSize;
  322. chunksOffset += chunkSize;
  323. payloadOffset += chunkSize;
  324. chunkBasicHeader3.copy(chunks, chunksOffset);
  325. chunksOffset += chunkBasicHeader3.length;
  326. if (useExtendedTimestamp) {
  327. chunks.writeUInt32BE(header.timestamp, chunksOffset);
  328. chunksOffset += 4;
  329. }
  330. } else {
  331. payload.copy(chunks, chunksOffset, payloadOffset, payloadOffset + payloadSize);
  332. payloadSize -= payloadSize;
  333. chunksOffset += payloadSize;
  334. payloadOffset += payloadSize;
  335. }
  336. }
  337. return chunks;
  338. }
  339. rtmpChunkRead(data, p, bytes) {
  340. let size = 0;
  341. let offset = 0;
  342. let extended_timestamp = 0;
  343. while (offset < bytes) {
  344. switch (this.parserState) {
  345. case RTMP_PARSE_INIT:
  346. this.parserBytes = 1;
  347. this.parserBuffer[0] = data[p + offset++];
  348. if (0 === (this.parserBuffer[0] & 0x3F)) {
  349. this.parserBasicBytes = 2;
  350. } else if (1 === (this.parserBuffer[0] & 0x3F)) {
  351. this.parserBasicBytes = 3;
  352. } else {
  353. this.parserBasicBytes = 1;
  354. }
  355. this.parserState = RTMP_PARSE_BASIC_HEADER;
  356. break;
  357. case RTMP_PARSE_BASIC_HEADER:
  358. while (this.parserBytes < this.parserBasicBytes && offset < bytes) {
  359. this.parserBuffer[this.parserBytes++] = data[p + offset++];
  360. }
  361. if (this.parserBytes >= this.parserBasicBytes) {
  362. this.parserState = RTMP_PARSE_MESSAGE_HEADER;
  363. }
  364. break;
  365. case RTMP_PARSE_MESSAGE_HEADER:
  366. size = rtmpHeaderSize[this.parserBuffer[0] >> 6] + this.parserBasicBytes;
  367. while (this.parserBytes < size && offset < bytes) {
  368. this.parserBuffer[this.parserBytes++] = data[p + offset++];
  369. }
  370. if (this.parserBytes >= size) {
  371. this.rtmpPacketParse();
  372. this.parserState = RTMP_PARSE_EXTENDED_TIMESTAMP;
  373. }
  374. break;
  375. case RTMP_PARSE_EXTENDED_TIMESTAMP:
  376. size = rtmpHeaderSize[this.parserPacket.header.fmt] + this.parserBasicBytes;
  377. if (this.parserPacket.header.timestamp === 0xFFFFFF) size += 4;
  378. while (this.parserBytes < size && offset < bytes) {
  379. this.parserBuffer[this.parserBytes++] = data[p + offset++];
  380. }
  381. if (this.parserBytes >= size) {
  382. if (this.parserPacket.header.timestamp === 0xFFFFFF) {
  383. extended_timestamp = this.parserBuffer.readUInt32BE(rtmpHeaderSize[this.parserPacket.header.fmt] + this.parserBasicBytes);
  384. }
  385. if (0 === this.parserPacket.bytes) {
  386. if (RTMP_CHUNK_TYPE_0 === this.parserPacket.header.fmt) {
  387. this.parserPacket.clock = 0xFFFFFF === this.parserPacket.header.timestamp ? extended_timestamp : this.parserPacket.header.timestamp;
  388. this.parserPacket.delta = 0;
  389. } else {
  390. this.parserPacket.delta = 0xFFFFFF === this.parserPacket.header.timestamp ? extended_timestamp : this.parserPacket.header.timestamp;
  391. }
  392. this.rtmpPacketAlloc();
  393. }
  394. this.parserState = RTMP_PARSE_PAYLOAD;
  395. }
  396. break;
  397. case RTMP_PARSE_PAYLOAD:
  398. size = Math.min(this.inChunkSize - (this.parserPacket.bytes % this.inChunkSize), this.parserPacket.header.length - this.parserPacket.bytes);
  399. size = Math.min(size, bytes - offset);
  400. if (size > 0) {
  401. data.copy(this.parserPacket.payload, this.parserPacket.bytes, p + offset, p + offset + size);
  402. }
  403. this.parserPacket.bytes += size;
  404. offset += size;
  405. if (this.parserPacket.bytes >= this.parserPacket.header.length) {
  406. this.parserState = RTMP_PARSE_INIT;
  407. this.parserPacket.bytes = 0;
  408. this.parserPacket.clock += this.parserPacket.delta;
  409. this.rtmpHandler();
  410. } else if (0 === (this.parserPacket.bytes % this.inChunkSize)) {
  411. this.parserState = RTMP_PARSE_INIT;
  412. }
  413. break;
  414. }
  415. }
  416. }
  417. rtmpPacketParse() {
  418. let fmt = this.parserBuffer[0] >> 6;
  419. let cid = 0;
  420. if (this.parserBasicBytes === 2) {
  421. cid = 64 + this.parserBuffer[1];
  422. } else if (this.parserBasicBytes === 3) {
  423. cid = 64 + this.parserBuffer[1] + this.parserBuffer[2] << 8;
  424. } else {
  425. cid = this.parserBuffer[0] & 0x3F;
  426. }
  427. let hasp = this.inPackets.has(cid);
  428. if (!hasp) {
  429. this.parserPacket = RtmpPacket.create(fmt, cid);
  430. this.inPackets.set(cid, this.parserPacket);
  431. } else {
  432. this.parserPacket = this.inPackets.get(cid);
  433. }
  434. this.parserPacket.header.fmt = fmt;
  435. this.parserPacket.header.cid = cid;
  436. this.rtmpChunkMessageHeaderRead();
  437. // Logger.log(this.parserPacket);
  438. }
  439. rtmpChunkMessageHeaderRead() {
  440. let offset = this.parserBasicBytes;
  441. // timestamp / delta
  442. if (this.parserPacket.header.fmt <= RTMP_CHUNK_TYPE_2) {
  443. this.parserPacket.header.timestamp = this.parserBuffer.readUIntBE(offset, 3);
  444. offset += 3;
  445. }
  446. // message length + type
  447. if (this.parserPacket.header.fmt <= RTMP_CHUNK_TYPE_1) {
  448. this.parserPacket.header.length = this.parserBuffer.readUIntBE(offset, 3);
  449. this.parserPacket.header.type = this.parserBuffer[offset + 3];
  450. offset += 4;
  451. }
  452. if (this.parserPacket.header.fmt === RTMP_CHUNK_TYPE_0) {
  453. this.parserPacket.header.stream_id = this.parserBuffer.readUInt32LE(offset);
  454. offset += 4;
  455. }
  456. return offset;
  457. }
  458. rtmpPacketAlloc() {
  459. if (this.parserPacket.capacity < this.parserPacket.header.length) {
  460. this.parserPacket.payload = Buffer.alloc(this.parserPacket.header.length + 1024);
  461. this.parserPacket.capacity = this.parserPacket.header.length + 1024;
  462. }
  463. }
  464. rtmpHandler() {
  465. switch (this.parserPacket.header.type) {
  466. case RTMP_TYPE_SET_CHUNK_SIZE:
  467. case RTMP_TYPE_ABORT:
  468. case RTMP_TYPE_ACKNOWLEDGEMENT:
  469. case RTMP_TYPE_WINDOW_ACKNOWLEDGEMENT_SIZE:
  470. case RTMP_TYPE_SET_PEER_BANDWIDTH:
  471. return 0 === this.rtmpControlHandler() ? -1 : 0;
  472. case RTMP_TYPE_EVENT:
  473. return 0 === this.rtmpEventHandler() ? -1 : 0;
  474. case RTMP_TYPE_AUDIO:
  475. return this.rtmpAudioHandler();
  476. case RTMP_TYPE_VIDEO:
  477. return this.rtmpVideoHandler();
  478. case RTMP_TYPE_FLEX_MESSAGE:
  479. case RTMP_TYPE_INVOKE:
  480. return this.rtmpInvokeHandler();
  481. case RTMP_TYPE_FLEX_STREAM:// AMF3
  482. case RTMP_TYPE_DATA: // AMF0
  483. return this.rtmpDataHandler();
  484. }
  485. }
  486. rtmpControlHandler() {
  487. let payload = this.parserPacket.payload;
  488. switch (this.parserPacket.header.type) {
  489. case RTMP_TYPE_SET_CHUNK_SIZE:
  490. this.inChunkSize = payload.readUInt32BE();
  491. // Logger.debug('set inChunkSize', this.inChunkSize);
  492. break;
  493. case RTMP_TYPE_ABORT:
  494. break;
  495. case RTMP_TYPE_ACKNOWLEDGEMENT:
  496. break;
  497. case RTMP_TYPE_WINDOW_ACKNOWLEDGEMENT_SIZE:
  498. this.ackSize = payload.readUInt32BE();
  499. // Logger.debug('set ack Size', this.ackSize);
  500. break;
  501. case RTMP_TYPE_SET_PEER_BANDWIDTH:
  502. break;
  503. }
  504. }
  505. rtmpEventHandler() {
  506. let payload = this.parserPacket.payload.slice(0, this.parserPacket.header.length);
  507. let event = payload.readUInt16BE();
  508. let value = payload.readUInt32BE(2);
  509. // Logger.log('rtmpEventHandler', event, value);
  510. switch (event) {
  511. case 6:
  512. this.rtmpSendPingResponse(value);
  513. break;
  514. }
  515. }
  516. rtmpInvokeHandler() {
  517. let offset = this.parserPacket.header.type === RTMP_TYPE_FLEX_MESSAGE ? 1 : 0;
  518. let payload = this.parserPacket.payload.slice(offset, this.parserPacket.header.length);
  519. let invokeMessage = AMF.decodeAmf0Cmd(payload);
  520. // Logger.log('rtmpInvokeHandler', invokeMessage);
  521. switch (invokeMessage.cmd) {
  522. case '_result':
  523. this.rtmpCommandOnresult(invokeMessage);
  524. break;
  525. case '_error':
  526. this.rtmpCommandOnerror(invokeMessage);
  527. break;
  528. case 'onStatus':
  529. this.rtmpCommandOnstatus(invokeMessage);
  530. break;
  531. }
  532. }
  533. rtmpCommandOnresult(invokeMessage) {
  534. // Logger.debug(invokeMessage);
  535. switch (invokeMessage.transId) {
  536. case RTMP_TRANSACTION_CONNECT:
  537. this.launcher.emit('status', invokeMessage.info);
  538. this.rtmpOnconnect();
  539. break;
  540. case RTMP_TRANSACTION_CREATE_STREAM:
  541. this.rtmpOncreateStream(invokeMessage.info);
  542. break;
  543. }
  544. }
  545. rtmpCommandOnerror(invokeMessage) {
  546. this.launcher.emit('status', invokeMessage.info);
  547. }
  548. rtmpCommandOnstatus(invokeMessage) {
  549. this.launcher.emit('status', invokeMessage.info);
  550. }
  551. rtmpOnconnect() {
  552. if (this.isPublish) {
  553. this.rtmpSendReleaseStream();
  554. this.rtmpSendFCPublish();
  555. }
  556. this.rtmpSendCreateStream();
  557. }
  558. rtmpOncreateStream(sid) {
  559. this.streamId = sid;
  560. if (this.isPublish) {
  561. this.rtmpSendPublish();
  562. this.rtmpSendSetChunkSize();
  563. } else {
  564. this.rtmpSendPlay();
  565. this.rtmpSendSetBufferLength(1000);
  566. }
  567. }
  568. rtmpAudioHandler() {
  569. let payload = this.parserPacket.payload.slice(0, this.parserPacket.header.length);
  570. this.launcher.emit('audio', payload, this.parserPacket.clock);
  571. }
  572. rtmpVideoHandler() {
  573. let payload = this.parserPacket.payload.slice(0, this.parserPacket.header.length);
  574. this.launcher.emit('video', payload, this.parserPacket.clock);
  575. }
  576. rtmpDataHandler() {
  577. let payload = this.parserPacket.payload.slice(0, this.parserPacket.header.length);
  578. this.launcher.emit('script', payload, this.parserPacket.clock);
  579. }
  580. sendInvokeMessage(sid, opt) {
  581. let packet = RtmpPacket.create();
  582. packet.header.fmt = RTMP_CHUNK_TYPE_0;
  583. packet.header.cid = RTMP_CHANNEL_INVOKE;
  584. packet.header.type = RTMP_TYPE_INVOKE;
  585. packet.header.stream_id = sid;
  586. packet.payload = AMF.encodeAmf0Cmd(opt);
  587. packet.header.length = packet.payload.length;
  588. let chunks = this.rtmpChunksCreate(packet);
  589. this.socket.write(chunks);
  590. }
  591. rtmpSendConnect() {
  592. let opt = {
  593. cmd: 'connect',
  594. transId: RTMP_TRANSACTION_CONNECT,
  595. cmdObj: {
  596. app: this.info.app,
  597. flashVer: FLASHVER,
  598. tcUrl: this.info.tcurl,
  599. fpad: 0,
  600. capabilities: 15,
  601. audioCodecs: 3191,
  602. videoCodecs: 252,
  603. videoFunction: 1,
  604. encoding: 0
  605. }
  606. }
  607. this.sendInvokeMessage(0, opt);
  608. }
  609. rtmpSendReleaseStream() {
  610. let opt = {
  611. cmd: 'releaseStream',
  612. transId: 0,
  613. cmdObj: null,
  614. streamName: this.info.stream,
  615. };
  616. this.sendInvokeMessage(this.streamId, opt);
  617. }
  618. rtmpSendFCPublish() {
  619. let opt = {
  620. cmd: 'FCPublish',
  621. transId: 0,
  622. cmdObj: null,
  623. streamName: this.info.stream,
  624. };
  625. this.sendInvokeMessage(this.streamId, opt);
  626. }
  627. rtmpSendCreateStream() {
  628. let opt = {
  629. cmd: 'createStream',
  630. transId: RTMP_TRANSACTION_CREATE_STREAM,
  631. cmdObj: null
  632. };
  633. this.sendInvokeMessage(0, opt);
  634. }
  635. rtmpSendPlay() {
  636. let opt = {
  637. cmd: 'play',
  638. transId: 0,
  639. cmdObj: null,
  640. streamName: this.info.stream,
  641. start: -2,
  642. duration: -1,
  643. reset: 1
  644. };
  645. this.sendInvokeMessage(this.streamId, opt);
  646. }
  647. rtmpSendSetBufferLength(bufferTime) {
  648. let packet = RtmpPacket.create();
  649. packet.header.fmt = RTMP_CHUNK_TYPE_0;
  650. packet.header.cid = RTMP_CHANNEL_PROTOCOL;
  651. packet.header.type = RTMP_TYPE_EVENT;
  652. packet.payload = Buffer.alloc(10);
  653. packet.header.length = packet.payload.length;
  654. packet.payload.writeUInt16BE(0x03);
  655. packet.payload.writeUInt32BE(this.streamId, 2);
  656. packet.payload.writeUInt32BE(bufferTime, 6);
  657. let chunks = this.rtmpChunksCreate(packet);
  658. this.socket.write(chunks);
  659. }
  660. rtmpSendPublish() {
  661. let opt = {
  662. cmd: 'publish',
  663. transId: 0,
  664. cmdObj: null,
  665. streamName: this.info.stream,
  666. type: 'live'
  667. };
  668. this.sendInvokeMessage(this.streamId, opt);
  669. }
  670. rtmpSendSetChunkSize() {
  671. let rtmpBuffer = Buffer.from('02000000000004010000000000000000', 'hex');
  672. rtmpBuffer.writeUInt32BE(this.inChunkSize, 12);
  673. this.socket.write(rtmpBuffer);
  674. this.outChunkSize = this.inChunkSize;
  675. }
  676. rtmpSendFCUnpublish() {
  677. let opt = {
  678. cmd: 'FCUnpublish',
  679. transId: 0,
  680. cmdObj: null,
  681. streamName: this.info.stream,
  682. };
  683. this.sendInvokeMessage(this.streamId, opt);
  684. }
  685. rtmpSendDeleteStream() {
  686. let opt = {
  687. cmd: 'deleteStream',
  688. transId: 0,
  689. cmdObj: null,
  690. streamId: this.streamId
  691. };
  692. this.sendInvokeMessage(this.streamId, opt);
  693. }
  694. rtmpSendPingResponse(time) {
  695. let packet = RtmpPacket.create();
  696. packet.header.fmt = RTMP_CHUNK_TYPE_0;
  697. packet.header.cid = RTMP_CHANNEL_PROTOCOL;
  698. packet.header.type = RTMP_TYPE_EVENT;
  699. packet.payload = Buffer.alloc(6);
  700. packet.header.length = packet.payload.length;
  701. packet.payload.writeUInt16BE(0x07);
  702. packet.payload.writeUInt32BE(time, 2);
  703. let chunks = this.rtmpChunksCreate(packet);
  704. this.socket.write(chunks);
  705. }
  706. }
  707. module.exports = NodeRtmpClient