h52.html 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. <!DOCTYPE html>
  2. <html lang=zh-CN>
  3. <head>
  4. <meta charset="utf-8" />
  5. <meta name="viewport"
  6. content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
  7. <title>监控详情页</title>
  8. <link href="https://unpkg.com/video.js@7.10.2/dist/video-js.min.css" rel="stylesheet" />
  9. <link rel="stylesheet" href="./font/iconfont.css" />
  10. <style type="text/css">
  11. body {
  12. margin: 0;
  13. background-color: #f3f3f3;
  14. }
  15. span.vjs-control-text {
  16. display: none;
  17. }
  18. /* iframe {
  19. width: 375px;
  20. height: 667px;
  21. background-color: #fff;
  22. box-sizing: content-box;
  23. border: none;
  24. } */
  25. .btn-box {
  26. margin-top: 20px;
  27. display: flex;
  28. flex-direction: row;
  29. justify-content: space-around;
  30. align-items: center;
  31. }
  32. .btn-container {
  33. position: relative;
  34. }
  35. .videoBtnPlay {
  36. position: absolute;
  37. height: 100px;
  38. width: 100px;
  39. top: 50%;
  40. left: 50%;
  41. transform: translate(-50%, -50%);
  42. }
  43. .more,
  44. .less {
  45. flex: 1
  46. }
  47. img {
  48. width: 100%;
  49. }
  50. .direc {
  51. width: 150px;
  52. height: 150px;
  53. background: url('https://s3.hnyfwlw.com/webstaticimg/bigdata_app/image/monitor/1.png');
  54. background-size: 100% auto;
  55. background-repeat: no-repeat;
  56. position: relative;
  57. display: flex;
  58. justify-content: center;
  59. align-items: center;
  60. }
  61. .direc div {
  62. width: 50px;
  63. height: 50px;
  64. position: absolute;
  65. }
  66. .photo {
  67. font-size: 30px;
  68. color: #4ec467;
  69. padding: 16px;
  70. }
  71. .top {
  72. top: 0;
  73. left: 50px
  74. }
  75. .bottom {
  76. bottom: 0;
  77. left: 50px
  78. }
  79. .left {
  80. left: 0;
  81. top: 50px
  82. }
  83. .right {
  84. right: 0;
  85. top: 50px
  86. }
  87. #box {
  88. height: 300px;
  89. width: 100%;
  90. background: #000;
  91. overflow: hidden;
  92. }
  93. #dialog {
  94. display: none;
  95. min-width: 200px;
  96. max-width: 90%;
  97. box-sizing: border-box;
  98. line-height: 1.6;
  99. padding: 12px;
  100. background: rgba(0, 0, 0, .8);
  101. color: #fff;
  102. text-align: center;
  103. position: absolute;
  104. left: 50%;
  105. transform: translateX(-50%);
  106. border-radius: 4px;
  107. z-index: 999;
  108. top: 50%;
  109. word-break: break-all;
  110. }
  111. .imgBtn {
  112. position: absolute;
  113. width: 100%;
  114. padding: 10px;
  115. bottom: 0;
  116. left: 0;
  117. text-align: center;
  118. color: #338cd9;
  119. font-size: 16px;
  120. box-sizing: border-box;
  121. display: flex;
  122. justify-content: center;
  123. gap: 16px;
  124. }
  125. .imgBtn div {
  126. /* padding: 0 16px; */
  127. }
  128. .imgBtn img {
  129. display: inline-block;
  130. width: 20px;
  131. height: 20px;
  132. vertical-align: middle;
  133. margin-right: 2px;
  134. margin-bottom: 3px;
  135. }
  136. .imgBtnDgp {
  137. display: none;
  138. }
  139. #myCanvas{
  140. display: none;
  141. }
  142. </style>
  143. </head>
  144. <body>
  145. <div class="btn-container">
  146. <div id="box">
  147. </div>
  148. <div class="videoBtnPlay">
  149. <img src="https://webstaticimg.oss-cn-hangzhou.aliyuncs.com/bigdata_app/img/wxplayer.png" />
  150. </div>
  151. </div>
  152. <div id="dialog">
  153. </div>
  154. <canvas id="myCanvas" width="400" height="300"></canvas>
  155. <div class="btn-box">
  156. <div class="more" ontouchstart="_configCamera('move', 8)" ontouchend="_stopConfigCamera()">
  157. <image src="https://s3.hnyfwlw.com/webstaticimg/bigdata_app/image/monitor/3.png" mode="widthFix">
  158. </image>
  159. </div>
  160. <div class="direc">
  161. <div class="top" ontouchstart="_configCamera('move', 0)" ontouchend="_stopConfigCamera()">
  162. </div>
  163. <div class="bottom" ontouchstart="_configCamera('move', 1)" ontouchend="_stopConfigCamera()">
  164. </div>
  165. <p class="photo yficonfont icon-paizhao-xianxing" ontouchstart="_configCamera('takephoto', '')">
  166. </p>
  167. <div class="left" ontouchstart="_configCamera('move', 2)" ontouchend="_stopConfigCamera()">
  168. </div>
  169. <div class="right" ontouchstart="_configCamera('move', 3)" ontouchend="_stopConfigCamera()">
  170. </div>
  171. </div>
  172. <div class="less" ontouchstart="_configCamera('move', 9)" ontouchend="_stopConfigCamera()">
  173. <image src="https://s3.hnyfwlw.com/webstaticimg/bigdata_app/image/monitor/2.png" mode="widthFix">
  174. </image>
  175. </div>
  176. </div>
  177. <div class="imgBtn">
  178. <div class="imgBtnCamrea"> <img
  179. src="https://webstaticimg.oss-cn-hangzhou.aliyuncs.com/bigdata_app/img/img_icon.png" />查看图片</div>
  180. <div class="imgBtnDgp"><img
  181. src="https://webstaticimg.oss-cn-hangzhou.aliyuncs.com/bigdata_app/img/img_icon.png" />查看光谱图片</div>
  182. </div>
  183. </body>
  184. <!-- <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script> -->
  185. <!-- 微信 JS-SDK 如果不需要兼容小程序,则无需引用此 JS 文件。 -->
  186. <!-- <script type="text/javascript" src="//res.wx.qq.com/open/js/jweixin-1.4.0.js"></script> -->
  187. <!-- uni 的 SDK -->
  188. <!-- <script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></script> -->
  189. <script src="./jquery-3.5.1.min.js"></script>
  190. <!-- <script src="https://unpkg.com/video.js@7.10.2/dist/video.min.js"></script> -->
  191. <script type="text/javascript" src="./jweixin-1.6.0.js"></script>
  192. <!-- uni 的 SDK -->
  193. <script type="text/javascript" src="./uni.webview.1.5.6.js" defer></script>
  194. <script src="./video.min.js"></script>
  195. <!-- <script src="./js/flv.min.js"></script> -->
  196. <script >
  197. // import Player from './videoPlayer/videoPlay-js.js'
  198. const domin = 'https://wx.hnyfwlw.com'
  199. // const domin = 'http://218.28.198.186:10508'
  200. let player = null
  201. let stopTimer = 0
  202. //上下左右和拍照
  203. var str = window.location.search.substr(1)
  204. var arr = str.split('&')
  205. console.log(str, arr, '参数');
  206. var device_id = arr[0].split('=')[1]
  207. var accessToken = arr[1].slice(12)
  208. var videoType = arr[2]?.split('=')[1]
  209. var token = accessToken
  210. window._configCamera = configCamera;
  211. window._stopConfigCamera = stopConfigCamera;
  212. window._postPic = postPic;
  213. if (videoType == 'dgp') {
  214. $('.imgBtnDgp').show()
  215. } else {
  216. $('.imgBtnDgp').hide()
  217. }
  218. function captureFrame() {
  219. const video = document.getElementById('myPlayer_html5_api');
  220. const canvas = document.getElementById('myCanvas');
  221. const ctx = canvas.getContext('2d');
  222. // 设置 canvas 尺寸与视频一致
  223. canvas.width = video.videoWidth;
  224. canvas.height = video.videoHeight;
  225. console.log(ctx,'---')
  226. // 绘制当前帧到 canvas
  227. ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
  228. // 获取图片数据
  229. const imageData = canvas.toDataURL('image/jpg');
  230. canvas.toBlob((blob)=>{
  231. postPic(blob)
  232. }, 'image/jpg', 0.92);
  233. // postPic(imageData)
  234. // // 可以创建图片显示或下载
  235. // const img = new Image();
  236. // img.src = imageData;
  237. // document.body.appendChild(img);
  238. // // 或者下载图片
  239. // downloadImage(imageData, 'captured-frame.png');
  240. }
  241. function initFlvPlayer(hlsHdSrc) {
  242. var playHtml =
  243. `<video width="100%" height="300px" id="huifangPlayer" controls autoplay muted></video>`;
  244. $("#box").html(playHtml)
  245. const container = document.getElementById('huifangPlayer')
  246. if (flvjs.isSupported()) {
  247. // console.log(videoElement);
  248. // let hlsHdSrc =
  249. // 'https://cmgw-vpc.lechange.com:8890/flv/LCO/BE049D6PAJ704F1/0/1/20251126103056/openhze749205130814fecba32ac68f5d30fe0.flv?proto=https&source=open';
  250. // 创建 FLV 播放器实例
  251. player = flvjs.createPlayer({
  252. type: 'flv', // 设置视频类型为 FLV
  253. isLive: true, // 指定这是点播
  254. hasAudio: false, // 指定视频流中没有音频
  255. url: hlsHdSrc
  256. })
  257. // 将播放器绑定到 HTML 视频元素
  258. player.attachMediaElement(container)
  259. // 加载视频流
  260. player.load()
  261. // 播放视频
  262. player.play()
  263. }
  264. }
  265. function destroyPlayer() {
  266. if (player) {
  267. const container = document.getElementById('huifangPlayer')
  268. player.pause()
  269. player.unload()
  270. player.detachMediaElement()
  271. player.destroy()
  272. player = null
  273. player = ''
  274. }
  275. }
  276. // videojs 播放失败时的统一处理:显示环境信息 + 改用原生 video 直接播 HLS 兜底
  277. function showPlayerError(myPlayer, src) {
  278. var err = myPlayer.error() || {}
  279. var os = videojs.browser.IS_ANDROID ? 'Android' : (videojs.browser.IS_IOS ? 'iOS' : 'PC')
  280. var mse = window.MediaSource || window.WebKitMediaSource
  281. var info = os + ' MSE:' + (mse ? 'y' : 'n') + ' code:' + (err.code || '?')
  282. console.log('videojs error:', info, err)
  283. $('#dialog').html('播放失败[' + info + ']<br>' + (err.message || '未知错误') + '<br>尝试原生播放兜底...').stop().show(0)
  284. try {
  285. myPlayer.dispose()
  286. } catch (e) {}
  287. $('#box').html('<video id="nativePlayer" src="' + src + '" controls autoplay playsinline webkit-playsinline style="width:100%;height:100%"></video>')
  288. var nv = document.getElementById('nativePlayer')
  289. if (nv) {
  290. nv.onerror = function() {
  291. var e2 = nv.error || {}
  292. $('#dialog').html('原生播放也失败[' + info + '] code:' + (e2.code || '?')).stop().show(0)
  293. }
  294. nv.onplaying = function() {
  295. $('#dialog').stop().hide(0)
  296. }
  297. }
  298. }
  299. // 播放开始后检查视频轨是否真的渲染出来了(安卓可能出现有声无画)
  300. function watchVideoTrack(myPlayer, src) {
  301. myPlayer.on('timeupdate', function() {
  302. if (myPlayer.currentTime() > 2 && !watchVideoTrack._done) {
  303. watchVideoTrack._done = true
  304. var vw = myPlayer.videoWidth()
  305. if (!vw) {
  306. $('#dialog').html('检测到有声无画(视频轨未渲染)<br>正在尝试原生播放兜底...').stop().show(0)
  307. try {
  308. myPlayer.dispose()
  309. } catch (e) {}
  310. $('#box').html('<video id="nativePlayer" src="' + src + '" controls autoplay playsinline webkit-playsinline style="width:100%;height:100%"></video>')
  311. var nv = document.getElementById('nativePlayer')
  312. if (nv) {
  313. nv.onplaying = function() {
  314. $('#dialog').html('原生播放中 ' + (nv.videoWidth || '?') + 'x' + (nv.videoHeight || '?')).stop().show(0).delay(2500).hide(500)
  315. // 2秒后复查:仍无画面则输出详细诊断信息
  316. setTimeout(function() {
  317. if (!nv.videoWidth) {
  318. var e3 = nv.error || {}
  319. var u = ''
  320. try {
  321. var uu = new URL(nv.currentSrc || nv.src)
  322. u = uu.host + uu.pathname.slice(-12)
  323. } catch (ex) {}
  324. var ua = navigator.userAgent
  325. var cm = ua.match(/Chrome\/([\d.]+)/)
  326. var kern = /TBS\/|MQQBrowser/.test(ua) ? 'X5' : (cm ? 'Chrome/' + cm[1] : 'other')
  327. $('#dialog').html('兜底2秒后仍无画面<br>ready:' + nv.readyState + ' net:' + nv.networkState +
  328. ' t:' + nv.currentTime.toFixed(1) + ' err:' + (e3.code || '-') +
  329. '<br>src:' + u +
  330. '<br>内核:' + kern + ' MSE:' + (window.MediaSource ? 'y' : 'n')).stop().show(0)
  331. }
  332. }, 2000)
  333. }
  334. nv.onerror = function() {
  335. $('#dialog').html('原生播放也失败 code:' + ((nv.error && nv.error.code) || '?')).stop().show(0)
  336. }
  337. }
  338. } else {
  339. $('#dialog').html('播放中 ' + vw + 'x' + myPlayer.videoHeight()).stop().show(0).delay(2500).hide(500)
  340. }
  341. }
  342. });
  343. }
  344. function configCamera(ctrl, movenum) {
  345. if (ctrl == "takephoto") {
  346. if (player) {
  347. // 云联的拍照特殊处理
  348. // player.screenshot('yunlianPlayer')
  349. $('#dialog').html('拍照指令正在下发,请等待...').stop().show(50)
  350. captureFrame()
  351. } else {
  352. $('#dialog').html('拍照指令正在下发,请等待...').stop().show(50)
  353. let url = domin+"/api/api_gateway?method=camera.camera_manage.camera_takephoto"
  354. if (videoType == 'dgp') {
  355. url = domin+"/api/api_gateway?method=camera.camera_manage.multi_camera_takephoto"
  356. }
  357. $.ajax({
  358. type: "POST",
  359. url: url,
  360. data: {
  361. device_id: device_id,
  362. token
  363. }
  364. }).then((res) => {
  365. if (res.message == '') {
  366. $('#dialog').html('拍照成功').stop().show(500).delay(1000).hide(500)
  367. } else {
  368. $('#dialog').html(res.message).stop().show(500).delay(1000).hide(500)
  369. }
  370. });
  371. }
  372. } else {
  373. let url = domin+"/api/api_gateway?method=camera.camera_manage.ctrl_camera"
  374. let postData = {
  375. device_id: device_id,
  376. token
  377. }
  378. if (videoType == 'dgp') {
  379. url = domin+"/api/api_gateway?method=camera.camera_manage.multi_ctrl_camera"
  380. postData.ctrl = movenum
  381. } else {
  382. postData.ctrl = ctrl
  383. postData.movenum = movenum
  384. }
  385. //上下左右、放大、缩小
  386. $.ajax({
  387. type: "POST",
  388. url: url,
  389. data: postData
  390. }).then((res) => {
  391. $('#dialog').html('指令下发成功,请等待...').stop().show(500).delay(3000).hide(500)
  392. })
  393. }
  394. }
  395. function stopConfigCamera() {
  396. if (player) {
  397. // 云联不需要停止
  398. return
  399. }
  400. if (videoType == 'dgp') {
  401. if (stopTimer) clearTimeout(stopTimer)
  402. stopTimer = setTimeout(() => {
  403. $.ajax({
  404. type: "POST",
  405. url: domin+"/api/api_gateway?method=camera.camera_manage.mulit_stop_move",
  406. data: {
  407. device_id: device_id,
  408. token
  409. },
  410. });
  411. }, 3000)
  412. } else {
  413. $.ajax({
  414. type: "POST",
  415. url: domin+"/api/api_gateway?method=camera.camera_manage.ctrl_camera",
  416. data: {
  417. device_id: device_id,
  418. ctrl: "stop",
  419. token
  420. },
  421. });
  422. }
  423. }
  424. function postPic(file) {
  425. let form = new FormData()
  426. form.append('img_file', file)
  427. form.append('device_id', device_id)
  428. form.append('token', token)
  429. $.ajax({
  430. type: "POST",
  431. url: domin+"/api/api_gateway?method=camera.camera_manage.save_camera_photo",
  432. contentType: false,
  433. processData: false,
  434. data: form
  435. }).then((res) => {
  436. if (res.message == '') {
  437. $('#dialog').html('拍照成功').stop().show(500).delay(1500).hide(500)
  438. } else {
  439. $('#dialog').html(res.message).stop().show(500).delay(1500).hide(500)
  440. }
  441. });
  442. }
  443. $('.videoBtnPlay').click(function() {
  444. watchVideoTrack._done = false
  445. $('.videoBtnPlay').hide()
  446. if (videoType == 'dgp') {
  447. $.ajax({
  448. type: "POST",
  449. url: domin+"/api/api_gateway?method=camera.camera_manage.multi_addr_camera",
  450. data: {
  451. device_id: device_id,
  452. token
  453. }
  454. }).then((res) => {
  455. // console.log(JSON.stringify(res))
  456. if (res.message == '') {
  457. var data = null
  458. if (typeof res.data == 'string') {
  459. data = eval('(' + res.data + ')');
  460. } else {
  461. data = res.data;
  462. }
  463. console.log(data, 'data');
  464. let hlsHdSrc = data.rtmp;
  465. var playHtml =
  466. `<video id="myPlayer" poster='' controls playsInline webkit-playsinline src=${hlsHdSrc} style="width:100%; height:100%;"></video>`;
  467. $("#box").html(playHtml)
  468. var myVideo = videojs(`myPlayer`, {
  469. controls: true,
  470. autoplay: 'play',
  471. url: hlsHdSrc,
  472. sources: [{
  473. type: 'application/x-mpegURL',
  474. src: hlsHdSrc,
  475. }],
  476. }, function onPlayerReady() {
  477. myVideo.play()
  478. console.log('准备好了')
  479. });
  480. myVideo.on('play', function() {
  481. console.log('开始播放')
  482. })
  483. myVideo.on('error', function() {
  484. showPlayerError(myVideo, hlsHdSrc)
  485. })
  486. watchVideoTrack(myVideo, hlsHdSrc)
  487. } else {
  488. $('#dialog').html('获取播放地址失败: ' + res.message).stop().show(0)
  489. }
  490. }).fail(function(xhr) {
  491. $('#dialog').html('获取播放地址接口异常: ' + xhr.status).stop().show(0)
  492. })
  493. } else {
  494. $.ajax({
  495. type: "POST",
  496. url: domin+"/api/api_gateway?method=camera.camera_manage.addr_camera",
  497. // url: "http://192.168.0.117:8003/api/api_gateway?method=camera.camera_manage.addr_camera",
  498. data: {
  499. device_id: device_id,
  500. is_dahua_app:'1',
  501. token
  502. // device_id: 'FA8690323-1',
  503. // token: localStorage.getItem('session_key')
  504. }
  505. }).then((res) => {
  506. if (res.message == '') {
  507. var data = null
  508. if (typeof res.data == 'string') {
  509. data = eval('(' + res.data + ')');
  510. } else {
  511. data = res.data;
  512. }
  513. var hlsHdSrc = ""
  514. if (data.type_id == 2) {
  515. // 大华云联
  516. // initFlvPlayer(data.rtsp)
  517. player = 1
  518. hlsHdSrc = data.rtsp;
  519. } else {
  520. // 高清主码流是 H.264 High@L5.1,安卓 webview MSE 解不了(有声无画),改用子码流
  521. hlsHdSrc = data.hls || (data.type_id == 0 ? data.hls : data.hlsHd);
  522. }
  523. var playHtml =
  524. `<video id="myPlayer" poster='' controls playsInline webkit-playsinline src=${hlsHdSrc} style="width:100%; height:100%;"></video>`;
  525. $("#box").html(playHtml)
  526. var myVideo = videojs(`myPlayer`, {
  527. controls: true,
  528. autoplay: 'play',
  529. url: hlsHdSrc,
  530. sources: [{
  531. type: 'application/x-mpegURL',
  532. src: hlsHdSrc,
  533. }],
  534. }, function onPlayerReady() {
  535. myVideo.play()
  536. console.log('准备好了')
  537. });
  538. myVideo.on('play', function() {
  539. console.log('开始播放')
  540. })
  541. myVideo.on('error', function() {
  542. showPlayerError(myVideo, hlsHdSrc)
  543. })
  544. watchVideoTrack(myVideo, hlsHdSrc)
  545. } else {
  546. $('#dialog').html('获取播放地址失败: ' + res.message).stop().show(0)
  547. }
  548. }).fail(function(xhr) {
  549. $('#dialog').html('获取播放地址接口异常: ' + xhr.status).stop().show(0)
  550. })
  551. }
  552. })
  553. document.addEventListener('UniAppJSBridgeReady', function() {
  554. $('.imgBtnCamrea').click(function() {
  555. // if (player) {
  556. // player.close('yunlianPlayer')
  557. // player = null
  558. // }
  559. uni.navigateTo({
  560. url: '/pages/monitor/imagelist?id=' + device_id
  561. })
  562. })
  563. $('.imgBtnDgp').click(function() {
  564. // if (player) {
  565. // player.close('yunlianPlayer')
  566. // player = null
  567. // }
  568. uni.navigateTo({
  569. url: '/pages/monitor/dgpImagelist?id=' + device_id
  570. })
  571. })
  572. });
  573. </script>
  574. </html>