H264Session.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. function H264Session() {
  2. let rtpTimeStamp = 0;
  3. let size1M = 1048576; //1024 * 1024
  4. let inputBuffer = new Uint8Array(size1M);
  5. let spsSegment = null;
  6. let ppsSegment = null;
  7. let SPSParser = null;
  8. let width = 0;
  9. let height = 0;
  10. let inputLength = 0;
  11. let initalSegmentFlag = true; //用于确定是否是initSegment
  12. let initalMediaFrameFlag = true;
  13. let frameRate = null; //根据SDP或者SPS设置
  14. let preSample = null; //上一个Sample
  15. let durationTimeCount = 0;
  16. let frameCount = 0;
  17. let inputSegBufferSub = null;
  18. //MSE使用的数据以及相关配置,顺序codecInfo -> initSegmentData -> mediaSample -> frameData
  19. //时间戳用于绘制人脸框
  20. let decodedData = {
  21. frameData: null, //视频数据
  22. timeStamp: null, //时间戳
  23. initSegmentData: null, //MP4配置,用于initsegment
  24. mediaSample: null, //使用duration控制每一帧的播放时间
  25. codecInfo: "", //MSE init时传入,用于创建mediasource
  26. };
  27. let FRAMETYPE = {
  28. 1: 'P',
  29. 5: 'I',
  30. 6: 'SEI',
  31. 7: 'I'
  32. };
  33. let frameType = '';
  34. let decodeMode = 'video';
  35. let outputSize = 0;
  36. let curSize = 0;
  37. const PREFIX = new Uint8Array(['0x00', '0x00', '0x00', '0x01']);
  38. let firstIframe = false;
  39. let SEIInfo = {
  40. ivs: null,
  41. timestamp: null,
  42. };
  43. let preWidth = null,
  44. preHeight = null;
  45. let resetTimeCount = 0;
  46. let lastTimeStamp = 0;
  47. //const RESETTIME = 162000000;
  48. const RESETTIME = 4320000;
  49. let SEIBuffer = null;
  50. let lastSEITime = 0;
  51. function constructor() {
  52. }
  53. constructor.prototype = {
  54. init() {
  55. SPSParser = new H264SPSParser();
  56. this.resolutionChangedCallback = () => {
  57. };
  58. },
  59. remuxRTPData(rtspInterleaved, rtpHeader, rtpPayload) {
  60. //console.log(rtspInterleaved)
  61. //console.log(rtpHeader)
  62. let PaddingSize = 0;
  63. let extensionHeaderLen = 0; //如果RtpHeader.X=1,则在RTP报头后跟有一个扩展报头
  64. let PAYLOAD = null;
  65. //console.log(rtpHeader)
  66. //console.log(rtspInterleaved, rtpHeader, rtpPayload.subarray(0,5))
  67. let RtpHeader = {
  68. V: rtpHeader[0] >>> 6,
  69. P: rtpHeader[0] & 0x20,
  70. X: rtpHeader[0] & 0x10,
  71. CC: rtpHeader[0] & 0x0F,
  72. M: (rtpHeader[1] & 0x80) >> 7,
  73. PT: rtpHeader[1] & 127,
  74. SN: (rtpHeader[2] << 8) + rtpHeader[3],
  75. timeStamp: (rtpHeader[4] << 24) + (rtpHeader[5] << 16) + (rtpHeader[6] << 8) + rtpHeader[7],
  76. SSRC: (rtpHeader[8] << 24) + (rtpHeader[9] << 16) + (rtpHeader[10] << 8) + rtpHeader[11],
  77. };
  78. if (RtpHeader.P) { //填充
  79. PaddingSize = rtpPayload[rtpPayload.length - 1];
  80. console.log("Padding - " + PaddingSize);
  81. }
  82. if (RtpHeader.X) { //扩展
  83. extensionHeaderLen = (((rtpPayload[2] << 8) | rtpPayload[3]) * 4) + 4;
  84. console.log('X: ' + rtpPayload[0])
  85. }
  86. //console.log('extensionHeaderLen: '+ extensionHeaderLen)
  87. PAYLOAD = rtpPayload.subarray(extensionHeaderLen, rtpPayload.length - PaddingSize);
  88. rtpTimeStamp = RtpHeader.timeStamp;
  89. //console.log(rtpTimeStamp, rtpHeader[4], rtpHeader[5], rtpHeader[6] , rtpHeader[7], PAYLOAD[0] & 0x1f)
  90. /* 载荷结构(https://blog.csdn.net/davebobo/article/details/52994596)
  91. +---------------+
  92. |0|1|2|3|4|5|6|7|
  93. +-+-+-+-+-+-+-+-+
  94. |F|NRI| Type |
  95. +---------------+
  96. Type = 1-23 单个NAL单元包
  97. Type = 24,25, 26, 27聚合包
  98. Type = 28,29, 分片单元
  99. */
  100. //console.log(rtspInterleaved,rtpHeader, PAYLOAD[0]);
  101. let nalType = (PAYLOAD[0] & 0x1f);
  102. //console.log(PAYLOAD[0] + ' nalType: ' + nalType);
  103. //console.log('rtpPayload.length: ' + rtpPayload.length)
  104. //console.log(nalType, PAYLOAD[0])
  105. //console.log('nalType: ' + nalType, RtpHeader.M)
  106. switch (nalType) {
  107. case 6: //SEI
  108. //console.log(PAYLOAD, String.fromCharCode.apply(null, PAYLOAD))
  109. if (SEIParse(PAYLOAD) === null) {
  110. return;
  111. }
  112. inputBuffer = setBuffer(inputBuffer, PREFIX);
  113. inputBuffer = setBuffer(inputBuffer, PAYLOAD);
  114. break;
  115. case 7: //SPS
  116. //console.log('SPS');
  117. SPSParser.parse(removeH264or5EmulationBytes(PAYLOAD));
  118. let sizeInfo = SPSParser.getSizeInfo();
  119. //console.log(SPSParser.getSpsMap())
  120. width = sizeInfo.width;
  121. height = sizeInfo.height;
  122. if (preWidth !== width || preHeight !== height) {
  123. console.log('resolution changed!');
  124. console.log('preWidth: ', preWidth, ' preHeight: ', preHeight, ' width: ', width, ' height: ', height);
  125. preWidth = width;
  126. preHeight = height;
  127. }
  128. inputBuffer = setBuffer(inputBuffer, PREFIX);
  129. inputBuffer = setBuffer(inputBuffer, PAYLOAD);
  130. spsSegment = PAYLOAD;
  131. //console.log('width: ',width, 'height: ', height)
  132. curSize = sizeInfo.decodeSize;
  133. firstIframe = true;
  134. //console.log(spsSegment)
  135. if (frameRate === null) {
  136. frameRate = SPSParser.getFPS();
  137. }
  138. break;
  139. case 8: //PPS
  140. //console.log('PPS')
  141. inputBuffer = setBuffer(inputBuffer, PREFIX);
  142. inputBuffer = setBuffer(inputBuffer, PAYLOAD);
  143. ppsSegment = PAYLOAD;
  144. //console.log(ppsSegment)
  145. break;
  146. case 28: //FU
  147. //console.log('FU');
  148. let startBit = ((PAYLOAD[1] & 0x80) === 0x80),
  149. endBit = ((PAYLOAD[1] & 0x40) === 0x40),
  150. fuType = PAYLOAD[1] & 0x1f,
  151. payloadStartIndex = 2;
  152. //console.log('startBit: ' + startBit + ' endBit: ' + endBit)
  153. //console.log('fuType: ' + fuType)
  154. if (startBit === true && endBit === false) {
  155. let newNalHeader = new Uint8Array(1);
  156. newNalHeader[0] = ((PAYLOAD[0] & 0xe0) | fuType);
  157. //console.log('newNalHeader: ', newNalHeader[0])
  158. //console.log('fuType: ' + fuType)
  159. //console.log((PAYLOAD[2] << 8) + PAYLOAD[3])
  160. //console.log(new Uint8Array(PAYLOAD.subarray(0, 100)));
  161. if (false) { //赛兰摄像头,SPS,PPS,I帧打在一个RTP包中
  162. PAYLOAD[1] = newNalHeader[0];
  163. SPSParser.parse(removeH264or5EmulationBytes(PAYLOAD.subarray(1, 27)));
  164. let sizeInfo = SPSParser.getSizeInfo();
  165. //console.log(sizeInfo, SPSParser.getSpsMap())
  166. //SPS
  167. width = sizeInfo.width;
  168. height = sizeInfo.height;
  169. inputBuffer = setBuffer(inputBuffer, PREFIX);
  170. inputBuffer = setBuffer(inputBuffer, PAYLOAD.subarray(1, 27));
  171. spsSegment = PAYLOAD.subarray(1, 27);
  172. //console.log('width: ',width, 'height: ', height)
  173. curSize = sizeInfo.decodeSize;
  174. firstIframe = true;
  175. //PPS
  176. inputBuffer = setBuffer(inputBuffer, PREFIX);
  177. inputBuffer = setBuffer(inputBuffer, PAYLOAD.subarray(30, 34));
  178. ppsSegment = PAYLOAD.subarray(30, 34);
  179. //I
  180. inputBuffer = setBuffer(inputBuffer, PREFIX);
  181. inputBuffer = setBuffer(inputBuffer, PAYLOAD.subarray(38, PAYLOAD.length));
  182. } else {
  183. //console.log(newNalHeader[0] & 0x1f)
  184. inputBuffer = setBuffer(inputBuffer, PREFIX);
  185. inputBuffer = setBuffer(inputBuffer, newNalHeader);
  186. inputBuffer = setBuffer(inputBuffer, PAYLOAD.subarray(payloadStartIndex, PAYLOAD.length));
  187. }
  188. } else {
  189. //console.log(startBit, endBit, 'endBit')
  190. inputBuffer = setBuffer(inputBuffer,
  191. PAYLOAD.subarray(payloadStartIndex, PAYLOAD.length));
  192. }
  193. //console.log(startBit,endBit)
  194. // if(endBit === true) {
  195. // end = true;
  196. // }
  197. break;
  198. case 1:
  199. inputBuffer = setBuffer(inputBuffer, PREFIX);
  200. inputBuffer = setBuffer(inputBuffer, PAYLOAD);
  201. break;
  202. default:
  203. //console.log('nalType: ' + nalType);
  204. //console.log(PAYLOAD)
  205. break;
  206. }
  207. //console.log('M: ' + RtpHeader.M + ' ' + (rtpHeader[1] & 0x80))
  208. //check marker bit
  209. if (RtpHeader.M) {
  210. if (!firstIframe) {
  211. inputLength = 0;
  212. return;
  213. }
  214. let inputBufferSub = inputBuffer.subarray(0, inputLength);
  215. frameType = FRAMETYPE[inputBufferSub[4] & 0x1f];
  216. //只根据视频帧计算resetTimeCount
  217. if (frameType !== 'SEI') {
  218. // rtp时间戳周期为RESETTIME,如果单向递增,设为0
  219. if (lastTimeStamp - rtpTimeStamp > (RESETTIME / 2)) { //判断lastTimeStamp远大于rtpTimeStamp,防止后一帧比前一帧先到的情况
  220. //console.log(lastTimeStamp - rtpTimeStamp)
  221. console.warn('时间戳重置', lastTimeStamp, rtpTimeStamp, frameType, new Date())
  222. resetTimeCount++;
  223. }
  224. rtpTimeStamp = rtpTimeStamp + RESETTIME * resetTimeCount;
  225. } else {
  226. //同一帧的SEI比视频发送慢时
  227. if (rtpTimeStamp - lastTimeStamp > (RESETTIME / 2)) {
  228. console.warn('SEI翻转', rtpTimeStamp, lastTimeStamp);
  229. rtpTimeStamp = rtpTimeStamp + RESETTIME * (resetTimeCount - 1);
  230. } else {
  231. rtpTimeStamp = rtpTimeStamp + RESETTIME * resetTimeCount;
  232. }
  233. //同一帧的SEI比视频发送快时
  234. // if(rtpTimeStamp > lastTimeStamp) {
  235. // rtpTimeStamp = rtpTimeStamp + RESETTIME * resetTimeCount;
  236. // } else {
  237. // rtpTimeStamp = rtpTimeStamp + RESETTIME * (resetTimeCount + 1);
  238. // }
  239. }
  240. //console.log('frameType: ', frameType, 'rtpTimeStamp: ', rtpTimeStamp)
  241. if (frameType === 'SEI') {
  242. //SEI被分片(nal === 28)时,分片包发送完后marker为1,不会和视频帧一起
  243. SEIBuffer = inputBuffer.subarray(4, inputLength);
  244. //console.log(SEIBuffer)
  245. inputBufferSub = new Uint8Array();
  246. }
  247. if (SEIBuffer) {
  248. let SEI = SEIParse(SEIBuffer);
  249. if (SEI) {
  250. SEIInfo.ivs = SEI;
  251. SEIInfo.timestamp = rtpTimeStamp;
  252. decodedData.SEIInfo = SEIInfo;
  253. if ((rtpTimeStamp - lastSEITime) !== (90000 / frameRate)) {
  254. //console.log('SEI 时间差:', (rtpTimeStamp - lastTime), rtpTimeStamp, lastTime)
  255. }
  256. lastSEITime = rtpTimeStamp;
  257. SEIInfo = {
  258. ivs: null,
  259. timestamp: 0,
  260. };
  261. }
  262. SEIBuffer = null;
  263. }
  264. if (!initalSegmentFlag) {
  265. decodedData.initSegmentData = null;
  266. decodedData.codecInfo = null;
  267. } else {
  268. initalSegmentFlag = false;
  269. const info = {
  270. id: 1,
  271. width: width,
  272. height: height,
  273. type: "video",
  274. profileIdc: SPSParser.getSpsValue("profile_idc"),
  275. profileCompatibility: 0,
  276. levelIdc: SPSParser.getSpsValue("level_idc"),
  277. sps: [spsSegment],
  278. pps: [ppsSegment],
  279. timescale: 1e3,
  280. fps: frameRate
  281. };
  282. decodedData.initSegmentData = info;
  283. decodedData.codecInfo = SPSParser.getCodecInfo();
  284. //console.log(info.pps)
  285. }
  286. if (frameType === 'I') {
  287. //console.log('ppsSegment: ', ppsSegment)
  288. let h264parameterLength = spsSegment.length + ppsSegment.length + 8;
  289. inputSegBufferSub = inputBufferSub.subarray(h264parameterLength, inputBufferSub.length);
  290. } else {
  291. inputSegBufferSub = inputBufferSub.subarray(0, inputBufferSub.length);
  292. }
  293. if (inputSegBufferSub.length) {
  294. let segSize = inputSegBufferSub.length - 4;
  295. //mp4 box头
  296. inputSegBufferSub[0] = (segSize & 0xFF000000) >>> 24;
  297. inputSegBufferSub[1] = (segSize & 0xFF0000) >>> 16;
  298. inputSegBufferSub[2] = (segSize & 0xFF00) >>> 8;
  299. inputSegBufferSub[3] = (segSize & 0xFF);
  300. decodedData.frameData = new Uint8Array(inputSegBufferSub);
  301. let sample = {
  302. duration: Math.round((1 / frameRate) * 1000),
  303. size: inputSegBufferSub.length,
  304. frame_time_stamp: null,
  305. frameDuration: null,
  306. };
  307. sample.frame_time_stamp = rtpTimeStamp; //Todo:暂时为null,通过帧率控制duration
  308. if (initalMediaFrameFlag) {
  309. sample.frameDuration = 0;
  310. initalMediaFrameFlag = false;
  311. } else {
  312. if (frameRate) {
  313. frameCount++;
  314. if (!(frameCount % frameRate)) { //每秒最后一帧时
  315. sample.frameDuration = 1000 - durationTimeCount;
  316. frameCount = 0;
  317. durationTimeCount = 0;
  318. } else {
  319. sample.frameDuration = Math.round(1000 / frameRate);
  320. durationTimeCount += Math.round(1000 / frameRate);
  321. }
  322. } else {
  323. sample.frameDuration = (sample.frame_time_stamp - preSample.frame_time_stamp) / 90; // 时钟频率90000,timescale=1000
  324. }
  325. //console.log(sample.frameDuration)
  326. }
  327. preSample = sample;
  328. decodedData.mediaSample = sample;
  329. decodedData.timeStamp = rtpTimeStamp;
  330. }
  331. this.handleDecodedData(decodedData);
  332. inputLength = 0;
  333. decodedData.SEIInfo = null;
  334. inputSegBufferSub = null;
  335. if (frameType !== 'SEI') {
  336. lastTimeStamp = RtpHeader.timeStamp;
  337. }
  338. frameType = '';
  339. }
  340. //console.log('xxxxxxxxxxxxxxxxxxxxxxxxx')
  341. },
  342. set rtpSessionCallback(func) {
  343. this.handleDecodedData = func;
  344. },
  345. setFrameRate(fps) {
  346. frameRate = fps;
  347. //console.log('frameRate: ', frameRate)
  348. },
  349. setResolutionChangedCallback(callback) {
  350. this.resolutionChangedCallback = callback;
  351. }
  352. }
  353. return new constructor();
  354. function setBuffer(buffer1, buffer2) {
  355. let bufferTemp = buffer1;
  356. if ((inputLength + buffer2.length) > buffer1.length) {
  357. bufferTemp = new Uint8Array(buffer1.length + size1M);
  358. }
  359. bufferTemp.set(buffer2, inputLength);
  360. inputLength += buffer2.length;
  361. return bufferTemp;
  362. }
  363. }
  364. /**
  365. * 去除SPS中的Emulation字节
  366. * @param data SPS源数据
  367. * @returns {Array} 去除后Emulation字节后的SPS
  368. */
  369. function removeH264or5EmulationBytes(data) {
  370. let toSize = 0;
  371. let i = 0;
  372. let to = [];
  373. let dataLength = data.length;
  374. while (i < dataLength) {
  375. if (i + 2 < dataLength && data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 3) {
  376. to[toSize] = to[toSize + 1] = 0;
  377. toSize += 2;
  378. i += 3;
  379. } else {
  380. to[toSize] = data[i];
  381. toSize += 1;
  382. i += 1;
  383. }
  384. }
  385. return to;
  386. }
  387. /**
  388. * 解析SEI信息
  389. * @param data
  390. * @return {Array}
  391. */
  392. function SEIParse(data) {
  393. //console.log(data)
  394. if ((data[0] & 0x1f) !== 6) {
  395. //非SEI
  396. return null;
  397. }
  398. if (data[1] !== 0x55 || data[2] !== 0x4C || data[3] !== 0x53 || data[4] !== 0x40) {
  399. //ULS@开头
  400. console.warn('unknown SEI type');
  401. return null;
  402. }
  403. let dataLength = data.length;
  404. let type = (data[5] << 8) + data[6];
  405. let checkSum = (data[7] << 8) + data[8];
  406. // if(dataLength !== (checkSum + 1 + 4 + 4)) {
  407. // console.log('SEI check fail!');
  408. // return null;
  409. // }
  410. let result;
  411. data = data.subarray(9);
  412. switch (type) {
  413. case 0:
  414. result = parseFace(data);
  415. break;
  416. case 1:
  417. //console.log(parseBody(data))
  418. result = parseBody(data);
  419. break;
  420. case 2:
  421. result = parseRegion(data);
  422. break;
  423. case 3:
  424. result = parseBodyEx(data);
  425. break;
  426. case 4:
  427. result = parseOverSpeed(data);
  428. break;
  429. default:
  430. result = null;
  431. break;
  432. }
  433. return result;
  434. }
  435. function parseFace(data) {
  436. //console.log(data)
  437. let dataLength = data.length;
  438. let contents = [];
  439. while (dataLength > 0) {
  440. //console.log('dataLength: ', dataLength)
  441. let x0 = ((data[4] & 0x7f) << 8) + data[5],
  442. y0 = ((data[6] & 0x7f) << 8) + data[7],
  443. width = ((data[8] & 0x7f) << 8) + data[9] - x0,
  444. height = ((data[10] & 0x7f) << 8) + data[11] - y0;
  445. let content = {
  446. type: 'rect',
  447. id: (data[2] << 8) + data[3],
  448. rect: [x0, y0, width, height],
  449. state: data[1] & 0x01,
  450. quality: (data[1] & 0x02) >> 1,
  451. };
  452. Array.prototype.push.apply(contents, [content]);
  453. data = data.subarray(12);
  454. dataLength = data.length;
  455. }
  456. return contents;
  457. }
  458. function parseBody(data) {
  459. let dataLength = data.length;
  460. let contents = [];
  461. while (dataLength > 0) {
  462. //console.log('dataLength: ', dataLength)
  463. let x0 = ((data[4] & 0x7f) << 8) + data[5],
  464. y0 = ((data[6] & 0x7f) << 8) + data[7],
  465. width = ((data[8] & 0x7f) << 8) + data[9] - x0,
  466. height = ((data[10] & 0x7f) << 8) + data[11] - y0,
  467. boxConfidence = ((data[12] & 0x7f) << 8) + data[13];
  468. let points = [];
  469. for (let i = 0; i < 17; i++) {
  470. let point = {
  471. x: ((data[16 + i * 8] & 0x7f) << 8) + data[17 + i * 8],
  472. y: ((data[18 + i * 8] & 0x7f) << 8) + data[19 + i * 8],
  473. confidence: ((data[20 + i * 8] & 0x7f) << 8) + data[21 + i * 8],
  474. // x: Math.random() * 8191,
  475. // y: Math.random() * 8191,
  476. // confidence: 1,
  477. };
  478. points.push(point);
  479. }
  480. let content = {
  481. type: 'coco-pose',
  482. id: (data[2] << 8) + data[3],
  483. handsUp: data[1] & 0x04,
  484. boundingBox: [x0, y0, width, height],
  485. boxConfidence,
  486. points: parseBodyToTree(points),
  487. state: data[1] & 0x01,
  488. };
  489. Array.prototype.push.apply(contents, [content]);
  490. data = data.subarray(152);
  491. dataLength = data.length;
  492. }
  493. return contents;
  494. }
  495. function parseRegion(data) {
  496. let dataLength = data.length;
  497. let contents = [];
  498. while (dataLength > 0) {
  499. let pointNum = (data[0] << 8) + data[1],
  500. state = data[3] & 0x03,
  501. area = [];
  502. for (let i = 0; i < pointNum; i++) {
  503. let point = {
  504. x: ((data[i * 4 + 4] & 0x7f) << 8) + data[i * 4 + 5],
  505. y: ((data[i * 4 + 6] & 0x7f) << 8) + data[i * 4 + 7],
  506. };
  507. area.push(point);
  508. }
  509. let content = {
  510. type: 'region-detect',
  511. state,
  512. area
  513. };
  514. Array.prototype.push.apply(contents, [content]);
  515. data = data.subarray(pointNum * 4 + 4);
  516. dataLength = data.length;
  517. }
  518. return contents;
  519. }
  520. function parseBodyEx(data) {
  521. let dataLength = data.length;
  522. let contents = [];
  523. while (dataLength > 0) {
  524. //console.log('dataLength: ', dataLength)
  525. let x0 = ((data[4] & 0x7f) << 8) + data[5],
  526. y0 = ((data[6] & 0x7f) << 8) + data[7],
  527. width = ((data[8] & 0x7f) << 8) + data[9] - x0,
  528. height = ((data[10] & 0x7f) << 8) + data[11] - y0,
  529. boxConfidence = ((data[12] & 0x7f) << 8) + data[13];
  530. let points = [];
  531. for (let i = 0; i < 17; i++) {
  532. let point = {
  533. x: ((data[16 + i * 8] & 0x7f) << 8) + data[17 + i * 8],
  534. y: ((data[18 + i * 8] & 0x7f) << 8) + data[19 + i * 8],
  535. confidence: ((data[20 + i * 8] & 0x7f) << 8) + data[21 + i * 8],
  536. };
  537. points.push(point);
  538. }
  539. let guides = [
  540. {
  541. x: ((data[16 + 17 * 8] & 0x7f) << 8) + data[17 + 17 * 8],
  542. y: ((data[18 + 17 * 8] & 0x7f) << 8) + data[19 + 17 * 8],
  543. },
  544. {
  545. x: ((data[20 + 17 * 8] & 0x7f) << 8) + data[21 + 17 * 8],
  546. y: ((data[22 + 17 * 8] & 0x7f) << 8) + data[23 + 17 * 8],
  547. }
  548. ];
  549. let content = {
  550. type: 'coco-poseex',
  551. id: (data[2] << 8) + data[3],
  552. Loiter: data[1] & 0x03,
  553. Standing: (data[1] & 0x04) >> 2,
  554. Alone: (data[1] & 0x08) >> 3,
  555. boundingBox: [x0, y0, width, height],
  556. boxConfidence,
  557. points: parseBodyToTree(points),
  558. guides,
  559. state: 1
  560. };
  561. Array.prototype.push.apply(contents, [content]);
  562. data = data.subarray(160);
  563. dataLength = data.length;
  564. }
  565. return contents;
  566. }
  567. function parseOverSpeed(data) {
  568. let dataLength = data.length;
  569. let contents = [];
  570. while (dataLength > 0) {
  571. //console.log('dataLength: ', dataLength)
  572. let speed = ((data[4] & 0x7f) << 8) + data[5],
  573. overSpeed = data[1] & 0x01;
  574. let content = {
  575. type: 'over-speed',
  576. id: (data[2] << 8) + data[3],
  577. speed,
  578. overSpeed,
  579. state: 1,
  580. };
  581. Array.prototype.push.apply(contents, [content]);
  582. data = data.subarray(8);
  583. dataLength = data.length;
  584. }
  585. return contents;
  586. }
  587. /**
  588. * 将智能帧中的人体姿态点转化为树结构(双亲表示法)
  589. * @param points
  590. */
  591. function parseBodyToTree(points) {
  592. let newPoints = [];
  593. newPoints[0] = {...points[0], parent: -1, pointColor: '#FF0002', lineColor: '#FF0002'};
  594. newPoints[1] = {...points[1], parent: 0, pointColor: '#FF0002', lineColor: '#FF0002'};
  595. newPoints[2] = {...points[2], parent: 0, pointColor: '#FF0002', lineColor: '#FF0002'};
  596. newPoints[3] = {...points[5], parent: 17, pointColor: '#D9E34F', lineColor: '#FF0002'};
  597. newPoints[4] = {...points[6], parent: 17, pointColor: '#D9E34F', lineColor: '#FF0002'};
  598. newPoints[5] = {...points[3], parent: 1, pointColor: '#00FF00', lineColor: '#FF0002'};
  599. newPoints[6] = {...points[4], parent: 2, pointColor: '#FFAC00', lineColor: '#FF0002'};
  600. newPoints[7] = {...points[7], parent: 3, pointColor: '#00FF45', lineColor: '#'};
  601. newPoints[8] = {...points[11], parent: 17, pointColor: '#EEC446', lineColor: '#'};
  602. newPoints[9] = {...points[8], parent: 4, pointColor: '#43D3AF', lineColor: '#'};
  603. newPoints[10] = {...points[12], parent: 17, pointColor: '#7A93E8', lineColor: '#'};
  604. newPoints[11] = {...points[9], parent: 7, pointColor: '#EFB842', lineColor: '#'};
  605. newPoints[12] = {...points[13], parent: 8, pointColor: '#E56C00', lineColor: '#'};
  606. newPoints[13] = {...points[10], parent: 9, pointColor: '#47CD43', lineColor: '#0096FF'};
  607. newPoints[14] = {...points[14], parent: 10, pointColor: '#3868D2', lineColor: '#00FF51'};
  608. newPoints[15] = {...points[15], parent: 12, pointColor: '#DF4D01', lineColor: '#5100FF'};
  609. newPoints[16] = {...points[16], parent: 14, pointColor: '#1E48D4', lineColor: '#00FFA0'};
  610. newPoints[17] = {
  611. x: (points[5].x + points[6].x) / 2,
  612. y: (points[5].y + points[6].y) / 2,
  613. parent: -1,
  614. pointColor: '#D9E34F',
  615. lineColor: '#00FFA0'
  616. };
  617. return newPoints;
  618. }