index.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. <template>
  2. <van-row class="recordList">
  3. <van-row class="titleWrap">
  4. <span class="line"></span>
  5. <span class="title">实时视频</span>
  6. <div class="videoSelect" v-if="videoSelect">
  7. <el-select @change="videoSelect1" v-model="video1" placeholder="请选择摄像头" class="videoSelect1">
  8. <el-option
  9. v-for="item in videoSelect"
  10. :key="item.url"
  11. :label="item.name"
  12. :value="item.url"
  13. >
  14. </el-option>
  15. </el-select>
  16. <!-- <el-select @change="videoSelect2" v-model="video2" placeholder="请选择摄像头2" class="videoSelect2">
  17. <el-option
  18. v-for="item in videoSelect"
  19. :key="item.url"
  20. :label="item.name"
  21. :value="item.url"
  22. >
  23. </el-option>
  24. </el-select> -->
  25. </div>
  26. </van-row>
  27. <el-row class="videoList" :gutter="20">
  28. <el-col class="video" :span="24" v-if="isIos">
  29. <div id="videobox" ref="video">
  30. <easyPlayer :videoUrl="url" autoplay :live="true" :poster="poster" ></easyPlayer>
  31. <div class="videoTitle">{{name1}}</div>
  32. <!-- <video id="videoPlayer" class="video-js" style="width: 100%; height: 100%;"></video> -->
  33. <!-- <video id="my-video" class="video-js vjs-default-skin" controls="" preload="none" x5-playsinline="" playsinline="" webkit-playsinline="" poster="" x-webkit-airplay="allow" >
  34. <source src="" type="application/x-mpegURL">
  35. </video> -->
  36. <!-- <video ref="videoPlayer" class="video-js"></video> -->
  37. <!-- <video id="myVideo" class="video-js vjs-default-skin vjs-big-play-centered" controls preload="auto" :poster="poster" width="100%" ref="video" >
  38. <source id="source" :src="url" type="application/x-mpegURL">
  39. </video> -->
  40. <!-- <div class="videoTitle">{{name1}}</div> -->
  41. <!-- <video id="myVideo"
  42. class="hlsVideo monitor-height"
  43. ref="hlsvideo"
  44. style="width: 100%"
  45. :poster="poster"
  46. controls
  47. >
  48. <source id="source" src="http://videocdn.didano.com/school765class0channelId2761namedingdangm/playlist.m3u8" type="application/x-mpegURL">
  49. </video> -->
  50. </div>
  51. <!-- <video id="myVideo1"
  52. preload="true"
  53. autoplay="autoplay"
  54. class="hlsVideo monitor-height"
  55. ref="hlsVideo1"
  56. style="width: 100%"
  57. :poster="poster"
  58. controls
  59. muted>
  60. <source id="source" :src="url1" type="application/x-mpegURL">
  61. </video> -->
  62. <!-- <div class="videoTitle">{{name1}}</div> -->
  63. </el-col>
  64. <el-col class="video" :span="24" v-if="isAndroid">
  65. <video
  66. preload="true"
  67. autoplay="autoplay"
  68. class="hlsVideo monitor-height"
  69. ref="hlsVideo1"
  70. style="width: 100%"
  71. :poster="poster"
  72. controls
  73. muted
  74. ></video>
  75. <div class="videoTitle">{{name1}}</div>
  76. </el-col>
  77. </el-row>
  78. </van-row>
  79. </template>
  80. <script>
  81. import "videojs-flash";
  82. import EasyPlayer from '@easydarwin/easyplayer'
  83. import poster from "@/assets/image/poster.jpg";
  84. import 'video.js/dist/video-js.css'
  85. import Hls from "hls.js";
  86. import videojs from 'video.js'
  87. import 'videojs-contrib-hls'
  88. export default {
  89. props:['info'],
  90. components: {
  91. EasyPlayer
  92. },
  93. data() {
  94. return {
  95. videoSelect:true,
  96. hls1:undefined,
  97. hls2:undefined,
  98. video1:undefined,
  99. video2:undefined,
  100. name1:undefined,
  101. name2:undefined,
  102. poster:poster,
  103. url:"http://10.208.64.8:7086/live/cameraid/1000001%240/substream/1.m3u8",
  104. // url:"http://10.208.64.8:7086/live/cameraid/1000001%240/substream/1.m3u8",
  105. url2:"https://video.hua.com/publicity_video_2020_m.mp4",
  106. isAndroid:false,
  107. isIos:false,
  108. options: {
  109. playsInline:true,
  110. poster: poster,//封面图
  111. fluid: true, // 自适应宽高
  112. autoplay: true, // 设置自动播放
  113. muted: true, // 设置了它为true,才可实现自动播放,同时视频也被静音(Chrome66及以上版本,禁止音视频的自动播放)
  114. preload: 'auto', // 预加载
  115. controls: true, // 显示播放的控件
  116. aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
  117. },
  118. player: null,
  119. };
  120. },
  121. created(){
  122. this.init()
  123. //this.isTerminal()
  124. },
  125. mounted() {
  126. videojs.addLanguage('zh-CN', {
  127. "You aborted the media playback": "视频播放被终止",
  128. "A network error caused the media download to fail part-way.": "网络错误导致视频下载中途失败。",
  129. "The media could not be loaded, either because the server or network failed or because the format is not supported.": "视频因格式不支持或者服务器或网络的问题无法加载。",
  130. "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "由于视频文件损坏或是该视频使用了你的浏览器不支持的功能,播放终止。",
  131. "No compatible source was found for this media.": "无法找到此视频兼容的源。",
  132. });
  133. },
  134. methods: {
  135. // 销毁
  136. beforeDestroy () {
  137. if (this.player) {
  138. this.player.dispose()
  139. }
  140. },
  141. creatVideo() {
  142. this.options = {
  143. poster: this.poster, // 视频封面图地址
  144. autoplay: true, // 如果true,浏览器准备好时开始播放。
  145. muted: true, // 默认情况下将会消除任何音频。
  146. loop: true, // 导致视频一结束就重新开始。
  147. preload: 'auto', // auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
  148. language: 'zh-CN', //汉化
  149. fluid: true, // 当true时,播放器将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
  150. sources: [{
  151. type: 'application/x-mpegURL',
  152. src: this.url //视频播放地址
  153. }],
  154. notSupportedMessage: '此视频暂无法播放,请稍后再试', // 无法播放媒体源时显示的默认信息。
  155. controlBar: {
  156. timeDivider: true,
  157. durationDisplay: true, //视频时长时间显示
  158. remainingTimeDisplay: false, //剩余时间显示
  159. fullscreenToggle: true // 全屏按钮
  160. },
  161. errorDisplay: false, //错误显示
  162. posterImage: false, //视频的预览海报图片显示
  163. bigPlayButton: true, //视频中间的播放按钮显示
  164. textTrackDisplay: false
  165. }
  166. // 视频初始化
  167. setTimeout(()=>{
  168. this.player = this.$video(this.$refs.videoPlayer, this.options);
  169. this.player.play();
  170. },1000)
  171. // let video = document.createElement('video');
  172. // video.id = 'video';
  173. // video.style = 'width: 100%; height: 100%;';
  174. // video.controls = true;
  175. // // video.autoplay = true;
  176. // // video.setAttribute('playsinline', true) //IOS微信浏览器支持小窗内播放
  177. // // video.setAttribute('webkit-playsinline',true) //这个bai属性是ios 10中设置可以让视频在小du窗内播放,也就是不是全zhi屏播放的video标签的一个属性
  178. // // video.setAttribute('x5-video-player-type', 'h5') //安卓 声明启用同层H5播放器 可以在video上面加东西
  179. // let source = document.createElement('source');
  180. // source.src = this.url; // 视频地址
  181. // source.type = 'application/x-mpegURL';
  182. // this.$nextTick(()=>{
  183. // let that = this;
  184. // video.appendChild(source);
  185. // this.$refs.video1.appendChild(video);
  186. // //setTimeout(()=>{
  187. // this.player = videojs('video', {
  188. // poster: this.poster, // 视频封面图地址
  189. // title: '',
  190. // playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度
  191. // autoDisable: true,
  192. // preload: 'none', //auto - 当页面加载后载入整个视频 meta - 当页面加载后只载入元数据 none - 当页面加载后不载入视频
  193. // language: 'zh-CN',
  194. // fluid: true, // 自适应宽高
  195. // muted: false, // 是否静音
  196. // //aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
  197. // controls: true, //是否拥有控制条 【默认true】,如果设为false ,那么只能通过api进行控制了。也就是说界面上不会出现任何控制按钮
  198. // autoplay: 'muted', //如果true,浏览器准备好时开始回放。 autoplay: "muted", // //自动播放属性,muted:静音播放
  199. // loop: true, // 导致视频一结束就重新开始。 视频播放结束后,是否循环播放
  200. // techOrder: ["html5", "flash"], //播放顺序
  201. // sources: [{
  202. // src: this.url
  203. // }],
  204. // //screenshot: true,
  205. // controlBar: {
  206. // // volumePanel: { //声音样式
  207. // // inline: false // 不使用水平方式
  208. // // },
  209. // // timeDivider: true, // 时间分割线
  210. // // durationDisplay: true, // 总时间
  211. // // progressControl: false, // 进度条
  212. // // remainingTimeDisplay: true, //当前以播放时间
  213. // // fullscreenToggle: true, //全屏按钮
  214. // // pictureInPictureToggle: false, //画中画
  215. // }
  216. // }, function (){
  217. // this.play()
  218. // this.on("waiting", function ()
  219. // {
  220. // this.addClass("vjs-custom-waiting");
  221. // });
  222. // this.on("playing", function ()
  223. // {
  224. // this.removeClass("vjs-custom-waiting");
  225. // });
  226. // this.on('loadeddata',function () {
  227. // });
  228. // this.on('error', function(err) {
  229. // this.errorDisplay.close(); //不显示错误信息
  230. // // 报错之后自动重新加载
  231. // that.player.src([
  232. // {
  233. // src: that.url, //播放视频地址
  234. // type: "application/x-mpegURL", // 告诉videojs,这是一个hls流
  235. // },
  236. // ]);
  237. // })
  238. // });
  239. // //})
  240. // })
  241. },
  242. videoPlay(){
  243. setTimeout(()=>{
  244. var myvideo = document.getElementsByTagName("video")[0]
  245. var setTimeFlag = 1;//currentTime属性赋值次数
  246. this.$refs.video.play();
  247. //当目前的播放位置已更改时触发
  248. myvideo.timeupdate = 2
  249. myvideo.addEventListener("timeupdate", function(){
  250. // this.$refs.video.play();
  251. // if(setTimeFlag == 1){
  252. // //获取localStorage中记录的currentTime值,并给currentTime属性进行赋值
  253. // if (localStorage.getItem('currentTime' + '{$study->study_id}') != null) {
  254. // var time = localStorage.getItem('currentTime' + '{$study->study_id}');
  255. // myvideo.currentTime = time;
  256. // }
  257. // setTimeFlag = 2;
  258. // }
  259. // //获取当前播放位置的秒数,并设置localStorage
  260. // var currentTime = myvideo.currentTime;
  261. // localStorage.setItem('currentTime' + '{$study->study_id}', currentTime)
  262. //this.$refs.video.play();
  263. });
  264. },0)
  265. },
  266. isTerminal() {//终端判断
  267. },
  268. init(){ //初始化数据
  269. axios({
  270. method: 'post',
  271. url: 'http://10.21.39.1:8082/dhController/getLocalChannel',
  272. data:{
  273. pageNo: 1,
  274. pageSize: 300,
  275. unitType: 1,
  276. },
  277. timeout: 3000,
  278. }).then(res =>{
  279. if(res.data.data.pageList){
  280. let data = res.data.data.pageList
  281. this.videoSelect = data
  282. this.isTerminal()
  283. for(let i = 0; i<data.length; i++){
  284. this.videoSelect[i].url = "http://" + data[i].nvr_ip + ":" + data[i].hls_port + "/live/cameraid/" + data[i].deviceId + "%24" + data[i].id.split("$")[(data[i].id.split("$")).length-1] + "/substream/1.m3u8"
  285. this.videoSelect[i].url = this.videoSelect[i].url + ',' + data[i].name
  286. }
  287. this.video1 = this.videoSelect[0].url
  288. let u = navigator.userAgent;
  289. let isAndroid = u.indexOf("Android") > -1 || u.indexOf("Adr") > -1; //android终端
  290. let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //
  291. setTimeout(()=>{
  292. if (isIOS) {
  293. this.isIos = true
  294. this.videoSelect1(this.videoSelect[0].url)
  295. } else {
  296. this.isAndroid = true
  297. this.videoSelect1(this.videoSelect[0].url)
  298. }
  299. },1000)
  300. }
  301. }).catch(err =>{
  302. })
  303. },
  304. videoSelect1(value){
  305. let arr = value.split(',')
  306. this.name1 = arr[1]
  307. if(this.isIos){
  308. this.url = arr[0]
  309. this.beforeDestroy()
  310. }else{
  311. this.destroyHls(1)
  312. this.loadVideoFn(1,arr[0])
  313. }
  314. },
  315. videoSelect2(value){
  316. this.destroyHls(2)
  317. let arr = value.split(',')
  318. this.name2 = arr[1]
  319. this.loadVideoFn(2,arr[0])
  320. },
  321. destroyHls(id){//关闭视频拉取
  322. if (this.hls) {
  323. if(id=="1"){
  324. this.$refs.hlsVideo1.pause();
  325. this.hls1.destroy();
  326. this.hls1 = null;
  327. }else{
  328. this.$refs.hlsVideo2.pause();
  329. this.hls2.destroy();
  330. this.hls2 = null;
  331. }
  332. }
  333. },
  334. loadVideoFn(id,url) {//视频播放
  335. setTimeout(() =>{
  336. if (Hls.isSupported()) {
  337. if(id == "1"){
  338. this.hls1 = new Hls();
  339. this.hls1.loadSource(url);
  340. this.hls1.attachMedia(this.$refs.hlsVideo1);
  341. this.hls1.on(Hls.Events.MANIFEST_PARSED, () => {
  342. this.$refs.hlsVideo1.play();
  343. });
  344. this.hls1.on(Hls.Events.ERROR, (event, data) => {
  345. });
  346. }else{
  347. this.hls2 = new Hls();
  348. this.hls2.loadSource(url);
  349. this.hls2.attachMedia(this.$refs.hlsVideo2);
  350. this.hls2.on(Hls.Events.MANIFEST_PARSED, () => {
  351. this.$refs.hlsVideo2.play();
  352. });
  353. this.hls2.on(Hls.Events.ERROR, (event, data) => {
  354. });
  355. }
  356. }
  357. },1000)
  358. },
  359. },
  360. };
  361. </script>
  362. <style lang="scss" scoped>
  363. .title{
  364. width:80px;
  365. display: inline-block;
  366. }
  367. .videoList{
  368. margin:15px 0 0px;
  369. .video{
  370. margin:0 auto;
  371. position: relative;
  372. background-color: #fff;
  373. video{
  374. // height:200px;
  375. }
  376. .videoTitle{
  377. position: absolute;
  378. right:16px;
  379. bottom:20px;
  380. font-size: 14px;
  381. color: #fff;
  382. background-color: rgba(0,0,0,0.3);
  383. padding:4px 12px;
  384. }
  385. }
  386. }
  387. ::v-deep .el-input__inner {
  388. background-color: transparent !important;
  389. color: #fff;
  390. height: 36px;
  391. }
  392. /**修改边框和字体颜色 */
  393. ::v-deep .el-select {
  394. position: relative;
  395. width: 120px;
  396. display: inline-block;
  397. margin-left:2px;
  398. .el-input {
  399. input {
  400. height: 20px;
  401. border-color: #ccc;
  402. color: #ccc;
  403. font-size: 10px;
  404. }
  405. }
  406. }
  407. /**修改下拉图标颜色 */
  408. ::v-deep .el-input__suffix {
  409. .el-input__suffix-inner {
  410. .el-icon-arrow-up:before {
  411. color: #ccc;
  412. padding-left: 0.11rem;
  413. margin-top:10px;
  414. }
  415. }
  416. }
  417. .el-select-dropdown__item{
  418. padding:0 12px !important;
  419. font-size: 12px !important;
  420. height:20px !important;
  421. line-height: 20px !important;
  422. color:#fff;
  423. }
  424. .el-select-dropdown__item.hover{
  425. background-color: red;
  426. }
  427. ::v-deep.el-input__icon{
  428. line-height: auto !important;
  429. }
  430. </style>
  431. <style >
  432. .el-scrollbar{
  433. background-color: rgba(35,40,49,1);
  434. }
  435. .el-scrollbar{
  436. border:1px solid rgba(35,40,49,1) !important;
  437. }
  438. .videoSelect{
  439. display: inline-block;
  440. float:right;
  441. }
  442. .el-input__icon{
  443. line-height: 0;
  444. }
  445. .video-js{
  446. width:100%;
  447. height:auto;
  448. }
  449. .vjs-big-play-button{
  450. left:50% !important;
  451. top: 50% !important;
  452. margin-top: -1em ;
  453. margin-left: -1.5em ;
  454. }
  455. #videobox{
  456. width:100%;
  457. height:200px;
  458. }
  459. .easy-player-loading{
  460. display: none !important;
  461. }
  462. .el-input__prefix, .el-input__suffix{
  463. margin-top:-3px !important;
  464. }
  465. </style>