Hikvison对接NVR实现WEB无插件开发包实现前端视频预览(htmlvuenginx代理)
Posted 霸道流氓气质
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hikvison对接NVR实现WEB无插件开发包实现前端视频预览(htmlvuenginx代理)相关的知识,希望对你有一定的参考价值。
场景
Vue中预览HIKVSION海康威视的NVR(网络硬盘录像机)中多个通道(摄像机)的视频:
Vue中预览HIKVSION海康威视的NVR(网络硬盘录像机)中多个通道(摄像机)的视频_霸道流氓气质的博客-CSDN博客_海康nvr网页预览
在上面进行NVR视频预览时采用的是WEB控件开发包,需要电脑安装插件,并且需要浏览器在
兼容模式下预览。
除此之外,还有另一种无插件开发包的方式
截止目前是WEB无插件开发包V3.2
WEB3.2无插件版本开发包,支持高版本谷歌、火狐浏览器,同时需要设备支持Websocket取流。无插件版本需要使用nginx代理服务器。
按照说明可知,需要NVR支持websocket取流。
验证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博客
预览效果
Windows远程桌面实现之十二:桌面屏幕通过ONVIF协议与NVR等监控录像设备对接,以及进一步增强直播功能
by fanxiushu 2021-10-27 转载或引用请注明原始作者。
标题还是取名叫 “Windows远程桌面实现“,
其实经过全面的移植,xdisp_virt程序已经支持 macOS系统,各类linux发行版,
(iOS也移植了,只是发布iOS的程序实在是麻烦,所以并没发布出来,
暂时也没有对应的Android手机,所以Android系统暂时也没有移植)
所以xdisp_virt已经不再是单一的Windows版本的程序了,标题中还保持”Windows“,是因为讲述这个系列的文章比较多。
本文讲述在xdisp_virt程序中添加新的功能:ONVIF监控类控制协议。
没有做过安防监控类的人,可能对ONVIF协议比较陌生,其实我也没在安防监控类的行业中混过。
之所以想起在xdisp_virt中添加ONVIF,一是有江郎才尽之感,都不知道还能在xdisp_virt中添加什么功能,
二是有人询问xdisp_virt是否能添加ONVIF功能,记得最早是好像两三年前就有人问过,
只是当时无暇顾及,而且感觉在当时做的远程桌面中添加ONVIF会显得不伦不类,
可是当把直播功能加进xdisp_virt之后,也就不再这么想了(反正现在已经是个集成多种功能的大杂烩了)。
说起安防监控,大部分人可能陌生,但是却处处出现在我们的身边,可以说是无孔不入。
比如马路上的大大小小的各种功能的摄像头,用于监控各种车辆的通行状况,
就连我们生活的小区里的公共区域里,也会看到各类摄像头。
电梯里的摄像头,楼层过道里的摄像头,公交车里的摄像头,商场里的摄像头,等等。。。
以上列举的只是一部分的情况,而且是那种正规的合法的使用场所。
还有许多不那么正规,甚至是违法使用的地方,
比如酒店房间里偷偷安装摄像头,比如厕所里安装摄像头,比如比较变的老板在公司里安装摄像头监控员工,等等。。。
不管怎么说,我们能直观感受到是摄像头这么一个实物,
但是摄像头摄录的视频如何处理,如何传输和保存。视频的数据量都是很大的,
即使经过各种编码压缩,视频数据量也是相当可观的,因此安防监控的核心工作是如何处理这些数据。
摄像头都是工作在第一线,他们距离控制中心的距离往往都是比较远的,
它们采集的视频如何传输,
传统的比如VGA,HDMI,USB,这些数据线可以传输原始视频信号,
但是传输的距离非常有限,顶多一两米就会出现严重信号衰减,而且造价比较贵,
比如小区里,公共区域的摄像距离监控室的距离不止几百米远,
而马路上的摄像头距离监控室就更加远了,几公里甚至几十公里都有可能。
而采用网络传输,就能解决所有这些问题,因此IPC(IP Camera)网络摄像机孕育而生。
IPC网络摄像机 = 传统摄像头 + 一个简单的嵌入式集成系统用于把视频编码成H264或H265,
然后通过网络流媒体传输协议把视频信号传输到控制中心。IPC一般都是采用RTSP流协议传输视频和音频信号。
以上说的使用RTSP协议,只是用来传输视频和音频数据的。
还有一个重要的内容:各种控制信息如何传递,有时需要远控控制摄像头转换方向进行摄录,
而且RTSP需要播放地址的,这个RTSP地址信息如何得知,等等。。。
于是早在2008年,就有几家公司组成一个联盟,共同指定了一套控制协议,目的就是为了让各个厂商生产的IPC和控制端能互相接入。
这个控制协议就是我们这篇文件讲述的ONVIF协议。而这个控制端就是NVR(Network Video Recoder)。
有时可能有这样的一些需求,需要把工作电脑的屏幕录制下来,尤其是一些重要的操作内容。
当然我们也可以直接录制成本地录像文件。
但是录制到 NVR 更方便处理和存储,也更加安全,因此这就是在xdisp_virt中为何要实现ONVIF的原因了。
xdisp_virt只是实现RTSP服务端还不够,还需要ONVIF控制监控协议,这样才能更好的接入通用的 NVR 设备。
以前的版本的 xdisp_virt 已经实现了RTSP流协议,当时是作为直播服务端实现的其中一个协议。
完全是是自己实现的RTSP协议,并且只支持TCP传输。具体可以去查询我的这个系列文章的上一篇文章的描述。
链接地址:Windows远程桌面实现之十一:桌面屏幕通过各种直播服务端直播(RTSP, RTMP, HTTP-FLV, HLS)_fanxiushu的专栏-CSDN博客
本以为这次添加ONVIF协议不用修改RTSP代码。
但是测试发现我自己实现的RTSP协议好像在有些ONVIF测试客户端不能正常播放音视频。
(主要是使用 ONVIF Device Manger 工具进行ONVIF测试的。)
具体原因也不想去查找了,于是决定移植一个兼容性更强的RTSP服务端,于是找到了 live555 开源代码。
这是个非常古老的开源项目,10几年前就已经出现了,
但是他的代码风格并不是我喜欢的,而且可能出现的问题也比较多。
它到处都在使用C++的类,各种封装的CLASS绕来绕去的,一不留神就能把人给绕晕。
本来很简单的功能,使用纯C语言很方便就能实现和描述的。
如果不加控制的使用C++的类来描述,绝对能把最简单的功能绕成世界上最复杂的。
这就是我对这些C++的封装类最坏的印象。
现在把移植 live555发现的问题和修改办法写下来,方便看到这篇文章也在做同样移植的有一点帮助:
其实最主要的问题是出现在live555底层网络通信上(它实现的底层通讯框架实在不敢恭维)
1,首先 live555 使用select进行网络基础通讯,而且只在一个大线程里处理所有客户端的连接。
因此不要指望live555能承担大量客户端的连接
(其实音视频数据量本来就大,能承担上百个客户端播放而不占光网络带宽就已经很不错了)。
windows端编译live555 需要修改 FD_SETSIZE 宏,否则默认只能支持64个。linux端默认就是1024,倒不必修改。
2,OutPacketBuffer::maxSize 这个参数尽量设置大点,比如设置2M,因为现在H264编码都是很大分辨率和超清图像,
会占用很多空间,live源码中设置的值太小。
3,RTSP默认是使用UDP传输音视频数据, 如果把 live555 改成TCP传输音视频数据,有可能长期出现花屏,
RTPInterface::sendDataOverTCP 函数中,send有可能会失败,然后他居然再把non-block的socket转成阻塞模式再发送,
这本身就是单线程的大循环,这么做会阻塞其他客户端的的数据的,反正它这个处理办法实在不咋地。
为了尽量让系统自己处理发送数据,我们可以把TCP这个连接的SendBuffer设置大些,比如设置 1M大小,
使用live提供的increaseSendBufferTo 函数,或者直接操作 setsocketopt 的 SO_SNDBUF命令。
具体做法就是在 RTSPServer::createNewClientConnection 函数中对建立连接的clientSocket进行修改,
或者申明一个myRTSPServer类继承自 RTSPServer 来修改 。
增大socket的发送缓存也只是折中的办法,遇到网络不好的情况下,依然会出现很多跟网络通信相关的问题。
不过似乎视频传输本身就需要良好的网络,否则本身就会造成视频播放的不稳定。
4,windows平台中,live并没有设置 SO_KEEPALIVE,因此同样的,我们也得在RTSPServer::createNewClientConnection 函数中进行修改。
设置 SO_KEEPALIVE会让死连接能及时被发现和断开。linux平台倒是不用修改,live已经修改好了。
5,这个问题也比较严重, live里边 session和connect是分开的,具体就是 RTSPClientSession和RTSPClientConnection类。
播放器连接上来之后,首先RTSPClientConnection会被建立,然后根据播放情况RTSPClientSession会被创建。
当播放器正常停止播放时候,RTSPClientConnection和RTSPClientSession都能正常清除,
但是当网络异常,播放器非正常退出时候,RTSPClientConnection能正常退出,但是RTSPClientSession却还在,
并且还在傻傻的拼命的通过UDP传输数据给客户端播放器,而客户端播放器早就异常退出了。
原本以为有个超时可以控制,结果遇到这种情况超时也没用,会一直这么传输,没有停止的迹象。
实在没办法,只好继承RTSPClientSession和RTSPClientConnection两个类,然后记录下他们的关联关系。
然后在RTSPClientConnect析构函数中删除掉没有正常删除的 RTSPClientSession类。
大致发现以上这些问题,基本都是关于网络通信方面的,
因此如果你移植live555,发现视频播放有问题,基本可以考虑是底层网络通信出现了问题。
当然xdisp_virt是实时直播流,而live555默认的是基于视频文件的,一个视频文件的编码格式是固定的,
而直播流可能随时在变。因此我们得重载live555里的 sdpLines 函数,自己构造动态的 SDP 描述信息。
自己构造SDP描述信息也是费时费力的事情。
经过这么一通的对live555修改,与我们自己完全开发RTSP协议能有多大差别!
只是可能自己开发RTSP有可能那些协议格式内容没有考虑到,从而造成兼容性问题。
再来看看ONVIF协议的开发。
ONVIF是基于WebService的,具体就是在HTTP协议基础上,
通过SOAP协议(类似RPC远程过程调用,建立在XML格式之上)进行通讯的。
SOAP协议也并不是我喜欢的协议格式,这玩意和XML一样,属于那种又笨重,处理起来又麻烦的东西。
而且很多简单的场所,简单的使用一行行的文本来描述就能解决的问题,非要搞个XML或者SOAP通讯充当冤大头!
这里采用开源的 gSOAP, ONVIF协议使用 官网(Home - ONVIF Mandarin)的wsdl 来生成 C语言格式的代码。
官网上的wsdl有很多,而我这里只需要 devicemgmt.wsdl,media.wsdl, remotediscorvry.wsdl 三个wsdl就可以了。
下面以linux 平台下如何生成基于wsdl的 ONVIF 代码为例。
1,首先建立 SOAP 编译环境。
从网上下载 开源gSOAP库,放到某个目录中,比如 /home
然后就是解压到某个目录和编译gSOAP,这里不再具体描述编译过程。
新建一个目录,比如 /home/onvif_build
把 gSOAP目录中custom,plugin,import三个子目录复制到 onvif_build目录中,
再把编译生成的wsdl2h 和 soapcpp2 两个程序复制到onvif_build目录中,
这样编译wsdl的环境基本就做好了。
2, 通过 wsdl 生成 onvif.h头文件。在onvif_build目录下使用如下命令,
./wsdl2h -d -o onvif.h -c -s -t ./typemap.dat https://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl https://www.onvif.org/onvif ;
/ver10/media/wsdl/media.wsdl https://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl
这个是在线生成方式,就是wsdl直接从官网下载,推荐使用这种办法。
-d 参数生成 __any dom相关结构,这个在ONVIF中会用到,
-c表示生成C风格的头文件,因为还是喜欢C那种简洁的风格,所以这里添加了-c参数。
3,通过上面步骤2,就会在onvif_build目录中生成 onvif.h 头文件。接着就是通过这个头文件,生成SOAP主体代码。
使用如下命令:
soapcpp2 -s onvif.h -x -L -I./ -I./gsoap -I./import/ -I./custom -I./plugin
需要去import目录下修改wsa5.h,找到SOAP_ENV__Fault这个结构体,并且直接把这个结构体注释掉,否则可能会编译失败。
生成的 是纯C风格的代码,会生成 文件名是 soapC.c, soapClient.c, soapServer.c, soapH.h, soapStub.h 这些源码文件。
4,组建我们自己的ONVIF工程。
把上面生成的这些 soap*** 文件复制到我们的工程目录中,再从gSOAP目录中复制一些有用的源码到我们的工程目录中,
gSOAP这主要包括 stdsoap2.c/h, dom.c等等这些,
到时根据编译的时候出现哪些函数未定义来确定复制哪些gSOAP中的源码到我们的工程目录中。
为了给ONVIF添加密钥验证,还得给编译工程添加WITH_DOM,WITH_OPENSSL宏,
并且连接openssl库,以及需要gSOAP中对应的一些源码文件。
然后就是实现由wsdl描述生成的onvif协议中相应的SOAP回调函数。就光上面三个wsdl生成的函数就达到两三百个函数之多。
非常离谱,不过好在大部分都不需要去实现,只是简单重复的返回 SOAP_OK 就可以了。
可是即便这样,也需要重复去填写几百个这样的函数,实在是够奇葩的!
首先实现discovery功能,需要创建组播socket套接字,并且固定在3702端口侦听,
因为windows平台有个 FDResPub 服务也是使用同样的协议,
因此我们要么禁用FDResPub 服务,要么别在windows平台开启ONVIF的discovery功能。
之后只需要实现 __wsdd__Probe 回调函数即可,在此函数中报告本机的ONVIF相关信息。
接着实现 一些ONVIF设备通用函数,包括
__tds__GetServices, __tds__GetCapabilities,__tds__GetServiceCapabilities,__tds__GetDeviceInformation,
__tds__GetSystemDateAndTime, __tds__GetNetworkInterfaces,__tds__GetHostname 等等这些,
可以根据自己的需求分别去实现这些函数的功能。
再然后就是去实现跟媒体相关的一些函数了,主要是包括RTSP协议的URL地址,
视频源的一些信息, 视频编码比如H264等的一些信息,如果包括声音的话,还需要音频源和音频编码信息。
这里也不再赘述了,反正就是一些非常枯燥乏味的控制信息的填写。
本来ONVIF还包括PTZ云台(控制摄像头转向的)等控制命令的,
但是xdisp_virt实现的是桌面屏幕,不存在云台的概念。
开发过程中可以辅助 ONVIF的 ONVIF Device Test Tool 工具查看每个ONVIF请求的准确性,
也可以使用 ONVIF Device Manager 工具来测试。
(我没有现成的NVR设备,所以只能使用ONVIF测试工具软件来验证ONVIF了。)
下图是已经集成到 xdisp_virt中的ONVIF效果,使用的是 ONVIF Device Manager 工具来显示的。
在新版本的 xdisp_virt 程序中, 对直播这部分做了加强,除了上面提到的移植live555作为RTSP服务端之外。
还增加了在图像中显示当前日期的功能,比如上图的视频中的左上角位置:
显示了当前的日期,时间,精确到毫秒。
同时还显示了当前的图像采集 FPS(帧率),图像传输的实时速度,xdisp_virt程序当前使用的CPU的百分比。
同时还考虑到 对于标准直播协议,RTSP,RTMP等,需要连续不断的传输视频流,
否则可能会出现无视频信号等各类错误。而xdisp_virt程序本身则是根据屏幕的变化情况来决定是否传输屏幕数据。
因此在电脑屏幕长期无变化的情况下,xdisp_virt 程序不会传输任何视频数据,
这会造成RTSP,RTMP这些标准协议可能因为无视频信号而中断传输。
为了解决这个问题,xdisp_virt新版本增加了屏幕无变化也传输的功能,具体设置可以查看下图所示:
其中视频图像添加日期和时间戳,就是在远端图像中添加时间戳,实现方式就跟水印一样。
同时还增加了录制本地视频文件保存到被控机器上,这个功能以前的版本也存在,只是以前的只能录制单个的视频文件,
而这次增加的是把视频文件按照时间段分成多个零散的视频文件,保存到目录中,如下图所示:
有兴趣可以关注发布到 github上的新版本 xdisp_virt 程序。
GitHub - fanxiushu/xdisp_virt: xfsredir file system
以上是关于Hikvison对接NVR实现WEB无插件开发包实现前端视频预览(htmlvuenginx代理)的主要内容,如果未能解决你的问题,请参考以下文章
EasyStreamClient对接海康流媒体V4.X实现无插件播放流程记录
Windows远程桌面实现之十二:桌面屏幕通过ONVIF协议与NVR等监控录像设备对接,以及进一步增强直播功能
Windows远程桌面实现之十二:桌面屏幕通过ONVIF协议与NVR等监控录像设备对接,以及进一步增强直播功能