赞
踩
公司业务需求,在前端实现海康摄像头的实时播放,参考文档资料后决定使用webrtc-streamer来实现rtsp流的播放,在此备份一下,也希望能够帮助到有需要的友友。
1.这里使用的是webrtc-streamer来对rtsp流视频进行转码,从而实现前端界面的播放。因为开始接手项目的时候公司没有流媒体服务器,所以是在本地测试,对本地cpu有一定的压力。你也可以将webrtc打包到docker镜像里运行。
https://wws.lanzouy.com/inBMK08uv02h (webrtc-streamer链接)
2.我这里使用的是videojs包,当然你也可以直接用原生的video标签进行播放。下面附上相关依赖。
{ "name": "videojsdemo", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { "chokidar": "^3.5.3", "core-js": "^3.23.4", "video.js": "^7.18.1", "videojs-contrib-hls": "^5.15.0", "vue": "^2.6.14", "vue-baidu-map": "^0.21.22", "vue-router": "^3.5.1", "vue-webrtc": "^2.0.0", "vueui": "^0.1.4", "vuex": "^3.6.2" }, "devDependencies": { "@babel/core": "^7.12.16", "@babel/eslint-parser": "^7.12.16", "@vue/cli-plugin-babel": "~5.0.0", "@vue/cli-plugin-eslint": "~5.0.0", "@vue/cli-plugin-router": "~5.0.0", "@vue/cli-plugin-vuex": "~5.0.0", "@vue/cli-service": "~5.0.0", "eslint": "^7.32.0", "eslint-plugin-vue": "^8.0.3", "vue-template-compiler": "^2.6.14" } }
1.这是我封装的视频播放的组件,可以按照需求自行修改。
<template> <div> <el-row> <el-col :span="24"> <!-- <map-video :showV="videoShow" :rtspIp="rtspIp"></map-video> --> <div style="height: 40vh"> <!-- this.hackReset = false;//销毁组件 this.$nextTick(() => { this.hackReset = true;//重建组件 }); --> <video :id="videoID" class="video-js" style="object-fit: fill" controls autoplay autobuffer muted preload="auto" ></video> </div> </el-col> </el-row> </div> </template> <script> import "video.js/dist/video-js.css"; import videojs from "video.js"; export default { name: "TanchengkjWebDialogCom", props: { videoID: { type: String, default() { return "videoID"; }, }, rtspIp: { type: String, default() { return ""; }, }, videoShow: { type: Boolean, default() { return false; }, }, videoPlayName: { type: String, default() { return ""; }, }, }, data() { return { dialogFromVisible: this.videoShow, inDate: "", t2: null, getUrl: localStorage.getItem("getUrl"), player: null, webRtcServer: null, webRtcList: [], playerList: [], videoSeting: { language: "zh-CN", autoplay: true, // true/false 播放器准备好之后,是否自动播放 【默认false】 controls: true, // /false 是否拥有控制条 【默认true】,如果设为false ,那么只能通过api进行控制了。也就是说界面上不会出现任何控制按钮 /* height: 100, // 视频容器的高度,字符串或数字 单位像素 比如: height:300 or height:‘300px‘ width: 100, // 视频容器的宽度, 字符串或数字 单位像素*/ loop: false, // /false 视频播放结束后,是否循环播放 muted: true, // /false 是否静音 poster: "", // 播放前显示的视频画面,播放开始之后自动移除。通常传入一个URL preload: "auto", // 预加载 ‘auto‘ 自动 ’metadata‘ 元数据信息 ,比如视频长度,尺寸等 ‘none‘ 不预加载任何数据,直到用户开始播放才开始下载 bigPlayButton: true, }, }; }, watch: { videoShow: { immediate: true, handler: function (value) { this.dialogFromVisible = value; if (value == false) { console.log("视频关闭了!"); // this.playerList[0].pause(); // this.webRtcList[0].webRtcServer.disconnect(); } else { this.$forceUpdate(); console.log("输出一下rtsp:", this.rtspIp); setTimeout(() => { this.getWebRtc(); }, 1000); } }, }, }, mounted() { let that = this; this.initNDate(); let procedure1 = new Promise(function (resolve, reject) { setTimeout(function () { console.log("1执行crtWebRtc成功!"); that.crtWebRtc(); resolve(); }); }); let procedure2 = new Promise(function (resolve, reject) { setTimeout(function () { console.log("2执行getvideo成功!"); that.getVideo(); resolve(); }); }); let procedure3 = new Promise(function (resolve, reject) { setTimeout(function () { console.log("3执行getWebRtc成功!"); that.getWebRtc(); resolve(); }); }); procedure1 .then((data) => { return procedure2; }) .then((data) => { return procedure3; }); // console.log("mounted输出rtspip:", this.rtspIp); // console.log("mounted输出videoID:", this.videoID); // console.log("mounted输出getUrl:", this.getUrl); }, created() {}, methods: { /*设置当前系统时间*/ initNDate: function () { this.t2 = setInterval(() => { this.inDate = new Date().toLocaleString(); }, 1000); }, getVideo() { let that = this; this.playerList[0] = videojs( this.videoID, this.videoSeting, function onPlayerReady() { videojs.log("Your player is ready!"); this.on("loadstart", function () { console.log("开始请求数据 "); }); this.on("progress", function () { console.log("正在请求数据 "); }); this.on("loadedmetadata", function () { console.log("获取资源长度完成 "); }); this.on("canplaythrough", function () { console.log("视频源数据加载完成"); }); this.on("waiting", function () { console.log("等待数据"); }); this.on("play", function () { console.log("视频开始播放"); }); this.on("playing", function () { console.log("视频播放中"); }); this.on("pause", function () { console.log("视频暂停播放"); that.webRtcList[0].webRtcServer.disconnect(); }); this.on("ended", function () { console.log("视频播放结束"); }); this.on("error", function () { console.log("加载错误"); }); this.on("seeking", function () { console.log("视频跳转中"); }); this.on("seeked", function () { console.log("视频跳转结束"); }); this.on("ratechange", function () { console.log("播放速率改变"); }); this.on("timeupdate", function () { console.log("播放时长改变"); }); this.on("volumechange", function () { console.log("音量改变"); }); this.on("stalled", function () { console.log("网速异常"); }); } ); this.playerList.push(this.player); console.log("playerList", this.playerList); console.log("id_", this.playerList[0].id_); }, crtWebRtc() { console.log("webRtcList:", this.webRtcList); for (const item of this.webRtcList) { if (this.videoID == item.name) { return; } } console.log("进来了开始初始化webRtc模块:", this.videoID); this.webRtcServer = new WebRtcStreamer( this.videoID, // "http://192.168.1.166:8000" this.getUrl ); let s = {}; s.name = this.videoID; s.webRtcServer = this.webRtcServer; this.webRtcList.push(s); console.log("this.webrtc哈", this.webRtcList); }, getWebRtc() { console.log("将rtspip传递给webRtcServer.connect:", this.rtspIp); console.log("打印:webrtclist", this.webRtcList); this.webRtcList[0].webRtcServer.connect(this.rtspIp); // for (const item of this.webRtcList) { // if (this.videoID == item.name) { // return; // } // item.webRtcServer.connect(this.rtspIp); // } }, }, beforeDestroy() { console.log("运行销毁前生命周期事件"); // this.webRtcList[0].webRtcServer.disconnect(); // if (this.player) { // this.playerList[0].dispose(); // } }, }; </script> <style scoped> .lf20 { margin-left: 20px; } /* // 覆盖层元素增加可穿透点击事件 */ .el-dialog__wrapper { pointer-events: none; } /* // 弹窗层元素不可穿透点击事件(不影响弹窗层元素的点击事件) */ .el-dialog { pointer-events: auto; } .el-dialog__title { line-height: 24px !important; font-size: 15px !important; color: #303133; } .el-dialog__header { padding: 5px 5px 0px 5px !important; text-align: left; } .el-dialog__headerbtn { position: absolute; top: 5px !important; right: 5px !important; padding: 0; background: 0 0; border: none; outline: 0; cursor: pointer; font-size: 16px; } .el-dialog__body { padding: 2px !important; color: #606266; font-size: 14px; word-break: break-all; height: 40vh; overflow-y: auto; } .el-divider--horizontal { display: block; height: 1px; width: 100%; margin: 0px 0 !important; } .el-menu:hover { opacity: 1 !important; } .ve-line { margin-left: 10px; padding-top: 20px; color: #fff4fd; width: 540px !important; } .el-row { margin-bottom: 2px; &:last-child { margin-bottom: 0; } } .el-col { border-radius: 4px; } .bg-purple-dark { background: #99a9bf; } .bg-purple { background: #d3dce6; } .bg-purple-light { background: #e5e9f2; } .grid-content { border-radius: 4px; min-height: 36px; height: 230px; } .row-bg { background-color: #f9fafc; } .cell-v { display: flex; flex-direction: column; height: 30vh; } .player { height: 100%; } .bk-button-group { color: #00a0e9; } .video-js { width: 100%; height: 100%; } .video-js .vjs-big-play-button { top: 50%; left: 50%; margin-left: -1.5em; margin-top: -1em; } .vjs-loading-spinner:before, .vjs-loading-spinner:after { content: ""; position: absolute; margin: -6px; -webkit-box-sizing: inherit; box-sizing: inherit; width: inherit; height: inherit; border-radius: inherit; opacity: 1; border: inherit; border-color: transparent; border-top-color: white; display: none; } </style>
###1.其实使用webrtc来实现rtsp流视频的播放很简单,只需要注意以下几点:
####1.首先是初始化webrtc服务
打开webrtc-streamer.exe我们可以看到,它这里监听的是8000端口
所以后续我们在初始化webrtcserver的时候需要将你运行项目的地址与8000端口拼接
这边的"video"是video标签的id,因为id是唯一标识
####2.初始化好了webrtc服务后是初始化video播放器了
methods: { playVideo() { this.webRtcServer.connect(this.rtspUrl); // 初始化播放器 this.getVideo(); }, getVideo() { //this.videoSeting let that = this; this.player = videojs( "video", this.videoSeting, function onPlayerReady() { videojs.log("Your player is ready!"); this.on("loadstart", function () { console.log("开始请求数据 "); }); this.on("progress", function () { console.log("正在请求数据 "); }); this.on("loadedmetadata", function () { console.log("获取资源长度完成 "); }); this.on("canplaythrough", function () { console.log("视频源数据加载完成"); }); this.on("waiting", function () { console.log("等待数据"); }); this.on("play", function () { console.log("视频开始播放"); }); this.on("playing", function () { console.log("视频播放中"); }); this.on("pause", function () { console.log("视频暂停播放"); that.webRtcServer.disconnect(); }); this.on("ended", function () { console.log("视频播放结束"); }); this.on("error", function () { console.log("加载错误"); }); this.on("seeking", function () { console.log("视频跳转中"); }); this.on("seeked", function () { console.log("视频跳转结束"); }); this.on("ratechange", function () { console.log("播放速率改变"); }); this.on("timeupdate", function () { console.log("播放时长改变"); }); this.on("volumechange", function () { console.log("音量改变"); }); this.on("stalled", function () { console.log("网速异常"); }); } ); }, },
这里的video同样指的是video播放器的id,与初始化播放器是一对一的关系
这里我将整个代码都贴上来吧,以免有友友看不太清楚
<template> <div class="container"> <div class="control_1"> <a>input http server:</a ><input type="text" v-model="httpServer" placeholder="请输入服务端地址" /> </div> <div class="control_2"> <a>input rtsp url:</a ><input type="text" v-model="rtspUrl" placeholder="请输入rtsp流地址" /> </div> <button style="margin-top: 15px" @click="playVideo"> 准备好了点击播放视频 </button> <div class="videoPlay"> <video id="video" class="video-js" style="object-fit: fill; width: 500px; height: 500px" controls autoplay autobuffer muted preload="auto" ></video> </div> </div> </template> <script> import "video.js/dist/video-js.css"; import videojs from "video.js"; export default { name: "TailwindCSSDaisyUIHomeView", data() { return { httpServer: "", rtspUrl: "", player: null, webRtcServer: null, videoSeting: { language: "zh-CN", autoplay: true, // true/false 播放器准备好之后,是否自动播放 【默认false】 controls: true, // /false 是否拥有控制条 【默认true】,如果设为false ,那么只能通过api进行控制了。也就是说界面上不会出现任何控制按钮 /* height: 100, // 视频容器的高度,字符串或数字 单位像素 比如: height:300 or height:‘300px‘ width: 100, // 视频容器的宽度, 字符串或数字 单位像素*/ loop: false, // /false 视频播放结束后,是否循环播放 muted: true, // /false 是否静音 poster: "", // 播放前显示的视频画面,播放开始之后自动移除。通常传入一个URL preload: "auto", // 预加载 ‘auto‘ 自动 ’metadata‘ 元数据信息 ,比如视频长度,尺寸等 ‘none‘ 不预加载任何数据,直到用户开始播放才开始下载 bigPlayButton: true, }, }; }, mounted() { var a = `${window.location.href}`; var b = a.split(":"); this.httpServer = `${b[0]}:${b[1]}:8000/`; console.log("httpServer", this.httpServer); // 初始化webrec服务 this.webRtcServer = new WebRtcStreamer("video", this.httpServer); }, methods: { playVideo() { this.webRtcServer.connect(this.rtspUrl); // 初始化播放器 this.getVideo(); }, getVideo() { //this.videoSeting let that = this; this.player = videojs( "video", this.videoSeting, function onPlayerReady() { videojs.log("Your player is ready!"); this.on("loadstart", function () { console.log("开始请求数据 "); }); this.on("progress", function () { console.log("正在请求数据 "); }); this.on("loadedmetadata", function () { console.log("获取资源长度完成 "); }); this.on("canplaythrough", function () { console.log("视频源数据加载完成"); }); this.on("waiting", function () { console.log("等待数据"); }); this.on("play", function () { console.log("视频开始播放"); }); this.on("playing", function () { console.log("视频播放中"); }); this.on("pause", function () { console.log("视频暂停播放"); that.webRtcServer.disconnect(); }); this.on("ended", function () { console.log("视频播放结束"); }); this.on("error", function () { console.log("加载错误"); }); this.on("seeking", function () { console.log("视频跳转中"); }); this.on("seeked", function () { console.log("视频跳转结束"); }); this.on("ratechange", function () { console.log("播放速率改变"); }); this.on("timeupdate", function () { console.log("播放时长改变"); }); this.on("volumechange", function () { console.log("音量改变"); }); this.on("stalled", function () { console.log("网速异常"); }); } ); }, }, }; </script> <style> .control_1 { display: block; /* margin-top: 20px; */ } .control_2 { display: block; margin-top: 15px; } .videoPlay { width: 500px; height: 500px; margin: 15px auto; } .video-js { width: 100%; height: 100%; } .video-js .vjs-big-play-button { top: 50%; left: 50%; margin-left: -1.5em; margin-top: -1em; } .vjs-loading-spinner:before, .vjs-loading-spinner:after { content: ""; position: absolute; margin: -6px; -webkit-box-sizing: inherit; box-sizing: inherit; width: inherit; height: inherit; border-radius: inherit; opacity: 1; border: inherit; border-color: transparent; border-top-color: white; display: none; } </style>
这个案例是通过input的双向绑定原理,将当前项目的url与8000端口拼接,然后输入rtsp流点击播放进行播放。
成功截图
第一次写,见谅
可能写得不太好
如果有啥问题,可以留言交流
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。