张飒漪, 刘全利, 王 伟
(大连理工大学控制科学与工程学院,辽宁大连116024)
闭路电视(Closed Circuit Television,CCTV)网络监控系统,是安全技术防范体系中的一个重要组成部分[1],现已应用于社会安防监控的各大领域[2],同时在轨道交通中也发挥了巨大的作用。列车CCTV监控系统可以分为前端、传输和终端3个部分[3]。就硬件而言,前端用于获取被监控区域的图像,传输部分通常由视频电缆补偿器、视频放大器等组成,终端包括监视器和控制设备以及记录设备等;就软件而言,前端属于服务器,传输部分属于网络通信,终端属于客户端。在实际项目应用中,一般CCTV的前端产品供应商都会配套一个SDK(Software Development Kit),针对自己的产品制定内部通信协议并实现API的封装,留出功能接口供给客户端编程使用。客户端程序开发人员可以在不了解视频压缩、回放和网络传输等技术的前提下,进行视频程序开发。
文中设计实现的CCTVSDK是一个基于某地铁项目的车载CCTV设备进行的接口封装,此项目中使用的前端摄像头编码卡和DVR(Digital Video Recorder)均集视频采集、压缩和网络传输于一体,为CCTVSDK的设计和实现提供了前提。另外,微软公司的DirectShow编程接口,为在Windows开发平台上处理各种格式的媒体文件回放、音视频采集等高性能要求的多媒体应用提供了完整解决方案。该CCTVSDK实现了取流、播放、存储、获取设备状态、获取视频数据等接口功能,以满足此项目地面监控系统的开发需求,并为本项目列车CCTV系统的地面监控开发提供高效、完整、便捷的开发和控制手段。
文中的CCTVSDK是在某城市地铁项目开发过程中,针对车载CCTV监控系统定制的开发包。该列车由6节编组组成,具体如图1所示。每个编组的监控系统由车载摄像机(Camera)、车载DVR、车载监控单元(TLCD)以及车载交换机(Switch)组成。
图1 列车CCTV系统结构示意Fig.1 Structure sketch of the train CCTV system
Camera由摄像头和编码卡两部分组成,分布在客室和司机室,一块编码卡引出两个TNC连接器,通过同轴电缆分别和一个摄像头相连。摄像头负责视频采集,编码卡采用Linux嵌入式平台,负责对视频图像数据按照H.264编码标准[4]进行编码压缩,并作为RTSP[5]服务器等待取流。
TDVR也采用Linux嵌入式平台,分布在司机室,车头车尾各一个,负责按照项目需求管理自己所属范围内的摄像机,上电后根据RTSP取流协议将摄像头实时视频数据按照一定规则(如时间、通道号等)存储在硬盘中,方便查询录像历史。
TLCD安装在司机室,车头车尾各一个,采用Linux嵌入式平台,可以通过在触摸板上进行人工操作,以轮询或者单独监控的方式对车厢内日常和紧急情况进行监控,还可以修改车内其他IP设备参数,便于司机在运营时掌握列车状态、不同车厢内乘客情况以及停车时维护。
Switch用于各车厢CCTV设备连接,上述设备均通过以太网连接到交换机上,各个车厢内交换机再级连,为列车的监控系统网络通信数据(包括控制命令数据和视频数据)提供传输通道,同时为构成局域网奠定了基础。
地面监控系统通过路由与车载交换机相连,可以通过主动发起通话,获取车载设备信息、车辆紧急信息,获取监控画面之后进行控制中心上墙显示,或进行控制中心本地存储等。
通过对系统的分析可以看出,文中的车载CCTV监控系统以全数字IP设备为基础,对其操作涉及到网络通信、流媒体等方面的技术,导致地面监控开发人员开发过程较为复杂。对于摄像头实时预览,地面监控开发人员需要知道RTSP取流协议,获取视频数据之后,需要将视频数据进行音视频解码并负责显示,当多路视频轮询切换时,要考虑不同显示窗口的切换问题、CPU的利用率等问题;对于以DVR录像文件下载和本地回放,地面监控开发人员需要知道DVR内部存储规则(时间段、通道号、硬盘号等),文件格式结构和文件转换过程,以及回放时播放器机制等;对于监测设备状态,地面监控开发人员要想知道设备是否在线、掉线、重连、有无紧急情况发生等状态,需要自己建立网络通信机制,通过一系列套接字实现状态查询功能。这一切都对地面监控系统的开发形成了不便和困扰。
因此,地面监控中心需要集成3大类功能:①网络视频流处理;②车载录像本地下载、存储和回放;③车载设备状态检测、紧急信息获取。
以上所有功能在CCTVSDK中都进行了封装,以Windows API的形式提供给地面监控系统的编程人员,最终产品以DLL[6](动态链接库)的形式生成。这样的设计就使用户不再关心内部通信和操作细节,极大地提高了二次开发的效率。
CCTVSDK接口功能主要包含设备操作、本地视频文件回放和控制操作、车载紧急信息获取3大模块。其中设备操作由设备取流、视频播放、视频本地存储、视频数据操作和设备状态监控这几部分组成。软件结构如图2所示。
图2 CCTVSDK软件结构Fig.2 Structure of CCTVSDK
其中网络视频流处理模块基于C/S模型,由设备取流、视频播放、视频本地存储、视频数据操作几部分组成;本地视频处理模块由录像文件回放和回放控制两部分组成;OCC报警监控模块同样基于C/S模型,由车载设备状态监控和车载紧急信息获取两部分组成。
应用程序开始时,无论想要实现何种功能,必须首先调用相应模块的初始化操作接口,主要用于开辟SDK内部资源,如为网络通信创建套接字、清空设备队列以及初始化COM库等。
应用程序调用过程中,可能有多种调用方法实现同一种功能,编程人员可根据自己需求选取不同分支方法。
应用程序结束时,需要调用相应模块的清除操作接口进行清理资源操作,主要目的是用来回收内部资源,避免内存泄露。
CCTVSDK实现的取流功能是基于标准RTSP协议,从开始申请网络取流到成功获取视频数据的过程就是一个RTSP协议的实现过程。
RTSP协议全称为实时流媒体协议(Real Time Streaming Protocol),是由Real Network和Netscape以及哥伦比亚大学共同提出的如何有效在IP网络上传输流媒体数据的应用层协议。这个协议位于RTP和RTCP之上,本身并不传输数据,而是使用TCP或RTP完成数据的传送。它只是相当于流媒体服务器的远程控制。
车载摄像头编码卡和车载DVR均为RTSP服务器,文中涉及到的控制过程中包含以下几个命令,具体见表1。各个步骤之后的状态流程如图3所示。CCTVSDK作为客户端进行相关取流操作,当一个RTSP的申请过程在Play命令成功之后就可以收到服务器发出的视频数据。
表1 RTSP请求流程Tab.1 Request process of RTSP
图3 RTSP状态转换Fig.3 State transition diagram of RTSP
C++的开源项目Live555为RTSP每个过程的实现提供了良好的开发基础。它实现了对标准流媒体传输协议(如RTP/RTCP、RTSP、SIP等)的支持,并实现对多种音视频编码格式的音视频数据接收和处理的支持。由于结构设计良好,Live555已被用于多款开源播放器的流媒体播放功能的实现,如VLC,MPlayer。Live555的整体框架包括 4 个库:UsageEnvironment模块是对系统环境的抽象,包括抽 象 类 UsageEnvironment,TaskScheduler 和HashTable,实现对事件的异步读取、对事件句柄的设置以及对错误信息的输出等;BasicUsageEnvironment模块中的类主要是对UsageEnvironment中对应类的实现;Groupsock模块用于数据包的接收和发送等网络通信,支持单播和多播;LiveMedia模块非常重要,包含了实现RTSP Server的类,还有针对不同流媒体类型(TS流或PS流)编码的类,其中Medium是基类。
运用这一开源代码,CCTVSDK将RTSP的实现过程加以提炼并抽象提取出来,封装成名为CstreamMedia的类,并将主要过程以Public成员函数的形式表现出来。这些接口依次为:
int rtspClientOpenStream(const char* url,int flag),负责PLAY命令之前的所有过程操作;
int rtspClientPlayStream(void),负责发送PLAY命令和解析应答;
int rtspClientReadFrame(FrameInfo*& frame),负责取流之后视频数据获取;
intrtspClinetGetMediaInfo(enum CodecType codectype,MediaInfo& mediainfo),负责视频流的类型判断;
int rtspClientPauseStream(void),负责发送PAUSE命令和解析应答;
int rtspClientCloseStream(void),负责发送TEARDOWN命令和解析应答。
当创建一个CstreamMedia类的对象时,通过调用以上不同接口,便可实现RTSP的整个流程,完成取流过程并在成功取流过程后加以过程控制。
设备视频播放功能基于微软的DirectShow SDK开发完成。DirectShow是经过DirectX 6.0中的Direct Media发展而来,集成了DirectX家族中其他成员(如 DirectDraw、DirectSound 等)的技术[7]。Dshow设计初衷就是要让应用程序开发人员从负责的数据传输、硬件差异、同步性等工作中解脱出来,使得多媒体应用变得简单。
DirectShow使用Filter Graph模型管理整个数据流的处理过程,参与数据处理的各个功能模块是Filter。各个Filter按照一定的顺序连接成一条“流水线”协同工作。Filter按照功能可以分为3类:Source Filters,Transform Filters和 Rendering Filters。
Source Filters主要负责获取数据(数据源可以是文件、计算机里的采集卡、数字摄像机等),然后将数据往下传输。数据传送可以分为推模式和拉模式,推模式最典型的情况发生在实时源中;拉模式典型发生在文件源中。Transform Filters主要负责数据的格式转换,例如数据流分离 /合成、解码 /编码等,然后将数据继续往下传输;Rendering Filters主要负责将数据送给显卡、声卡进行多媒体的演示播放,或者输出到文件进行存储。CCTVSDK中视频播放功能实现严格按照Source Filter-Transform Filter-Rendering Filter的结构设计完成。
Source Filter采用推模式,被封装为CRTSPStreamFilter类,由CBaseFilter和CAMThread派生出来。Source Filter可以使用专门的线程将经过RTSP谈判获取的网络数据流推送下去。因此,在CRtspStream的构造函数中开启了RTSP的请求过程,如果成功,在循环推送的线程中将会把车载监控设备发出的H.264视频数据流推送到下一级Filter,在类的具体实现代码中,以上阐述的过程体现 在 CRTSPStreamFilter::DoBufferProcessing-Loop()和CRTSPStreamFilter::FillBuffer()中。每次传递的过程都可以获取到此次传递buffer大小。
CH264Decoder是CTransformFilter的子类,当这一Filter对象被添加到Graph中并收到上级传来的数据时,会进入CH264Decoder::Transform()的阶段,将视频数据根据buffer类型和大小进行标准H.264解码。解码的过程运用了ffmpeg[8]开源解决方案。解码之后的YUV数据,继续传送到下一级Filter。
Rendering Filter运用了DirectShow新一代的Video Render即 VMR7(Video Mixing Render)。VMR有很多新特性,其中典型的特性是实现了真正的无窗口模式(Windowsless)。普通Render在显示视频时,总是需要创建窗口,将此窗口作为应用程序创建窗口的子窗口;而VMR无需如此,应用程序使用时,只需要传入窗口句柄,画面就可以在传入的窗口中显示,没有父子关系。
当用户准备启动视频播放时,运用IGraphBuilder::Connect()方法将以上3个Filter连接,形成一条播放链路,具体如图4所示。再运用IMediaControl::Run()这一方法,数据流就会在Filter中自动传递,画面显示流畅,实现预览功能。
图4 Dshow数据传输流程Fig.4 Data transform chart of Dshow
视频本地存储功能是在取流成功的基础上,在Source Filter内部加入接口,将网络原始视频数据存储成标准AVI文件,存储文件名称以“CAM_当前时间_设备IP_端口号.avi”和“DVR_当前时间_设备IP_通道号_录像起始时间_录像结束时间.avi”规则进行命名,以避免录像文件名称重复。
AVI(AudioVideoInterleaved)是微软推出的一种音视频交互格式[9]。它采用了一种有损压缩方式,分为文件头、数据块、索引块3部分(见图5)。
图5 AVI数据存储格式Fig.5 Data storage layout of AVI
AVI文件采用的是RIFF文件结构方式,存储过程简单易操作,且满足列车监控单元对视频文件的存储需求。RIFF(Resource Interchange File Format,资源互换文件格式)是微软公司定义的一种用于管理Windows环境中多媒体数据的文件格式。构造RIFF文件的基本单元为数据块(Chunk)。整个AVI文件是一个以ID为RIFF的块,其中包含3种块(此种结构中只有视频信息,没有音频信息):
1)ID 为 LIST,数据类型为“hdrl”:包含 AVI文件一般信息,如文件头、数据流头、数据流格式。
2)ID为 LIST,数据类型为“movi”:以帧为单位,记录视频数据信息。
00dc:表示该帧数据的类型和流编号,dc-压缩视频帧;db-非压缩视频帧。
3)ID为idx1,记录数据帧索引信息,包括:
数据块标记(如00dc),数据块属性,数据块相对位置,数据块大小。
在SDK中,将AVI文件的存储操作封装成CGenerateAVI类,留出公共操作接口。
设备状态监控功能是地面监控系统的又一功能需求。在监控过程中可实时获取不同列车的各个CCTV设备的在线状态,以便应用程序根据掉线情况作出冗余处理或者告知维护人员及时处理故障。
CCTVSDK实现流程如下:①设备状态通过客户端发送消息、服务器给与应答的方式进行判断。以UDP的方式逐一发送检测设备状态协议的网络通信数据包,设备接收后会根据协议给与应答。②CCTVSDK初始化时,经用户指定后开启一个内部循环检测线程,用户每注册一个设备,会得到一个设备ID号,CCTVSDK内部记录该设备IP,并将这一ID号与此IP地址相互关联,同时此设备信息则被添加到设备队列中,该检测线程在此设备队列中不断检测每个设备的在线状态。③如果在一定的超时时间内(文中实现代码中默认设置为2 s)没有检测到正确的回复包,则认为设备掉线;假如在断线一段时间后的某次检测过程中,又收到该设备正确的回复包,则认为此设备恢复正常。④设备断线、在线的变化状态,CCTVSDK都会以回调函数的方式通知用户。
检测设备状态的通信协议中的数据包采用“报文头 +报文内容”的格式(见表2)。
表2 报文格式Tab.2 Message layout
表2中COMM_HEAD包含此数据包的一般信息,如包长度、校验和等;INTER_HEAD用于区分数据包功能,记录此数据包消息编号等信息;消息数据根据不同的功能进行不同的特殊定义,用不同消息ID号区分,包含了所有需求功能的信息。
设计这种数据包格式基于现有网络通信协议数据包格式定义。在网络通信协议中,以UDP协议为例,用户数据报被分为报头和数据两部分。报头包括源端口、目的端口、报文长度及校验和。传输过程中,通过检查报头信息就可以获取此数据包的源地址和目的地址,校验和可以保证数据的安全性[10]。同理,通过COMM_HEAD可对数据包进行校验,一旦发现数据有错误,则不再对余下数据进行判断;如果校验正确,则再通过INTER_HEAD中的消息ID号确定报文的具体功能,作出不同的处理。可以看出,以这种格式安排数据包不仅整齐而且便于扩展,添加功能时只需添加不同的消息ID号即可,为编程过程中整包数据的解析和解析后的处理流程都提供了很大便捷。
基于以上实现过程,用户在使用CCTVSDK时,只需在初始化时注册一个有关设备状态的回调函数,在此函数实现中作出自己应有的动作即可。这一设计方法极大地简化了用户调用流程。
本地视频文件回放和控制功能同样是基于微软的DirectShow开发平台。
Dshow的播放操作都是基于Filter的COM组件完成的,根据不同视频编码选取不同的Filter,通过Pin之间的连接就能串联起来,从而构建一个完整的Filter Graph。对于一些具体的已知文件格式,Dshow为开发人员准备了一种非常方便的“智能连接”方法,不用开发人员手动添加不同的Filter,而是自动加入必要的Filter完成文件回放Filter Graph的构建。
Dshow实现多种智能连接方法,其中一种是通过IGraphBuilder::RenderFile()实现的。该方法给出一个文件名称之后,会首先查找注册表,根据一些算法寻找并创建正确的Source Filter,然后从该Source Filter的各个输出pin开始,进行智能连接。这是一个“递归”过程,直到所有分支都连接到一个Rendering Filter上为止。对于编程人员而言,如果想实现智能连接过程,只需要在初始化COM之后调用IGraphBuilder::RenderFile()这一接口,用已知的文件名作为参数,即可成功播放。
对于播放控制方面,Dshow也已经方便地提供了各种属性接口:开始、停止、暂停、恢复、快进、慢进、单帧播放、获取播放文件大小和帧数、获取当前播放速度和帧数、改变播放进度、帧定位、调整窗口等等。这些属性接口都是通过Graph的属性方法实现的。在智能连接过程中的初始化阶段,只要再添加IMediaControl和IMediaPosition属性操作接口,即可方便地完成以上功能操作。
车载紧急信息的获取功能是通过用户调用CCTVSDK的相应接口从车载监控单元(TLCD)处获取。这一获取过程是通过基于UDP传输协议的网络通信方式实现。
以上涉及的网络通信消息包和应答包协议属于内部协议,格式见表3。
表3 消息格式Tab.3 Message layout
用户在使用CCTVSDK功能时,需要通过IP地址获取某个车载TLCD的访问权限后,再发送获取紧急信息的数据包。TLCD在一定超时时间内给予回复,回复包中包含本车所有紧急信息的内容,CCTVSDK负责按字节解析之后,将紧急信息类型、优先级以及对应车厢号等有用信息返回给用户,用户负责进行下一步处理操作。
在城市轨道交通实际项目开发中,基于车载监控系统的SDK开发包应用非常广泛,文中基于CCTV监控网络的SDK开发包针对车载设备内部通信协议和标准取流协议等,提供了满足地面监控中心开发需求的各功能接口。
1)具备设备取流、实时预览、实时回放、存储视频文件、文件回放等视频功能,为了方便用户不同调用习惯,还将这些功能接口以不同的接口形式给予开放。具有极大的灵活性和可操作性。
2)具备检测车载设备状态和获取车载设备紧急信息功能,此项设计优于传统SDK。用户可以运用此项报警联动功能,使得整个监控体系变得更加完善。
[1]王唯.远距离多路电视监控系统的设计[J].科技信息,2009(35):889-890.
WANG Wei.The design of long-range multi-channel TV monitoring system[J].Science and Technology Information,2009(35):889-890.(in Chinese)
[2]袁文凤.论视频技术的使用与发展[J].科技信息,2009(18):199-201.
YUAN Wen-feng.Discussing on using and development of video technology[J].Science and Technology Information,2009(18):199-201.(in Chinese)
[3]姜涛.浅谈闭路监控(CCTV)系统[J].哈尔滨职业技术学院学报,2009(1):126-127.
JIANG Tao.Discussing on closed circuit television(CCTV)system[J].Journal of Harbin Institute of Vocational Technology,2009(1):126-127.(in Chinese)
[4]毕厚杰.新一代视频压缩编码标准:H.264/AVC[M].北京:人民邮电出版社,2009.
[5]孟怀军,朱义胜.实时流协议RTSP的浅析[J].盐城工学院学报:自然科学版,2003,16(3):29-31.
MENG Huai-jun,ZHU Yi-sheng.Study on real time streaming protocol[J].Journal of Yancheng Institute of Technology:Nature Science Edition,2003,16(3):29-31.(in Chinese)
[6]Johnson M H.Windows System Programming[M].3rd.MA,Boston:Addison-Wesley Professional,2005.
[7]陆其明.Dshow开发指南[M].北京:清华大学出版社,2003.
[8]FFmpeg.About FFmpeg.[EB/OL].(2007-03-09)[2013-04-19].http://www.ffmpeg.org/about.html.
[9]徐敏,陆达,赵洪志,等.廉价冗余盘阵列(RAID)发展综述[J].计算机工程与应用,1999,35(6):27-29.
XU Min,LU Da,ZHAO Hong-zhi,et al.Review of RAID technology[J].Computer Engineering and Applications,1999,35(6):27-29.(in Chinese)
[10]姜楠,王健.常用多媒体文件格式压缩标准解析[M].北京:电子工业出版社,2005.