index.js 66 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711
  1. /*
  2. * 本文件仅供demo使用
  3. */
  4. let curLang = 'SimpChinese';
  5. //webpack中引用此代码块,格式不要随意改动
  6. //开发模式需要import,打包给3.0的变成了全局变量,不需要
  7. document.querySelector('#device_ipc_tab').addEventListener('click', (event) => {
  8. event.target.style.color = 'blue';
  9. document.querySelector('#device_ipc_content').style.display = '';
  10. ipcFunc();
  11. });
  12. document.querySelector('#onChangeLanguage').addEventListener('change', (event) => {
  13. let val = event.target.value;
  14. curLang = val;
  15. languageTrans();
  16. });
  17. const ipcFunc = () => {
  18. let $stream,
  19. $volume,
  20. $canvas, //canvas播放视频DOM
  21. $video, //video播放视频DOM
  22. $ivsCanvas,
  23. $detectCanvas,
  24. $drawCanvas, //canvas绘图DOM
  25. $video_wrap, //视频外层Wrap
  26. $videoLoading, //“加载中...”提示
  27. WndIndex = 0; //宫格窗口Id
  28. let channel = 0; //当前通道
  29. let curPage = 1; //视频下载列表的当前页
  30. let totalPage = 1; //视频下载列表的总页数
  31. let recordArr = []; //视频文件数组
  32. let canvasInstance = null; //canvas绘图实例
  33. let playerInstance = []; //预览实例数组
  34. let speedX = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];
  35. let recordInstance = []; //录像下载实例数组
  36. let talkInstance = []; //对讲实例数组
  37. let drawInstance = []; //canvas绘图实例数组
  38. let cutInstance = []; //视频裁剪实例数组
  39. let ivsInstance = []; //智能帧实例数组
  40. let positionInstance = []; //快读定位实例数组
  41. let isCuting = false; //是否正在进行视频裁剪
  42. let downList = []; //勾选的视频下载列表
  43. let downItemIndex = 0; //视频下载索引
  44. let canvasParam = { //canvas绘图默认传参
  45. 'strokeColor': '#FFFF00',
  46. 'title': '',
  47. 'resizeEnable': false,
  48. 'moveEnable': false,
  49. 'closeEnable': true,
  50. 'array': true,
  51. 'showSize': false,
  52. 'selectType': 'inSide',
  53. 'disappear': true,
  54. 'selected': true
  55. }
  56. let curDeviceIndex = 0;
  57. const lINENUMBER = 16; //回放列表每页显示录像条数
  58. let deviceObject = {
  59. loginState: [],
  60. session: []
  61. }
  62. let isStartAll = false;
  63. let curEnlargeWnd = 0;
  64. let _doc = document.getElementById('device_ipc_content');
  65. let playbackOptions = {};
  66. let ChannelPTZCaps = [];
  67. /**
  68. * @description 初始化
  69. */
  70. const init = () => {
  71. let videoStr = '';
  72. for(let i = 0; i < 16; i++) {
  73. videoStr += '<div wnd-index="' + i + '" style="float: left; background-color: #000; position: relative; width: 100%; height: 100%;overflow:hidden;border:1px solid rgb(125,125,125)">' +
  74. '<canvas id="h5_canvas_' + i + '" style="width:100%;height:100%;"></canvas>' +
  75. '<p id="h5_loading_' + i + '" class="video_loading" style="display:none">加载中...</p>' +
  76. '<video id="h5_video_' + i + '" style="display: none;width:100%;height:100%;position:absolute;top:0;left:0"></video>' +
  77. '<canvas id="h5_ivs_' + i + '" style="position: absolute;left: 0;top: 0;width: 100%;height: 100%;" width="500" height="300"></canvas>' + //智能帧的canvas
  78. '<canvas id="h5_detect_' + i + '" style="position: absolute;left: 0;top: 0;width: 100%;height: 100%;" width="500" height="300"></canvas>' + //智能区域的canvas
  79. '<canvas id="h5_draw_' + i + '" style="position: absolute;left: 0;top: 0;width: 100%;height: 100%;" width="500" height="300"></canvas>' + //绘制的canvas
  80. '</div>';
  81. }
  82. _doc.querySelector('.h5-play-wrap').innerHTML = videoStr;
  83. _doc.querySelectorAll('.h5-play-wrap div').forEach((item, index) => {
  84. item.addEventListener('click', function(event) {
  85. let _wndIndex = event.target.parentNode.getAttribute('wnd-index') - 0;
  86. _doc.querySelectorAll('.h5-play-wrap div').forEach(function(item, index) {
  87. if(index === _wndIndex) {
  88. item.style.borderColor = 'rgb(255, 204, 0)';
  89. WndIndex = _wndIndex;
  90. curDeviceIndex = item.getAttribute('deviceindex') - 0;
  91. setVideoDom();
  92. } else {
  93. item.style.borderColor = 'rgb(125, 125, 125)';
  94. }
  95. });
  96. document.getElementById('currentTimes').innerText = speedX[WndIndex] + 'X';
  97. })
  98. });
  99. let deviceDom = _doc.querySelectorAll('.h5-menu-list li');
  100. deviceObject.loginState = new Array(deviceDom.length).fill(false);
  101. deviceObject.session = new Array(deviceDom.length).fill(0);
  102. deviceDom.forEach(function(item, index) {
  103. item.addEventListener('click', async () => {
  104. await onLogout();
  105. curDeviceIndex = index;
  106. onLogin();
  107. });
  108. });
  109. $stream = $('#h5_stream');
  110. $volume = $('#h5_volume');
  111. $video_wrap = _doc.querySelector('.h5-play-wrap');
  112. setVideoDom();
  113. let inputArr = _doc.querySelectorAll('input[btn-for]');
  114. for(let node of inputArr) {
  115. node.addEventListener('click', bindClickEvent);
  116. }
  117. let selArr = _doc.querySelectorAll('select[sel-for]');
  118. for(let node of selArr) {
  119. node.addEventListener('change', bindChangeEvent);
  120. }
  121. $volume.addEventListener('input', function(event) {
  122. let vol = event.target.value - 0;
  123. $('#h5_volume_value').innerText = vol;
  124. });
  125. $volume.addEventListener('change', function(event) {
  126. let vol = event.target.value - 0;
  127. if(playerInstance[WndIndex]) {
  128. playerInstance[WndIndex].setAudioVolume(vol);
  129. }
  130. });
  131. $('#h5_first').addEventListener('click', function() {
  132. if(curPage != 1) {
  133. curPage = 1;
  134. updateTable();
  135. }
  136. });
  137. $('#h5_pre').addEventListener('click', function() {
  138. if(curPage > 1) {
  139. curPage = curPage - 1;
  140. updateTable();
  141. }
  142. });
  143. $('#h5_next').addEventListener('click', function() {
  144. if(curPage < totalPage) {
  145. curPage = curPage + 1;
  146. updateTable();
  147. }
  148. });
  149. $('#h5_last').addEventListener('click', function() {
  150. if(curPage != totalPage) {
  151. curPage = totalPage;
  152. updateTable();
  153. }
  154. });
  155. $('#h5_goPage').addEventListener('click', function() {
  156. let val = $('#h5_goNumber').value - 0;
  157. if(curPage != val) {
  158. curPage = val;
  159. updateTable();
  160. }
  161. });
  162. _doc.querySelectorAll('.testButton').forEach(item => {
  163. item.addEventListener('click', async function(event) {
  164. // 阻止事件冒泡和默认行为
  165. event.stopPropagation();
  166. event.preventDefault();
  167. event.stopImmediatePropagation(); // 阻止同一元素上的其他监听器
  168. // 获取点击的li元素
  169. let liElement = event.target.parentNode;
  170. // 获取所有li元素列表
  171. let deviceDom = _doc.querySelectorAll('.h5-menu-list li');
  172. // 找到当前li在列表中的索引
  173. let deviceIndex = Array.from(deviceDom).indexOf(liElement);
  174. console.log('测试按钮被点击,设备索引:', deviceIndex); // 调试信息
  175. if(deviceIndex !== -1) {
  176. // 先登出当前窗口的设备
  177. await onLogout();
  178. // 设置当前设备索引
  179. curDeviceIndex = deviceIndex;
  180. // 登录并播放该设备
  181. onLogin();
  182. } else {
  183. alert('无法找到设备信息!');
  184. }
  185. return false; // 额外确保阻止默认行为
  186. }, true); // 使用捕获阶段,确保先处理
  187. });
  188. ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'msfullscreenchange'].forEach((item, index) => {
  189. document.addEventListener(item, fullscreenchange, false);
  190. });
  191. onChangeWdnNum();
  192. }
  193. /**
  194. * @description 切换宫格时重新设置当前视频dom
  195. */
  196. const setVideoDom = () => {
  197. $canvas = $('#h5_canvas_' + WndIndex);
  198. $video = $('#h5_video_' + WndIndex);
  199. $drawCanvas = $('#h5_draw_' + WndIndex);
  200. $videoLoading = $('#h5_loading_' + WndIndex);
  201. $ivsCanvas = $('#h5_ivs_' + WndIndex);
  202. $detectCanvas = $('#h5_detect_' + WndIndex);
  203. let _session = deviceObject.session[curDeviceIndex];
  204. let _dom = _doc.querySelector('.h5-menu-list li:nth-child('+(curDeviceIndex+1)+')');
  205. let ip = _dom.getAttribute('data-ip');
  206. let port = _dom.getAttribute('data-port');
  207. setIP(ip + ':' + port);
  208. if(_session !== 0) {
  209. _setSession(_session);
  210. }
  211. }
  212. /**
  213. * @description 登录之后调用,获取设备能力,获取通道、码流
  214. */
  215. const afterLogin = () => {
  216. deviceObject.loginState[curDeviceIndex] = true;
  217. deviceObject.session[curDeviceIndex] = _getSession();
  218. $stream.options.length = 0;
  219. $('#h5_playback_channel').options.length = 0;
  220. $('#h5_channel').options.length = 0;
  221. /**
  222. * RPC.MagicBox.getProductDefinition 获取产品定义
  223. * @param {string} 'MaxExtraStream' 定义名称
  224. * @returns {Promise}
  225. */
  226. RPC.MagicBox.getProductDefinition('MaxExtraStream').then(function(params) {
  227. let maxExtra = params.definition;
  228. let main, extra;
  229. main = new Option(tl('com.MainStream'), 0);
  230. main.setAttribute('t', 'com.MainStream');
  231. $stream.options.add(main);
  232. if (maxExtra > 1) {
  233. for(let i = 1; i <= maxExtra; i++) {
  234. extra = new Option(tl('com.ExtraStream') + i, i);
  235. extra.setAttribute('t', 'com.ExtraStream' + i);
  236. $stream.options.add(extra);
  237. }
  238. } else {
  239. extra = new Option(tl('com.ExtraStream'), 1);
  240. extra.setAttribute('t', 'com.ExtraStream');
  241. $stream.options.add(new Option(tl('com.ExtraStream'), 1));
  242. }
  243. });
  244. RPC.DevVideoInput.getCollect().then(function (params) {
  245. let chnls = params.channels;
  246. for(let i = 0; i < chnls;i++) {
  247. $('#h5_playback_channel').options.add(new Option(i+1,i));
  248. $('#h5_channel').options.add(new Option(i+1,i));
  249. }
  250. });
  251. RPC.PTZManager('getCurrentProtocolCaps',channel).then(res => {
  252. ChannelPTZCaps[channel] = res?.caps;
  253. }).catch(err => {console.log(err);});
  254. let curDate = new Date();
  255. let dateString = curDate.toLocaleDateString();
  256. let dateSplit = dateString.split('/');
  257. let month = dateSplit[1] - 0;
  258. if(month < 10) {
  259. dateSplit[1] = '0' + month;
  260. }
  261. let day = dateSplit[2] - 0;
  262. if(day < 10) {
  263. dateSplit[2] = '0' + day;
  264. }
  265. let date = dateSplit.join('-');
  266. $('#h5_startTime').value = date + 'T' + '00:00';
  267. $('#h5_endTime').value = date + 'T' + '23:59:59';
  268. }
  269. const onStartAll = async () => {
  270. await onLogoutAll()
  271. curDeviceIndex = 0;
  272. _doc.querySelector('#h5_draw_' + curDeviceIndex).click();
  273. isStartAll = true;
  274. onLogin();
  275. }
  276. /**
  277. * @description 登录
  278. */
  279. const onLogin = () => {
  280. let _dom = _doc.querySelector('.h5-menu-list li:nth-child('+(curDeviceIndex+1)+')');
  281. let ip = _dom.getAttribute('data-ip');
  282. let port = _dom.getAttribute('data-port');
  283. let name = _dom.getAttribute('data-user');
  284. let pswd = _dom.getAttribute('data-pswd');
  285. let target = ip + ':' + port;
  286. setIP(target);
  287. RPC.login(name, pswd, false).then((res) => {
  288. setCookie('DWebClientSessionID', '', -1);
  289. setCookie('DhWebClientSessionID', '', -1);
  290. /**
  291. * RPC.keepAlive 保活
  292. */
  293. RPC.keepAlive(300, 60000, _getSession(), target, WndIndex);
  294. afterLogin();
  295. onRealPreview(ip,port,name,pswd);//登录后拉流
  296. }).catch((err) => {
  297. console.log(err);
  298. loginError(err);
  299. $videoLoading.style.display = '';
  300. $videoLoading.innerText = tl('com.OpenFailed'); //打开失败!
  301. let curWndType = _doc.querySelector('[sel-for=onChangeWdnNum]').value - 0;
  302. let _newDom = _doc.querySelector('.h5-menu-list li:nth-child(' + (curDeviceIndex + 2) + ')');
  303. if(curWndType !== 1 && _newDom != null && isStartAll) {
  304. clickNextWnd();
  305. _newDom.click();
  306. };
  307. });
  308. }
  309. const onRealPreview = (ip,port,name,pswd) => {
  310. if(playerInstance[WndIndex]) {
  311. playerInstance[WndIndex].stop();
  312. playerInstance[WndIndex].close();
  313. playerInstance[WndIndex] = null;
  314. let dom = $canvas;
  315. if (dom.style.display === 'none') {
  316. dom = $video;
  317. }
  318. dom.style.display = 'none';
  319. }
  320. let player = null;
  321. let curChannel = channel + 1; //无插件通道号从1开始
  322. let stream = $stream.value - 0;
  323. let options = {
  324. wsURL: 'ws://'+ ip +':' + port + '/rtspoverwebsocket',
  325. rtspURL: 'rtsp://'+ ip +':' + port + '/cam/realmonitor?channel=' + curChannel + '&subtype='+ stream +'&proto=Private3',
  326. username: name,
  327. password: pswd,
  328. lessRateCanvas:true,
  329. wndIndex: WndIndex,
  330. h265AccelerationEnabled: true
  331. };
  332. player = new PlayerControl(options);
  333. player.on('MSEResolutionChanged', function (e) {
  334. console.log(e)
  335. });
  336. player.on('PlayStart', function (e) {
  337. console.log(e);
  338. $canvas.parentNode.setAttribute('deviceindex', curDeviceIndex);
  339. $videoLoading.style.display = 'none';
  340. $ivsCanvas.style.display = '';
  341. let curWndType = _doc.querySelector('[sel-for=onChangeWdnNum]').value - 0;
  342. let _newDom = _doc.querySelector('.h5-menu-list li:nth-child(' + (curDeviceIndex + 2) + ')');
  343. let hasLoginNum = getLoginState();
  344. if(curWndType !== 1 && _newDom != null && isStartAll && hasLoginNum < curWndType * curWndType) {
  345. clickNextWnd();
  346. _newDom.click();
  347. } else {
  348. isStartAll = false;
  349. }
  350. });
  351. player.on('DecodeStart', function (e) {
  352. console.log(e)
  353. if(e.decodeMode === 'video'){
  354. $canvas.style.display = 'none';
  355. $video.style.display = '';
  356. }else{
  357. $video.style.display = 'none';
  358. $canvas.style.display = '';
  359. }
  360. canvasInstance = new PluginCanvasES6();
  361. let instance = new H5Canvas($ivsCanvas, $drawCanvas, $detectCanvas, WndIndex);
  362. canvasInstance.init($drawCanvas, function (data) {
  363. rebackActivateLocalEnlarging(data);
  364. });
  365. canvasInstance.addChangeShapeEvent();
  366. playerInstance[WndIndex] = player;
  367. drawInstance[WndIndex] = canvasInstance;
  368. ivsInstance[WndIndex] = instance;
  369. });
  370. player.on('GetFrameRate', function (e) {
  371. console.log('GetFrameRate: ' + e)
  372. });
  373. player.on('FrameTypeChange', function (e) {
  374. console.log('FrameTypeChange: ' + e)
  375. });
  376. player.on('Error', function (e) {
  377. //console.log('Error: ' + JSON.stringify(e))
  378. });
  379. player.on('WorkerReady',function(){
  380. player.connect();
  381. });
  382. player.on('IvsDraw', function(data, index) {
  383. let ivsShow = $('#h5_ivs').value;
  384. let winID = index;
  385. if(ivsShow === '1') {
  386. ivsInstance[winID] && ivsInstance[winID].receiveDataFromStream(data, 0);
  387. } else {
  388. ivsInstance[winID] && ivsInstance[winID].close();
  389. }
  390. });
  391. player.init($canvas, $video);
  392. $videoLoading.style.display = '';
  393. $videoLoading.innerText = tl('com.Loading');
  394. }
  395. const onPlayBack = (url, index) => {
  396. if(playerInstance[WndIndex]) {
  397. playerInstance[WndIndex].stop();
  398. playerInstance[WndIndex].close();
  399. playerInstance[WndIndex] = null;
  400. let dom = $canvas;
  401. if (dom.style.display === 'none') {
  402. dom = $video;
  403. }
  404. dom.style.display = 'none';
  405. }
  406. let ip = playbackOptions.ip;
  407. let port = playbackOptions.port;
  408. let name = playbackOptions.name;
  409. let pswd = playbackOptions.pswd;
  410. let player = null;
  411. let firstTime = 0;
  412. let options = {
  413. wsURL: 'ws://'+ ip +':' + port + '/rtspoverwebsocket',
  414. rtspURL: 'rtsp://'+ ip +':' + port + '/' + url,
  415. username: name,
  416. password: pswd,
  417. lessRateCanvas:true,
  418. playbackIndex: index,
  419. wndIndex: WndIndex
  420. };
  421. player = new PlayerControl(options);
  422. player.on('UpdateCanvas', function (e) {
  423. let playbackIndex = player.playbackIndex;
  424. let _index = playbackIndex%16;
  425. if(firstTime === 0){
  426. firstTime = e.timestamp;//获取录像文件的第一帧的时间戳
  427. }
  428. $('#h5_curTime_'+ _index).innerText = e.timestamp - firstTime;
  429. });
  430. player.on('GetTotalTime', function (e) {
  431. let playbackIndex = player.playbackIndex;
  432. let _index = playbackIndex%16;
  433. $('#h5_totalTime_'+ _index).innerText = e;
  434. });
  435. player.on('MSEResolutionChanged', function (e) {
  436. console.log(e)
  437. });
  438. player.on('PlayStart', function (e) {
  439. console.log(e);
  440. $canvas.parentNode.setAttribute('deviceindex', curDeviceIndex);
  441. $videoLoading.style.display = 'none';
  442. $ivsCanvas.style.display = '';
  443. });
  444. player.on('DecodeStart', function (e) {
  445. console.log(e)
  446. if(e.decodeMode === 'video'){
  447. $video.style.display = '';
  448. $canvas.style.display = 'none';
  449. }else{
  450. $video.style.display = 'none';
  451. $canvas.style.display = '';
  452. }
  453. canvasInstance = new PluginCanvasES6();
  454. let instance = new H5Canvas($ivsCanvas, $drawCanvas, $detectCanvas, WndIndex);
  455. canvasInstance.init($drawCanvas, function (data) {
  456. rebackActivateLocalEnlarging(data);
  457. });
  458. canvasInstance.addChangeShapeEvent();
  459. playerInstance[WndIndex] = player;
  460. drawInstance[WndIndex] = canvasInstance;
  461. ivsInstance[WndIndex] = instance;
  462. });
  463. player.on('GetFrameRate', function (e) {
  464. console.log('GetFrameRate: ' + e)
  465. });
  466. player.on('FrameTypeChange', function (e) {
  467. console.log('FrameTypeChange: ' + e)
  468. });
  469. player.on('FileOver', function (e) {
  470. player.close();
  471. });
  472. player.on('IvsDraw', function(data, index) {
  473. let ivsShow = $('#h5_ivs').value;
  474. let winID = index;
  475. if(ivsShow === '1') {
  476. ivsInstance[winID] && ivsInstance[winID].receiveDataFromStream(data, 0);
  477. } else {
  478. ivsInstance[winID] && ivsInstance[winID].close();
  479. }
  480. });
  481. player.on('Error', function (e) {
  482. //console.log('Error: ' + JSON.stringify(e))
  483. });
  484. player.on('WorkerReady',function() {
  485. player.connect();
  486. });
  487. player.init($canvas, $video);
  488. $videoLoading.style.display = '';
  489. $videoLoading.innerText = tl('com.Loading');
  490. }
  491. const getLoginState = () => {
  492. let num = 0;
  493. deviceObject.loginState.map(item => {
  494. if(item === true) {
  495. num++;
  496. }
  497. });
  498. return num;
  499. }
  500. const clickNextWnd = () => {
  501. let curWndType = _doc.querySelector('[sel-for=onChangeWdnNum]').value - 0;
  502. if(curWndType === 2 && WndIndex === 3 || curWndType === 3 && WndIndex === 8 || curWndType === 4 && WndIndex === 15) {
  503. // _doc.querySelector('#h5_draw_0').click();
  504. } else {
  505. _doc.querySelector('#h5_draw_' + (WndIndex + 1)).click();
  506. }
  507. }
  508. /**
  509. * @description 关闭拉流
  510. */
  511. const onStopPreview = () => {
  512. if(playerInstance[WndIndex]) {
  513. playerInstance[WndIndex].stop();
  514. playerInstance[WndIndex].close();
  515. playerInstance[WndIndex] = null;
  516. let dom = $canvas;
  517. if (dom.style.display === 'none') {
  518. dom = $video;
  519. }
  520. dom.style.display = 'none';
  521. }
  522. if(talkInstance[WndIndex]) {
  523. talkInstance[WndIndex].talk('off');
  524. talkInstance[WndIndex] = null;
  525. }
  526. if(recordInstance[WndIndex]) {
  527. recordInstance[WndIndex].startRecord(false);
  528. recordInstance[WndIndex] = null;
  529. }
  530. $videoLoading.style.display = 'none';
  531. $ivsCanvas.style.display = 'none';
  532. }
  533. const onStopAllPreview = () => {
  534. for(let i = 0; i < playerInstance.length; i++) {
  535. if(playerInstance[i]) {
  536. playerInstance[i].stop();
  537. playerInstance[i].close();
  538. playerInstance[i] = null;
  539. let canvasDom = $('#h5_canvas_' + i);
  540. let videoDom = $('#h5_video_' + i);
  541. let ivsCanvas = $('#h5_ivs_' + i);
  542. let dom = canvasDom;
  543. if (dom.style.display === 'none') {
  544. dom = videoDom;
  545. }
  546. dom.style.display = 'none';
  547. ivsCanvas.style.display = 'none';
  548. }
  549. }
  550. talkInstance.forEach(item => {
  551. if(item) {
  552. item.talk('off');
  553. item = null;
  554. }
  555. });
  556. recordInstance.forEach(item => {
  557. if(item) {
  558. item.startRecord(false);
  559. item = null;
  560. }
  561. });
  562. _doc.querySelectorAll('[id^=h5_loading_]').forEach(item => {
  563. item.style.display = 'none';
  564. });
  565. }
  566. const startFastPostion = () => {
  567. onStopEnlarge();
  568. if(!positionInstance[WndIndex]) {
  569. positionInstance[WndIndex] = new PluginCanvasES6();
  570. positionInstance[WndIndex].init($drawCanvas, function (data) {
  571. fastPostionEvent(data);
  572. });
  573. positionInstance[WndIndex].addChangeShapeEvent();
  574. }
  575. positionInstance[WndIndex].setRegionNum('rect', 1);
  576. positionInstance[WndIndex].drawStart('rect', {...canvasParam});
  577. const fastPostionEvent = (data) => {
  578. let pos = data.data;
  579. let left = pos[0][0];
  580. let top = pos[0][1];
  581. let right = pos[1][0];
  582. let bottom = pos[1][1];
  583. let x = (right + left) / (2 * 8192),
  584. y = (bottom + top) / (2 * 8192);
  585. let maxlength = Math.abs(bottom - top) > Math.abs(right - left) ? (bottom - top) : (right - left);
  586. let zoomtemp = (maxlength / 8192);
  587. let zoom = (right - left) > 0 ? zoomtemp * zoomtemp : (-1) * zoomtemp * zoomtemp;
  588. let request = Promise.resolve();
  589. let useNewApi = ChannelPTZCaps?.[channel]?.MoveDirectly?.Type?.includes('Real');
  590. if(useNewApi) {
  591. request = RPC.PTZBase.moveDirectly({
  592. channel : channel,
  593. startPoint : [left,top],
  594. endPoint : [right, bottom]
  595. });
  596. }else{
  597. request = RPC.PTZManager('moveDirectly',channel,{screen:[x, y, zoom]});
  598. }
  599. request.then(() => {
  600. console.log('快读定位成功!')
  601. });
  602. }
  603. }
  604. const stopFastPostion = () => {
  605. if(positionInstance[WndIndex]) {
  606. positionInstance[WndIndex].removeShapeDrawEvent();
  607. positionInstance[WndIndex] = null;
  608. }
  609. }
  610. /**
  611. * @description 登出当前设备
  612. */
  613. const onLogout = async () => {
  614. let _realDevice = _doc.querySelector('div[wnd-index="'+WndIndex+'"]').getAttribute('deviceindex');
  615. if(_realDevice === null) {
  616. return;
  617. }
  618. let _session = deviceObject.session[curDeviceIndex];
  619. if(_session === 0) {
  620. return;
  621. }
  622. _setSession(_session);
  623. await RPC.Global.logout().then(function() {
  624. if(playerInstance[WndIndex]) {
  625. playerInstance[WndIndex].stop();
  626. playerInstance[WndIndex].close();
  627. playerInstance[WndIndex] = null;
  628. let dom = $canvas;
  629. if (dom.style.display === 'none') {
  630. dom = $video;
  631. }
  632. dom.style.display = 'none';
  633. }
  634. if(talkInstance[WndIndex]) {
  635. talkInstance[WndIndex].talk('off');
  636. talkInstance[WndIndex] = null;
  637. }
  638. if(cutInstance[WndIndex]) {
  639. cutInstance[WndIndex].stop();
  640. cutInstance[WndIndex].close();
  641. cutInstance[WndIndex] = null;
  642. }
  643. if(recordInstance[WndIndex]) {
  644. recordInstance[WndIndex].startRecord(false);
  645. recordInstance[WndIndex] = null;
  646. }
  647. $videoLoading.style.display = 'none';
  648. $ivsCanvas.style.display = 'none';
  649. deviceObject.session[curDeviceIndex] = 0;
  650. deviceObject.loginState[curDeviceIndex] = false;
  651. pubsub.publish('_clearTime_', WndIndex);
  652. curPage = 1;
  653. totalPage = 1;
  654. });
  655. }
  656. /**
  657. * @description 登出全部设备
  658. */
  659. const onLogoutAll = async () => {
  660. for(let i = 0; i < playerInstance.length; i++) {
  661. if(playerInstance[i]) {
  662. playerInstance[i].stop();
  663. playerInstance[i].close();
  664. playerInstance[i] = null;
  665. let canvasDom = $('#h5_canvas_' + i);
  666. let videoDom = $('#h5_video_' + i);
  667. let ivsCanvas = $('#h5_ivs_' + i);
  668. let dom = canvasDom;
  669. if (dom.style.display === 'none') {
  670. dom = videoDom;
  671. }
  672. dom.style.display = 'none';
  673. ivsCanvas.style.display = 'none';
  674. }
  675. }
  676. talkInstance.forEach(item => {
  677. if(item) {
  678. item.talk('off');
  679. item = null;
  680. }
  681. });
  682. recordInstance.forEach(item => {
  683. if(item) {
  684. item.startRecord(false);
  685. item = null;
  686. }
  687. });
  688. _doc.querySelectorAll('[id^=h5_loading_]').forEach(item => {
  689. item.style.display = 'none';
  690. });
  691. let len = deviceObject.session.length; //登出已经登录的全部设备
  692. for (let i = 0; i < len; i++) {
  693. if(deviceObject.session[i] != 0) {
  694. let _dom = _doc.querySelector('.h5-menu-list li:nth-child('+(i+1)+')');
  695. let ip = _dom.getAttribute('data-ip');
  696. let port = _dom.getAttribute('data-port');
  697. setIP(ip + ':' + port);
  698. _setSession(deviceObject.session[i]);
  699. await RPC.Global.logout().then(() => {
  700. deviceObject.session[i] = 0;
  701. deviceObject.loginState[i] = false;
  702. _doc.querySelector('div[wnd-index="'+WndIndex+'"]').removeAttribute('deviceindex');
  703. });
  704. }
  705. }
  706. curPage = 1;
  707. totalPage = 1;
  708. }
  709. /**
  710. * @description 切换码流
  711. */
  712. const onChangeStream = () => {
  713. let _dom = _doc.querySelector('.h5-menu-list li:nth-child('+(curDeviceIndex+1)+')');
  714. let ip = _dom.getAttribute('data-ip');
  715. let port = _dom.getAttribute('data-port');
  716. let name = _dom.getAttribute('data-user');
  717. let pswd = _dom.getAttribute('data-pswd');
  718. onRealPreview(ip, port, name, pswd);
  719. }
  720. /**
  721. * @description 切换通道
  722. */
  723. const onChangeChannel = () => {
  724. channel = $('#h5_channel').value - 0;
  725. let _dom = _doc.querySelector('.h5-menu-list li:nth-child('+(curDeviceIndex+1)+')');
  726. let ip = _dom.getAttribute('data-ip');
  727. let port = _dom.getAttribute('data-port');
  728. let name = _dom.getAttribute('data-user');
  729. let pswd = _dom.getAttribute('data-pswd');
  730. onRealPreview(ip, port, name, pswd);
  731. }
  732. /**
  733. * @description 开启音频
  734. */
  735. const onTurnOnSound = () => {
  736. let vol = $volume.value - 0;
  737. if(playerInstance[WndIndex]) {
  738. playerInstance[WndIndex].setAudioVolume(vol);
  739. }
  740. }
  741. /**
  742. * @description 关闭音频
  743. */
  744. const onTurnSoundOff = () => {
  745. if(playerInstance[WndIndex]) {
  746. playerInstance[WndIndex].setAudioVolume(0);
  747. }
  748. }
  749. /**
  750. * @description 开启对讲
  751. */
  752. const onStartTalk = () => {
  753. let _realDevice = _doc.querySelector('div[wnd-index="'+WndIndex+'"]').getAttribute('deviceindex');
  754. if(_realDevice === null) {
  755. return;
  756. }
  757. let talkPlayer = null;
  758. let _dom = _doc.querySelector('.h5-menu-list li:nth-child(' + (curDeviceIndex + 1) + ')');
  759. let curChannel = channel + 1; //无插件通道号从1开始
  760. let ip = _dom.getAttribute('data-ip');
  761. let port = Number(_dom.getAttribute('data-port'));
  762. let username = _dom.getAttribute('data-user');
  763. let password = _dom.getAttribute('data-pswd');
  764. let rtspURL = 'rtsp://'+ ip +':' + port + '/cam/realmonitor?channel=' + curChannel + '&subtype=5&proto=Private3';
  765. let optionsAudio = {
  766. wsURL: 'ws://'+ ip +':' + port + '/rtspoverwebsocket',
  767. rtspURL: rtspURL,
  768. username: username,
  769. password: password,
  770. isTalkService: true
  771. }
  772. talkPlayer = new PlayerControl(optionsAudio);
  773. talkPlayer.talk('on');
  774. talkInstance[WndIndex] = talkPlayer;
  775. }
  776. /**
  777. * @description 关闭对讲
  778. */
  779. const onStopTalk = () => {
  780. if(talkInstance[WndIndex]) {
  781. talkInstance[WndIndex].talk('off');
  782. talkInstance[WndIndex] = null;
  783. }
  784. }
  785. /**
  786. * @description slow
  787. */
  788. const onSlow = () => {
  789. if(speedX[WndIndex] == null){
  790. speedX[WndIndex] = 1;
  791. }
  792. if(speedX[WndIndex] < 0.125){
  793. return;
  794. }
  795. speedX[WndIndex] = speedX[WndIndex] / 2;
  796. document.getElementById('currentTimes').innerText = speedX[WndIndex] + 'X';
  797. playerInstance[WndIndex].playFF(speedX[WndIndex]);
  798. }
  799. /**
  800. * @description fast
  801. */
  802. const onFast = () => {
  803. if(speedX[WndIndex] == null){
  804. speedX[WndIndex] = 1;
  805. }
  806. if(speedX[WndIndex] > 8){
  807. return;
  808. }
  809. speedX[WndIndex] = speedX[WndIndex] * 2;
  810. document.getElementById('currentTimes').innerText = speedX[WndIndex] + 'X';
  811. playerInstance[WndIndex].playFF(speedX[WndIndex]);
  812. }
  813. /**
  814. * @description 抓图
  815. */
  816. const onSnap = () => {
  817. if(playerInstance[WndIndex]) {
  818. playerInstance[WndIndex].capture('test');
  819. }
  820. }
  821. /**
  822. * @description 获取抓图数据
  823. */
  824. const onGetCapture = () => {
  825. if(playerInstance[WndIndex]) {
  826. let picData = playerInstance[WndIndex].getCapture('jpg', 1.0); //参数释义:type(jpg/jpeg/png),quality(0-1.0)
  827. console.log(picData);
  828. }
  829. }
  830. /**
  831. * @description 针对直播的,开始本地下载
  832. */
  833. const onStartRecord = () => {
  834. let _realDevice = _doc.querySelector('div[wnd-index="'+WndIndex+'"]').getAttribute('deviceindex');
  835. if(_realDevice === null) {
  836. return;
  837. }
  838. let recordPlayer = null;
  839. let _dom = _doc.querySelector('.h5-menu-list li:nth-child(' + (curDeviceIndex + 1) + ')');
  840. let curChannel = channel + 1; //无插件通道号从1开始
  841. let ip = _dom.getAttribute('data-ip');
  842. let port = Number(_dom.getAttribute('data-port'));
  843. let username = _dom.getAttribute('data-user');
  844. let password = _dom.getAttribute('data-pswd');
  845. let stream = $stream.value - 0;
  846. let rtspURL = 'rtsp://'+ ip +':' + port + '/cam/realmonitor?channel=' + curChannel + '&subtype='+ stream +'&proto=Private3';
  847. let optionsRecord = {
  848. wsURL: 'ws://'+ ip +':' + port + '/rtspoverwebsocket',
  849. rtspURL: rtspURL,
  850. username: username,
  851. password: password
  852. }
  853. recordPlayer = new PlayerControl(optionsRecord);
  854. recordPlayer.startRecord(true, {
  855. nameOptions: {
  856. nameFormat: ['{fileName}_ymd_his', {
  857. fileName: 'testFileName'
  858. }]
  859. }
  860. });
  861. recordInstance[WndIndex] = recordPlayer;
  862. $videoLoading.style.display = '';
  863. $videoLoading.innerText = tl('com.Recording'); //录像中!
  864. }
  865. /**
  866. * @description 针对直播的,停止本地下载
  867. */
  868. const onStopRecord = () => {
  869. if(recordInstance[WndIndex]) {
  870. recordInstance[WndIndex].startRecord(false);
  871. recordInstance[WndIndex] = null;
  872. $videoLoading.style.display = 'none';
  873. }
  874. }
  875. /**
  876. * @description 针对回放视频的,视频裁剪
  877. */
  878. const onStartCut = () => {
  879. let _cutIndex = _doc.querySelector('[btn-for=onStartCut]').getAttribute('cutIndex') - 0;
  880. let cutPlayer = null;
  881. let ip = playbackOptions.ip;
  882. let port = playbackOptions.port;
  883. let username = playbackOptions.name;
  884. let password = playbackOptions.pswd;
  885. let url = recordArr[_cutIndex].FilePath;
  886. let _rtspURL = 'rtsp://'+ ip +':' + port + '/' + url;
  887. let cutStartTime = $('#h5_cutStartTime').value;
  888. let s = new Date(cutStartTime.replace('T', ' ')).getTime();
  889. let startTime = new Date(recordArr[_cutIndex].StartTime).getTime();
  890. let range1 = (s - startTime)/1000;
  891. let optionsRecord = {
  892. wsURL: 'ws://'+ ip +':' + port + '/rtspoverwebsocket',
  893. rtspURL: _rtspURL,
  894. username: username,
  895. password: password,
  896. isPrivateProtocol: false, //是否私有协议,默认false
  897. realm: RPC.realm, //登录返回的设备Realm值
  898. speed: 16, //倍速拉流,16倍速
  899. playback: true, //是否是回放
  900. range: range1, //视频裁剪时间与视频的StartTime时间差值
  901. isDownLoad:true
  902. }
  903. cutPlayer = new PlayerControl(optionsRecord);
  904. cutPlayer.on('WorkerReady',function(){
  905. cutPlayer.connect();
  906. });
  907. cutPlayer.on('FileOver',function(){
  908. console.log('File Over');
  909. cutPlayer.startCut(false);
  910. isCuting = false;
  911. $('#h5_cut_process').innerText = '100%';
  912. });
  913. cutPlayer.on('UpdateTimeStamp', function (e) {
  914. let cutStartTime1 = $('#h5_cutStartTime').value;
  915. let cutEndTime1 = $('#h5_cutEndTime').value;
  916. let s1 = new Date(cutStartTime1.replace('T', ' ')).getTime() / 1000;
  917. let e1 = new Date(cutEndTime1.replace('T', ' ')).getTime() / 1000;
  918. let process = parseInt((1 - (e1 - e.timestamp) / (e1 - s1)) * 100);
  919. // console.log(new Date(e.timestamp * 1000));
  920. $('#h5_cut_process').innerText = (process > 100 ? 100 : process) + '%';
  921. if((e.timestamp >= s1) && !isCuting) {
  922. cutPlayer.startCut(true);
  923. isCuting = true;
  924. }
  925. if((e.timestamp >= e1 ) && isCuting) {
  926. cutPlayer.startCut(false);
  927. isCuting = false;
  928. $('#h5_cut_process').innerText = '100%';
  929. }
  930. });
  931. cutPlayer.init($canvas, $video);
  932. cutInstance[WndIndex] = cutPlayer;
  933. }
  934. /**
  935. * @description 开启电子放大
  936. */
  937. const onStartEnlarge = () => {
  938. stopFastPostion();
  939. if (drawInstance[WndIndex]) {
  940. $ivsCanvas.style.display = 'none';
  941. drawInstance[WndIndex].setRegionNum('rect', 1);
  942. let param = {...canvasParam};
  943. drawInstance[WndIndex].drawStart('rect', param);
  944. curEnlargeWnd = WndIndex;
  945. }
  946. }
  947. /**
  948. * @description 开启区域放大
  949. */
  950. const onStartGridEnlarge = () => {
  951. _doc.querySelectorAll('[wnd-index]').forEach((item, index) => {
  952. if(index === WndIndex) {
  953. _doc.querySelector('[wnd-index="' + WndIndex +'"]').style.width = '100%';
  954. _doc.querySelector('[wnd-index="' + WndIndex +'"]').style.height = '100%';
  955. _doc.querySelector('#h5_draw_' + WndIndex).setAttribute('height', 300);
  956. _doc.querySelector('#h5_draw_' + WndIndex).setAttribute('width', 500);
  957. drawInstance[WndIndex] && drawInstance[WndIndex].resize();
  958. } else {
  959. item.style.display = 'none';
  960. }
  961. });
  962. }
  963. /**
  964. * @description 关闭区域放大
  965. */
  966. const onCloseGridEnlarge = () => {
  967. _doc.querySelectorAll('[wnd-index]').forEach((item, index) => {
  968. item.style.display = '';
  969. });
  970. onChangeWdnNum();
  971. }
  972. /**
  973. * @description 关闭电子放大
  974. */
  975. const onStopEnlarge = () => {
  976. let dom = $canvas;
  977. if (dom.style.display === 'none') {
  978. dom = $video;
  979. }
  980. dom.style.width = '100%';
  981. dom.style.height = '100%';
  982. dom.style.left = 0;
  983. dom.style.top = 0;
  984. dom.style.position = 'static';
  985. $ivsCanvas.style.display = '';
  986. }
  987. /**
  988. * @description 绘制电子放大后的回调函数
  989. * @param {object} data 矩形框的坐标信息
  990. */
  991. const rebackActivateLocalEnlarging = data => {
  992. if(curEnlargeWnd != WndIndex) return;
  993. let pos = data.data;
  994. let newData;
  995. if (pos[0][0] === pos[1][0]) {
  996. // return false;
  997. } else {
  998. newData = {
  999. left: pos[0][0],
  1000. top: pos[0][1],
  1001. right: pos[1][0],
  1002. bottom: pos[1][1]
  1003. }
  1004. }
  1005. let dom = $canvas;
  1006. if (dom.style.display === 'none') {
  1007. dom = $video;
  1008. }
  1009. // 倒着画
  1010. if (newData.right < newData.left) {
  1011. let tmp = newData.left;
  1012. newData.left = newData.right;
  1013. newData.right = tmp;
  1014. }
  1015. if (newData.bottom < newData.top) {
  1016. let tmp = newData.top;
  1017. newData.top = newData.bottom;
  1018. newData.bottom = tmp;
  1019. }
  1020. let scaleW = $video_wrap.childNodes[WndIndex].clientWidth / 8191;
  1021. let scaleH = $video_wrap.childNodes[WndIndex].clientHeight / 8191;
  1022. let result = zoomArea(newData.left * scaleW, newData.top * scaleH, newData.right * scaleW, newData.bottom * scaleH, $video_wrap.childNodes[WndIndex].clientWidth, $video_wrap.childNodes[WndIndex].clientHeight);
  1023. dom.style.width = result.width + 'px';
  1024. dom.style.height = result.height + 'px';
  1025. dom.style.left = result.left + 'px';
  1026. dom.style.top = result.top + 'px';
  1027. dom.style.position = 'absolute';
  1028. drawInstance[WndIndex].removeShapeDrawEvent();
  1029. }
  1030. /**
  1031. * @description 设置全屏
  1032. */
  1033. const onSetFull = () => {
  1034. if (getFull()) {
  1035. exitfullScreen();
  1036. } else {
  1037. setfullScreen();
  1038. }
  1039. }
  1040. /**
  1041. * @description 切换窗口分割
  1042. */
  1043. const onChangeWdnNum = () => {
  1044. let val = _doc.querySelector('[sel-for=onChangeWdnNum]').value;
  1045. let ivsDom = _doc.querySelectorAll('[id^=h5_draw_]');
  1046. let divDom = _doc.querySelectorAll('.h5-play-wrap div');
  1047. if(val === '1') {
  1048. divDom.forEach(item => {
  1049. item.style.width = '100%';
  1050. item.style.height = '100%';
  1051. item.style.borderColor = '#000';
  1052. });
  1053. } else if(val === '2' ) {
  1054. divDom.forEach((item, index) => {
  1055. item.style.width = 'calc(50% - 2px)';
  1056. item.style.height = 'calc(50% - 2px)';
  1057. });
  1058. } else if(val === '3') {
  1059. divDom.forEach((item,index) => {
  1060. item.style.height = 'calc(33.333% - 2px)';
  1061. item.style.width = 'calc(33.333% - 2px)';
  1062. });
  1063. } else if(val === '4') {
  1064. divDom.forEach((item, index) => {
  1065. item.style.width = 'calc(25% - 2px)';
  1066. item.style.height = 'calc(25% - 2px)';
  1067. });
  1068. }
  1069. ivsDom.forEach(item => {
  1070. item.setAttribute('width', `${item.parentNode.clientWidth}`);
  1071. item.setAttribute('height', `${item.parentNode.clientHeight}`);
  1072. });
  1073. drawInstance.forEach(item => {
  1074. item && item.resize();
  1075. });
  1076. _doc.querySelector('#h5_draw_0').click();
  1077. }
  1078. /**
  1079. * @description 自定义选择器
  1080. * @param {string} str dom元素
  1081. */
  1082. function $(str) {
  1083. if(str.charAt(0) == '#') {
  1084. return document.getElementById(str.substring(1));
  1085. } else if(str.charAt(0) == '.') {
  1086. return _doc.getElementsByClassName(str.substring(1));
  1087. } else {
  1088. return _doc.getElementsByTagName(str);
  1089. }
  1090. }
  1091. /**
  1092. * @description 设置样式
  1093. * @param {object} obj dom元素
  1094. * @param {*} json css样式
  1095. */
  1096. function setStyle (obj, json){
  1097. for(let i in json) {
  1098. obj.style[i] = json[i];
  1099. }
  1100. }
  1101. /**
  1102. * @description 绑定click事件
  1103. * @param {object} event event对象
  1104. */
  1105. function bindClickEvent(event) {
  1106. let $el = event.target,
  1107. method = $el.getAttribute('btn-for'),
  1108. disabled = $el.getAttribute('disabled');
  1109. if(!disabled) {
  1110. eval(method + "()");
  1111. }
  1112. }
  1113. /**
  1114. * @description 绑定change事件
  1115. * @param {object} event event对象
  1116. */
  1117. function bindChangeEvent(event) {
  1118. let $el = event.target,
  1119. method = $el.getAttribute('sel-for'),
  1120. disabled = $el.getAttribute('disabled');
  1121. if(!disabled) {
  1122. eval(method + "()");
  1123. }
  1124. }
  1125. /**
  1126. * @description 转换数据坐标
  1127. * @param {*} x1 左上角x坐标
  1128. * @param {*} y1 左上角y坐标
  1129. * @param {*} x2 右下角x坐标
  1130. * @param {*} y2 右下角y坐标
  1131. * @param {*} width 宫格宽
  1132. * @param {*} height 宫格高
  1133. */
  1134. function zoomArea (x1, y1, x2, y2, width, height) {
  1135. // 小框区域的数据
  1136. let rectArea = {
  1137. width: x2 - x1,
  1138. height: y2 - y1,
  1139. centerX: (x1 + x2) / 2, // 圆心坐标
  1140. centerY: (y1 + y2) / 2
  1141. };
  1142. // 放大比例,控件放大倍数上限是20
  1143. let scale = Math.min(width / rectArea.width, height / rectArea.height, 20);
  1144. // 原始窗口信息
  1145. let sourceWin = {
  1146. width: width,
  1147. height: height,
  1148. centerX: width / 2,
  1149. centerY: height / 2
  1150. };
  1151. // 放大后的窗口区域
  1152. let bigWinArea = {
  1153. width: width * scale,
  1154. height: height * scale,
  1155. left: sourceWin.centerX - rectArea.centerX * scale,
  1156. top: sourceWin.centerY - rectArea.centerY * scale
  1157. };
  1158. // 数据矫正
  1159. if (bigWinArea.left > 0) {
  1160. bigWinArea.left = 0;
  1161. }
  1162. if (bigWinArea.left < sourceWin.width - bigWinArea.width) {
  1163. bigWinArea.left = sourceWin.width - bigWinArea.width;
  1164. }
  1165. if (bigWinArea.top > 0) {
  1166. bigWinArea.top = 0;
  1167. }
  1168. if (bigWinArea.top < sourceWin.height - bigWinArea.height) {
  1169. bigWinArea.top = sourceWin.height - bigWinArea.height;
  1170. }
  1171. return bigWinArea;
  1172. }
  1173. /**
  1174. * @description 获取全屏状态
  1175. */
  1176. function getFull() {
  1177. return window.top.document.mozFullScreen || window.top.document.webkitIsFullScreen || window.top.document.msFullscreenElement;
  1178. }
  1179. /**
  1180. * @description 全屏状态改变的回调事件
  1181. */
  1182. function fullscreenchange() {
  1183. if (getFull()) {
  1184. return;
  1185. } else {
  1186. exitfullScreen();
  1187. }
  1188. }
  1189. /**
  1190. * @description 设置全屏
  1191. */
  1192. function setfullScreen() {
  1193. let docElm = window.top.document.documentElement;
  1194. if (docElm.requestFullScreen) {
  1195. docElm.requestFullScreen();
  1196. } else if (docElm.mozRequestFullScreen) {
  1197. docElm.mozRequestFullScreen();
  1198. } else if (docElm.webkitRequestFullScreen) {
  1199. docElm.webkitRequestFullScreen();
  1200. } else if (docElm.msRequestFullscreen) {
  1201. docElm.msRequestFullscreen();
  1202. }
  1203. handleFullscreen(true);
  1204. }
  1205. /**
  1206. * @description 退出全屏
  1207. */
  1208. function exitfullScreen() {
  1209. let docElm = window.top.document.documentElement;
  1210. if (docElm.exitFullscreen) {
  1211. docElm.exitFullscreen();
  1212. } else if (docElm.mozCancelFullScreen) {
  1213. docElm.mozCancelFullScreen();
  1214. } else if (docElm.webkitCancelFullScreen) {
  1215. docElm.webkitCancelFullScreen();
  1216. } else if (docElm.msExitFullscreen) {
  1217. docElm.msExitFullscreen();
  1218. }
  1219. handleFullscreen(false);
  1220. }
  1221. /**
  1222. * @description 处理全屏开关时的窗口大小
  1223. * @param {boolean} bool 是否要全屏
  1224. */
  1225. function handleFullscreen(bool) {
  1226. if (bool) {
  1227. let wrap = {
  1228. position: 'absolute',
  1229. left: 0,
  1230. top: 0,
  1231. width: window.screen.width + 'px',
  1232. height: window.screen.height + 'px',
  1233. overflow: 'visible'
  1234. }
  1235. setStyle($video_wrap, wrap);
  1236. } else {
  1237. let wrap = {
  1238. position: 'relative',
  1239. overflow: 'hidden',
  1240. width:'500px',
  1241. height: '300px',
  1242. }
  1243. setStyle($video_wrap, wrap);
  1244. }
  1245. }
  1246. /**
  1247. * @description 查询录像
  1248. */
  1249. const onSearchRecord = async () => {
  1250. let allRecords = [];
  1251. let recordNums = 0;
  1252. let playChannel = $('#h5_playback_channel').value - 0;
  1253. let _dom = _doc.querySelector('.h5-menu-list li:nth-child('+(curDeviceIndex+1)+')');
  1254. let _ip = _dom.getAttribute('data-ip');
  1255. let _port = _dom.getAttribute('data-port');
  1256. let _name = _dom.getAttribute('data-user');
  1257. let _pswd = _dom.getAttribute('data-pswd');
  1258. playbackOptions = {
  1259. ip: _ip,
  1260. port: _port,
  1261. name: _name,
  1262. pswd: _pswd
  1263. }
  1264. const getMediaFile = (params) => {
  1265. return new Promise((resolve, reject) => {
  1266. /**
  1267. * RPC.MediaFileFind.instance 创建媒体文件查找实例
  1268. * @returns {Promise}
  1269. */
  1270. RPC.MediaFileFind.instance().then(json => {
  1271. let queryId = json.result;
  1272. /**
  1273. * RPC.MediaFileFind.findFile 设置查找条件,并判断是否存在文件
  1274. * @param {number} queryId 实例id
  1275. * @param {object} params condition参数
  1276. * @returns {Promise}
  1277. */
  1278. RPC.MediaFileFind.findFile(queryId, params).then(() => {
  1279. findNextFile(queryId).then(() => {
  1280. resolve(true);
  1281. }).catch((err) => {
  1282. reject(err);
  1283. });
  1284. }).catch((err) => {
  1285. reject(err);
  1286. });
  1287. }).catch((err) => {
  1288. reject(err);
  1289. });
  1290. });
  1291. }
  1292. const findNextFile = (queryId) => {
  1293. return new Promise((resolve, reject) => {
  1294. /**
  1295. * RPC.MediaFileFind.findNextFile 在指定条件基础上查询文件信息
  1296. * @param {number} queryId 实例
  1297. * @param {object} 需要查找的数目
  1298. * @returns {Promise}
  1299. */
  1300. RPC.MediaFileFind.findNextFile(queryId, { 'count': 13 }).then(data => {
  1301. if (Number.isInteger(data.found)) {
  1302. recordNums = recordNums + data.found;
  1303. allRecords = allRecords.concat([...data.infos]);
  1304. if (data.found === 13) {
  1305. findNextFile(queryId).then(() => {
  1306. resolve(true);
  1307. }).catch((err) => {
  1308. reject(err);
  1309. });
  1310. } else {
  1311. recordArr = [...allRecords];
  1312. updateInfos(recordArr.slice(0, lINENUMBER));
  1313. updatePages();
  1314. stopFind(queryId);
  1315. resolve(true);
  1316. }
  1317. } else {
  1318. stopFind(queryId);
  1319. resolve(true);
  1320. }
  1321. }).catch((err) => {
  1322. reject(err);
  1323. stopFind(queryId);
  1324. });
  1325. })
  1326. }
  1327. const stopFind = object => {
  1328. return new Promise((resolve, reject) => {
  1329. /**
  1330. * PC.MediaFileFind.close 结束查询
  1331. * @param {number} object 媒体文件查找实例ID
  1332. * @returns {Promise}
  1333. */
  1334. RPC.MediaFileFind.close(object).then(() => {
  1335. /**
  1336. * PC.MediaFileFind.destroy 销毁媒体文件查找实例
  1337. * @param {number} object 媒体文件查找实例ID
  1338. */
  1339. RPC.MediaFileFind.destroy(object);
  1340. resolve(true);
  1341. }).catch(() => {
  1342. reject();
  1343. }).finally(() => {
  1344. });
  1345. })
  1346. }
  1347. const updateInfos = (infos) => {
  1348. let table = _doc.querySelector('#h5_table tbody');
  1349. table.innerHTML = '';
  1350. for(let i = 0; i < infos.length; i++) {
  1351. let time = infos[i].StartTime + ' - ' + infos[i].EndTime; //<input type="button" class="h5-button" btn-for="onGoTime" value="GO!">
  1352. let size = Math.round(infos[i].Length / 1024);
  1353. let newRow = table.insertRow(-1);
  1354. newRow.innerHTML = `<td><input type="checkbox" id="h5_check_${i}"></td><td>${i+1}</td><td>${time}</td><td>${size}</td><td><span id="h5_curTime_${i}">--</span><span>/</span><span id="h5_totalTime_${i}">--</span><input type="text" id="h5_goTime_${i}" style="width: 50px;"><input type="button" class="h5-button" id="h5_button_go_${i}" value="GO!"></td>`;
  1355. }
  1356. _doc.querySelectorAll('[id^=h5_button_go_]').forEach(item => {
  1357. item.addEventListener('click', function(event) {
  1358. let id = item.getAttribute('id').split('_')[3] - 0;
  1359. onGoTime(id);
  1360. });
  1361. });
  1362. _doc.querySelectorAll('[id^=h5_check_]').forEach(function(item) {
  1363. item.addEventListener('click', function(event) {
  1364. event.stopPropagation();
  1365. if(event.target.checked) {
  1366. //渲染裁剪时间
  1367. let _index = event.target.getAttribute('id').split('_')[2] - 0;
  1368. let startTime = recordArr[_index].StartTime.split(' ').join('T');
  1369. let endTime = recordArr[_index].EndTime.split(' ').join('T');
  1370. if(startTime.split(':')[2] === '00') {
  1371. startTime = startTime.substr(0, startTime.length - 3);
  1372. }
  1373. if(endTime.split(':')[2] === '00') {
  1374. endTime = endTime.substr(0, endTime.length - 3);
  1375. }
  1376. $('#h5_cutStartTime').value = startTime;
  1377. $('#h5_cutEndTime').value = endTime;
  1378. _doc.querySelector('[btn-for=onStartCut]').setAttribute('cutIndex', _index);
  1379. }
  1380. });
  1381. });
  1382. _doc.querySelectorAll('#h5_table tbody tr').forEach(function(item) {
  1383. item.addEventListener('dblclick', function(event) {
  1384. event.stopPropagation();
  1385. if(event.target.nodeName === 'TD') {
  1386. let dom = event.target.parentNode.childNodes[1];
  1387. let value = dom.innerText - 1;
  1388. let url = recordArr[value].FilePath;
  1389. onPlayBack(url, value);
  1390. }
  1391. });
  1392. });
  1393. }
  1394. const updatePages = () => {
  1395. totalPage = Math.ceil(recordNums/lINENUMBER);
  1396. $('#h5_curPage').innerText = 1;
  1397. $('#h5_totalPage').innerText = totalPage;
  1398. }
  1399. let tmpDir = [];
  1400. try {
  1401. /**
  1402. * RPC.getDeviceAllInfo 获取存储信息
  1403. * @param {string} 'getDeviceAllInfo' 方法名
  1404. * @return {Promise}
  1405. */
  1406. tmpDir = await RPC.getDeviceAllInfo('getDeviceAllInfo');
  1407. } catch(e) {
  1408. console.log(e);
  1409. }
  1410. let dirs = null;
  1411. if (tmpDir.info && tmpDir.info.length > 1) {
  1412. dirs = 'All';
  1413. }else {
  1414. dirs = tmpDir.info && tmpDir.info[0] && tmpDir.info[0].Detail && tmpDir.info[0].Detail[0] && tmpDir.info[0].Detail[0].Path || '/mnt/sd';
  1415. }
  1416. let startTime = $('#h5_startTime').value.replace('T', ' ');
  1417. let endTime = $('#h5_endTime').value.replace('T', ' ');
  1418. if(startTime.split(' ')[1].split(':').length < 3) {
  1419. startTime = startTime + ':00';
  1420. }
  1421. if(endTime.split(' ')[1].split(':').length < 3) {
  1422. endTime = endTime + ':00';
  1423. }
  1424. let params = {
  1425. condition: {
  1426. Channel: playChannel,
  1427. Dirs: [dirs],
  1428. StartTime: startTime,
  1429. EndTime: endTime,
  1430. Flags: null,
  1431. Events: ['*'],
  1432. Types: ['dav']
  1433. }
  1434. };
  1435. getMediaFile(params).catch((err) => {
  1436. if (err && err.error && err.error.code === 285409409) {
  1437. alert(tl('com.SDEncryptPlaybackTip')); //回放功能需要确保SD卡经过设备认证
  1438. } else {
  1439. let table = _doc.querySelector('#h5_table tbody');
  1440. table.innerHTML = '';
  1441. $('#h5_curPage').innerText = 1;
  1442. $('#h5_totalPage').innerText = 1;
  1443. alert(tl('com.NoData')); //无数据
  1444. }
  1445. });
  1446. }
  1447. /**
  1448. * @description 勾选当前页的全部录像
  1449. */
  1450. const onCheckAll = () => {
  1451. let dom = $('#h5_checkAll');
  1452. let ele = _doc.querySelectorAll('[id^=h5_check_]');
  1453. let domChecked = dom.checked;
  1454. ele.forEach((item, index) => {
  1455. item.checked = domChecked;
  1456. })
  1457. }
  1458. /**
  1459. * @description 下载录像
  1460. */
  1461. const onDownload = async () => {
  1462. let ele = _doc.querySelectorAll('[id^=h5_check_]');
  1463. downList = [];
  1464. ele.forEach((item, index) => {
  1465. let _id = item.getAttribute('id').split('_')[2] - 0;
  1466. if(item.checked) {
  1467. recordArr[(curPage - 1) * lINENUMBER + _id].selfCheckIndex = _id;
  1468. downList.push(recordArr[(curPage - 1) * lINENUMBER + _id]);
  1469. }
  1470. });
  1471. downItemIndex = 0;
  1472. onStartDownload(downList[downItemIndex]);
  1473. }
  1474. /**
  1475. * @description 开始下载录像
  1476. * @param {object} item 录像信息
  1477. */
  1478. const onStartDownload = (item) => {
  1479. let _cutIndex;
  1480. if(item) {
  1481. _cutIndex = item.selfCheckIndex;
  1482. }
  1483. let cutPlayer = null;
  1484. let _dom = _doc.querySelector('.h5-menu-list li:nth-child(' + (curDeviceIndex + 1) + ')');
  1485. let ip = _dom.getAttribute('data-ip');
  1486. let port = Number(_dom.getAttribute('data-port'));
  1487. let username = _dom.getAttribute('data-user');
  1488. let password = _dom.getAttribute('data-pswd');
  1489. let url = recordArr[_cutIndex].FilePath;
  1490. let _rtspURL = 'rtsp://'+ ip +':' + port + '/' + url;
  1491. let optionsRecord = {
  1492. wsURL: 'ws://'+ ip +':' + port + '/rtspoverwebsocket',
  1493. rtspURL: _rtspURL,
  1494. username: username,
  1495. password: password,
  1496. isPrivateProtocol: false,
  1497. realm: RPC.realm,
  1498. speed: 16,
  1499. playback: true,
  1500. isDownLoad:true
  1501. }
  1502. cutPlayer = new PlayerControl(optionsRecord);
  1503. cutPlayer.on('WorkerReady',function(){
  1504. cutPlayer.connect();
  1505. });
  1506. cutPlayer.on('FileOver',function(){
  1507. cutPlayer.startCut(false);
  1508. isCuting = false;
  1509. $('#h5_down_process').innerText = '100%';
  1510. downItemIndex++;
  1511. if(downList[downItemIndex]) {
  1512. onStartDownload(downList[downItemIndex]);
  1513. }
  1514. });
  1515. cutPlayer.on('UpdateTimeStamp', function (e) {
  1516. let s1 = new Date(item.StartTime).getTime()/1000;
  1517. let e1 = new Date(item.EndTime).getTime()/1000;
  1518. let process = parseInt((1 - (e1 - e.timestamp) / (e1 - s1)) * 100);
  1519. $('#h5_down_process').innerText = (process > 100 ? 100 : process) + '%';
  1520. if((e.timestamp >= s1) && !isCuting) {
  1521. cutPlayer.startCut(true);
  1522. isCuting = true;
  1523. }
  1524. if((e.timestamp >= e1 ) && isCuting) {
  1525. cutPlayer.startCut(false);
  1526. isCuting = false;
  1527. $('#h5_down_process').innerText = '100%';
  1528. downItemIndex++;
  1529. if(downList[downItemIndex]) {
  1530. onStartDownload(downList[downItemIndex]);
  1531. }
  1532. }
  1533. });
  1534. cutPlayer.init($canvas, $video);
  1535. cutInstance[WndIndex] = cutPlayer;
  1536. }
  1537. /**
  1538. * @description 更新录像列表当前页
  1539. */
  1540. const updateTable = () => {
  1541. $('#h5_checkAll').checked = false;
  1542. $('#h5_curPage').innerText = curPage;
  1543. let table = _doc.querySelector('#h5_table tbody');
  1544. table.innerHTML = '';
  1545. let index = (curPage - 1 ) * lINENUMBER;
  1546. let infos = recordArr.slice(index, index + lINENUMBER);
  1547. for(let i = 0; i < infos.length; i++) {
  1548. let time = infos[i].StartTime + '-' + infos[i].EndTime;
  1549. let size = Math.round(infos[i].Length / 1024) + '(KB)';
  1550. let newRow = table.insertRow(-1);
  1551. newRow.innerHTML = `<td><input type="checkbox" id="h5_check_${i}"></td><td>${index + i+1}</td><td>${time}</td><td>${size}</td><td><span id="h5_curTime_${i}">--</span><span>/</span><span id="h5_totalTime_${i}">--</span><input type="text" id="h5_goTime_${i}" style="width: 50px;"><input type="button" class="h5-button" id="h5_button_go_${i}" value="GO!"></td>`;
  1552. }
  1553. _doc.querySelectorAll('[id^=h5_check_]').forEach(function(item) {
  1554. item.addEventListener('click', function(event) {
  1555. event.stopPropagation();
  1556. if(event.target.checked) {
  1557. //渲染裁剪时间
  1558. let pageToIndex = (curPage - 1 ) * lINENUMBER;
  1559. let _index = event.target.getAttribute('id').split('_')[2] - 0 + pageToIndex;
  1560. let startTime = recordArr[_index].StartTime;
  1561. let endTime = recordArr[_index].EndTime;
  1562. $('#h5_cutStartTime').value = startTime;
  1563. $('#h5_cutEndTime').value = endTime;
  1564. _doc.querySelector('[btn-for=onStartCut]').setAttribute('cutIndex', _index);
  1565. }
  1566. });
  1567. });
  1568. _doc.querySelectorAll('#h5_table tbody tr').forEach(function(item) {
  1569. item.addEventListener('dblclick', function(event) {
  1570. event.stopPropagation();
  1571. let dom = event.target.parentNode.childNodes[1];
  1572. let value = dom.innerText - 1;
  1573. let url = recordArr[value].FilePath;
  1574. onPlayBack(url, value);
  1575. });
  1576. });
  1577. _doc.querySelectorAll('[id^=h5_button_go_]').forEach(item => {
  1578. item.addEventListener('click', function(event) {
  1579. event.stopPropagation();
  1580. let id = item.getAttribute('id').split('_')[3] - 0;
  1581. onGoTime(id);
  1582. });
  1583. });
  1584. }
  1585. /**
  1586. * @description 暂停回放
  1587. */
  1588. const onPausePlayback = () => {
  1589. if(playerInstance[WndIndex]) {
  1590. playerInstance[WndIndex].pause();
  1591. }
  1592. }
  1593. /**
  1594. * @description 继续回放
  1595. */
  1596. const onContinuePlayback = () => {
  1597. if(playerInstance[WndIndex]) {
  1598. playerInstance[WndIndex].play();
  1599. }
  1600. }
  1601. /**
  1602. * @description 停止回放
  1603. */
  1604. const onClosePlayback = () => {
  1605. if(playerInstance[WndIndex]) {
  1606. playerInstance[WndIndex].stop();
  1607. playerInstance[WndIndex].close();
  1608. playerInstance[WndIndex] = null;
  1609. let dom = $canvas;
  1610. if (dom.style.display === 'none') {
  1611. dom = $video;
  1612. }
  1613. dom.style.display = 'none';
  1614. }
  1615. }
  1616. /**
  1617. * @description 录像跳到指定时间
  1618. * @param 要跳转时间的录像的id
  1619. */
  1620. const onGoTime = (id) => {
  1621. let curTime = $('#h5_goTime_'+id).value - 0;
  1622. if(playerInstance[WndIndex]) {
  1623. playerInstance[WndIndex].playByTime(curTime);
  1624. }
  1625. }
  1626. /**
  1627. * @description ptz云台事件
  1628. * @param {string} type 云台事件类型
  1629. * @param {boolean} isStop 是否停止相应事件
  1630. */
  1631. window.onHandlePTZ = function(type, isStop) {
  1632. let _realDevice = _doc.querySelector('div[wnd-index="'+WndIndex+'"]').getAttribute('deviceindex');
  1633. if(_realDevice === null) {
  1634. return;
  1635. }
  1636. let _session = deviceObject.session[curDeviceIndex];
  1637. _setSession(_session);
  1638. let stepVal = $('#h5_ptz_step').value - 0;
  1639. let arg2 = 0;
  1640. let arg2Arr = ['LeftUp', 'RightUp', 'LeftDown', 'RightDown'];
  1641. let presetArr = ['GotoPreset','SetPreset', 'ClearPreset'];
  1642. let presetNum = $('#h5_preset').value - 0;
  1643. if(arg2Arr.indexOf(type) > -1) {
  1644. arg2 = stepVal;
  1645. }
  1646. if(!isStop) {
  1647. if(presetArr.indexOf(type) > -1) {
  1648. /**
  1649. * RPC.PTZManager 云台相关
  1650. * @param {string} 方法
  1651. * @param {number} channel 通道
  1652. * @param {object} 参数集合
  1653. */
  1654. RPC.PTZManager('start', channel, { 'code': type, 'arg1': presetNum, 'arg2': 0, 'arg3': 0 });
  1655. } else {
  1656. RPC.PTZManager('start', channel, { 'code': type, 'arg1': stepVal, 'arg2': arg2, 'arg3': 0 });
  1657. }
  1658. } else {
  1659. RPC.PTZManager('stop', channel, { 'code': type, 'arg1': stepVal, 'arg2': arg2, 'arg3': 0 });
  1660. }
  1661. };
  1662. //进入页面自动初始化
  1663. init();
  1664. }
  1665. document.querySelector('#device_ipc_tab').click();
  1666. /*
  1667. * 加载翻译
  1668. */
  1669. const languageTrans = () => {
  1670. fetch('./lang/' + curLang + '.txt')
  1671. .then(res => {
  1672. return res.json()
  1673. }).then(text => {
  1674. setLanguage(JSON.parse(JSON.stringify(text)));
  1675. translation();
  1676. });
  1677. }
  1678. languageTrans();
  1679. window.onload = function() {
  1680. if (!['http:', 'https:'].includes(window.location.protocol)) {
  1681. confirm('请参考readme.txt,在https server中打开此页面');
  1682. try {
  1683. window.close();
  1684. } catch (e) {
  1685. alert('请手动关闭此标签页');
  1686. }
  1687. }
  1688. };