workerManager.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. import VideoMediaSource from './MediaSource.js';
  2. import MP4Remux from './MP4Remux.js';
  3. import IvsDrawer from './ivsDrawer.js';
  4. function WorkerManager() {
  5. let videoWorker;
  6. let SDPInfo;
  7. let messageArray = [];
  8. let rtpStackCount = 0;
  9. let videoElement = null;
  10. let canvasElement = null;
  11. let videoMS = null;
  12. const rtpStackCheckNum = 10;
  13. let codecInfo = null;
  14. let initSegmentData = null;
  15. let mediaInfo = {
  16. id: 1,
  17. samples: null,
  18. baseMediaDecodeTime: 0
  19. };
  20. let numBox = 1;
  21. let mediaSegNum = 0; //用于记录缓存的box个数
  22. let mediaFrameData = null; //用于缓存未喂入mse的box
  23. let mediaFrameSize = 0; //mediaFrameData的大小
  24. let preBaseDecodeTime = 0; //上一个解码时间
  25. let curBaseDecodeTime = 0; //从第一帧到当前帧的持续时间
  26. let mediaSegmentData = null; //MP4化的数据
  27. let sequenseNum = 1;
  28. let mp4Remux;
  29. let firstTimeStamp = null; //第一个视频帧的时间戳
  30. let SEIinfo = null;
  31. let ivsDrawer = null;
  32. let info = null;
  33. let MAX_INFO = 25; // 限制info最大长度
  34. let startDrawIVS = false;
  35. let onCanplayCallback = null;
  36. let ROIdrawer = null;
  37. let initialCompleted = false;
  38. let onInitialCompletedCallback = null;
  39. let frameRate = 25; //默认25
  40. let resizeObserver = null;
  41. let vw = null;
  42. function constructor() {
  43. }
  44. constructor.prototype = {
  45. init(video,canvas,ROIElement,vue) {
  46. videoWorker = new Worker('/libs/videoWorker.js');
  47. // vw = new VideoWorker();// 这里初始化
  48. // videoWorker = vue.$worker.create([
  49. // {
  50. // message:'message',
  51. // func:(data)=>{
  52. // if(data.vw && data.vw.receive){
  53. // let msg = {type:data.type,data:data.data}
  54. // data.vw.receive(msg)
  55. // }
  56. // }
  57. // }
  58. // ])
  59. videoWorker.onmessage = videoWorkerMessage;
  60. videoElement = video;
  61. canvasElement = canvas;
  62. mp4Remux = new MP4Remux();
  63. mp4Remux.init();
  64. SEIinfo = new IVSQueue();
  65. info = new LruCache(MAX_INFO);
  66. ivsDrawer = new IvsDrawer(canvasElement);
  67. ROIdrawer = ROIElement;
  68. resizeObserver = new ResizeObserver( entries => {
  69. ivsDrawer.cover(videoElement);
  70. ROIdrawer.cover(videoElement);
  71. ROIdrawer.redrawROI();
  72. });
  73. resizeObserver.observe(videoElement);
  74. },
  75. sendSdpInfo(SDPinfo) {
  76. SDPInfo = SDPinfo;
  77. console.log(SDPinfo)
  78. let message = {
  79. type: "sdpInfo",
  80. data: {
  81. sdpInfo: SDPInfo
  82. }
  83. };
  84. videoWorker.postMessage(message);
  85. //Todo:暂时采用第一路视频的帧率
  86. if(SDPInfo.length && SDPInfo[0].Framerate) {
  87. frameRate = SDPinfo[0].Framerate;
  88. }
  89. },
  90. parseRtpData(rtspinterleave, rtpheader, rtpPacketArray) {
  91. // console.log(rtspinterleave)
  92. // console.log( rtpheader)
  93. // //console.log(rtpPacketArray)
  94. // console.log(rtpheader[3])
  95. let mediaType = rtspinterleave[1];
  96. let idx = parseInt(mediaType / 2, 10);
  97. let markerBitHex = 128;
  98. let message = {
  99. type: "rtpData",
  100. data: {rtspInterleave: rtspinterleave, header: rtpheader, payload: rtpPacketArray}
  101. };
  102. //console.log(rtspinterleave)
  103. //console.log('idx: ',idx)
  104. if(idx !== 0) {
  105. console.log('idx: ',rtspinterleave);
  106. //console.log(SDPInfo)
  107. return;
  108. }
  109. switch (SDPInfo[idx].codecName) {
  110. case"H264":
  111. messageArray.push(message);
  112. if (rtpStackCount >= rtpStackCheckNum || (rtpheader[1] & markerBitHex) === markerBitHex) {
  113. if((rtpheader[1] & markerBitHex) === markerBitHex) {
  114. //onsole.log('遇到终止位: ' + rtpheader[1])
  115. }
  116. let sendMessage = {type: "rtpDataArray", data: messageArray};
  117. if (videoWorker) {
  118. videoWorker.postMessage(sendMessage)
  119. }
  120. sendMessage = null;
  121. messageArray = [];
  122. rtpStackCount = 0
  123. //console.log('1111111111')
  124. } else {
  125. rtpStackCount++
  126. }
  127. break;
  128. default:
  129. }
  130. },
  131. /**
  132. * 更新需要绘制的其它信息
  133. * @param obj
  134. */
  135. updateInfo(obj) {
  136. //if((obj.name !== '') && (obj.name !== undefined) && (obj.name !== null)) {
  137. info.set(obj.id, obj.name);
  138. //}
  139. },
  140. setEventCallBack(event, callback) {
  141. switch (event) {
  142. case 'canplay':
  143. onCanplayCallback = callback;
  144. break;
  145. case 'initialCompleted':
  146. onInitialCompletedCallback = callback;
  147. break;
  148. case 'ROIFinished':
  149. ROIdrawer.setROIFinishedCallback(callback);
  150. break;
  151. default:
  152. break;
  153. }
  154. },
  155. setROIDrawer(ROIElement) {
  156. ROIdrawer = ROIElement;
  157. },
  158. terminate() {
  159. videoWorker.terminate();
  160. ivsDrawer.terminate();
  161. resizeObserver && resizeObserver.unobserve(videoElement);
  162. resizeObserver = null;
  163. info.clear();
  164. startDrawIVS = false;
  165. window.onresize = null;
  166. if(videoMS) {
  167. videoMS.close();
  168. videoMS = null;
  169. }
  170. }
  171. }
  172. return new constructor();
  173. function videoWorkerMessage(event) {
  174. let videoMessage = event.data;
  175. let type = videoMessage.type;
  176. //console.log(videoMessage.data)
  177. switch (type) {
  178. // case 'codecInfo': //设置codecType
  179. // break;
  180. // case 'initSegment': //第一个buffer,设置SPS等
  181. case 'videoInit'://合并codecInfo和initSegment
  182. console.log(videoMessage)
  183. codecInfo = videoMessage.data.codecInfo;
  184. //console.log(videoMessage.data)
  185. initSegmentData = mp4Remux.initSegment(videoMessage.data.initSegmentData);
  186. //console.log(initSegmentData)
  187. videoMS = new VideoMediaSource(videoElement);
  188. videoMS.CodecInfo = codecInfo;
  189. videoMS.InitSegment = initSegmentData;
  190. //console.log(videoMS.CodecInfo, videoMS.InitSegment)
  191. videoMS.init();
  192. videoMS.onCanplayCallback(()=>{
  193. ivsDrawer.cover(videoElement);
  194. onCanplayCallback && onCanplayCallback();
  195. if(!initialCompleted) {
  196. ROIdrawer.cover(videoElement);
  197. onInitialCompletedCallback && onInitialCompletedCallback();
  198. initialCompleted = true;
  199. }
  200. });
  201. break;
  202. case 'firstvideoTimeStamp':
  203. firstTimeStamp = videoMessage.data;
  204. videoMS.setFirstTimeStamp(firstTimeStamp);
  205. //videoMS.setDurationChangeCallBack(drawIVS);
  206. console.log('first frame timestamp: ', firstTimeStamp);
  207. startDrawIVS = true;
  208. window.requestAnimationFrame(draw);
  209. break;
  210. case 'videoTimeStamp'://时间戳,用于智能同步
  211. //videoMS.setFirstTimeStamp(videoMessage.data);
  212. //console.log('frame timestamp: ', videoMessage.data);
  213. //console.log('npt: ', ( videoMessage.data - firstTimeStamp)/90000)
  214. break;
  215. case 'mediaSample': //用于设置baseMediaDecodeTime
  216. if(mediaInfo.samples == null) {
  217. mediaInfo.samples = new Array(numBox);
  218. }
  219. //console.log('frameDuration: ' + videoMessage.data.frameDuration)
  220. curBaseDecodeTime += videoMessage.data.frameDuration;
  221. mediaInfo.samples[mediaSegNum++] = videoMessage.data;
  222. break;
  223. case 'videoRender': //视频数据
  224. //缓存该segment数据
  225. let tempBuffer = new Uint8Array(videoMessage.data.length + mediaFrameSize);
  226. if(mediaFrameSize !== 0) {
  227. tempBuffer.set(mediaFrameData);
  228. }
  229. //console.log(videoMessage)
  230. tempBuffer.set(videoMessage.data, mediaFrameSize);
  231. mediaFrameData = tempBuffer;
  232. mediaFrameSize = mediaFrameData.length;
  233. if(mediaSegNum % numBox === 0 && mediaSegNum !== 0) {
  234. if (sequenseNum === 1) {
  235. mediaInfo.baseMediaDecodeTime = 0
  236. } else {
  237. mediaInfo.baseMediaDecodeTime = preBaseDecodeTime;
  238. }
  239. preBaseDecodeTime = curBaseDecodeTime;
  240. mediaSegmentData = mp4Remux.mediaSegment(sequenseNum, mediaInfo, mediaFrameData);
  241. sequenseNum++;
  242. mediaSegNum = 0;
  243. mediaFrameData = null;
  244. mediaFrameSize = 0;
  245. if (videoMS !== null) {
  246. videoMS.setMediaSegment(mediaSegmentData)
  247. } else {
  248. }
  249. }
  250. break;
  251. case 'YUVData'://FFMPEG解码的数据
  252. //console.log(videoMessage.data)
  253. //draw(videoMessage.data);
  254. //yuv2canvas(videoMessage.data.data, videoMessage.data.width, videoMessage.data.height,canvasElement)
  255. break;
  256. case 'SEI': //处理SEI信息
  257. //console.log('SEI timestamp: ', videoMessage.data.timestamp);
  258. //console.log('SEI-npt: ', (videoMessage.data.timestamp - firstTimeStamp)/90000)
  259. if(videoMessage.data.ivs !== null) {
  260. let ivs = [];
  261. videoMessage.data.ivs.map((content, k) => {
  262. if(content.state) { //state=1, 绘制该信息
  263. ivs.push(content);
  264. }else { //state=0, 清除info中对应的id:name
  265. // let id = content.id;
  266. // console.log('删除', id, info[id]);
  267. // delete info[id];
  268. // console.log(info)
  269. }
  270. });
  271. //console.log('PUSH SEI: ', videoMessage.data.timestamp)
  272. SEIinfo.push(videoMessage.data.timestamp, ivs);
  273. //console.log(videoMessage.data.timestamp - lastTime)
  274. //lastTime = videoMessage.data.timestamp;
  275. }
  276. //console.log('timestamp: ', videoMessage.data.timestamp)
  277. //console.log(SEIinfo)
  278. break;
  279. default:
  280. console.log('暂不支持其他类型');
  281. break;
  282. }
  283. }
  284. function draw() {
  285. let timestamp = parseInt((videoElement.currentTime.toFixed(2) * 90000).toFixed(0)) + firstTimeStamp + 90000/frameRate;//
  286. drawIVS(timestamp);
  287. if(startDrawIVS) {
  288. window.requestAnimationFrame(draw);
  289. }
  290. }
  291. /**
  292. * 根据时间戳获取相应的ivs信息
  293. * @param timestamp 当前帧的时间戳
  294. * @returns {*} ivs信息
  295. */
  296. function getIVS(timestamp) {
  297. let preNode = null;
  298. let nextNode = null;
  299. preNode = SEIinfo.shift();
  300. nextNode = SEIinfo.top();
  301. while((preNode !== undefined) && (preNode !== null)) {
  302. if(preNode[0] > timestamp) {
  303. SEIinfo.unshift(preNode);
  304. //console.log('SEI时间大于video: ', preNode[0], timestamp);
  305. return null;
  306. } else if(preNode[0] === timestamp) {
  307. return preNode;
  308. } else {
  309. if(nextNode === undefined || nextNode === null) {
  310. console.log('last ivs info: ', timestamp, preNode[0], SEIinfo);
  311. //console.log(preNode[0] - lastTime);
  312. //lastTime = preNode[0];
  313. if(timestamp - preNode[0] < 90000/frameRate) {
  314. return preNode;//最后一个node
  315. }
  316. return null;
  317. }
  318. if(nextNode[0] > timestamp) {
  319. // console.log('video time: ', timestamp, preNode[0], SEIinfo.length());
  320. // if(SEIinfo.length()) {
  321. // SEIinfo.map((v, k)=>{
  322. // console.log(v);
  323. // });
  324. // }
  325. //console.log(preNode[0] - lastTime);
  326. //lastTime = preNode[0];
  327. return preNode;
  328. } else if(nextNode[0] === timestamp){
  329. nextNode = SEIinfo.shift();
  330. //console.log('video time: ', timestamp, nextNode[0], SEIinfo);
  331. //console.log(nextNode[0] - lastTime);
  332. //lastTime = nextNode[0];
  333. return nextNode;
  334. } else {
  335. preNode = SEIinfo.shift();
  336. nextNode = SEIinfo.top();
  337. }
  338. }
  339. }
  340. return null;
  341. }
  342. /**
  343. * 绘制智能信息
  344. * @param timestamp
  345. */
  346. function drawIVS(timestamp) {
  347. let data = getIVS(timestamp);
  348. if(data === undefined || data === null) {
  349. //清空画布
  350. //ivsDrawer.clearCanvas();
  351. if(!SEIinfo.length() || (SEIinfo.length() && (SEIinfo.list[0][0] - timestamp) > 90000/frameRate)) {
  352. ivsDrawer.clearCanvas();
  353. }
  354. }else {
  355. //console.log('GET SEI: ', data[0], ' videoTimestamp', timestamp);
  356. data = data[1];
  357. //console.log(info.map.length)
  358. if(info.map.length > MAX_INFO) {
  359. console.log('info length: ', info.map.length);
  360. }
  361. //获取鹰眼信息
  362. data.map((content, k) =>{
  363. let result = info.get(content.id);
  364. if(result !== undefined && result !== null) {
  365. data[k].text = result.value;
  366. }
  367. });
  368. ivsDrawer.draw(data, timestamp);
  369. }
  370. }
  371. }
  372. function ElementResizeCallback(someElement, callback) {
  373. const ro = new ResizeObserver( entries => {
  374. // for (let entry of entries) {
  375. // const cr = entry.contentRect;
  376. // console.log('Element:', entry.target);
  377. // console.log(`Element size: ${cr.width}px x ${cr.height}px`);
  378. // }
  379. callback();
  380. });
  381. ro.observe(someElement);
  382. }
  383. function yuv2canvas(yuv, width, height, canvas) {
  384. canvas.width = width;
  385. canvas.height = height;
  386. var context = canvas.getContext("2d");
  387. var output = context.createImageData(width, height);
  388. var outputData = output.data;
  389. var yOffset = 0;
  390. var uOffset = width * height;
  391. var vOffset = width * height + (width*height)/4;
  392. for (var h=0; h<height; h++) {
  393. for (var w=0; w<width; w++) {
  394. var ypos = w + h * width + yOffset;
  395. var upos = (w>>1) + (h>>1) * width/2 + uOffset;
  396. var vpos = (w>>1) + (h>>1) * width/2 + vOffset;
  397. var Y = yuv[ypos];
  398. var U = yuv[upos] - 128;
  399. var V = yuv[vpos] - 128;
  400. var R = (Y + 1.371*V);
  401. var G = (Y - 0.698*V - 0.336*U);
  402. var B = (Y + 1.732*U);
  403. var outputData_pos = w*4 + width*h*4;
  404. outputData[0+outputData_pos] = R;
  405. outputData[1+outputData_pos] = G;
  406. outputData[2+outputData_pos] = B;
  407. outputData[3+outputData_pos] = 255;
  408. }
  409. }
  410. context.putImageData(output, 0, 0);
  411. }
  412. class IVSQueue {
  413. constructor() {
  414. this.list = [];
  415. }
  416. push(timestamp, ivs) {
  417. for (let i = 0, len = this.list.length; i < len; i++) {
  418. if(this.list[i][0] === timestamp) {
  419. let oldIvs = this.list[i][1];
  420. Array.prototype.push.apply(oldIvs, ivs);
  421. this.list[i][1] = oldIvs;
  422. return;
  423. }
  424. }
  425. this.list.push([timestamp, ivs]);
  426. }
  427. shift() {
  428. let tmp = this.list.shift();
  429. return tmp;
  430. }
  431. unshift(node) {
  432. this.list.unshift(node);
  433. }
  434. top() {
  435. let tmp = this.list[0];
  436. return tmp;
  437. }
  438. length() {
  439. return this.list.length;
  440. }
  441. map(v,k) {
  442. return this.list.map(v,k);
  443. }
  444. }
  445. class LruCache {
  446. constructor(limit) {
  447. this.limit = limit || 20;
  448. this.map = [];
  449. }
  450. get(key) {
  451. return this._search(key);
  452. }
  453. set(key, value) {
  454. let result = this._search(key);
  455. if(!result) {
  456. this.map.unshift({
  457. key: key,
  458. value: value
  459. });
  460. if(this.map.length > this.limit) {
  461. this.map.pop();
  462. }
  463. }
  464. }
  465. //每次查找将该元素置于队首
  466. _search(key) {
  467. for(let i = 0, length = this.map.length; i < length; i++) {
  468. if(this.map[i].key === key) {
  469. let head = this.map.splice(i, 1);
  470. this.map.unshift(head[0]);
  471. return head[0];
  472. }
  473. }
  474. return null;
  475. }
  476. clear() {
  477. this.map = [];
  478. }
  479. }
  480. export default WorkerManager;