首先简介基于嵌入式Linux系统的S3C2410平台和在平台上进行开发所需的软件环境,接着详尽阐述在该平台上怎样实现视频采集这一应用,并对视频采集程序的实现进行具体的介绍,最后完成应用程序向目标平台的移植。
随着多媒体技术、网络技术的迅猛发展和后PC机时代的到来,借助嵌入式系统实现远程视频监控、可视电话和视频大会等应用已成为可能。为了实现这种应用,实时获得视频数据是一个重要环节。针对这一点,本文在基于嵌入式Linux系统平台上,借助Video4Linux内核应用编程插口函数,实现了单帧图象和视频连续帧的采集,并保存成文件的方式供进一步视频处理和网路传输用。
1系统平台上的硬件系统
本文使用的系统平台硬件功能框图如图1所示。该平台采用Samsung公司的处理器S3C2410。该处理器内部集成了ARM公司ARM920920T处理器核的32位微控制器,资源丰富,带独立的16KB的指令Cache和16KB数据Cache、LCD控制器、RAM控制器、NAND闪存控制器、3路UART、4路DMA、4路带PWM的Timer、并行I/O口、8路10位ADC、TouchScreen插口、I2C插口、I2S插口、2个USB插口控制器、2路SPI,显存最高可达203MHz。在处理器丰富资源的基础上,还进行了相关的配置和扩充,平台配置了16MB16位的Flash和64MB32位的SDRAM。通过以太网控制器芯片DM90009000E扩充了一个网口,另外引出了一个HOSTUSB插口。通过在USB插口北外接一个带USB口的摄像头linux命令tar,将采集到的视频图象数据装入输入缓冲区中。之后,或则保存成文件的方式,或则运行移植到平台上的图象处理程序,对缓冲的图象数据直接进行相关处理,再保存并打成UDP包。最后,通过网路插口将图象发送到Internet上。本文只讨论其中视频采集部份的具体实现。
2系统平台中的软件系统
2.1Linux与嵌入式系统
Linux具有内核小,效率高,源代码开放,内核直接提供网路支持等优点。但嵌入式系统的硬件资源虽然有限,因而不能直接把Linux作为操作系统,须要针对具体的应用通过配置内核、裁减shell和嵌入式C库对系统订制,使整个系统还能储存到容量较小的Flash中。Linux的动态模块加载,使Linux的裁减极为便捷,高度模块化的部件使添加特别容易。正由于Linux的上述优点,在本文实现的平台上,使用的操作系统是对Linux进行了订制的armlinux。它启用了MMU(显存管理单元),是针对支持MMU的处理器设计的。
2.2开发环境的构建
绝大多数Linux的软件开发都以native形式进行,即本机开发、调试,本机运行的方法。这些方法一般不易于嵌入式系统的软件开发,由于对于嵌入式系统的开发,它没有足够的资源在本机(即嵌入式系统平台)运行开发工具和调试工具。一般的嵌入式系统软件开发采用交叉编译调试的形式。交叉编译调试环境构建在宿主机(即图1所示通过并口联接的宿主机PC)上,对应的开发板称作目标板(即嵌入式ARM2410系统)。
一般宿主机和目标板上的处理器不同,宿主机一般为Intel处理器,而目标板如图1所示为SAMSUNGS3C2410,所以程序须要使用针对处理器特性的编译器能够生成在相应平台上可运行的代码。GNU编译器提供这样的功能,在编译时,可以选择开发所需的宿主机和目标机,因而构建开发环境。在进行嵌入式开发前的第一步工作就是把一台PC机作为宿主机开发机,并在其上安装指定的操作系统。对于嵌入式Linux,宿主机PC上应安装Linux系统。以后,在宿主机上构建交叉编译调试的开发环境,开发环境的具体完善这儿不再议。本文采用移植性很强的C语言在宿主机上编撰视频采集程序,再借助交叉编译调试工具编译链接生成可执行代码,最后向目标平台移植。
3视频采集的具体实现
里面提及系统平台上运行的是armlinux。在启动后,启用了MMU,系统步入保护模式,所以应用程序就不能直接读写外设的I/O区域(包括I/O端口和I/O显存),这时通常就要利用于该外设的驱动来步入内核完成这个工作。本系统中的视频采集分两步实现:一是为USB口数码摄像头在内核中写入驱动,二是要再写入下层应用程序获取视频数据。本文侧重讨论后一步。
3.1USB口数码摄像头的驱动实现
在Linux下,设备驱动程序可以看成Linux内核与外部设备之间的插口。设备驱动程序向应用程序屏蔽了硬件实现了的细节,致使应用程序可以像操作普通文件一样来操作外部设备,可以使用和操作文件中相同的、标准的系统调用插口函数来完成对硬件设备的打开、关闭、读写和I/O控制操作,而驱动程序的主要任务也就是要实现这种系统调用函数。本系统平台使用的嵌入式armLinux系统在内核主要功能上与Linux操作系统没本质区别,所以驱动程序要实现的任务也一样,只要编译时使用的编译器、部分头文件和库文件等要涉及到具体处理器体系结构,这种都可以在Makefile文件中具体指定。
Video4Linux(简V4L)是Linux中关于视频设备的内核驱动,它为针对视频设备的应用程序编程提供一系列插口函数,这种视频设备包括现在市场上流行的TV卡、视频捕捉卡和USB摄像头等。对于USB口摄像头,其驱动程序中须要提供基本的I/O操作插口函数open、read、write、close的实现。对中断的处理实现,显存映射功能以及对I/O通道的控制插口函数ioct1的实现等,并把它们定义在structfile_operations中。这样当应用程序对设备文件进行例如open、close、read、write等系统调用操作时,Linux内核将通过file_operations结构访问驱动程序提供的函数。比如,当应用程序对设备文件执行读操作时,内核将调用file_operations结构中的read函数。在系统平台上对USB口数码摄像头驱动,首先把USB控制器驱动模块静态编译进内核,使平台中支持USB插口,再在须要使用摄像头采集时,使用insmode动态加载其驱动模块,这样摄像头就可正常工作了,接着进行了下一步对视频流的采集编程。
3.2Video4Linux下的摄像头采集编程
在USB摄像头被驱动后,只须要再编撰一个对视频流采集的应用程序就可以了。依据嵌入式系统开发特点,先在宿主机上编撰应用程序,再使用交叉编译器进行编译链接,生成在目标平台的可执行文件。宿主机与目标板通讯采用复印终端的方法进行交叉调试,成功后移植到目标平台。本文编撰采集程序是在安装Linux操作系统的宿主机PC机上进行的,下边是具体阐述。
(1)程序中定义的数据结构
structvoide_capabilitygrab_cap;
structvoide_picturegrab_pic;
structvoide_mmapgrab_buf;
structvoide_mbufgrab_vm;
这种数据结构都是由Video4Linux支持的linux 视频采集 软件,它们的用途如下:
*video_capability包含摄像头的基本信息,比如设备名称、支持的最大最小码率、信号源信息等,分别对应着结构体中成员变量name[32]、maxwidth、maxheight、minwidth、minheight、channels(讯号源个数)、type等;
*voide_picture包含设备采集图象的各类属性,如brightness(色温)、hue(色彩)、contrast(对比度)、whiteness(浊度)、depth(深度)等;
*video_mmap用于显存映射;
*voido_mbuf借助mmap进行映射的帧信息,实际上是输入到摄像头储存器缓冲中的帧信息,包括size(帧的大小)、frames(最多支持的帧率)、offsets(每帧相对基址的偏斜)。
程序中用到的主要系统调用函数有:open("/dev/voideo0",intflags)、close(fd)、mmap(void*start,size_tlength,intprot,intflags,intfd,off_toffset)、munmap(void*start,size_tlength)和ioctl(intfd,intcmd,…)。
后面提及Linux系统中把设备看成设备文件,在用户空间可以通过标准的I/O系统调用函数操作设备文件,因而达到与设备通讯交互的目的。其实,在设备驱动中要提供对这种函数的相应支持。这儿说明一下ioctl(intfd,intcmd,…)函数,它在用户程序中拿来控制I/O通道,其中,fd代表设备文件描述符,cmd代表用户程序对设备的控制命令,省略号通常是一个表示类型宽度的参数,也可没有。
(2)采集程序实现过程
首先打开视频设备,摄像头在系统中对应的设备文件为/dev/video0,采用系统调用函数grab_fd=open("/dev/video0",O_RDWR),grab_fd是设备打开后返回的文件描述符(打开错误返回-1),之后的系统调用函数就可使用它来对设备文件进行操作了。接着,借助ioct1(grab_fd,VIDIOCGCAP,%26;amp;grab_cap)函数读取structvideo_capability中有关摄像头的信息。该函数成功返回后,这种信息从内核空间拷贝到用户程序空间grab_cap各成员份量中,使用printf函数就可得到各成员份量信息,比如printf("maxheight=%d",grab_fd.maxheight)获得最大垂直码率的大小。不规则用ioct1(grab_fd,VIDIOCGPICT,%26;amp;grab_pic)函数读取摄像头缓冲中voideo_picture信息。在用户空间程序中可以改变这种信息linux操作系统教程,具体方式为先给份量赋新值,再调用VIDIOCSPICTioct1函数,比如:
grab_fd.depth=3;
if(ioct1(grab_fd,VIDIOCSPICT,%26;amp;grab_pic){perror("VIDIOCSPICT");return-1;};
完成以上初始化设备工作后,就可以对视频图象截取了,有两种方式:一种是read()直接读取;另外一种mmap()显存映射。Read()通过内核缓冲区来读取数据;而mmap()通过把设备文件映射到显存中,绕开了内核缓冲区,最快的c盘访问常常还是慢于最慢的显存访问,所以mmap()方法加速了I/O访问。另外,mmap()系统调用促使进程之间通过映射同一文件实现共享显存,各进程可以像访问普通显存一样对文件进行访问,访问时只须要使用表针而不用调用文件操作函数。由于mmap()的以上优点,所以在程序实现中采用了显存映射方法,即mmap()方法。
借助mmap()方法视频裁取具体进行操作如下。
①先使用ioct1(grab_fd,VIDIOCGMBUF,%26;amp;grab_vm)函数获得摄像头储存缓冲区的帧信息,然后更改voideo_mmap中的设置,比如重新设置图象帧的垂直及水平码率、彩色显示格式。可借助如下句子
grab_buf.height=240;
grab_buf.width=320;
grab_buf.format=VIDEO_PALETTE_RGB24;
②接着把摄像头对应的设备文件映射到显存区,具体使用grab_data=(unsignedchar*)mmap(0,grab_vm.size,PROT_READ|PROT_WRITE,MAP_SHARED,grad_fd,0)操作。这样设备文件的内容就映射到显存区,该映射内容区可读可写而且不同进程间可共享。该函数成功时返回映像显存区的表针,挫败时返回值为-1。
下边对单帧采集和连续帧采集进行说明:
*单帧采集。在里面获取的摄像头储存缓冲区帧信息中,最多可支持的帧率(frames的值)通常为两帧。对于单帧采集只需设置grab_buf.frame=0,即采集其中的第一帧,使用ioctl(grab_fd,VIDIOCMCAPTURE,%26;amp;grab_buf)函数,若调用成功,则激活设备真正开始一帧图象的截取,是非阻塞的。接着使用ioct1(grab_fd,VIDIOCSYNC,%26;amp;frame)函数判定该帧图象是否截取完毕,成功返回表示截取完毕,然后就可把图象数据保存成文件的方式。
*连续帧采集。在单帧的基础上,借助grab_fd.frames值确定采集完毕摄像头帧缓冲区帧数据进行循环的次数。在循环句子中,也是使用VIDIOCMCCAPTUREioct1和VIDIOCSYNCioctl函数完成每帧截取,但要给采集到的每帧图象赋地址,借助句子buf=grab_data+grab_vm.offsets[frame],之后保存文件的方式。若要继续采集可再加一个外循环,在外循环句子只要给原先的内循环再赋frame=0即可。
4小结
笔者最后在宿主机PC上使用交叉编译器编译链接连续帧采集程序(以双帧采集为例并保存成bmp文件文件方式)使之生成可执行代码,并完成了向目标平台的移植。为了进一步观察采集的图象疗效,笔者在目标平台带网路支持的基础上linux 视频采集 软件,编撰了一个简单的网路通讯程序,把采集到并保存为bmp的图象文件通过网路传输到PC机上进行显示,把采集到并保存为bmp的图象文件通过网路传输到PC机上进行显示,通过对疗效的剖析,再回到采集程序中重新设置video_picture中的信息,如色温、对比度等和voide_mmap中的帧率,重新移植以达到最好疗效为准。
在图1中的嵌入式系统平台上,应用本文所述方式完成视频采集工作,再加上相关的视频处理并接入网路,就构成了一个智能终端设备,可用于鞋厂、银行及新村等场合全天候的智能监控,具有宽广的市场和应用前景。