H265Session.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. /* exported H265Session */
  2. /* global Uint8Array, H265SPSParser, H265Decoder, inheritObject,
  3. RtpSession, VideoBufferList, ArrayBuffer, decodeMode */
  4. /* eslint-disable no-magic-numbers */
  5. var H265Session = function () {
  6. 'use strict';
  7. var rtpTimeStamp = 0,
  8. inputLength = 0,
  9. size1K = 1024,
  10. size1M = size1K * size1K,
  11. playback = false,
  12. outputSize = 0,
  13. curSize = 0,
  14. inputBuffer = new Uint8Array(size1M),
  15. PREFIX = new Uint8Array(4),
  16. SPSParser = null,
  17. privIRtpTime = 0,
  18. // frameDiffTime = 0,
  19. // needDropCnt = 0,
  20. delayingTime = 0,
  21. DELAY_LIMIT = 8000,
  22. decodedData = {
  23. frameData: null,
  24. timeStamp: null,
  25. },
  26. timeData = {
  27. 'timestamp': null,
  28. 'timezone': null,
  29. },
  30. frameRate = 0;
  31. var width = 0,
  32. height = 0;
  33. PREFIX[0] = '0x00';
  34. PREFIX[1] = '0x00';
  35. PREFIX[2] = '0x00';
  36. PREFIX[3] = '0x01';
  37. var setBuffer = function (buffer1, buffer2) {
  38. var tempBuffer = buffer1;
  39. if ((inputLength + buffer2.length) > tempBuffer.length) {
  40. tempBuffer = new Uint8Array(tempBuffer.length + size1M);
  41. }
  42. tempBuffer.set(buffer2, inputLength);
  43. inputLength += buffer2.length;
  44. return tempBuffer;
  45. };
  46. var decodeMode = 'canvas';
  47. function Constructor() {
  48. this.decoder = new H265Decoder();
  49. }
  50. Constructor.prototype = {
  51. init: function () {
  52. SPSParser = new H265SPSParser();
  53. console.log(SPSParser.parse)
  54. //this.decoder.setIsFirstFrame(false);
  55. this.videoBufferList = new VideoBufferList();
  56. this.firstDiffTime = 0;
  57. this.checkDelay = true;
  58. },
  59. setFrameRate(fps) {
  60. frameRate = fps;
  61. //console.log('frameRate: ', frameRate)
  62. },
  63. remuxRTPData: function (rtspInterleaved, rtpHeader, rtpPayload, isBackup) {
  64. var HEADER = rtpHeader,
  65. PAYLOAD = null,
  66. timeData = {
  67. 'timestamp': null,
  68. 'timezone': null,
  69. },
  70. extensionHeaderLen = 0,
  71. PaddingSize = 0,
  72. data = {};
  73. if (rtspInterleaved[0] !== 0x24) {
  74. console.log("H265Session::it is not valid interleave header (RTSP over TCP)");
  75. return;
  76. } else if ((rtpHeader[0] & 0x0F) === 0x0F) {
  77. console.log("H265Session::There is additional CSRC which is not handled in this version");
  78. return;
  79. } else if ((rtpHeader[0] & 0x20) === 0x20) {
  80. PaddingSize = rtpPayload[rtpPayload.length - 1];
  81. console.log("H265Session::PaddingSize - " + PaddingSize);
  82. }
  83. //Extension bit check in RTPHeader
  84. if ((rtpHeader[0] & 0x10) === 0x10) {
  85. extensionHeaderLen = (((rtpPayload[2] << 8) | rtpPayload[3]) * 4) + 4;
  86. //Playback check
  87. if (rtpPayload[0] === 0xAB && rtpPayload[1] === 0xAD) {
  88. var startHeader = 4,
  89. NTPmsw = new Uint8Array(new ArrayBuffer(4)),
  90. NTPlsw = new Uint8Array(new ArrayBuffer(4)),
  91. gmt = new Uint8Array(new ArrayBuffer(2)),
  92. fsynctime = {
  93. 'seconds': null,
  94. 'useconds': null,
  95. },
  96. microseconds = null;
  97. NTPmsw.set(rtpPayload.subarray(startHeader, startHeader + 4), 0);
  98. startHeader += 4;
  99. NTPlsw.set(rtpPayload.subarray(startHeader, startHeader + 4), 0);
  100. startHeader += 6;
  101. gmt.set(rtpPayload.subarray(startHeader, startHeader + 2), 0);
  102. microseconds = (this.ntohl(NTPlsw) / 0xffffffff) * 1000;
  103. fsynctime.seconds = ((this.ntohl(NTPmsw) - 0x83AA7E80) >>> 0);
  104. fsynctime.useconds = microseconds;
  105. gmt = (((gmt[0] << 8) | gmt[1]) << 16) >> 16;
  106. timeData = {
  107. timestamp: fsynctime.seconds,
  108. timestamp_usec: fsynctime.useconds,
  109. timezone: gmt,
  110. };
  111. if ((this.getFramerate() === 0 || typeof this.getFramerate() === "undefined") &&
  112. (typeof this.getTimeStamp() !== "undefined")) {
  113. var diffUsec = timeData.timestamp_usec - this.getTimeStamp().timestamp_usec;
  114. this.setFramerate(Math.round(1000 / (((timeData.timestamp -
  115. this.getTimeStamp().timestamp) === 0 ? 0 : 1000) + (diffUsec))));
  116. }
  117. this.setTimeStamp(timeData);
  118. playback = true;
  119. }
  120. }
  121. PAYLOAD = rtpPayload.subarray(extensionHeaderLen, rtpPayload.length - PaddingSize);
  122. rtpTimeStamp = this.ntohl(rtpHeader.subarray(4, 8));
  123. var nalType = (PAYLOAD[0] >> 1) & 0x3f;
  124. switch (nalType) {
  125. default:
  126. inputBuffer = setBuffer(inputBuffer, PREFIX);
  127. inputBuffer = setBuffer(inputBuffer, PAYLOAD);
  128. break;
  129. // Fragmentation unit(FU)
  130. case 49:
  131. var startBit = ((PAYLOAD[2] & 0x80) === 0x80);
  132. var endBit = ((PAYLOAD[2] & 0x40) === 0x40);
  133. var fuType = PAYLOAD[2] & 0x3f;
  134. var payloadStartIndex = 3;
  135. if (startBit === true && endBit === false) {
  136. var newNalHeader = new Uint8Array(2);
  137. newNalHeader[0] = (PAYLOAD[0] & 0x81) | (fuType << 1);
  138. newNalHeader[1] = PAYLOAD[1];
  139. inputBuffer = setBuffer(inputBuffer, PREFIX);
  140. inputBuffer = setBuffer(inputBuffer, newNalHeader);
  141. inputBuffer = setBuffer(inputBuffer,
  142. PAYLOAD.subarray(payloadStartIndex, PAYLOAD.length));
  143. } else {
  144. inputBuffer = setBuffer(inputBuffer,
  145. PAYLOAD.subarray(payloadStartIndex, PAYLOAD.length));
  146. }
  147. break;
  148. //SPS
  149. case 33:
  150. SPSParser.parse(PAYLOAD);
  151. var resolution = SPSParser.getSizeInfo();
  152. curSize = resolution.width * resolution.height;
  153. if (playback && !isBackup) {
  154. var limitSize = 1280 * 720;
  155. if (curSize > limitSize) {
  156. data.error = {
  157. errorCode: "998",
  158. description: "Resolution is too big",
  159. place: "h265Session.js",
  160. };
  161. this.rtpReturnCallback(data);
  162. return; // data;
  163. }
  164. }
  165. inputBuffer = setBuffer(inputBuffer, PREFIX);
  166. inputBuffer = setBuffer(inputBuffer, PAYLOAD);
  167. width = resolution.width;
  168. height = resolution.height;
  169. break;
  170. } //end of switch(nalType)
  171. //case 48 Remove because not use more
  172. //check marker bit
  173. var frameType = '';
  174. if ((HEADER[1] & 0x80) === 0x80) {
  175. // var DepacketizingTime = Date.now() - beforeDepacketizing;
  176. if (outputSize !== curSize) {
  177. outputSize = curSize;
  178. this.decoder.setOutputSize(outputSize);
  179. }
  180. var inputBufferSub = inputBuffer.subarray(0, inputLength);
  181. if (inputBufferSub[4] === 0x40) {
  182. frameType = 'I';
  183. if (this.firstDiffTime == 0) {
  184. privIRtpTime = rtpTimeStamp;
  185. delayingTime = 0;
  186. this.firstDiffTime = (Date.now() - (rtpTimeStamp / 90).toFixed(0));
  187. // needDropCnt = 0;
  188. } else {
  189. // frameDiffTime = Math.round(((rtpTimeStamp / 90).toFixed(0) - privIRtpTime) / this.getGovLength());
  190. if((rtpTimeStamp - privIRtpTime) < 0){
  191. this.firstDiffTime = delayingTime + (Date.now() - (rtpTimeStamp / 90).toFixed(0));
  192. // console.log("firstDiffTime = " + this.firstDiffTime + " rtpTimeStamp = " + rtpTimeStamp + " privIRtpTime = " + privIRtpTime );
  193. }
  194. delayingTime = (Date.now() - (rtpTimeStamp / 90).toFixed(0)) - this.firstDiffTime;
  195. privIRtpTime = rtpTimeStamp;
  196. if (delayingTime > DELAY_LIMIT) {
  197. if (this.checkDelay === true && playback === false) {
  198. data.error = {
  199. errorCode: "997",
  200. description: "Delay time is too long",
  201. place: "h265Session.js",
  202. };
  203. // console.log("h265Session::Delay time is too long 997 error ");
  204. this.rtpReturnCallback(data);
  205. return; // data;
  206. }
  207. }
  208. // needDropCnt = (needDropCnt > 0) ? Math.round(needDropCnt / frameDiffTime) : 0;
  209. }
  210. } else {
  211. frameType = 'P';
  212. }
  213. decodedData.frameData = null;
  214. if (isBackup !== true || playback !== true) {
  215. console.log('frameType', frameType)
  216. decodedData.frameData = this.decoder.decode(inputBufferSub);
  217. }
  218. decodedData.timeStamp = null;
  219. inputLength = 0;
  220. if (playback === true) {
  221. timeData = (timeData.timestamp === null ? this.getTimeStamp() : timeData);
  222. decodedData.timeStamp = timeData;
  223. }
  224. if (isBackup) {
  225. data.backupData = {
  226. 'stream': inputBufferSub,
  227. 'frameType': frameType,
  228. 'width': width,
  229. 'height': height,
  230. 'codecType': 'h265',
  231. };
  232. if (timeData.timestamp !== null && typeof timeData.timestamp !== "undefined") {
  233. data.backupData.timestamp_usec = timeData.timestamp_usec;
  234. } else {
  235. data.backupData.timestamp = (rtpTimeStamp / 90).toFixed(0);
  236. }
  237. }
  238. data.decodedData = decodedData;
  239. if (decodeMode !== "canvas") {
  240. data.decodeMode = "canvas";
  241. }
  242. console.log(data)
  243. this.rtpReturnCallback(data);
  244. return; // data;
  245. }
  246. },
  247. bufferingRtpData: function (rtspInterleaved, rtpHeader, rtpPayload) {
  248. var HEADER = rtpHeader,
  249. PAYLOAD = null,
  250. extensionHeaderLen = 0,
  251. PaddingSize = 0;
  252. if (rtspInterleaved[0] !== 0x24) {
  253. console.log("H265Session::it is not valid interleave header (RTSP over TCP)");
  254. return;
  255. } else if ((rtpHeader[0] & 0x0F) === 0x0F) {
  256. console.log("H265Session::There is additional CSRC which is not handled in this version");
  257. return;
  258. } else if ((rtpHeader[0] & 0x20) === 0x20) {
  259. PaddingSize = rtpPayload[rtpPayload.length - 1];
  260. console.log("H265Session::PaddingSize - " + PaddingSize);
  261. }
  262. //Extension bit check in RTPHeader
  263. if ((rtpHeader[0] & 0x10) === 0x10) {
  264. extensionHeaderLen = (((rtpPayload[2] << 8) | rtpPayload[3]) * 4) + 4;
  265. //Playback check
  266. if (rtpPayload[0] === 0xAB && rtpPayload[1] === 0xAD) {
  267. var startHeader = 4,
  268. NTPmsw = new Uint8Array(new ArrayBuffer(4)),
  269. NTPlsw = new Uint8Array(new ArrayBuffer(4)),
  270. gmt = new Uint8Array(new ArrayBuffer(2)),
  271. fsynctime = {
  272. 'seconds': null,
  273. 'useconds': null,
  274. },
  275. microseconds = null;
  276. NTPmsw.set(rtpPayload.subarray(startHeader, startHeader + 4), 0);
  277. startHeader += 4;
  278. NTPlsw.set(rtpPayload.subarray(startHeader, startHeader + 4), 0);
  279. startHeader += 6;
  280. gmt.set(rtpPayload.subarray(startHeader, startHeader + 2), 0);
  281. microseconds = (this.ntohl(NTPlsw) / 0xffffffff) * 1000;
  282. fsynctime.seconds = ((this.ntohl(NTPmsw) - 0x83AA7E80) >>> 0);
  283. fsynctime.useconds = microseconds;
  284. gmt = (((gmt[0] << 8) | gmt[1]) << 16) >> 16;
  285. timeData = {
  286. timestamp: fsynctime.seconds,
  287. timestamp_usec: fsynctime.useconds,
  288. timezone: gmt,
  289. };
  290. playback = true;
  291. }
  292. }
  293. PAYLOAD = rtpPayload.subarray(extensionHeaderLen, rtpPayload.length - PaddingSize);
  294. rtpTimeStamp = new Uint8Array(new ArrayBuffer(4));
  295. rtpTimeStamp.set(rtpHeader.subarray(4, 8), 0);
  296. rtpTimeStamp = this.ntohl(rtpTimeStamp);
  297. var nalType = (PAYLOAD[0] >> 1) & 0x3f;
  298. switch (nalType) {
  299. default:
  300. inputBuffer = setBuffer(inputBuffer, PREFIX);
  301. inputBuffer = setBuffer(inputBuffer, PAYLOAD);
  302. break;
  303. // Fragmentation unit(FU)
  304. case 49:
  305. var startBit = ((PAYLOAD[2] & 0x80) === 0x80);
  306. var endBit = ((PAYLOAD[2] & 0x40) === 0x40);
  307. var fuType = PAYLOAD[2] & 0x3f;
  308. var payloadStartIndex = 3;
  309. if (startBit === true && endBit === false) {
  310. var newNalHeader = new Uint8Array(2);
  311. newNalHeader[0] = (PAYLOAD[0] & 0x81) | (fuType << 1);
  312. newNalHeader[1] = PAYLOAD[1];
  313. inputBuffer = setBuffer(inputBuffer, PREFIX);
  314. inputBuffer = setBuffer(inputBuffer, newNalHeader);
  315. inputBuffer = setBuffer(inputBuffer,
  316. PAYLOAD.subarray(payloadStartIndex, PAYLOAD.length));
  317. } else {
  318. inputBuffer = setBuffer(inputBuffer,
  319. PAYLOAD.subarray(payloadStartIndex, PAYLOAD.length));
  320. }
  321. break;
  322. //SPS
  323. case 33:
  324. SPSParser.parse(PAYLOAD);
  325. var resolution = SPSParser.getSizeInfo();
  326. curSize = resolution.width * resolution.height;
  327. inputBuffer = setBuffer(inputBuffer, PREFIX);
  328. inputBuffer = setBuffer(inputBuffer, PAYLOAD);
  329. width = resolution.width;
  330. height = resolution.height;
  331. break;
  332. } //end of switch(nalType)
  333. //case 48 Remove because not use more
  334. //check marker bit
  335. if ((HEADER[1] & 0x80) === 0x80) {
  336. if (outputSize !== curSize) {
  337. outputSize = curSize;
  338. this.decoder.setOutputSize(outputSize);
  339. }
  340. var stepBufferSub = new Uint8Array(inputBuffer.subarray(0, inputLength));
  341. if (this.videoBufferList !== null) {
  342. this.videoBufferList.push(stepBufferSub, width, height,
  343. 'h265', (stepBufferSub[4] === 0x40) ? 'I' : 'P', timeData);
  344. }
  345. inputLength = 0;
  346. }
  347. },
  348. findIFrame: function () {
  349. if (this.videoBufferList !== null) {
  350. var bufferNode = this.videoBufferList.findIFrame();
  351. if (bufferNode === null || typeof bufferNode === "undefined") {
  352. return false;
  353. } else {
  354. var data = {};
  355. this.setTimeStamp(bufferNode.timeStamp);
  356. data.frameData = this.decoder.decode(bufferNode.buffer);
  357. data.timeStamp = bufferNode.timeStamp;
  358. return data;
  359. }
  360. }
  361. },
  362. ntohl: function (buffer) {
  363. return (((buffer[0] << 24) + (buffer[1] << 16) +
  364. (buffer[2] << 8) + buffer[3]) >>> 0);
  365. },
  366. set rtpSessionCallback(func) {
  367. this.rtpReturnCallback = func;
  368. },
  369. };
  370. return new Constructor();
  371. };
  372. /* eslint-enable no-magic-numbers */