基于嵌入式Linux與S3C2410平臺的視頻采集
摘要:首先簡介基于嵌入式Linux系統(tǒng)的S3C2410平臺和在平臺上進行開發(fā)所需的軟件環(huán)境,接著詳細論述在該平臺上如何實現(xiàn)視頻采集這一應(yīng)用,并對視頻采集程序的實現(xiàn)進行具體的介紹,最后完成應(yīng)用程序向目標平臺的移植。
關(guān)鍵詞:嵌入式Linux Video4Linux S3C2410 內(nèi)存映射
隨著多媒體技術(shù)、網(wǎng)絡(luò)技術(shù)的迅猛發(fā)展和后PC機時代的到來,利用嵌入式系統(tǒng)實現(xiàn)遠程視頻監(jiān)控、可視電話和視頻會議等應(yīng)用已成為可能。為了實現(xiàn)這些應(yīng)用,實時獲得視頻數(shù)據(jù)是一個重要環(huán)節(jié)。針對這一點,本文在基于嵌入式Linux系統(tǒng)平臺上,利用Video4Linux內(nèi)核應(yīng)用編程接口函數(shù),實現(xiàn)了單幀圖像和視頻連續(xù)幀的采集,并保存成文件的形式供進一步視頻處理和網(wǎng)絡(luò)傳輸用。
1 系統(tǒng)平臺上的硬件系統(tǒng)
本文使用的系統(tǒng)平臺硬件功能框圖如圖1所示。該平臺采用Samsung公司的處理器S3C2410。該處理器內(nèi)部集成了ARM公司ARM920T處理器核的32位微控制器,資源豐富,帶獨立的16KB的指令Cache和16KB數(shù)據(jù)Cache、LCD控制器、RAM控制器、NAND閃存控制器、3路UART、4路DMA、4路帶PWM的Timer、并行I/O口、8路10位ADC、Touch Screen接口、I2C接口、I2S接口、2個USB接口控制器、2路SPI,主頻最高可達203MHz。在處理器豐富資源的基礎(chǔ)上,還進行了相關(guān)的配置和擴展,平臺配置了16MB 16位的Flash和64MB 32位的SDRAM。通過以太網(wǎng)控制器芯片DM9000E擴展了一個網(wǎng)口,另外引出了一個HOST USB接口。通過在USB接口上外接一個帶USB口的攝像頭,將采集到的視頻圖像數(shù)據(jù)放入輸入緩沖區(qū)中。然后,或者保存成文件的形式,或者運行移植到平臺上的圖像處理程序,對緩沖的圖像數(shù)據(jù)直接進行相關(guān)處理,再保存并打成UDP包。最后,通過網(wǎng)絡(luò)接口將圖像發(fā)送到Internet上。本文只討論其中視頻采集部分的具體實現(xiàn)。
2 系統(tǒng)平臺中的軟件系統(tǒng)
2.1 Linux與嵌入式系統(tǒng)
Linux具有內(nèi)核小,效率高,源代碼開放,內(nèi)核直接提供網(wǎng)絡(luò)支持等優(yōu)點。但嵌入式系統(tǒng)的硬件資源畢竟有限,因此不能直接把Linux作為操作系統(tǒng),需要針對具體的應(yīng)用通過配置內(nèi)核、裁減shell和嵌入式C庫對系統(tǒng)定制,使整個系統(tǒng)能夠存放到容量較小的Flash中。Linux的動態(tài)模塊加載,使Linux的裁減極為方便 ,高度模塊化的部件使添加非常容易。正因為Linux的上述優(yōu)點,在本文實現(xiàn)的平臺上,使用的操作系統(tǒng)是對Linux進行了定制的armlinux。它啟用了MMU(內(nèi)存管理單元),是針對支持MMU的處理器設(shè)計的。
2.2 開發(fā)環(huán)境的建立
絕大多數(shù)Linux的軟件開發(fā)都以native方式進行,即本機開發(fā)、調(diào)試,本機運行的方式。這種方式通常不適于嵌入式系統(tǒng)的軟件開發(fā),因為對于嵌入式系統(tǒng)的開發(fā),它沒有足夠的資源在本機(即嵌入式系統(tǒng)平臺)運行開發(fā)工具和調(diào)試工具。通常的嵌入式系統(tǒng)軟件開發(fā)采用交叉編譯調(diào)試的方式。交叉編譯調(diào)試環(huán)境建立在宿主機(即圖1所示通過串口連接的宿主機PC)上,對應(yīng)的開發(fā)板叫做目標板(即嵌入式ARM2410系統(tǒng))。
通常宿主機和目標板上的處理器不同,宿主機通常為Intel處理器,而目標板如圖1所示為SAMSUNG S3C2410,所以程序需要使用針對處理器特點的編譯器才能生成在相應(yīng)平臺上可運行的代碼。GNU編譯器提供這樣的功能,在編譯時,可以選擇開發(fā)所需的宿主機和目標機,從而建立開發(fā)環(huán)境。在進行嵌入式開發(fā)前的第一步工作就是把一臺PC機作為宿主機開發(fā)機,并在其上安裝指定的操作系統(tǒng)。對于嵌入式Linux,宿主機PC上應(yīng)安裝Linux系統(tǒng)。之后,在宿主機上建立交叉編譯調(diào)試的開發(fā)環(huán)境,開發(fā)環(huán)境的具體建立這里不細談。本文采用移植性很強的C語言在宿主機上編寫視頻采集程序,再利用交叉編譯調(diào)試工具編譯鏈接生成可執(zhí)行代碼,最后向目標平臺移植。
3 視頻采集的具體實現(xiàn)
上面提到系統(tǒng)平臺上運行的是armlinux。在啟動后,啟用了MMU,系統(tǒng)進入保護模式,所以應(yīng)用程序就不能直接讀寫外設(shè)的I/O區(qū)域(包括I/O端口和I/O內(nèi)存),這時一般就要借助于該外設(shè)的驅(qū)動來進入內(nèi)核完成這個工作。本系統(tǒng)中的視頻采集分兩步實現(xiàn):一是為USB口數(shù)碼攝像頭在內(nèi)核中寫入驅(qū)動,二是要再寫入上層應(yīng)用程序獲取視頻數(shù)據(jù)。本文著重討論后一步。
3.1 USB口數(shù)碼攝像頭的驅(qū)動實現(xiàn)
在Linux下,設(shè)備驅(qū)動程序可以看成Linux內(nèi)核與外部設(shè)備之間的接口。設(shè)備驅(qū)動程序向應(yīng)用程序屏蔽了硬件實現(xiàn)了的細節(jié),使得應(yīng)用程序可以像操作普通文件一樣來操作外部設(shè)備,可以使用和操作文件中相同的、標準的系統(tǒng)調(diào)用接口函數(shù)來完成對硬件設(shè)備的打開、關(guān)閉、讀寫和I/O控制操作,而驅(qū)動程序的主要任務(wù)也就是要實現(xiàn)這些系統(tǒng)調(diào)用函數(shù)。本系統(tǒng)平臺使用的嵌入式armLinux系統(tǒng)在內(nèi)核主要功能上與Linux操作系統(tǒng)沒本質(zhì)區(qū)別,所以驅(qū)動程序要實現(xiàn)的任務(wù)也一樣,只要編譯時使用的編譯器、部分頭文件和庫文件等要涉及到具體處理器體系結(jié)構(gòu),這些都可以在Makefile文件中具體指定。
Video4Linux(簡V4L)是Linux中關(guān)于視頻設(shè)備的內(nèi)核驅(qū)動,它為針對視頻設(shè)備的應(yīng)用程序編程提供一系列接口函數(shù),這些視頻設(shè)備包括現(xiàn)今市場上流行的TV卡、視頻捕捉卡和USB攝像頭等。對于USB口攝像頭,其驅(qū)動程序中需要提供基本的I/O操作接口函數(shù)open、read、write、close的實現(xiàn)。對中斷的處理實現(xiàn),內(nèi)存映射功能以及對I/O通道的控制接口函數(shù)ioct1的實現(xiàn)等,并把它們定義在struct file_operations中。這樣當應(yīng)用程序?qū)υO(shè)備文件進行諸如open、close、read、write等系統(tǒng)調(diào)用操作時,Linux內(nèi)核將通過file_operations結(jié)構(gòu)訪問驅(qū)動程序提供的函數(shù)。例如,當應(yīng)用程序?qū)υO(shè)備文件執(zhí)行讀操作時,內(nèi)核將調(diào)用file_operations結(jié)構(gòu)中的read函數(shù)。在系統(tǒng)平臺上對USB口數(shù)碼攝像頭驅(qū)動,首先把USB控制器驅(qū)動模塊靜態(tài)編譯進內(nèi)核,使平臺中支持USB接口,再在需要使用攝像頭采集時,使用insmode動態(tài)加載其驅(qū)動模塊,這樣攝像頭就可正常工作了,接著進行了下一步對視頻流的采集編程。
3.2 Video4Linux下的攝像頭采集編程
在USB攝像頭被驅(qū)動后,只需要再編寫一個對視頻流采集的應(yīng)用程序就可以了。根據(jù)嵌入式系統(tǒng)開發(fā)特征,先在宿主機上編寫應(yīng)用程序,再使用交叉編譯器進行編譯鏈接,生成在目標平臺的可執(zhí)行文件。宿主機與目標板通信采用打印終端的方式進行交叉調(diào)試,成功后移植到目標平臺。本文編寫采集程序是在安裝Linux操作系統(tǒng)的宿主機PC機上進行的,下面是具體論述。
(1)程序中定義的數(shù)據(jù)結(jié)構(gòu)
struct voide_capability grab_cap;
struct voide_picture grab_pic;
struct voide_mmap grab_buf;
struct voide_mbuf grab_vm;
這些數(shù)據(jù)結(jié)構(gòu)都是由Video4Linux支持的,它們的用途如下:
*video_capability包含攝像頭的基本信息,例如設(shè)備名稱、支持的最大最小分辨率、信號源信息等,分別對應(yīng)著結(jié)構(gòu)體中成員變量name[32]、maxwidth、maxheight、minwidth、minheight、channels(信號源個數(shù))、type等;
*voide_picture包含設(shè)備采集圖像的各種屬性,如brightness(亮度)、hue(色調(diào))、contrast(對比度)、whiteness(色度)、depth(深度)等;
*video_mmap用于內(nèi)存映射;
*voido_mbuf利用mmap進行映射的幀信息,實際上是輸入到攝像頭存儲器緩沖中的幀信息,包括size(幀的大?。rames(最多支持的幀數(shù))、offsets(每幀相對基址的偏移)。
程序中用到的主要系統(tǒng)調(diào)用函數(shù)有:open("/dev/voideo0",int flags)、close(fd)、mmap(void *start,size_t length,int prot,int flags,int fd,off_t offset)、munmap(void *start,size_tlength)和ioctl(int fd,int cmd,…)。
前面提到Linux系統(tǒng)中把設(shè)備看成設(shè)備文件,在用戶空間可以通過標準的I/O系統(tǒng)調(diào)用函數(shù)操作設(shè)備文件,從而達到與設(shè)備通信交互的目的。當然,在設(shè)備驅(qū)動中要提供對這些函數(shù)的相應(yīng)支持。這里說明一下ioctl(int fd,int cmd,…)函數(shù),它在用戶程序中用來控制I/O通道,其中,fd代表設(shè)備文件描述符,cmd代表用戶程序?qū)υO(shè)備的控制命令,省略號一般是一個表示類型長度的參數(shù),也可沒有。
(2)采集程序?qū)崿F(xiàn)過程
首先打開視頻設(shè)備,攝像頭在系統(tǒng)中對應(yīng)的設(shè)備文件為/dev/video0,采用系統(tǒng)調(diào)用函數(shù)grab_fd=open("/dev/video0",O_RDWR),grab_fd是設(shè)備打開后返回的文件描述符(打開錯誤返回-1),以后的系統(tǒng)調(diào)用函數(shù)就可使用它來對設(shè)備文件進行操作了。接著,利用ioct1(grab_fd,VIDIOCGCAP,&grab_cap)函數(shù)讀取struct video_capability中有關(guān)攝像頭的信息。該函數(shù)成功返回后,這些信息從內(nèi)核空間拷貝到用戶程序空間grab_cap各成員分量中,使用printf函數(shù)就可得到各成員分量信息,例如printf("maxheight=%d",grab_fd.maxheight)獲得最大垂直分辨率的大小。不規(guī)則用ioct1(grab_fd,VIDIOCGPICT,&grab_pic)函數(shù)讀取攝像頭緩沖中voideo_picture信息。在用戶空間程序中可以改變這些信息,具體方法為先給分量賦新值,再調(diào)用VIDIOCSPICT ioct1函數(shù),例如:
grab_fd.depth=3;
if(ioct1(grab_fd,VIDIOCSPICT,&grab_pic)<0)
{perror("VIDIOCSPICT");return -1;};
完成以上初始化設(shè)備工作后,就可以對視頻圖像截取了,有兩種方法:一種是read()直接讀??;另外一種mmap()內(nèi)存映射。Read()通過內(nèi)核緩沖區(qū)來讀取數(shù)據(jù);而mmap()通過把設(shè)備文件映射到內(nèi)存中,繞過了內(nèi)核緩沖區(qū),最快的磁盤訪問往往還是慢于最慢的內(nèi)存訪問,所以mmap()方式加速了I/O訪問。另外,mmap()系統(tǒng)調(diào)用使得進程之間通過映射同一文件實現(xiàn)共享內(nèi)存,各進程可以像訪問普通內(nèi)存一樣對文件進行訪問,訪問時只需要使用指針而不用調(diào)用文件操作函數(shù)。因為mmap()的以上優(yōu)點,所以在程序?qū)崿F(xiàn)中采用了內(nèi)存映射方式,即mmap()方式。
利用mmap()方式視頻裁取具體進行操作如下。
①先使用ioct1(grab_fd,VIDIOCGMBUF,&grab_vm)函數(shù)獲得攝像頭存儲緩沖區(qū)的幀信息,之后修改voideo_mmap中的設(shè)置,例如重新設(shè)置圖像幀的垂直及水平分辨率、彩色顯示格式??衫萌缦抡Z句
grab_buf.height=240;
grab_buf.width=320;
grab_buf.format=VIDEO_PALETTE_RGB24;
②接著把攝像頭對應(yīng)的設(shè)備文件映射到內(nèi)存區(qū),具體使用grab_data=(unsigned char*)mmap(0,grab_vm.size,PROT_READ|PROT_WRITE,MAP_SHARED,grad_fd,0)操作。這樣設(shè)備文件的內(nèi)容就映射到內(nèi)存區(qū),該映射內(nèi)容區(qū)可讀可寫并且不同進程間可共享。該函數(shù)成功時返回映像內(nèi)存區(qū)的指針,挫敗時返回值為-1。
下面對單幀采集和連續(xù)幀采集進行說明:
*單幀采集。在上面獲取的攝像頭存儲緩沖區(qū)幀信息中,最多可支持的幀數(shù)(frames的值)一般為兩幀。對于單幀采集只需設(shè)置grab_buf.frame=0,即采集其中的第一幀,使用ioctl(grab_fd,VIDIOCMCAPTURE,&grab_buf)函數(shù),若調(diào)用成功,則激活設(shè)備真正開始一幀圖像的截取,是非阻塞的。接著使用ioct1(grab_fd,VIDIOCSYNC,&frame)函數(shù)判斷該幀圖像是否截取完畢,成功返回表示截取完畢,之后就可把圖像數(shù)據(jù)保存成文件的形式。
*連續(xù)幀采集。在單幀的基礎(chǔ)上,利用grab_fd.frames值確定采集完畢攝像頭幀緩沖區(qū)幀數(shù)據(jù)進行循環(huán)的次數(shù)。在循環(huán)語句中,也是使用VIDIOCMCCAPTURE ioct1和VIDIOCSYNC ioctl函數(shù)完成每幀截取,但要給采集到的每幀圖像賦地址,利用語句buf=grab_data+grab_vm.offsets[frame],然后保存文件的形式。若要繼續(xù)采集可再加一個外循環(huán),在外循環(huán)語句只要給原來的內(nèi)循環(huán)再賦frame=0即可。
4 小結(jié)
筆者最后在宿主機PC上使用交叉編譯器編譯鏈接連續(xù)幀采集程序(以雙幀采集為例并保存成bmp文件文件形式)使之生成可執(zhí)行代碼,并完成了向目標平臺的移植。為了進一步觀察采集的圖像效果,筆者在目標平臺帶網(wǎng)絡(luò)支持的基礎(chǔ)上,編寫了一個簡單的網(wǎng)絡(luò)通信程序,把采集到并保存為bmp的圖像文件通過網(wǎng)絡(luò)傳輸?shù)絇C機上進行顯示,把采集到并保存為bmp的圖像文件通過網(wǎng)絡(luò)傳輸?shù)絇C機上進行顯示,通過對效果的分析,再回到采集程序中重新設(shè)置video_picture中的信息,如亮度、對比度等和voide_mmap中的分辨率,重新移植以達到最好效果為準。
在圖1中的嵌入式系統(tǒng)平臺上,應(yīng)用本文所述方法完成視頻采集工作,再加上相關(guān)的視頻處理并接入網(wǎng)絡(luò),就構(gòu)成了一個智能終端設(shè)備,可用于工廠、銀行及小區(qū)等場合全天候的智能監(jiān)控,具有廣闊的市場和應(yīng)用前景。