林北洪,钟洪声
(电子科技大学 电子工程学院,成都 611731)
视频采集系统以其直观的视频影像数据,对现场场景进行记录,目前已经在各行各业得到广泛应用。对于特定运用场合,如汽车行进过程,由于其环境的不确定性和多样性,以及事故的突发性和偶然性,要求采集系统能够稳定、连续地实时记录事发过程。行车过程事故发生时,需要掌握的事故原因往往在事故发生前短短的一段时间内。因此,分清事故责任和分析事故原因所需要的视频信息,往往是在事故时间点之前的一小段时间内[1]。
日常生活中常见的交通纠纷,往往是因为没有直接证据造成取证困难。基于此,结合嵌入式系统低功耗、低成本等优点,本文利用嵌入式Linux系统实现行车视频存储,存储的视频可以为交通纠纷的调解以及交通事故认定,提供现场录像证据。
系统硬件结构如图1所示。主控制芯片为三星的ARM9芯片S3C2440,它内部含有 MMC/SD/SDIO控制器、NAND Flash控制器、USB主控制器、存储器控制器、时钟和电源管理等模块[2]。其中,存储器控制器为访问外部存储提供必要的存储控器控制信号;NAND Flash控制器为外挂的NAND Flash存储器提供控制信号,而NAND Flash控制器由于其价格经济,用于执行引导程序;时钟和电源管理模块由时钟控制、USB控制和电源控制3部分模块组成,时钟控制模块用于产生必要的时钟信号,电源管理模块用于提供电源管理方案,保证对给定任务的最佳功耗;USB主控制器支持2个端口的USB主机接口,支持低速及全速USB设备,在本系统中用于连接USB摄像头;MMC/SD/SDIO控制器为外接SD卡提供控制和数据信号,使视频数据的存储变得可能。
图1 系统总体框图
NAND Flash大小为256MB,用于存储文件系统,执行必要的引导程序。SDRAM为64MB内存。考虑行车过程的震动环境,视频存储设备采用能够牢靠固定的SD卡。除了稳固的优点外,SD卡还有价格便宜、方便拔插的优点。要实现行车视频的录制存储,首先需要采集视频数据。考虑到嵌入式存储器的资源相对有限,本设计使用中星微电子公司ZC301芯片的摄像头,此类摄像头的视频数据经过摄像头内部专用DSP芯片处理过,输出的信号是JPEG格式的。移植Linux系统后,利用Linux下视频采集和输出驱动的内核接口V4L2对视频进行采集,然后对采集到的视频数据按照avi标准格式进行封装,添加必要的帧头信息后,将其一帧一帧存入插在SD卡座上的SD卡内,采用特定方法对视频文件进行管理,实现视频的循环存储,以节约存储空间。将采集存储到SD卡的视频文件拷贝到任意的带视频播放器的平台上,即可查看录制的视频信息。
由于ZC301摄像头输出的每帧是JPEG格式数据,因而存储的avi视频文件是MJPEG压缩,其中,每一帧图像都分别使用JPEG编码。MJPEG视频压缩仅采用帧内压缩,不需要过多的运算能力,无需为视频压缩浪费大量的计算效率,适合用于嵌入式设备中。虽然其压缩率不是很高,造成视频文件尺寸较大,但是由于该行车视频录制系统只需保存事故发生前一段时间内的现场视频,综合考虑MJPEG压缩可以满足要求。
要在嵌入式平台实现系统功能,需要通过主机构建基本的软件系统,并烧写到设备中。采用交叉开发模式以适应嵌入式设备的资源不足。首先,在主机编译Bootloader,将编译出的镜像文件烧入设备;然后在主机编译嵌入式Linux内核,通过Bootloader烧入板子;最后,在主机上编译实现相关功能的应用程序,通过NFS运行,经过验证成功后再烧入板子,过程如图2所示。该部分内容不作详细阐述,具体的搭建过程可参考相应的资料。
图2 开发环境搭建
系统的软件设计主要包括视频图像的采集、使用特定方法实现视频数据的循环存储,其中重点在于,按照avi格式将采集的数据进行封装,并且实现视频文件循环存储。
avi(audio video interleaved)格式即音频视频交错格式,1992年由 Microsoft公司推出。所谓“音频视频交错”,就是可以将视频格式和音频格式交织在一起进行同步播放。
RIFF文件使用4字符码FOURCC(Four-Character Code)来表示数据类型,例如“RIFF”表示这是一个RIFF文件,“AVI”表示文件的具体类型,“LIST”表示用列表的形式来组织数据。其结构如图3所示。作为一种多媒体文件的存储方式,不同编码的音频、视频文件,都可以按照RIFF定义的存储规则保存、记录各自不同的数据,如数据内容、采集信息、显示尺寸、编码方式等。播放器播放这些文件时,会根据RIFF的规则分析文件,正确解析出音视频信息进行播放。
图3 RIFF文件结构
图4 avi文件格式
avi文件是目前使用的最复杂的RIFF文件,它包含信息块、数据块和索引块3个子块,每个子块都按照RIFF文件结构来填充各字段。其总体结构如图4所示。
avi文件有两种基本的数据结构CHUNK和LIST,其形式如图5所示。
图5 CHUNK块和LIST块的结构
其中,信息块是一个以“hdrl”为标识的LIST块,定义avi文件的数据格式;数据块是一个以“movi”为标识的LIST块,它保存真正的音视频数据;索引块是一个以“idxl”为标识的LIST块,为avi文件中的媒体数据块进行索引,不是必需的。详细的avi文件格式见参考文献[5]。
嵌入式Linux下视频采集的实现使用Linux视频驱动接口V4L2。它的操作流程如下:打开视频设备、设置视频设备属性和采集方式、处理视频数据、关闭视频设备。把视频设备当作普通文件来打开关闭,并进行数据的读写。视频设备属性的设置是通过ioctl函数来进行的,V4L2驱动接口包含很多控制命令,对视频采集进行控制。
视频的循环存储是很有必要的。一方面,因本系统特点,对于事故前很长时间的视频信息没有保存下来的必要,因而可以在保存必要视频信息的前提下,当SD卡存储满的时候对最先录制的视频进行覆盖,以充分利用存储空间。
循环录制可以只在单个文件中进行,参考文献[1]采用此方法。该方法通过修改位置指针,对存储帧数和预定义的最大帧常量进行判断,以实现循环存储。该方法实现较为繁琐,而且由于采用avi文件存储,其单个文件最大只有2G,保存的视频数据有限。本系统采用多个文件循环存储的方法。每次录制以开始录制的时间作为avi的文件名,格式为“年月日-时分秒”形式,这样便于以后查阅检索。
3.3.1 循环存储原理
循环存储的原理简单直观,首先创建一个avi文件对视频数据进行录制,当录制到文件预定义大小时,新建一个avi文件继续存储。如此一直下去,直到SD卡存储已满。当系统检测到SD卡剩余容量小于预定义的阈值时,则查找最先录制的avi文件并删除,这样便有空闲容量继续存储视频,如此往复便可实现视频的循环录制。
3.3.2 循环存储方法
根据上面的循环存储原理,按照avi视频循环存储流程可以清楚地实现循环存储,如图6所示。
系统初始化主要是对视频采集的初始化,设定采集视频的相关信息,通过init_videoIn()函数将参数传入相应的结构体,主要代码如下:
接着创建一个avi文件,以当前日期加时间作为文件名,这样便于将来对视频文件查询检索。然后就进入程序大循环,对SD卡剩余容量判断,可以保证即使更换不同容量的SD卡也能充分利用存储空间。对于SD卡剩余容量小于预定义的RESTSIZE情况,则说明SD卡剩余容量不足,这样就需要删除最早保存的视频文件。为了获取SD卡目录里的文件信息,使用了dirent结构体,查找SD卡里最早保存的avi文件是关键,其核心代码如下:
图6 avi视频循环存储流程
找到最早创建的avi文件后,删除它即获得足够的容量保存新的视频。对于SD卡剩余容量足够的情况,直接往avi文件写入一帧视频,这也是本设计的一个重点,在下一节介绍。若写入视频数据失败,直接跳出大循环,系统结束工作。否则,使用stat()函数对当前视频文件大小进行判断,看该avi文件是否达到预定大小。若是,则要新建一个avi文件以保证SD卡的每个avi文件大小固定且存储多个avi文件;不是,则avi文件还未达到预定大小,继续往avi文件写入一帧视频数据。如此往复,即实现视频的循环存储功能。
3.3.3 写一帧avi视频数据
向SD卡中创建的avi文件写入一帧视频,就是按照3.1节介绍的avi文件的格式,把从摄像头采集到的一帧图像保存到相应位置,同时填写必要的avi文件头信息。
打开以日期时间命名的avi文件后,设置视频相关信息,主要是视频压缩方式、视频帧大小和帧率等,然后便是填充avi文件的头信息。avi文件的头信息是其区别于其他视频文件一个重要标志,根据参考文献[5],头部信息主要包括2个LIST基本结构,第一个“hdrl”LIST嵌套一个“strl”LIST,其中“hdrl”是主avi信息头数据,“strl”LIST是流的头信息数据(strl可以有多个,对应多个流),它们用于保存视频流的详细信息。每个“strl”至少包含一个“strh”块(用于说明该流的头信息)和一个“strf”块(说明该流是视频流还是音频流)。第二个LIST是“movi”列表,它用来保存真正的视频音频流数据,它以4字符码“00db”(非压缩视频流)或“00wb”(非压缩音频流)等开始作为标记,接下来存储流数据。“movi”列表后是avi文件的索引块,这部分是可选的,主要是为avi文件每个数据块进行索引。填写完avi文件头部信息后,即可将采集的视频数据保存下来。
写一帧视频数据时,首先将写指针指向avi文件头的尾部,接着调用一个 write_avi_data(avi_t*AVI,char*data,unsigned long length)函数,将length长度的由指针data指向的数据写入到avi指向的文件。在写真正的流媒体数据之前,先写入4字符码“00db”指示要写的是视频数据,然后是要写入的一帧数据的长度字段,最后写入一帧视频数据。
一个avi文件只有一个头部信息,即填充完avi文件头部信息后,便不断地向文件写入一帧视频数据,直到文件大小达到预定大小时,按照循环存储规则新建avi文件。
用 Linux C[7-8]实现了视频采集功能、循环存储功能,将交叉编译成功后的应用程序下载到板子的根文件系统。自动挂载SD卡后执行应用程序,功能实现正常,连续长时间运行能够实现视频的循环录制。取下SD卡,把视频复制到计算机上使用视频播放器播放,视频清晰流畅。
本文用嵌入式Linux实现了行车视频录制的功能。详细介绍了视频循环存储的实现和视频文件的保存。作为独立的功能部件,本系统可以方便地安装到汽车上。经过软硬件联合测试,系统工作稳定可靠,有一定的实用价值。
[1] 刘少刚,王学军.基于嵌入式Linux系统的单个AVI文件视频循环录制的研究[J].林业机械与木工设备,2008(8):48-51.
[2] Samsung Electronics.S3C2440 32-Bit Microcontroller User's Manual,2004.
[3] 韦东山,嵌入式Linux应用开发完全手册[M].北京:人民邮电出版社,2009.
[4] 陆其明.DirectShow实务精选[M].北京:科学出版社,2004.
[5] AVI File Format.[2012-08].http://ishare.games.sina.com.cn/f/6723744.html?from=like.
[6] Michael H Schimek.Video for Linux Two API Specification Revision 0.24,2008.
[7] 杨宗德,邓玉春.Linux高级程序设计[M].北京:人民邮电出版社,2009.
[8] 史蒂文斯,拉戈.UNIX环境高级编程[M].尤晋元,等译.北京:人民邮电出版社,2006.