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等监控录像设备对接,以及进一步增强直播功能相关推荐

  1. C#之windows桌面软件第十二课:电脑ADC值显示(上位机),记忆上次串口号,并用TrackBar控件显示ADC值

    C#之windows桌面软件第十二课:电脑ADC值显示(上位机),记忆上次串口号,并用TrackBar控件显示ADC值 using System; using System.Collections.G ...

  2. 网络云存储技术Windows server 2012 (项目十二 为企业构建虚拟共享服务(工作组模式下的DFS))

    网络云存储技术Windows server 2012 (项目十二 为企业构建虚拟共享服务(工作组模式下的DFS)) 前言 网络存储技术,是以互联网为载体实现数据的传输与存储,它采用面向网络的存储体系结 ...

  3. 实验十二、十三 配置PPP协议、配置Frame-relay协议

    实验十二     配置PPP协议 要求: 配置PPP协议,实现广域网连接.然后,分别采用PAP和CHAP进行验证,最后启用OSPF协议,实现局域网之间相互通信. 基础配置 为路由器R1.R2设置相应的 ...

  4. Windows Server 2008 R2之三十二:证书注册WEB服务(一)

    由于证书注册WEB服务的部署方法,和CA与证书注册WEB服务是否安装在同一台计算机,以及安装过程中身份验证方式的选择有关, 以下CA与证书注册WEB服务安装在同一台计算机的设置过程. 实验环境: 所有 ...

  5. Chapter 22 UDP and TCP 第二十二章UDP和TCP协议作业

    Question 2 How is the well-known port different from an ephemeral port? 著名端口与临时端口之间的区别是什么? Answer:th ...

  6. [网络安全自学篇] 四十四.Windows远程桌面服务缺陷(CVE-2019-0708)复现及防御详解

    这是作者的网络安全自学教程系列,主要是关于安全工具和实践操作的在线笔记,特分享出来与博友们学习,希望您们喜欢,一起进步.前文分享了木马原理知识,并通过远程服务器IPC $ 漏洞实现木马植入及控制远程服 ...

  7. Windows远程桌面实现之十 - 把xdisp_virt项目移植到iOS,macOS,linux平台(一)

    by fanxiushu 2019-12-06 转载或引用请注明原始作者. xdisp_virt项目到目前为止,持续了两年多时间,几乎都是在windows平台下的实现各种功能, 因为持续时间比较长,能 ...

  8. Windows远程桌面实现之十三:浏览器客户端使用WebRTC传输,以及WebRTC和MSE渲染显示(二)

    by fanxiushu 2022-03-12 转载或引用请注明原作者. 接上文. 我们先来编译kvswebrtc开源代码. 首先得从github下载 ksvwebrtc源码, 分别需要  amazo ...

  9. VMware vSphere 服务器虚拟化之二十五 桌面虚拟化之终端服务池

    VMware vSphere 服务器虚拟化之二十五 桌面虚拟化之终端服务池 终端服务池是指由一台或多台微软终端服务器提供服务的桌面源组成的池.终端服务器桌面源可交付多个桌面.它具有以下特征: 1.终端 ...

  10. 推荐三个Windows远程桌面客户端,mRemote、TSMMC.MSC、Terminals

    推荐非常好用的三个WINDOWS远程桌面客户端,mRemote.TSMMC.MSC.Terminals WINDOWS下大多都是直接用远程桌面进行远程管理,服务器稍微一多,有那十来多台以上的服务器,还 ...

最新文章

  1. 程序员,唯有锻炼与读书不能辜负
  2. python映射类型-python笔记-映射类型(字典)
  3. OTA固件升级对于物联网设备的重要性
  4. [转]详细讲解提高数据库查询效率的实用方法、外键关于性能
  5. 研旭电子dsp配套程序_为什么自动调音DSP还火不了?
  6. SpringCloud工作笔记060---SpringBoot中使用WebSocket实现即时通讯_实现呼叫中心业务封装
  7. QtCreator 编译时提示 warning C4819 的解决办法
  8. 黑马博客——详细步骤(九)项目功能的实现之mongoDB数据库添加账号
  9. jira 6.x 日期设置
  10. 地理空间数据云下载的dem数据打不开怎么办? 显示光盘映像已损坏
  11. 华为畅享20为什么没有计算机,华为畅享20 Pro强势来袭:即刻5G,不等待
  12. 测试版ios15怎么信任软件,苹果ios15描述文件位置在哪 ios15授权信任设置方法教程...
  13. SSL/TLS 单双向认证代码示例
  14. 基于JARM指纹的C2识别
  15. Learning Convolutional Neural Network for Graphs
  16. HTML/CSS-花样边框案例
  17. 一个应届生是怎样搞定google、微软、百度、腾讯、搜狗的
  18. sFlow-rt 3.0流量监控工具安装部署及简单实验
  19. 超级牙医html5游戏,‎App Store 上的“我是 小 牙医 游戏 - 牙醫 診所”
  20. Unity--PicoVR开发--记录

热门文章

  1. 程序员鼓励师写下的励志名言,我干了
  2. 关于AP没办法获取IP地址故障排查及思路。
  3. 关闭数字健康 android 魅族,魅族手机隐私模式开启关闭使用方法详解
  4. matlab生成正弦系数表
  5. InfoGAN 翻译
  6. 佰马DTU连接远程服务器操作教程
  7. 系列4—BabeLua常见问题
  8. sangerbox制作heapmap_ArcMap制作土地利用转移矩阵——图文详解版.pdf
  9. 一阶惯性环节如何实现跟踪性能与滤波性能共存(三)
  10. Drill模块——孔加工与螺纹铣削