公司业务需求,在前端实现海康摄像头的实时播放,参考文档资料后决定使用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流点击播放进行播放。
成功截图
总结
第一次写,见谅
可能写得不太好
如果有啥问题,可以留言交流