前端vue页面实时播放海康威视监控视频 3种方法
用于web播放海康威视rtsp://admin:ca123456@192.168.64.82:554//Streaming/Channels/1 格式的视频。
注意:前2个目前都在windows上使用,服务器安装部署多多少少有些问题,如果急需解决则直接看第3个方法。
1、WebRtcStreamer
github:https://github.com/mpromonet/webrtc-streamer/releases 但是经常打不开 ,如果有需要私信我,因为太忙了没时间放网盘,见谅
里面有windows版也有linux版的
在本地使用,进入exe目录
启动,默认是8000
打开测试
默认是index.html,我们可以再html里面新建一个test.html修改一下
test.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
<html>
<head>
<link rel="stylesheet" type="text/css" href="styles.css" />
<script src="libs/adapter.min.js"></script>
<script src="webrtcconfig.js"></script>
<script src="webrtcstreamer.js"></script>
<script>
let options = webrtcConfig.options;
let codec = webrtcConfig.codec;
console.log(codec);
window.onload = function () {
this.webRtcServer = new WebRtcStreamer("video", webrtcConfig.url);
webRtcServer.connect(
"rtsp://stream.strba.sk:1935/strba/VYHLAD_JAZERO.stream",
"",
options,
undefined,
codec
);
};
window.onbeforeunload = function () {
this.webRtcServer.disconnect();
};
</script>
</head>
<body>
<video id="video" muted playsinline controls></video>
<!-- <iframe src="http://127.0.0.1:8000/webrtcstreamer.html?video=rtsp://stream.strba.sk:1935/strba/VYHLAD_JAZERO.stream"></iframe> -->
</body>
</html>
|
其中我们测试的rtsp是 rtsp://stream.strba.sk:1935/strba/VYHLAD_JAZERO.stream
如果不知道网页上的rtsp是否可用,可以下载vlc-3.0.20-win64 播放器打开网络串流测试
可以播放即能使用。
打开test页面, 播放成功。
这个使用起来还是比较简单,但是linux服务器安装比较麻烦,目前还在实践中。
2、node websockt+ffmpeg转码成flv
node服务端调用ffmpeg转码然后前端使用
服务端
serve.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
const WebSocket = require('ws')
const webSocketStream = require('websocket-stream/stream')
// const ffmpeg = require('fluent-ffmpeg')
const ffmpegInstaller = require('@ffmpeg-installer/ffmpeg');
const ffmpeg = require('fluent-ffmpeg');
ffmpeg.setFfmpegPath(ffmpegInstaller.path);
// 建立WebSocket服务
const wss = new WebSocket.Server({ port: 8888, perMessageDeflate: false })
// 监听连接
wss.on('connection', handleConnection)
// 连接时触发事件
function handleConnection (ws, req) {
// 获取前端请求的流地址(前端websocket连接时后面带上流地址)
const url = req.url.slice(1)
// 传入连接的ws客户端 实例化一个流
const stream = webSocketStream(ws, { binary: true })
// 通过ffmpeg命令 对实时流进行格式转换 输出flv格式
const ffmpegCommand = ffmpeg(url)
.addInputOption('-analyzeduration', '100000', '-max_delay', '1000000')
.on('start', function () { console.log('Stream started.') })
.on('codecData', function () { console.log('Stream codecData.') })
.on('error', function (err) {
console.log('An error occured: ', err.message)
stream.end()
})
.on('end', function () {
console.log('Stream end!')
stream.end()
})
.outputFormat('flv').videoCodec('copy')
// .outputFormat('flv').videoCodec('copy').noAudio() // 取消音频输出
stream.on('close', function () {
ffmpegCommand.kill('SIGKILL')
})
try {
// 执行命令 传输到实例流中返回给客户端
ffmpegCommand.pipe(stream)
} catch (error) {
console.log(error)
}
}
|
创建一个package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
{
"name": "rtsp-vue-server",
"version": "1.0.0",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"@ffmpeg-installer/ffmpeg": "^1.1.0",
"express-ws": "^5.0.2",
"fluent-ffmpeg": "^2.1.2",
"node-media-server": "^2.6.2",
"node-rtsp-stream": "^0.0.9",
"websocket-stream": "^5.5.2"
}
}
|
安装完必须的包后可以通过npm start启动。
启动完成。
然后下载一个windows版本ffmpeg进行转码
ffmpeg-N-103197-gbff7d662d7-win64-gpl 官网:https://www.ffmpeg.org/download.html#build-windows
解压完成后通过环境变量加入path,否则会找不到
测试 ffmpeg -v
然后前端使用
1
2
3
4
|
npm install flv.js --save
需要安装flv
import flvjs from
'flv.js';
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
<video
muted="muted"
controls
width="600"
height="600"
style="width:100%; height:100%"
ref="video"></video>
url: 'rtsp://stream.strba.sk:1935/strba/VYHLAD_JAZERO.stream',
createVideo() {
if (flvjs.isSupported()) {
const videoElement = this.$refs.video;
this.flvPlayer = flvjs.createPlayer(
{
type: "flv",
// isLive: false,
// hasAudio: false,
url: "ws://localhost:8888/" + this.url,
},
{
cors: true, // 是否跨域
// enableWorker: true, // 是否多线程工作
enableStashBuffer: false, // 是否启用缓存
// stashInitialSize: 128, // 缓存大小(kb) 默认384kb
autoCleanupSourceBuffer: true, // 是否自动清理缓存
fixAudioTimestampGap: false, //false才会音视频同步
}
);
this.flvPlayer.attachMediaElement(videoElement);
this.flvPlayer.load();
this.flvPlayer.play();
// 报错重连
this.flvPlayer.on(flvjs.Events.ERROR, (errType, errDetail) => {
console.log("errorType:", errType);
console.log("errorDetail:", errDetail);
this.play();
});
}
},
// 销毁video
destoryVideo() {
if (this.flvPlayer) {
this.flvPlayer.pause();
this.flvPlayer.unload();
this.flvPlayer.detachMediaElement();
this.flvPlayer.destroy();
this.flvPlayer = null;
}
},
// 重播/播放
play() {
this.destoryVideo();
this.createVideo();
},
|
加载片刻后即可播放
目前也还在实践linux部署。
3.海康威视无插件开发包
前提需要确保摄像头可以支持websocket,可以登录 海康威视的平台查看网络配置。
需要注意的是,海康威视会有一个大的ip以及子ip摄像头,需要登录包含所有的摄像头ip去查看。
ex:
ip1: 192.168.64.214 登录之后可以查看所有通道下的摄像头视频
ip2: 192.168.64.xx 登录之后只能查看一个摄像头的视频 与之对应的视频地址:rtsp://admin:ca123456@192.168.64.xx:554//Streaming/Channels/1
这是需要注意的,我当时就是使用了ip2测试,但是一直播放不了,但使用ip1即可。
首先下载开发包
https://open.hikvision.com/download/5cda567cf47ae80dd41a54b3?type=20&id=4c945d18fa5f49638ce517ec32e24e24
下载完成解压如下:
其中webs里面会有测试demo nginx是用于起服务的以及连接websocket ,其中nginx的配置需要拷贝到服务器上的nginx里面。
doc里面还会有一些问题。如下:
因为看到了不支持本地配置 所以我并没有在本地测试,可以直接在服务器上测试demo,首先确保服务器可以连到摄像头对应的ip。
服务器测试:
配置nginx 只需要配置指向web以及sdk和websocket的配置
location / {
root “usr/wen/webs”;
index index.html index.htm;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
location ~ /ISAPI|SDK/ {
if ($http_cookie ~ "webVideoCtrlProxy=(.+)") {
proxy_pass http://$cookie_webVideoCtrlProxy;
break;
}
}
location ^~ /webSocketVideoCtrlProxy {
#web socket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
if ($http_cookie ~ "webVideoCtrlProxyWs=(.+)") {
proxy_pass http://$cookie_webVideoCtrlProxyWs/$cookie_webVideoCtrlProxyWsChannel?$args;
break;
}
if ($http_cookie ~ "webVideoCtrlProxyWss=(.+)") {
proxy_pass http://$cookie_webVideoCtrlProxyWss/$cookie_webVideoCtrlProxyWsChannel?$args;
break;
}
}
|
然后再服务器上打开对应的ip进行测试。
测试的流程可以根据开发包里面的pdf来操作
如果没有什么问题的话,就可以打开,其中也可以根据里面的操作信息提示判断一些问题。
点击登录,如果可以登录成功表明以及连上了。
然后点开始预览就会去获取通道的一些信息,有零通道 模拟通道 数字通道等,就可以看到摄像头到底使用的哪一个通道,倒是后集成到前端时我们就可以去掉一些通道。
如果获取通道都失败可能表明 nginx没配置好 或者 摄像头是否开启了或者是否支持websocket等原因。
测试结果因为当时在内网没有截图,所以无法展现。
如果测试通过了,即可集成到vue项目中了。
按照demo的引入方式,我们需要引入这些内容
1
2
3
4
5
|
<script type="text/javascript" src="/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="/codebase/encryption/AES.js"></script>
<script type="text/javascript" src="/codebase/encryption/cryptico.min.js"></script>
<script type="text/javascript" src="/codebase/encryption/crypto-3.1.2.min.js"></script>
<script type="text/javascript" id="videonode" src="/codebase/webVideoCtrl.js"></script>
|
在video页面使用
新建一个webVideo.js 配置初始化连接海康的文件 // 初始化插件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
|
export function WebVideo() {
this.g_iWndIndex = 0;
this.szDeviceIdentify = "";
this.deviceport = "8000";
this.rtspPort = "554";
this.channels = [];
this.ip = "192.168.64.214";
this.port = "80";
this.username = "admin";
this.password = "ca123456";
this.idchannel = 1; //默认的通道id
this.init = function (ip, username, password, idchannel) {
this.ip = ip;
this.username = username;
this.password = password;
this.idchannel = idchannel; //传进来的通道
// var self = this
// 检查插件是否已经安装过
// var iRet = WebVideoCtrl.I_CheckPluginInstall();
// if (-1 == iRet) {
// alert("您还未安装过插件,双击开发包目录里的WebComponentsKit.exe安装!");
// return;
// }
// 初始化插件参数及插入插件
WebVideoCtrl.I_InitPlugin(454, 315, {
szColorProperty:
"plugin-background:#102749; sub-background:#102749; sub-border:#18293c; sub-border-select:red",
bWndFull: true, // 全屏
// iPackageType: 2,
iWndowType: 1, //分屏
bNoPlugin: true, // 支持无插件
cbInitPluginComplete: function () {
WebVideoCtrl.I_InsertOBJECTPlugin("divPlugin");
},
});
};
// 登录
this.clickLogin = function () {
var self = this;
if ("" == self.ip || "" == self.port) {
return;
}
self.szDeviceIdentify = self.ip + "_" + self.port;
console.log(self.idchannel);
WebVideoCtrl.I_Login(self.ip, 1, self.port, self.username, self.password, {
success: function (xmlDoc) {
setTimeout(function () {
console.log("登录成功"); //登录完成之后需要获取该ip下所有通道,可以通过demo测试查看使用的哪一个通道 我这里用的是数字通道 有的可能是模拟通道 // 由于我已经知晓了所有通道信息 我就没有去吊数字通道
// self.getChannelInfoData();
// self.getDevicePort();
}, 10);
setTimeout(function () {
self.clickStartRealPlay();
}, 5000);
},
error: function (status, xmlDoc) {
console.log("登录失败");
},
});
};
// 退出
this.clickLogout = function () {
var self = this;
self.channels = [];
var szDeviceIdentify = $("#ip").val(),
szInfo = "";
if (null == szDeviceIdentify) {
return;
}
// if (null == self.szDeviceIdentify) {
// return;
// }
// var iRet = WebVideoCtrl.I_Logout(self.szDeviceIdentify);
var iRet = WebVideoCtrl.I_Logout(szDeviceIdentify);
if (0 == iRet) {
console.log("退出成功");
// self.getChannelInfoData();
// self.getDevicePort();
}
};
// 获取通道
this.getChannelInfo = function () {
var self = this;
self.channels = [];
if (null == self.szDeviceIdentify) {
return;
}
// 模拟通道
WebVideoCtrl.I_GetAnalogChannelInfo(self.szDeviceIdentify, {
async: false,
success: function (xmlDoc) {
var oChannels = $(xmlDoc).find("VideoInputChannel");
console.log(oChannels);
$.each(oChannels, function (i) {
var id = $(this).find("id").eq(0).text(),
name = $(this).find("name").eq(0).text();
if ("" == name) {
name = "Camera " + (i < 9 ? "0" + (i + 1) : i + 1);
}
self.channels.push({
id: id,
name: name,
});
});
console.log("获取模拟通道号成功");
},
error: function (status, xmlDoc) {
console.log("获取模拟通道号失败");
},
});
};
//数字通道
this.getChannelInfoData = function () {
var self = this;
self.channels = [];
if (null == self.szDeviceIdentify) {
return;
}
WebVideoCtrl.I_GetDigitalChannelInfo(self.szDeviceIdentify, {
async: false,
success: function (xmlDoc) {
var oChannels = $(xmlDoc).find("InputProxyChannelStatus");
console.log(oChannels);
$.each(oChannels, function (i) {
var id = $(this).find("id").eq(0).text(),
name = $(this).find("name").eq(0).text();
if ("" == name) {
name = "Camera " + (i < 9 ? "0" + (i + 1) : i + 1);
}
self.channels.push({
id: id,
name: name,
});
});
console.log("获取模拟通道号成功");
},
error: function (status, xmlDoc) {
console.log("获取模拟通道号失败");
},
});
};
// 显示回调信息
this.showCBInfo = (szInfo) => {
szInfo =
"<div>" + szInfo +"</div>";
$("#title-video").html(szInfo);
};
// 获取端口
this.getDevicePort = function () {
var self = this;
if (null == self.szDeviceIdentify) {
return;
}
var oPort = WebVideoCtrl.I_GetDevicePort(self.szDeviceIdentify);
if (oPort != null) {
self.deviceport = oPort.iDevicePort;
self.rtspPort = oPort.iRtspPort;
}
console.log("获取端口号成功");
};
// 开始预览
this.clickStartRealPlay = function () {
var self = this;
var oWndInfo = WebVideoCtrl.I_GetWindowStatus(self.g_iWndIndex),
// iChannelID = self.channels[0].id
iChannelID = self.idchannel;
console.log(self.channels);
if (null == self.szDeviceIdentify) {
return;
}
var startRealPlay = function () {
WebVideoCtrl.I_StartRealPlay(self.szDeviceIdentify, {
iChannelID: iChannelID,
bZeroChannel: false,
iStreamType: 2,
success: function () {
console.log("预览成功");
self.showCBInfo("")
},
error: function (status, xmlDoc) {
if (403 === status) {
console.log("设备不支持Websocket取流");
} else {
console.log("预览失败");
}
},
});
};
if (oWndInfo != null) {
// 已经在播放了,先停止
WebVideoCtrl.I_Stop({
success: function () {
startRealPlay();
},
});
} else {
startRealPlay();
}
};
// 停止预览
this.clickStopRealPlay = function (id = 0) {
var self = this;
var oWndInfo = WebVideoCtrl.I_GetWindowStatus(self.g_iWndIndex);
if (oWndInfo != null) {
WebVideoCtrl.I_Stop({
success: function () {
// console.log("停止预览成功");
// alert("停止预览成功");
// console.log("开始登出");
self.showCBInfo("停止预览中,请稍后...")
// self.clickLogout();
if (id > 0) {
self.idchannel = id;
self.clickStartRealPlay();
}
},
error: function () {
console.log("停止预览失败");
},
});
}
};
}
|
在页面中使用:
正常播放视频的逻辑是,先初始化->登录->获取通道->选择通道播放
其中通道我们可以在demo测试那里知道到底用的哪一个通道,甚至我们可以直接把通道复制下来写死,正如下方所示:
通道格式是xml,如下:
我们配置数据如下:
1
2
3
4
5
6
7
8
9
10
11
|
{ id: 1, name: "CALY涂装排气塔4" },
{ id: 2, name: "CALY储运车场H库" },
{ id: 3, name: "CALY涂装RTO4" },
{ id: 4, name: "CALY涂装VOC内4" },
{ id: 5, name: "CALY涂装VOC外4" },
{ id: 6, name: "CALY废水站槽体A4" },
{ id: 7, name: "CALY涂装危废间4" },
{ id: 8, name: "CALY废水站槽体B4" },
{ id: 9, name: "CALY排气塔东侧3" },
{ id: 10, name: "CALY一厂污水处理站排水槽" },
{ id: 11, name: "CALY一厂污水处理过滤池" },这就是该ip下所有的通道,我们只需要通道id用于播放。如果你知晓了通道,就可以不用再获取通道信息。正如我上面的js一样,我注释调了获取通道的内容,我直接将通道传入。
|
this.idchannel = 1;
this.init = function (ip, username, password, idchannel) {
this.ip = ip;
this.username = username;
this.password = password;
this.idchannel = idchannel;
1
|
我默认就播放第一个通道,如果你需要真实的不写死的话就可以直接去获取通道,然后选择其中的来播放。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
<template>
<Backdrop :title="'轨道巡查视频'">
<template #Assembly>
<div class="on-line-monitoring-video">
<div class="title" id="title-video"></div>
<el-icon
class="iconswitch"
@click="showChannelList = !showChannelList"
title="切换视频"
><Switch
/></el-icon>
<div class="srollList" ref="srollListRef" v-show="showChannelList">
<div
v-for="(item, key) in channelList"
:ref="'srollDiv' + key + 'ref'"
:title="item.name"
:key="key"
@click="selectVideo(item.id)"
>
<span :class="activeIndex == item.id ? 'active' : ''">{{
item.name
}}</span>
</div>
</div>
<div
id="divPlugin"
class="divPlugin"
style="width: 100%; height: 100%; overflow-y: auto"
/>
</div>
</template>
</Backdrop>
</template>
<script>
import { ref } from "vue";
import { WebVideo } from "@/utils/webVideo.js";
export default {
name: "OnLineMonitoringVideo",
data() {
return {
activeIndex: 1,
webVideoPlayer: null,
hkvInfo: {
ip: "192.168.64.214", //ip
username: "admin", //用户名
password: "ca123456", //密码
},
showChannelList: false, //所有的通道list
channelList: [
{ id: 1, name: "CALY涂装排气塔4" },
{ id: 2, name: "CALY储运车场H库" },
{ id: 3, name: "CALY涂装RTO4" },
{ id: 4, name: "CALY涂装VOC内4" },
{ id: 5, name: "CALY涂装VOC外4" },
{ id: 6, name: "CALY废水站槽体A4" },
{ id: 7, name: "CALY涂装危废间4" },
{ id: 8, name: "CALY废水站槽体B4" },
{ id: 9, name: "CALY排气塔东侧3" },
{ id: 10, name: "CALY一厂污水处理站排水槽" },
{ id: 11, name: "CALY一厂污水处理过滤池" },
],
};
},
mounted() {
this.initVideoPlay();
},
beforeDestroy() {
// this.stopVideoPlay();
this.webVideoPlayer.clickStopRealPlay();
},
methods: {
selectVideo(key) { //切换视频 先停止再播放下一个
this.activeIndex = key;
this.stopVideoPlay(key);
// this.initVideoPlay();
},
initVideoPlay() {
if (this.webVideoPlayer == null) this.webVideoPlayer = new WebVideo();
this.$nextTick(() => {
this.webVideoPlayer.init(
this.hkvInfo.ip,
this.hkvInfo.username,
this.hkvInfo.password,
this.activeIndex
);
this.webVideoPlayer.clickLogin();
});
},
stopVideoPlay(key) {
this.webVideoPlayer.clickStopRealPlay(key);
},
},
};
</script>
<style lang="less" scoped>
.iconswitch {
position: absolute;
left: 0px;
top: 50%;
font-weight: bold;
cursor: pointer;
}
#title-video {
color: red;
height: 20px;
position: absolute;
top: 0px;
text-align: center;
width: 100%;
padding: 10px;
}
</style>
|
然后部署到服务器上就可以正常播放第一个视频了,但是目前切换视频很慢还没有解决。
其中关闭上一个视频的操作很慢,主要是WebVideoCtrl.I_Stop 这个方法特别慢,内置的无法知晓。