阅读 272

前端播放视频(前端播放视频框架 js)

1.m3u8

这种格式前端video标签可以直接播放,也可以使用封装好的插件,

在线播放地址(mr158.cn/m3u8test/)

vue项目demo

<template>   <video ref="videoPlayer" class="video-js vjs-big-play-centered"></video> </template> <script> import videoPlayer from 'video.js' import 'video.js/dist/video-js.css' export default {   name: 'com-VideoPlayer',   props: {     sources: {       type: Array,       default() {         return []       },     },     options: {       type: Object,       default() {         return {}       },     },   },   data() {     return {       player: null,     }   },   watch: {     sources(newValue) {       const src = newValue && newValue.length > 0 ? newValue[0].src : ''       if (src && this.player) {         this.player.src(src)         this.player.load()       }     },   },   mounted() {     const defaultOptions = {       autoplay: true,       controls: true,       preload: 'auto',       muted: true,     }     this.player = videoPlayer(       this.$refs.videoPlayer,       { ...defaultOptions, ...this.options, sources: this.sources },       function onPlayerReady() {},       function onPlayerError() {}     )   },   beforeDestroy() {     if (this.player) {       this.player.dispose()     }   }, } </script> <style lang="scss" scoped> .video-js {   width: 100%;   video {     width: 100%;     height: 100%;   } } .video-js .vjs-tech {   /* 视频填充整个容器 */   object-fit: cover; } </style> 复制代码

2.rtsp

这种视频前端不能直接播放,需要用插件

rtsp(x协议)://admin(用户名):ys123456(密码)@112.27.144.16(IP地址):554(端口)/Streaming/Channels/2

相关解决方案链接

(1.Vue项目中使用海康威视web插件:evestorm.github.io/posts/762/)

方案1(已购买海康服务器)

当例如海康的rtsp的视频,如果海康那边推上平台了,要海康提供AppKey 和

API网关提供的appkey API网关提供的secret

代码(组件内代码)

