h5player.html 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>H5playerVue</title>
  8. <link rel="stylesheet" href="/static/css/antd.min.css">
  9. <style>
  10. body {
  11. padding: 0px;
  12. }
  13. #app {
  14. width: 100% !important;
  15. height: 100% !important;
  16. display: none;
  17. }
  18. .actions {
  19. padding-left: 8px;
  20. }
  21. .ant-form-item {
  22. margin-bottom: 8px
  23. }
  24. .ant-btn {
  25. margin-right: 2px;
  26. }
  27. .icon-wrapper {
  28. position: relative;
  29. padding-left: 20px;
  30. }
  31. .icon-wrapper .anticon {
  32. position: absolute;
  33. top: -2px;
  34. width: 16px;
  35. height: 16px;
  36. line-height: 1;
  37. font-size: 16px;
  38. left: 0;
  39. }
  40. ::-webkit-media-controls {
  41. display: none !important;
  42. }
  43. #player {
  44. width: 100% !important;
  45. height: 100% !important;
  46. }
  47. .control-wrapper {
  48. position: fixed;
  49. z-index: 2025;
  50. bottom: 4%;
  51. right: 4%;
  52. /* width:20%; */
  53. width: 200px;
  54. height: 200px;
  55. margin: 0 auto;
  56. border-radius: 50%;
  57. }
  58. .control-btn {
  59. position: absolute;
  60. cursor: pointer;
  61. width: 38%;
  62. height: 38%;
  63. display: flex;
  64. align-items: center;
  65. justify-content: center;
  66. border: 1px solid #78aee4;
  67. box-sizing: border-box;
  68. transition: all .3s linear;
  69. }
  70. .control-btn:after {
  71. content: '';
  72. position: absolute;
  73. width: 60%;
  74. height: 60%;
  75. background: #000000;
  76. z-index: 2;
  77. }
  78. .control-btn:before {
  79. content: '';
  80. position: relative;
  81. display: block;
  82. /* width: 16px; */
  83. /* height: 16px; */
  84. width: 14%;
  85. height: 14%;
  86. border-top: 3px solid #78aee4;
  87. border-right: 3px solid #78aee4;
  88. border-radius: 0 4px 0 0;
  89. box-sizing: border-box;
  90. z-index: 2;
  91. }
  92. .control-top {
  93. top: 0;
  94. left: 50%;
  95. transform: translateX(-50%) rotate(-45deg);
  96. border-radius: 4px 100% 4px 4px;
  97. }
  98. .control-top:before {
  99. transform: translate(30%, -25%);
  100. }
  101. .control-top:after {
  102. left: 0;
  103. bottom: 0;
  104. border-top: 1px solid #78aee4;
  105. border-right: 1px solid #78aee4;
  106. border-radius: 0 100% 0 0;
  107. }
  108. .control-bottom {
  109. left: 50%;
  110. bottom: 0;
  111. transform: translateX(-50%) rotate(45deg);
  112. border-radius: 4px 4px 100% 4px;
  113. }
  114. .control-bottom:before {
  115. transform: translate(25%, 25%) rotate(90deg);
  116. }
  117. .control-bottom:after {
  118. top: 0;
  119. left: 0;
  120. border-bottom: 1px solid #78aee4;
  121. border-right: 1px solid #78aee4;
  122. border-radius: 0 0 100% 0;
  123. }
  124. .control-left {
  125. top: 50%;
  126. left: 0;
  127. transform: translateY(-50%) rotate(45deg);
  128. border-radius: 4px 4px 4px 100%;
  129. }
  130. .control-left:before {
  131. transform: translate(-25%, 30%) rotate(180deg);
  132. }
  133. .control-left:after {
  134. right: 0;
  135. top: 0;
  136. border-bottom: 1px solid #78aee4;
  137. border-left: 1px solid #78aee4;
  138. border-radius: 0 0 0 100%;
  139. }
  140. .control-right {
  141. top: 50%;
  142. right: 0;
  143. transform: translateY(-50%) rotate(45deg);
  144. border-radius: 4px 100% 4px 4px;
  145. }
  146. .control-right:before {
  147. transform: translate(30%, -25%);
  148. }
  149. .control-right:after {
  150. left: 0;
  151. bottom: 0;
  152. border-top: 1px solid #78aee4;
  153. border-right: 1px solid #78aee4;
  154. border-radius: 0 100% 0 0;
  155. }
  156. .control-round {
  157. position: absolute;
  158. cursor: pointer;
  159. top: 50%;
  160. left: 50%;
  161. transform: translate(-50%, -50%);
  162. width: 51.2%;
  163. height: 51.2%;
  164. background: #000000;
  165. border-radius: 50%;
  166. }
  167. .control-round-inner {
  168. position: absolute;
  169. top: 50%;
  170. left: 50%;
  171. transform: translate(-50%, -50%);
  172. display: flex;
  173. justify-content: center;
  174. align-items: center;
  175. width: 66%;
  176. height: 66%;
  177. border: 1px solid #78aee4;
  178. border-radius: 50%;
  179. }
  180. .control-round-inner:after {
  181. content: "| |";
  182. display: flex;
  183. align-items: center;
  184. justify-content: center;
  185. width: 50%;
  186. height: 50%;
  187. font-size: 80%;
  188. text-align: center;
  189. background: #78aee4;
  190. font-weight: bolder;
  191. color: #fff;
  192. border-radius: 50%;
  193. }
  194. </style>
  195. </head>
  196. <body>
  197. <div id="app">
  198. <div id="player"></div>
  199. <a-locale-provider :locale="zh_CN">
  200. <a-row>
  201. <!-- <a-col :span="24" :md="24">
  202. <a-affix :offset-top="8">
  203. <div id="player"></div>
  204. </a-affix>
  205. <a-form-item>
  206. 分屏
  207. <a-radio-group v-model="splitNum" @change="arrangeWindow">
  208. <a-radio-button :value="1">1x1</a-radio-button>
  209. <a-radio-button :value="2">2x2</a-radio-button>
  210. <a-radio-button :value="3" v-show="!isMoveDevice">3x3</a-radio-button>
  211. <a-radio-button :value="4" v-show="!isMoveDevice">4x4</a-radio-button>
  212. </a-radio-group>
  213. <a-button @click="wholeFullScreen">整体全屏</a-button>
  214. <a-button @click="singleFullScreen">单窗口全屏</a-button>
  215. </a-form-item>
  216. </a-col> -->
  217. <a-col :span="24" :md="12">
  218. <div class="actions">
  219. <a-form :label-col="labelCol" :wrapper-col="wrapperCol" v-show="tabActive !== 'log'">
  220. <a-form-item label="预览URL">
  221. <a-input v-model="urls.realplay"></a-input>
  222. </a-form-item>
  223. <a-form-item label="对讲URL">
  224. <a-input v-model="urls.talk"></a-input>
  225. </a-form-item>
  226. <a-form-item label="预览&对讲">
  227. <a-button id="btn-realplay" @click="realplay">开始预览</a-button>
  228. <a-button id="btn-realplay-stop" @click="stopPlay">停止预览</a-button>
  229. <a-button id="btn-stopall" @click="stopAllPlay">停止全部窗口</a-button>
  230. </a-form-item>
  231. </a-form>
  232. </div>
  233. </a-col>
  234. </a-row>
  235. </a-locale-provider>
  236. <div class="control-wrapper">
  237. <div class="control-btn control-top" @click="goTop"></div>
  238. <div class="control-btn control-left" @click="goLeft"></div>
  239. <div class="control-btn control-bottom" @click="goBottom"></div>
  240. <div class="control-btn control-right" @click="goRight"></div>
  241. <div class="control-round">
  242. <div class="control-round-inner" @click="goStop"></div>
  243. </div>
  244. </div>
  245. </div>
  246. <template id="play-log">
  247. <div></div>
  248. </template>
  249. <!-- 移动端调试 -->
  250. <script src="/static/js/vconsole.min.js"></script>
  251. <!-- <script>
  252. if (IS_MOVE_DEVICE) {
  253. const vc = new VConsole()
  254. }
  255. </script> -->
  256. <script src="/static/js/moment.js"></script>
  257. <script src="/static/js/vue.js"></script>
  258. <script src="/static/js/antd.min.js"></script>
  259. <script src="/static/js/antd-with-locales.min.js"></script>
  260. <script src="/static/h5player.min.js"></script>
  261. <script>
  262. const {
  263. LocaleProvider,
  264. locales
  265. } = window.antd
  266. moment.locale('/static/js/zh-cn.js')
  267. const IS_MOVE_DEVICE = document.body.clientWidth < 992 // 是否移动设备
  268. const MSE_IS_SUPPORT = !!window.MediaSource // 是否支持mse
  269. if (IS_MOVE_DEVICE) {
  270. const vc = new VConsole()
  271. }
  272. // function streamcb(data) {
  273. // console.log(data);
  274. // }
  275. const app = new Vue({
  276. el: '#app',
  277. // components: { Log },
  278. data() {
  279. return {
  280. command: '',
  281. zh_CN: locales.zh_CN,
  282. isMoveDevice: IS_MOVE_DEVICE,
  283. player: null,
  284. splitNum: 1,
  285. mseSupport: MSE_IS_SUPPORT,
  286. tabActive: MSE_IS_SUPPORT ? 'mse' : 'decoder',
  287. labelCol: {
  288. span: 5
  289. },
  290. wrapperCol: {
  291. span: 18
  292. },
  293. urls: {
  294. realplay: {{.wsurl}},
  295. talk: {{.wsurl}},
  296. playback: {{.wsurl}},
  297. },
  298. playback: {
  299. startTime: '2023-08-16T00:00:00',
  300. endTime: '2023-08-16T23:00:00',
  301. valueFormat: moment.HTML5_FMT.DATETIME_LOCAL_SECONDS,
  302. seekStart: '2023-08-16T10:00:00',
  303. rate: ''
  304. },
  305. SmoothDragTime: '2023-08-16T07:47:36.000+08:00',
  306. ThumbnailsTime: '2023-08-16T07:47:36.000+08:00',
  307. judgeurlname: 'wss://10.19.147.57:6014/proxy/10.19.147.57:559/EUrl/KjuVUic',
  308. rate: 1,
  309. audioType: 2,
  310. muted: true,
  311. volume: 50,
  312. InstantParam: 10,
  313. token: "",
  314. volumeOnSvg: {
  315. template: '<svg t="1624453273744" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1421" width="200" height="200"><path d="M597.994667 138.005333q130.005333 28.010667 213.994667 132.992t84.010667 241.002667-84.010667 241.002667-213.994667 132.992l0-88q93.994667-28.010667 153.002667-106.005333t59.008-180.010667-59.008-180.010667-153.002667-106.005333l0-88zM704 512q0 120-106.005333 172.010667l0-344q106.005333 52.010667 106.005333 172.010667zM128 384l170.005333 0 213.994667-213.994667 0 684.010667-213.994667-213.994667-170.005333 0 0-256z" p-id="1422"></path></svg>'
  316. },
  317. volumeOffSvg: {
  318. template: '<svg t="1624453193279" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9147" width="200" height="200"><path d="M512 170.005333l0 180.010667-90.005333-90.005333zM181.994667 128l714.005333 714.005333-53.994667 53.994667-88-88q-74.005333 58.005333-156.010667 77.994667l0-88q50.005333-13.994667 96-50.005333l-181.994667-181.994667 0 288-213.994667-213.994667-170.005333 0 0-256 202.005333 0-202.005333-202.005333zM810.005333 512q0-101.994667-59.008-180.010667t-153.002667-106.005333l0-88q130.005333 28.010667 213.994667 132.992t84.010667 241.002667q0 96-44.010667 178.005333l-64-66.005333q21.994667-53.994667 21.994667-112zM704 512q0 18.005333-2.005333 26.005333l-104-104 0-93.994667q106.005333 52.010667 106.005333 172.010667z" p-id="9148"></path></svg>'
  319. },
  320. zoom: {
  321. rotate: 0,
  322. ratio: 'fill'
  323. },
  324. watermarkConfig: {
  325. text: 'h5player',
  326. color: 'rgba(255, 255, 255, 0.5)',
  327. font: '24px 微软雅黑',
  328. rotateDegree: -15,
  329. space: 240
  330. }
  331. }
  332. },
  333. computed: {
  334. mode: function() {
  335. return this.tabActive === 'mse' ? 0 : 1
  336. }
  337. },
  338. methods: {
  339. init() {
  340. // 设置播放容器的宽高并监听窗口大小变化
  341. window.addEventListener('resize', () => {
  342. this.player.JS_Resize()
  343. })
  344. },
  345. // 向上
  346. goTop() {
  347. console.log('向上')
  348. let arr = {
  349. action: 0,
  350. command: 'UP',
  351. }
  352. this.command = 'UP'
  353. this.sendControl(arr)
  354. },
  355. // 向左
  356. goLeft() {
  357. console.log('向左')
  358. let arr = {
  359. action: 0,
  360. command: 'LEFT',
  361. }
  362. this.command = 'LEFT'
  363. this.sendControl(arr)
  364. },
  365. // 向下
  366. goBottom() {
  367. console.log('向下')
  368. let arr = {
  369. action: 0,
  370. command: 'DOWN',
  371. }
  372. this.command = 'DOWN'
  373. this.sendControl(arr)
  374. },
  375. // 向右
  376. goRight() {
  377. console.log('向右')
  378. let arr = {
  379. action: 0,
  380. command: 'RIGHT',
  381. }
  382. this.command = 'RIGHT'
  383. this.sendControl(arr)
  384. },
  385. // 停止
  386. goStop() {
  387. let arr = {
  388. action: 1,
  389. command: this.command,
  390. }
  391. this.sendControl(arr)
  392. },
  393. // 发送云台控制
  394. sendControl(value) {
  395. let cameraIndexCode = {{.cameraIndexCode}}
  396. const url =
  397. './controlling?cameraIndexCode=' + cameraIndexCode +
  398. '&action=' + value.action + '&command=' + value.command;
  399. // 使用fetch发送GET请求
  400. fetch(url)
  401. .then(response => response.json())
  402. .then(data => {
  403. console.log('成功', data);
  404. })
  405. .catch(error => {
  406. console.error('请求出错:', error);
  407. });
  408. },
  409. createPlayer() {
  410. this.player = new JSPlugin({
  411. szId: 'player', //父窗口id,需要英文字母开头 必填
  412. szBasePath: "./", // 必填,与h5player.min.js的引用路径一致
  413. iMaxSplit: 1,
  414. iCurrentSplit: IS_MOVE_DEVICE ? 1 : 2,
  415. openDebug: true,
  416. mseWorkerEnable: false, //是否开启多线程解码,分辨率大于1080P建议开启,否则可能卡顿
  417. bSupporDoubleClickFull: true, //是否支持双击全屏,true-双击是全屏;false-双击无响应
  418. oStyle: {
  419. borderSelect: IS_MOVE_DEVICE ? '#000' : '#FFCC00',
  420. }
  421. })
  422. // 事件回调绑定
  423. this.player.JS_SetWindowControlCallback({
  424. windowEventSelect: function(iWndIndex) { //插件选中窗口回调
  425. console.log('windowSelect callback: ', iWndIndex);
  426. },
  427. pluginErrorHandler: function(iWndIndex, iErrorCode, oError) { //插件错误回调
  428. console.log('pluginError callback: ', iWndIndex, iErrorCode, oError);
  429. },
  430. windowEventOver: function(iWndIndex) { //鼠标移过回调
  431. //console.log(iWndIndex);
  432. },
  433. windowEventOut: function(iWndIndex) { //鼠标移出回调
  434. //console.log(iWndIndex);
  435. },
  436. windowEventUp: function(iWndIndex) { //鼠标mouseup事件回调
  437. //console.log(iWndIndex);
  438. },
  439. windowFullCcreenChange: function(bFull) { //全屏切换回调
  440. console.log('fullScreen callback: ', bFull);
  441. },
  442. firstFrameDisplay: function(iWndIndex, iWidth, iHeight) { //首帧显示回调
  443. console.log('firstFrame loaded callback: ', iWndIndex, iWidth, iHeight);
  444. },
  445. performanceLack: function(iWndIndex) { //性能不足回调
  446. console.log('performanceLack callback: ', iWndIndex);
  447. },
  448. StreamEnd: function(iWndIndex) { //性能不足回调
  449. console.log('recv StreamEnd: ', iWndIndex);
  450. },
  451. StreamHeadChanged: function(iWndIndex) {
  452. console.log('recv StreamHeadChanged: ', iWndIndex);
  453. },
  454. ThumbnailsEvent: (iWndIndex, eventType, eventCode) => {
  455. console.log('recv ThumbnailsEvent: ' + iWndIndex + ", eventType:" +
  456. eventType + ", eventCode:" + eventCode);
  457. },
  458. InterruptStream: (iWndIndex, iTime) => {
  459. console.log('recv InterruptStream: ' + iWndIndex + ", iTime:" + iTime);
  460. },
  461. ElementChanged: (iWndIndex, szElementType) => { //回调采用的是video还是canvas
  462. console.log('recv ElementChanged: ' + iWndIndex + ", szElementType:" +
  463. szElementType);
  464. },
  465. });
  466. },
  467. arrangeWindow() {
  468. let splitNum = this.splitNum
  469. this.player.JS_ArrangeWindow(splitNum).then(
  470. () => {
  471. console.log(`arrangeWindow to ${splitNum}x${splitNum} success`)
  472. },
  473. e => {
  474. console.error(e)
  475. }
  476. )
  477. },
  478. wholeFullScreen() {
  479. this.player.JS_FullScreenDisplay(true).then(
  480. () => {
  481. console.log(`wholeFullScreen success`)
  482. },
  483. e => {
  484. console.error(e)
  485. }
  486. )
  487. },
  488. singleFullScreen() {
  489. this.player.JS_FullScreenSingle(this.player.currentWindowIndex).then(
  490. () => {
  491. console.log(`singleFullScreen success`)
  492. },
  493. e => {
  494. console.error(e)
  495. }
  496. )
  497. },
  498. /* 预览&对讲 */
  499. realplay() {
  500. let {
  501. player,
  502. mode,
  503. urls,
  504. token
  505. } = this,
  506. index = player.currentWindowIndex,
  507. playURL = urls.realplay
  508. player.JS_SetTraceId(index, true)
  509. player.JS_Play(playURL, {
  510. playURL,
  511. mode,
  512. keepDecoder: 0,
  513. token
  514. }, index).then(
  515. () => {
  516. console.log('realplay success');
  517. player.JS_GetTraceId(index).then((id) => {
  518. console.log("traceid:", id)
  519. })
  520. },
  521. e => {
  522. console.error(e)
  523. }
  524. )
  525. },
  526. stopPlay() {
  527. this.player.JS_Stop().then(
  528. () => {
  529. this.playback.rate = 0;
  530. console.log('stop realplay success')
  531. },
  532. e => {
  533. console.error(e)
  534. }
  535. )
  536. },
  537. stopAllPlay() {
  538. this.player.JS_StopRealPlayAll().then(
  539. () => {
  540. this.playback.rate = 0;
  541. console.log('stopAllPlay success')
  542. },
  543. e => {
  544. console.error(e)
  545. }
  546. )
  547. },
  548. streamcb(data) {
  549. console.log(data);
  550. },
  551. jsdecoderVersion() {
  552. let v = this.player.JS_GetSdkVersion();
  553. console.log("JS_GetSdkVersion:", v);
  554. },
  555. },
  556. mounted() {
  557. this.$el.style.setProperty('display', 'block')
  558. this.init()
  559. this.createPlayer()
  560. }
  561. })
  562. </script>
  563. </body>
  564. </html>