Windows远程桌面实现之十二:桌面屏幕通过ONVIF协议与NVR等监控录像设备对接,以及进一步增强直播功能
Posted 雨中风华
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
以上是关于Windows远程桌面实现之十二:桌面屏幕通过ONVIF协议与NVR等监控录像设备对接,以及进一步增强直播功能的主要内容,如果未能解决你的问题,请参考以下文章
Windows远程桌面实现之十二:桌面屏幕通过ONVIF协议与NVR等监控录像设备对接,以及进一步增强直播功能
Windows远程桌面实现之十二:桌面屏幕通过ONVIF协议与NVR等监控录像设备对接,以及进一步增强直播功能
Windows远程桌面实现之十三:浏览器客户端使用WebRTC传输,TCP的TURN中转传输
Windows远程桌面实现之十三:浏览器客户端使用WebRTC传输,TCP的TURN中转传输