<template>   <div class="video-dialog" v-if="showVideo">     <div class="close">       <button type="primary" @click="stopVideo">关闭视频</button>     </div>     <div id="videoPlayerBox" ref="videoPlayerBox">       <div id="playBox" v-html="oWebControl === null ? playText : ''">11</div>     </div>   </div> </template> <script> export default {   name: "videoPlayer",   props: [     "videoVisible",     "monitorDeviceNo",     "monitorDeviceName",     "videoInfo",     "layoutGet",     "hideWin",     "destoryWin",   ],   data() {     return {       showVideo:true,       oWebControl: null,       pubKey: "", // 公钥       appkey: "",       secret: "",       ip: "",       port: 1443,       width: `${document.documentElement.clientWidth}` * 0.5,       height: 400, // 弹框高度       playHeight: `${document.documentElement.clientHeight}` * 0.78,       layout: "1x1",       left: "",       top: "",       buttonIDs: "0,16,256,257,258,259,260,512,513,514,515,516,517,768,769",       initCount: 0,       playMode: 0, // 0 预览 1回放       playText: "启动中。。。",       cameraIndexCode: "d6c11f20e1ce4aea91d0817576fcbfb3", // 监控点编号     };   },   methods: {     setLayout() {       if (this.layoutGet == 1) {         this.layout = "1x1";         // this.width = `${document.documentElement.clientWidth}` * 0.6 / 1         // this.playHeight = `${document.documentElement.clientHeight}` * 0.7 / 1       } else if (this.layoutGet == 2) {         this.layout = "2x2";         // this.width = `${document.documentElement.clientWidth}` * 0.6 / 2         // this.playHeight = `${document.documentElement.clientHeight}` * 0.7 / 2       } else if (this.layoutGet == 3) {         this.layout = "3x3";         // this.width = `${document.documentElement.clientWidth}` * 0.6 / 3         // this.playHeight = `${document.documentElement.clientHeight}` * 0.7 / 3       } else if (this.layoutGet == 4) {         this.layout = "4x4";         // this.width = `${document.documentElement.clientWidth}` * 0.6 / 4         // this.playHeight = `${document.documentElement.clientHeight}` * 0.7 / 4       }     },     show() {       // console.log(this.videoInfo, 78787)       this.setLayout();       // 设置top left       let bodyW = document.body.clientWidth;       let bodyH = document.body.clientHeight;       this.left = bodyW / 2 - this.width / 2;       this.top = bodyH / 3 - this.height / 3;       let videoInfo = this.videoInfo;       this.appkey = videoInfo[0].ekey;       this.secret = videoInfo[0].esecret;       this.cameraIndexCode = videoInfo[0].eNote;       this.ip = videoInfo[0].eaddress.split(":")[0];       this.port = parseInt(videoInfo[0].eaddress.split(":")[1]);       this.stopVideo();       setTimeout(() => {         this.$nextTick(() => {           this.initPlugin(() => {             this.previewVideo();           });         });       }, 500);     },     hide() {       this.handleClose();     },     handleClose() {       this.stopVideo();     },     stopVideo() {       if (this.oWebControl) {         this.oWebControl.JS_RequestInterface({           funcName: "stopAllPreview",         });         this.oWebControl.JS_HideWnd();         this.oWebControl.JS_Disconnect().then(           () => {             // 断开与插件服务连接成功             console.log("断开与插件服务连接成功");           },           () => {             // 断开与插件服务连接失败             console.log("断开与插件服务连接失败");           }         );         this.oWebControl = null;         this.showVideo=false;       }     },     // 推送消息     cbIntegrationCallBack() {       // console.log(oData, '推送消息');     },     // RSA加密     setEncrypt(value) {       /* eslint-disable */       let encrypt = new JSEncrypt();       encrypt.setPublicKey(this.pubKey);       return encrypt.encrypt(value);     },     initPlugin(callback) {       /* eslint-disable */       let that = this;       this.oWebControl = new WebControl({         szPluginContainer: "playBox", // 指定容器id         iServicePortStart: 15900, // 指定起止端口号,建议使用该值         iServicePortEnd: 15909,         szClassId: "23BF3B0A-2C56-4D97-9C03-0CB103AA8F11", // 用于IE10使用ActiveX的clsid         cbConnectSuccess: function () {           // 创建WebControl实例成功           that.oWebControl             .JS_StartService("window", {               // WebControl实例创建成功后需要启动服务               dllPath: "./VideoPluginConnect.dll", // 值"./VideoPluginConnect.dll"写死             })             .then(               function () {                 // 启动插件服务成功                 that.oWebControl.JS_SetWindowControlCallback({                   // 设置消息回调                   cbIntegrationCallBack: that.cbIntegrationCallBack,                 });                 that.oWebControl                   .JS_CreateWnd("playBox", that.width, that.playHeight)                   .then(function () {                     // JS_CreateWnd创建视频播放窗口,宽高可设定                     that.init(callback); // 创建播放实例成功后初始化                   });               },               function () {                 // 启动插件服务失败               }             );         },         cbConnectError: function () {           // 创建WebControl实例失败           that.oWebControl = null;           that.playText = "插件未启动,正在尝试启动,请稍候...";           WebControl.JS_WakeUp("VideoWebPlugin://"); // 程序未启动时执行error函数,采用wakeup来启动程序           // WebControl.JS_WakeUp("VideoWebPlugin://"); // 程序未启动时执行           that.initCount++;           if (that.initCount < 3) {             setTimeout(function () {               that.initPlugin();             }, 3000);           } else {             that.playText =               '插件启动失败,请检查插件是否安装!<a target="_blank" style="color: #30a8ff;text-decoration: underline;" href="http://xx.com/VideoWebPlugin.zip">下载地址(软件大小:62.7MB)</a>';           }         },         cbConnectClose: function (bNormalClose) {           // 异常断开:bNormalClose = false           // JS_Disconnect正常断开:bNormalClose = true           that.oWebControl = null;         },       });     },     // 获取公钥     getPubKey(callback) {       this.oWebControl         .JS_RequestInterface({           funcName: "getRSAPubKey",           argument: JSON.stringify({             keyLength: 1024,           }),         })         .then((oData) => {           if (oData.responseMsg.data) {             this.pubKey = oData.responseMsg.data;             callback();           }         });     },     init(callback) {       let that = this;       this.getPubKey(() => {         //  请自行修改以下变量值         let appkey = this.appkey; // 综合安防管理平台提供的appkey,必填         let secret = that.setEncrypt(this.secret); // 综合安防管理平台提供的secret,必填         let ip = this.ip; // 综合安防管理平台IP地址,必填         let playMode = this.playMode; // 初始播放模式:0-预览,1-回放         let port = this.port; // 综合安防管理平台端口,若启用HTTPS协议,默认443         let snapDir = "D:\SnapDir"; // 抓图存储路径         let videoDir = "D:\VideoDir"; // 紧急录像或录像剪辑存储路径         let layout = this.layout; // playMode指定模式的布局         let enableHTTPS = 1; // 是否启用HTTPS协议与综合安防管理平台交互,是为1,否为0         let encryptedFields = "secret"; // 加密字段,默认加密领域为secret         let showToolbar = 0; // 是否显示工具栏,0-不显示,非0-显示         let showSmart = 0; // 是否显示智能信息(如配置移动侦测后画面上的线框),0-不显示,非0-显示         let buttonIDs = this.buttonIDs; // 自定义工具条按钮         // /// 请自行修改以上变量值         that.oWebControl           .JS_RequestInterface({             funcName: "init",             argument: JSON.stringify({               appkey: appkey, // API网关提供的appkey               secret: secret, // API网关提供的secret               ip: ip, // API网关IP地址               playMode: playMode, // 播放模式(决定显示预览还是回放界面)               port: port, // 端口               snapDir: snapDir, // 抓图存储路径               videoDir: videoDir, // 紧急录像或录像剪辑存储路径               layout: layout, // 布局               enableHTTPS: enableHTTPS, // 是否启用HTTPS协议               encryptedFields: encryptedFields, // 加密字段               showToolbar: showToolbar, // 是否显示工具栏               showSmart: showSmart, // 是否显示智能信息               buttonIDs: buttonIDs, // 自定义工具条按钮             }),           })           .then((oData) => {             that.oWebControl.JS_Resize(that.width, that.playHeight); // 初始化后resize一次,规避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题             if (callback) {               callback();             }           });       });     },     // 视频预览功能     previewClick() {       if (!this.oWebControl) {         return;       }       // 如果是回放,重新初始化       if (this.playMode === 1) {         this.playMode = 0;         this.oWebControl.JS_HideWnd();         this.initPlugin(() => {           this.previewVideo();         });       } else if (this.playMode === 0) {         this.previewVideo();       }     },     previewVideo() {       for (let i = 0; i < this.videoInfo.length; i++) {         // let cameraIndexCode = this.cameraIndexCode;             // 获取输入的监控点编号值,必填         if (this.videoInfo[i].enote) {           let cameraIndexCode = this.videoInfo[i].enote; // 获取输入的监控点编号值,必填           let streamMode = 0; // 主子码流标识:0-主码流,1-子码流           let transMode = 0; // 传输协议:0-UDP,1-TCP           let gpuMode = 0; // 是否启用GPU硬解,0-不启用,1-启用           // let wndId = -1;                                         // 播放窗口序号(在2x2以上布局下可指定播放窗口)           let wndId = i + 1; // 播放窗口序号(在2x2以上布局下可指定播放窗口)           console.log(cameraIndexCode, 8989);           this.oWebControl.JS_RequestInterface({             funcName: "startPreview",             argument: JSON.stringify({               cameraIndexCode: cameraIndexCode.trim(), // 监控点编号               streamMode: streamMode, // 主子码流标识               transMode: transMode, // 传输协议               gpuMode: gpuMode, // 是否开启GPU硬解               wndId: wndId, // 可指定播放窗口             }),           });         }       }     },     // 回放     playBack() {       if (!this.oWebControl) {         return;       }       // 如果是预览       if (this.playMode === 0) {         this.playMode = 1;         this.oWebControl.JS_HideWnd();         this.initPlugin(() => {           this.backVideo();         });       } else if (this.playMode === 1) {         this.backVideo();       }     },     backVideo() {       let cameraIndexCode = this.cameraIndexCode;       // 前30天       let date = new Date(new Date().getTime() - 30 * 24 * 60 * 60 * 1000);       let month =         date.getMonth() + 1 < 10           ? "0" + (date.getMonth() + 1)           : date.getMonth() + 1;       // 开始时间当天00点       let str =         date.getFullYear() + "/" + month + "/" + date.getDate() + " 00:00:00";       let startTime = String(         parseInt(new Date(str).getTime() / 1000) - 3 * 60 * 60       );       let endTime = String(parseInt(date.getTime() / 1000));       this.oWebControl.JS_RequestInterface({         funcName: "startPlayback",         argument: JSON.stringify({           cameraIndexCode: cameraIndexCode.trim(), // 监控点编号           startTimeStamp: startTime, // 录像查询开始时间戳,单位:秒           endTimeStamp: endTime, // 录像查询结束时间戳,单位:秒           recordLocation: 1, // 录像存储类型 0-中心存储 1-设备存储           transMode: 0, // 传输协议 ,0-UDP 1-TCP           gpuMode: 0, // 是否开启 GPU 硬解,0-不开启 1-开启           wndId: -1, //可指定播放窗口         }),       });     },     // 设置窗口裁剪,当因滚动条滚动导致窗口需要被遮住的情况下需要JS_CuttingPartWindow部分窗口     setWndCover() {       let iWidth = $(window).width();       let iHeight = $(window).height();       let oDivRect = $("#playBox").get(0).getBoundingClientRect();       let iCoverLeft = oDivRect.left < 0 ? Math.abs(oDivRect.left) : 0;       let iCoverTop = oDivRect.top < 0 ? Math.abs(oDivRect.top) : 0;       let iCoverRight =         oDivRect.right - iWidth > 0 ? Math.round(oDivRect.right - iWidth) : 0;       let iCoverBottom =         oDivRect.bottom - iHeight > 0           ? Math.round(oDivRect.bottom - iHeight)           : 0;       iCoverLeft = iCoverLeft > this.width ? this.width : iCoverLeft;       iCoverTop = iCoverTop > this.playHeight ? this.playHeight : iCoverTop;       iCoverRight = iCoverRight > this.width ? this.width : iCoverRight;       iCoverBottom =         iCoverBottom > this.playHeight ? this.playHeight : iCoverBottom;       this.oWebControl.JS_RepairPartWindow(         0,         0,         this.width + 1,         this.playHeight       ); // 多1个像素点防止还原后边界缺失一个像素条       if (iCoverLeft != 0) {         this.oWebControl.JS_CuttingPartWindow(           0,           0,           iCoverLeft,           this.playHeight         );       }       if (iCoverTop != 0) {         this.oWebControl.JS_CuttingPartWindow(0, 0, this.width + 1, iCoverTop); // 多剪掉一个像素条,防止出现剪掉一部分窗口后出现一个像素条       }       if (iCoverRight != 0) {         this.oWebControl.JS_CuttingPartWindow(           this.width - iCoverRight,           0,           iCoverRight,           this.playHeight         );       }       if (iCoverBottom != 0) {         this.oWebControl.JS_CuttingPartWindow(           0,           this.playHeight - iCoverBottom,           this.width,           iCoverBottom         );       }     },     // 拖拽窗口     onmousedown(e) {       let that = this;       let cWidth = document.body.clientWidth;       let cHeight = document.body.clientHeight;       this.$refs.videoPlayerBox.onmousemove = function (el) {         let ev = el || window.event;         ev.preventDefault();         // 解决点击标题窗口抖动的问题         if (Math.abs(ev.movementX) === 0 && Math.abs(ev.movementY) === 0) {           return;         }         that.left += ev.movementX;         that.top += ev.movementY;         // 顶部不能超出,左侧、右侧、底部可以超出一半         if (that.top < 0) {           that.top = 0;         }         if (that.top > cHeight - that.height / 2) {           that.top = cHeight - that.height / 2;         }         if (that.left < -that.width / 2) {           that.left = -that.width / 2;         }         if (that.left > cWidth - that.width / 2) {           that.left = cWidth - that.width / 2;         }         that.oWebControl.JS_Resize(that.width, that.playHeight);       };       this.$refs.videoPlayerBox.onmouseup = function () {         this.onmousemove = null;         this.onmouseup = null;       };       // 阻止默认事件       if (e.preventDefault) {         e.preventDefault();       } else {         return false;       }     },     onmouseleave(e) {       // 拖拽过快鼠标划出标题位置,重新拖拽是,鼠标还未移入标题,由于上一次的onmouseup方法没有执行,导致鼠标靠近弹框,弹框移动,解决方法,给onmousemove和onmouseup赋值为null       this.$refs.videoPlayerBox.onmousemove = null;       this.$refs.videoPlayerBox.onmouseup = null;       // 解决拖拽过快,播放器残影问题       this.oWebControl.JS_Resize(this.width, this.playHeight);     },   },   mounted() {     // let hksystem = JSON.parse(sessionStorage.getItem('hksystem'));     // this.appkey = hksystem.appKey;     // this.secret = hksystem.appSecret;     // this.ip = hksystem.host.split(':')[0];     // this.port = hksystem.host.split(':')[1];     // 监听resize事件,使插件窗口尺寸跟随DIV窗口变化     // $(window).resize(() => {     //     if (this.oWebControl != null) {     //         this.oWebControl.JS_Resize(this.width, this.playHeight);     //         // this.setWndCover();     //     }     // });     // 监听滚动条scroll事件,使插件窗口跟随浏览器滚动而移动     // $(window).scroll(() => {     //     if (this.oWebControl != null) {     //         this.oWebControl.JS_Resize(this.width, this.playHeight);     //         this.setWndCover();     //     }     // });     // 标签关闭     $(window).unload(() => {       if (this.oWebControl != null) {         this.oWebControl.JS_HideWnd(); // 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题         this.oWebControl.JS_Disconnect().then(           () => {             // 断开与插件服务连接成功           },           () => {             // 断开与插件服务连接失败           }         );       }     });     // this.setLayout()     this.show();   },   watch: {     monitorDeviceNo: {       handler(newV, oldV) {         this.cameraIndexCode = newV;         if (newV && this.playMode === 0) {           this.previewVideo();         } else if (newV && this.playMode === 1) {           this.backVideo();         }       },     },     layoutGet: {       handler(newV, oldV) {         console.log(newV);         if (newV && newV != oldV) {           this.show();           // this.previewVideo()         }       },     },     hideWin: {       handler(newV, oldV) {         if (newV) {           this.oWebControl.JS_HideWnd();           this.showVideo=false         } else {           this.oWebControl.JS_ShowWnd();         }       },     },     destoryWin: {       handler(newV, oldV) {           this.stopVideo();       },     },     videoInfo: {       handler(newV, oldV) {         if (newV) {           console.log(newV, 2222);           this.show();         }       },     },   }, }; </script> <style lang="scss" scoped> .video-dialog {   // width:790px;   // height: 100%;   left:480px;   top:100px;   position: fixed;   z-index: 10000;   .close{       width:100%;       display: flex;       align-items: center;       justify-content: center;       img{           width:30px;           height:auto;       }   } } #videoPlayerBox {   // width: 100%;   // height: 100%;   z-index: 4012;   .header {     height: 0.24rem;     width: 100%;     display: flex;     align-items: center;     font-size: 0.1rem;     color: #fff;     text-align: center;     justify-content: center;     background: #142c50;   }   .closeBtn {     width: 0.12rem;     height: 0.12rem;     position: absolute;     top: 0.06rem;     right: 0.06rem;     cursor: pointer;   }   .topbutton {     height: 26px;     padding-top: 6px;     padding-left: 14px;     background-color: #0d1f3a;     cursor: pointer;     .topBtns {       padding: 0 12px 6px;       border-bottom: 1px solid #0d1f3a;       color: #fff;     }     .activeBtn {       color: #4ba2ff;       border-color: #4ba2ff;     }   }   #playBox {     // width: 100%;     // height: 100%;     color: #ffffff;     display: flex;     flex-direction: column;     align-items: center;     justify-content: center;     font-size: 12px;   } } </style> 复制代码

调用处代码

<template>   <div class="list_view">         <div class="item_view" v-for="(item, index) in testList" :key="index">           <rtsp-video         :hideWin="maskOpen"         :destoryWin="destroy"         :data-active="updateTime"     ref="videoRef"     :videoInfo="[form]"     layoutGet="1"     >22</rtsp-video>     33     <div class="name">{{ item.name }}</div>     <demo-video :list="curShowList" :layout="layout"></demo-video>     </div>     </div> </template> <script> import TitleBar from '@/components/TitleBar' import AutoLoadMixin from '@/plugin/AutoLoadMixin' import rtspVideo from "@/components/RTSPVideo"; // import demoVideo from "@/components/demoVideo" export default {   components: {     TitleBar,     rtspVideo,     // demoVideo   },   mixins: [AutoLoadMixin],   data() {     return {       currentIndex: 0,       curShowList: [         {           cameraIndexCode: '2',           streamMode: 'rtsp',           transMode: 'rtsp'         }       ],       destroy: false,       maskOpen: false,       updateTime: 1,       activeTab: "1",       form: {         areaId: 6,         createBy: null,         createTime: "2021-09-07 14:09:38",         deviceCode: "59560b1dcbdc49ba986149b5d48c60ec",         deviceCount: "767",         deviceName: "会所西通道",         eaddress: "219.146.242.46:1443",         ekey: "25891522",         endTime: null,         enote: "59560b1dcbdc49ba986149b5d48c60ec",         ertsp: null,         esecret: "X1LHdWow0bztDywl48j9",         isWarn: 25,         latitude: 39.988395,         left: 834.829268292683,         longitude: 116.496051,         online: 0,         params: {},         parentId: null,         parentName: null,         picName: null,         picUrl: "http://123.57.155.205/comprehensive/72a6894a56cc47458078ec7bc963de30/72a6894a56cc47458078ec7bc963de30-202151721.png",         remark: null,         rootArea: null,         searchValue: null,         spaceName: null,         startTime: null,         top: 714.0044642857142,         typeId: 285,         typeName: "人脸抓拍摄像机",         updateBy: null,         updateTime: null,       }     }   }, } </script> 复制代码

方案2(vlc插件播放)

直接使用rtsp地址来进行播放

(rtsp://admin:ys123456@112.27.144.16:554/Streaming/Channels/2)

使用vlc插件来进行播放,只支持win 并且只能使用ie浏览器或者360兼容模式(vue项目需要配置来兼容ie浏览器)

方案3(ffmpeg转码)

rtsp 视频流,没法在网页中直接播放。在技术支持的群里问了管理员也说没有网页端播放的方案,那只能自己找了。

网上找了一圈很多都是较老的技术了,有用老版本的 chrome 安装 vlc 插件的来播放的,有用 flash 来播放的,而且很多博客抄来抄去都差不多。

最后找到两个看着靠谱的方案:

  • 一种是用 ffmpeg 的 wasm 版直接前端解析转码 rtsp 视频流,转成前端能直接播放的格式

  • 第二种是写一个服务还是调用 ffmpeg 将 rtsp 视频流转码,给前端返回新的视频地址

看到这两个方案我感觉第二种是肯定行得通的,第一种方案暂时不清楚,没怎么用过前端 wasm 技术,只能再去搜索一下。

针对第一种方案,去 =github= 上搜索了一下看看别人有没有用这个的,结果发现暂时还不支持 rtsp 视频流,这是2020年11月4号的回复,后续也没见有啥更新,当他暂时不支持,那就选第二种方案。

方案二的实现就分两步:

  • 首先是转码,想着可以用 java 调用 ffmpeg 来实现,但是要转成什么视频流又没有思路。

  • 第二步就是找前端能播放视频流的插件,转码的最终目的是为了前端网页中能播放,所以先看看有什么播放器能支持什么格式。

之前在很多网站看视频的时候发现网页端播放的视频很多都以 m3u8 结尾的。 之前好奇下载过发现这是一个文件,里面记录了很多视频分片,最后查了知道这个叫 hls,搜索了之后找到了网页播放 hls 的插件 hls.js。 所以现在目标就明确了,首先将 rtsp 转成 hls, 再用 hls.js 来播放。

转码服务

一开始想着自己用 java 调用 ffmpeg 来做转码服务,实现起来应该也不难,但想着这种应该是比较常见的需求,看看有没有轮子。 经过一番搜索,还真找到了一个 rtsp-stream,真的是帮了大忙,节约了好多时间,感谢开源大佬的无私奉献。 这个就是正好完全解决了我的需求,而且是用 Go 写的,部署简单,跨平台,而且作者还很贴心的构建了 Docker 镜像。

根据文档介绍,转换接口是 start 接口,会返回一个 m3u8 的链接,前端播放器只需要播放这个链接就行。 这些都可以交给后端来实现,后端只需要暴露一个接口返回一个 m3u8 的链接即可。流程如下:

前端播放

这里用的是 react, 用 vue 或者别的应该差不多,首先是加载 hls.js 插件

useEffect(() => {   if (hlsRef.current) {     hlsRef.current.detachMedia();   }   setLoading(true);   stopVideo();   const meta = document.createElement('meta');   meta.name = 'referrer';   meta.content = 'no-referrer';   document.getElementsByTagName('head')[0].appendChild(meta);   const script = document.createElement('script');   if (script.readyState) {     // IE     script.onreadystatechange = () => {       if (script.readyState === 'loaded' || script.readyState === 'complete') {         script.onreadystatechange = null;         initVideo();       }     };   } else {     // 其他浏览器     script.onload = () => {       initVideo();     };   }   script.src = 'https://cdn.jsdelivr.net/npm/hls.js@latest';   document.getElementsByTagName('head')[0].appendChild(script);   return () => {     if (hlsRef.current) {       hlsRef.current.detachMedia();     }     stopVideo();   }; }, []); 复制代码

initVideo() 方法就是具体要设置播放的操作,首先是获取播放地址,然后再将 hls 实例绑定到 video 标签上。

function initVideo() {   const requestBody = { cameraCode: '1' };   axios.post('/video/get-url', requestBody).then(res => {     setUrlInfo(res);     const video = document.getElementById('realTime');     const videoSrc = res.uri;     if (window.Hls.isSupported()) {       const hls = new window.Hls();       hlsRef.current = hls;       hls.loadSource(videoSrc);       hls.attachMedia(video);     }     setLoading(false);   }).catch(e => {     // eslint-disable-next-line no-console     console.log('err: ', e.message);     message.error('获取本地视频失败');     setLoading(false);   }); } 复制代码

最后是加上一个 video 标签:

<video id="realTime" controls="controls" className={`${prefixCls}-video`} /> 复制代码

注意事项

  • hls.js 只支持 H.264 编码的视频,不支持 H.265 编码的视频。如果遇到转码没问题,返回的链接能在 vlc 等播放器播放,但在网页中进度条正常,就是黑屏无图像时,可以去 nvr800 查看一下视频编码。

  • 结束播放时可以给服务器发送一个请求停止转码,避免浪费资源。


作者:怕是个胖虎哟
链接:https://juejin.cn/post/7034052661125054471


文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