簡介
對中斷的處理實現,記憶體映射功能以及對I/O通道的控制接口函式ioct1的實現等,並把 它們定義在struct file_operations中。這樣當應用程式對設備檔案進行諸如open、close、read、write等系統調用操作時,Linux核心將通 過file_operations結構訪問驅動程式提供的函式。例如,當應用程式對設備檔案執行讀操作時,核心將調用file_operations結構 中的read函式。在系統平台上對USB口數碼攝像頭驅動,首先把USB控制器驅動模組靜態編譯進核心,使平台中支持USB接口,再在需要使用攝像頭採集 時,使用insmod動態載入其驅動模組,這樣攝像頭就可正常工作了,接著進行了下一步對視頻流的採集編程。
程式中定義的數據結構
struct video_capability grab_cap;
struct video_picture grab_pic;
struct video_mmap grab_buf;
struct video_mbuf grab_vm;
這些數據結構都是由Video4Linux支持的,它們的用途如下:
*video_capability包含攝像頭的基本信息,例如設備名稱、支持的最大最小解析度、信號源信息等,分別對應著結構體中成員變數name [32]、maxwidth、maxheight、minwidth、minheight、channels(信號源個數)、type等;
*voide_picture包含設備採集圖像的各種屬性,如brightness(亮度)、hue(色調)、contrast(對比度)、whiteness(色度)、depth(深度)等;
*video_mmap用於記憶體映射;
*video_mbuf利用mmap進行映射的幀信息,實際上是輸入到攝像頭存儲器緩衝中的幀信息,包括size(幀的大小)、frames(最多支持的幀數)、offsets(每幀相對基址的偏移)。
程式中用到的主要系統調用函式有: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系統中把設備看成設備檔案,在用戶空間可以通過標準的I/O系統調用函式操作設備檔案,從而達到與設備通信互動的目的。當然,在設備驅動中要提供對這些函式的相應支持。這裡說明一下ioctl(int fd,int cmd,…)函式,它在用戶程式中用來控制I/O通道,其中,fd代表設備檔案描述符,cmd代表用戶程式對設備的控制命令,省略號一般是一個表示類型長度的參數,也可沒有。
(2)採集程式實現過程
首先打開視頻設備,攝像頭在系統中對應的設備檔案為/dev/video0,採用系統調用函式grab_fd=open("/dev/video0", O_RDWR),grab_fd是設備打開後返回的檔案描述符(打開錯誤返回-1),以後的系統調用函式就可使用它來對設備檔案進行操作了。接著,利用 ioctl(grab_fd,VIDIOCGCAP,&grab_cap)函式讀取struct video_capability中有關攝像頭的信息。該函式成功返回後,這些信息從核心空間拷貝到用戶程式空間grab_cap各成員分量中,使用 printf函式就可得到各成員分量信息,例如printf("maxheight=%d",grab_fd.maxheight)獲得最大垂直解析度的 大小。不規則用ioctl(grab_fd,VIDIOCGPICT,&grab_pic)函式讀取攝像頭緩衝中voideo_picture信息。在用戶空間程式中可以改變這些信息,具體方法為先給分量賦新值,再調用VIDIOCSPICT ioctl函式,例如:
grab_fd.depth=3;
if(ioctl(grab_fd,VIDIOCSPICT,&grab_pic)<0)
{perror("VIDIOCSPICT");return -1;};
完成以上初始化設備工作後,就可以對視頻圖像截取了,有兩種方法:一種是read()直接讀取;另外一種mmap()記憶體映射。Read()通過核心緩衝 區來讀取數據;而mmap()通過把設備檔案映射到記憶體中,繞過了核心緩衝區,最快的磁碟訪問往往還是慢於最慢的記憶體訪問,所以mmap()方式加速了 I/O訪問。另外,mmap()系統調用使得進程之間通過映射同一檔案實現共享記憶體,各進程可以像訪問普通記憶體一樣對檔案進行訪問,訪問時只需要使用指針 而不用調用檔案操作函式。因為mmap()的以上優點,所以在程式實現中採用了記憶體映射方式,即mmap()方式。
利用mmap()方式視頻裁取具體進行操作如下。
①先使用ioctl(grab_fd,VIDIOCGMBUF,&grab_vm)函式獲得攝像頭存儲緩衝區的幀信息,之後修改voideo_mmap中的設定,例如重新設定圖像幀的垂直及水平解析度、彩色顯示格式。可利用如下語句
grab_buf.height=240;
grab_buf.width=320;
grab_buf.format=VIDEO_PALETTE_RGB24;
②接著把攝像頭對應的設備檔案映射到記憶體區,具體使用grab_data=(unsigned char*)mmap(0,grab_vm.size,PROT_READ|PROT_WRITE,MAP_SHARED,grad_fd,0)操作。這 樣設備檔案的內容就映射到記憶體區,該映射內容區可讀可寫並且不同進程間可共享。該函式成功時返回映像記憶體區的指針,挫敗時返回值為-1。
下面對單幀採集和連續幀採集進行說明:
*單幀採集。在上面獲取的攝像頭存儲緩衝區幀信息中,最多可支持的幀數(frames的值)一般為兩幀。對於單幀採集只需設定 grab_buf.frame=0,即採集其中的第一幀,使用ioctl(grab_fd,VIDIOCMCAPTURE,&grab_buf) 函式,若調用成功,則激活設備真正開始一幀圖像的截取,是非阻塞的。接著使用ioctl(grab_fd,VIDIOCSYNC,&frame) 函式判斷該幀圖像是否截取完畢,成功返回表示截取完畢,之後就可把圖像數據保存成檔案的形式。
*連續幀採集。在單幀的基礎上,利用grab_fd.frames值確定採集完畢攝像頭幀緩衝區幀數據進行循環的次數。在循環語句中,也是使用VIDIOCMCCAPTURE ioct1和VIDIOCSYNC ioctl函式完成每幀截取,但要給採集到的每幀圖像賦地址,利用語句buf=grab_data+grab_vm.offsets[frame],然後保存檔案的形式。若要繼續採集可再加一個外循環,在外循環語句只要給原來的內循環再賦frame=0即可。
小結
筆者最後在宿主機PC上使用交叉編譯器編譯連結連續幀採集程式(以雙幀採集為例並保存成bmp檔案檔案形式)使之生成可執行代碼,並完成了向目標平台的移 植。為了進一步觀察採集的圖像效果,筆者在目標平台帶網路支持的基礎上,編寫了一個簡單的網路通信程式,把採集到並保存為bmp的圖像檔案通過網路傳輸到 PC機上進行顯示,把採集到並保存為bmp的圖像檔案通過網路傳輸到PC機上進行顯示,通過對效果的分析,再回到採集程式中重新設定 video_picture中的信息,如亮度、對比度等和video_mmap中的解析度,重新移植以達到最好效果為準。
在圖1中的嵌入式系統平台上,套用本文所述方法完成視頻採集工作,再加上相關的視頻處理並接入網路,就構成了一個智慧型終端設備,可用於工廠、銀行及小區等場合全天候的智慧型監控,具有廣闊的市場和套用前景。