Vue中预览HIKVSION海康威视的NVR(网络硬盘录像机)中多个通道(摄像机)的视频:
Vue中预览HIKVSION海康威视的NVR(网络硬盘录像机)中多个通道(摄像机)的视频_霸道流氓气质的博客-CSDN博客_海康nvr网页预览
在上面进行NVR视频预览时采用的是WEB控件开发包,需要电脑安装插件,并且需要浏览器在
兼容模式下预览。
除此之外,还有另一种无插件开发包的方式
截止目前是WEB无插件开发包V3.2
WEB3.2无插件版本开发包,支持高版本谷歌、火狐浏览器,同时需要设备支持Websocket取流。无插件版本需要使用nginx代理服务器。
按照说明可知,需要NVR支持websocket取流。
这里的NVR型号为:DS-8664n-k16
登录到NVR的web页面-配置-系统设置-网络-高级配置-启用websocket
如果有启用WebSokcet选项,则代表可以,为了进一步验证,下载上面的官方demo。
按照官方提供的说明,运行nginx的代理
修改nginx目录下的配置文件
这里只是简单修改了端口号为8000
然后点击start.bat启动nginx,访问
http://127.0.0.1:8000/cn/demo.html
输入NVR对应的地址、用户名、密码等信息,然后点击登录和开始预览
如果官方demo能预览成功,那么就可以使用无插件开发包了。
注:
博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主
关注公众号
霸道的程序猿
获取编程相关电子书、教程推送与免费下载。
1、在demo.html中能实现预览的基础上,只需要根据自己的需要改造即可,
对应的demo以及接口文档说的很清楚。
那么怎么将demo的示例与现有项目进行结合,比如前后端分离的SpringBoot+Vue的项目。
若依前后端分离版本地搭建开发环境并运行项目的教程:
若依前后端分离版手把手教你本地搭建环境并运行项目_霸道流氓气质的博客-CSDN博客_前后端分离项目本地运行
在基于Vue的项目上进行代码改造
后台存储摄像头的相关信息,最需要的就是通道号,拿其来进行预览时传参用
选中摄像头时点击预览按钮,传递通道号参数至预览页面。
前面流程可参考
SpringBoot+Vue+HIKVSION实现摄像头多选并多窗口预览(插件版):
SpringBoot+Vue+HIKVSION实现摄像头多选并多窗口预览(插件版)_霸道流氓气质的博客-CSDN博客_websocket取流
然后根据官方demo引入需要的js等文件
下面主要是预览页面的代码
- <template>
- <el-dialog
- title="视频监控"
- :visible.sync="videoOpen"
- width="800px"
- :append-to-body=false
- @close="videoClose"
- class="video_box"
- :modal=false
- >
- <!-- 摄像头 -->
- <!--视频窗口展示-->
- <div id="playWnd" class="playWnd" ref="playWnd"></div>
- </el-dialog>
- </template>
-
- <script>
- const g_iWndIndex = 0; //可以不用设置这个变量,有窗口参数的接口中,不用传值,开发包会默认使用当前选择窗口
- export default {
- name: "HkVideo1",
- components: {},
- props: {
- channelId: "",
- },
- watch: {},
- data() {
- return {
- isLogin: false,
- videoOpen: false,
- szDeviceIdentify: "", // 设备标识(IP_Port)
- ip: "NVR的ip",
- port: "80",
- username: "NVR的用户名",
- password: "NVR的密码",
- };
- },
- created() {},
- mounted() {},
- destroyed() {},
- methods: {
- // 创建播放实例
- async initPlugin() {
-
- let iRet = window.WebVideoCtrl.I_CheckPluginInstall();
-
- if (-1 == iRet) {
- alert("您还未安装过插件,请安装WebComponentsKit.exe!");
- this.$confirm("是否下载WebComponentsKit.exe插件?", "提示", {
- confirmButtonText: "确定",
- cancelButtonText: "取消",
- type: "warning",
- }).then(() => {
- window.location.href = "/static/HK_3.2/WebComponentsKit.exe";
- });
-
- return;
- }
- // 初始化插件参数及插入插件
- window.WebVideoCtrl.I_InitPlugin(800, 600, {
- bWndFull: true, //是否支持单窗口双击全屏,默认支持 true:支持 false:不支持
- iPackageType: 2,
- //szColorProperty:"plugin-background:0000ff; sub-background:0000ff; sub-border:00ffff; sub-border-select:0000ff", //2:PS 11:MP4
- iWndowType: 1,
- bNoPlugin: true,
- // 窗口选中事件回调
- cbSelWnd: (xmlDoc) => {
- var szInfo = "当前选择的窗口编号:" + g_iWndIndex;
- console.log(szInfo);
- },
- // 窗口双击事件回调
- cbDoubleClickWnd: (iWndIndex, bFullScreen) => {
- var szInfo = "当前放大的窗口编号:" + iWndIndex;
- if (!bFullScreen) {
- szInfo = "当前还原的窗口编号:" + iWndIndex;
- }
- console.log(szInfo);
- },
- // 插件事件回调
- cbEvent: (iEventType, iParam1, iParam2) => {
- if (2 == iEventType) {
- // 回放正常结束
- console.log("窗口" + iParam1 + "回放结束!");
- } else if (-1 == iEventType) {
- console.log("设备" + iParam1 + "网络错误!");
- } else if (3001 == iEventType) {
- this.clickStopRecord(g_szRecordType, iParam1);
- }
- },
- cbRemoteConfig: () => {
- console.log("关闭远程配置库!");
- },
- // 插件初始化完成回调
- cbInitPluginComplete: () => {
- this.$nextTick(() => {
- console.log('窗口', this.$refs.playWnd)
- let isInit = window.WebVideoCtrl.I_InsertOBJECTPlugin('playWnd');
- console.log('isInit', isInit)
-
- // 检查插件是否最新
- if (-1 == window.WebVideoCtrl.I_CheckPluginVersion()) {
- alert("检测到新的插件版本,请对WebComponentsKit.exe进行升级!");
- return;
- } else this.clickLogin();
- })
- },
- });
- },
-
- // 登录
- clickLogin() {
- let { ip, port, username, password } = this;
-
- if ("" == ip || "" == port) {
- return;
- }
-
- this.szDeviceIdentify = ip + "_" + port;
-
- let iRet = window.WebVideoCtrl.I_Login(ip, 1, port, username, password, {
- success: (xmlDoc) => {
- setTimeout(() => {
- this.getChannelInfo();
- this.getDevicePort();
- }, 10);
- },
- error: (status, xmlDoc) => {
- console.log(" 登录失败!", status, xmlDoc);
- },
- });
-
- if (-1 == iRet) {
- this.clickStartRealPlay();
- }
- },
- // 获取通道
- getChannelInfo() {
- if (null == this.szDeviceIdentify) {
- return;
- }
-
- // 模拟通道
- window.WebVideoCtrl.I_GetAnalogChannelInfo(this.szDeviceIdentify, {
- async: false,
- success: (xmlDoc) => {
- },
- error: (status, xmlDoc) => {
- console.log(" 获取模拟通道失败!");
- },
- });
- // 数字通道
- window.WebVideoCtrl.I_GetDigitalChannelInfo(this.szDeviceIdentify, {
- async: false,
- success: (xmlDoc) => {
- },
- error: (status, xmlDoc) => {
- console.log(" 获取数字通道失败!");
- },
- });
- // 零通道
- window.WebVideoCtrl.I_GetZeroChannelInfo(this.szDeviceIdentify, {
- async: false,
- success: (xmlDoc) => {
- },
- error: (status, xmlDoc) => {
- console.log(" 获取零通道失败!");
- },
- });
- },
- // 获取端口
- getDevicePort() {
- if (null == this.szDeviceIdentify) {
- return;
- }
-
- this.port = window.WebVideoCtrl.I_GetDevicePort(this.szDeviceIdentify);
- if (this.port != null) {
- this.clickStartRealPlay();
- return true
- } else {
- console.log(" 获取端口失败!");
- return false
- }
- },
- // 开始预览
- clickStartRealPlay(iStreamType) {
- let wndInfo = window.WebVideoCtrl.I_GetWindowStatus(g_iWndIndex);
-
- let iChannelID = this.channelId; // 通道列表
- let bZeroChannel = false; // 是否播放零通道(下拉框)
- let szInfo = "";
-
- if ("undefined" === typeof iStreamType) {
- iStreamType = 2; // 1主码流 2子码流 3第三码流 4转码码流
- }
-
- if (null == this.szDeviceIdentify) {
- return;
- }
- let startRealPlay = () => {
- window.WebVideoCtrl.I_StartRealPlay(this.szDeviceIdentify, {
- iRtspPort: 554,
- iStreamType: iStreamType,
- iChannelID: iChannelID,
- bZeroChannel: bZeroChannel,
- success: () => {
- szInfo = "开始预览成功!";
- console.log(szInfo);
- },
- error: (status, xmlDoc) => {
- if (403 === status) {
- szInfo = "设备不支持Websocket取流!";
- } else {
- szInfo = "开始预览失败!";
- }
- this.$message.error(szInfo);
- },
- });
- };
-
- if (wndInfo != null) {
- // 已经在播放了,先停止
- window.WebVideoCtrl.I_Stop({
- success: () => {
- startRealPlay();
- },
- });
- } else {
- startRealPlay();
- }
- },
- // 停止预览
- clickStopRealPlay() {
- let oWndInfo = window.WebVideoCtrl.I_GetWindowStatus(g_iWndIndex),
- szInfo = "";
-
- if (oWndInfo != null) {
- window.WebVideoCtrl.I_Stop({
- success: () => {
- szInfo = "停止预览成功!";
- console.log(szInfo);
- },
- error: () => {
- szInfo = "停止预览失败!";
- console.log(szInfo);
- },
- });
- }
- },
- // 全屏
- clickFullScreen() {
- window.WebVideoCtrl.I_FullScreen(true);
- },
- // 停止录像
- clickStopRecord(szType, iWndIndex) {
- if ("undefined" === typeof iWndIndex) {
- iWndIndex = g_iWndIndex;
- }
- var oWndInfo = window.WebVideoCtrl.I_GetWindowStatus(iWndIndex),
- szInfo = "";
-
- if (oWndInfo != null) {
- window.WebVideoCtrl.I_StopRecord({
- success: () => {
- if ("realplay" === szType) {
- szInfo = "停止录像成功!";
- } else if ("playback" === szType) {
- szInfo = "停止剪辑成功!";
- }
- showOPInfo(oWndInfo.szDeviceIdentify + " " + szInfo);
- },
- error: () => {
- if ("realplay" === szType) {
- szInfo = "停止录像失败!";
- } else if ("playback" === szType) {
- szInfo = "停止剪辑失败!";
- }
- showOPInfo(oWndInfo.szDeviceIdentify + " " + szInfo);
- },
- });
- }
- },
- // 查看摄像
- videoChange() {
- this.videoOpen = true;
- this.$nextTick(() => {
- if(!this.isLogin) {
- this.isLogin = true
- this.initPlugin()
- } else {
- this.clickStartRealPlay();
- }
- });
- },
- // 关闭摄像头弹窗
- videoClose() {
- this.videoOpen = false;
- console.log(this.isLogin)
- this.clickStopRealPlay();
- },
- },
- };
- </script>
- <style scoped lang="scss">
- .video_box {
- width: 100%;
- height: 100%;
- }
-
- .plugin {
- width: 100%;
- height: 100%;
- }
-
- .playWnd {
- width: 800px;
- height: 600px;
- margin: 0;
- }
-
- .video_box {
- ::v-deep .el-dialog__body {
- padding: 0 !important;
- }
- }
- </style>
2、关于nginx代理
查看官方示例nginx配置文件中
需要做两部分的代理。最前面的
- location / {
- root "../webs";
- index index.html index.htm;
- }
是其示例demo的静态页面的代理,对应自己的Vue的dist包的代理
对应的线上要改成
- location / {
- root C:\dist;
- try_files $uri $uri/ /index.html;
- index index.html index.htm;
- }
剩下两个代理一个是接口代理,比如调用登录接口时进行反向代理
-
- location ~ /ISAPI|SDK/ {
- if ($http_cookie ~ "webVideoCtrlProxy=(.+)") {
- proxy_pass http://$cookie_webVideoCtrlProxy;
- break;
- }
- }
-
-
这个意思大概是如果是ISAPI或者SDK开头的请求,波浪线代表不忽略大小写,就被被代理到下面的地址。
但是这里会疑问为什么代理配置文件中没有配置任何关于nvr的ip地址的配置,那我代理时是怎么请求接口的。
这里是在中间加一个nginx进行转发,nginx通过请求中的Cookie找到NVR的Ip地址进行转发,包括预览时获取视频流
也是通过这种方式进行websocket代理并获取视频流。
类似如下这张流程图。
所以只要按照官方的nginx的配置文件将自己项目的nginx配置文件进行修改即可
下面提供一个改造之后项目的nginx的配置文件示例
-
- worker_processes 1;
- events {
- worker_connections 1024;
- }
- http {
- include mime.types;
- default_type application/octet-stream;
- sendfile on;
- keepalive_timeout 65;
- map $http_upgrade $connection_upgrade {
- default upgrade;
- '' close;
- }
- server {
- listen 90;
- server_name localhost;
-
- client_max_body_size 300M;
-
- #websocket相关配置
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection "upgrade";
- proxy_set_header X-real-ip $remote_addr;
- proxy_set_header X-Forwarded-For $remote_addr;
-
-
- location / {
- root D:\font\dist;
- try_files $uri $uri/ /index.html;
- index index.html index.htm;
- }
- location /prod-api/ {
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header REMOTE-HOST $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_pass http://服务器ip:8888/;
- }
- 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;
- }
- }
-
- error_page 500 502 503 504 /50x.html;
- location = /50x.html {
- root html;
- }
- }
- }
-
-
前后端项目使用Nginx代理可以参考
若依前后端分离版本,Windows下使用Nginx代理的方式进行部署(全流程,图文教程):
若依前后端分离版本,Windows下使用Nginx代理的方式进行部署(全流程,图文教程)_霸道流氓气质的博客-CSDN博客
预览效果