基於Video4Linux的視頻採集模塊開發(轉)

Linux系統中,攝像頭驅動程序安裝好後,爲了進行視頻採集必須加入Video4Linux模塊,從而能夠經過Video4Linux模塊提供的編程接口(API)從攝像頭設備中獲取圖像幀。下面具體研究基於V4L的視頻採集程序設計。
1 Video4Linux概述
     Video4Linux是Linux中關於視頻設備的內核驅動,爲針對視頻設備的應用程序編程提供一系列接口函數,在Linux下,視頻採集的設備的正常使用依賴Video4-Linux標準的支持,在編譯內核時要選中Video4Linux項,對應的設備文件是/dev/video。對於USB口攝像頭,其驅動程序中須要提供基本的I/O操做接口函數open、read、write、close的實現。對中斷的處理實現,內存映射功能以及對I/O通道的控制接口函數ioctl的實現等,並把它們定義在structfile operations中。這樣當應用程序對設備文件進行諸如open、read、write、close等系統調用時,Linux內核將經過file operations結構訪問驅動程序提供的函數。linux

2.視頻編程的數據結構及函數編程

        Linux下V4L視頻採集,主要是調用V4L模塊參數進行視頻原始數據採集。使用的一些主要參數和函數定義在系統/include/linux/videodev.h文件中。下面是videodev.h中定義的幾個重要的數據結構以下:數據結構

[cpp]  view plain  copy
 
  1. typedef struct v4l_struct  
  2. {  
  3.   int   fd;  
  4.   struct video_capability capability;  
  5.   struct video_channel channel[4];  
  6.   struct video_picture picture;  
  7.   struct video_window window;  
  8.   struct video_capture capture;  
  9.   struct video_buffer buffer;  
  10.   
  11.   struct video_mmap mmap;  
  12.   struct video_mbuf mbuf;  
  13.   unsigned char *map;  
  14.   int framestat[2];  
  15. }v4l_device;  

video_capability結構包含設備的基本信息,如設備名稱、支持的最大最小分辨率、信號源信息等。它包含如下份量:name[32]指定設備名稱,maxwidth,maxheight,minwidth,minheight指定攝像頭能捕捉的最大和最小的圖像尺寸。Channels指定信號源個數,type表示所截取的圖像是否能被捕捉,是彩色仍是黑白,是否能裁剪,值如VID TYPE CAPTURE等。
video_channel結構指定各個信號源的屬性,它包含如下份量:channel指定信號源的編號,name爲每一個信號源的名稱,Type指定所輸入的信號類型,包含兩種類型:VIDEO_TYPE_TV表示爲電視信號;VIDEO_TYPE_CAMERA表示爲攝像頭信號。Norm表示電視信號所使用的制式,有VIDEO_MODE_PAL、VIDEO_MODE_NTSC、VIDEO_MODE_SECAM、VIDEO_MODE_AUTO等幾
種選項。
video_picture結構用來設置採集圖像的各類屬性。它包括如下幾個份量:brightness、hue、colour、contrast來爲採集的圖像提供相似電視信號的控制,whiteness來爲灰度圖像提供額外控制。
video_window結構設置採集的圖像的顯示方式。它包括如下幾個份量:X用來描述窗口左上方的X座標,Y用來描述左上方的Y座標,width用來描述採集圖像的寬度,height用來描述採集圖像的高度,當採用色度鍵時chromakey用來表示顏色,用RGB32格式表示的。
video_mbuf結構用來描述利用mmap進行映射的幀的信息,其實是輸入到攝像頭存儲器緩衝區中的幀信息。它包含如下幾個份量:size表示每幀大小,frames用來表示最多支持的幀數,offsets表示每幀相對基址的偏移。
video_buffer結構用來對最底層的buffer進行描述,它包含如下幾個含量:void *baseBase描述buffer的物理地址,height描述frame buffer的高度,width描述frame buffer的寬度,depth描述frame buffer的深度,bytesperline描述每兩個相鄰的線之間的存儲器字節數。
video mmap用於內存映射。ide

     在V4L編程中,與圖像設備交互主要是依靠系統調用ioctl函數來實現的。Ioctl(input/output control)函數是經過打開的文件描述符對各類文件尤爲是字符設備文件進行控制,完成特定的I/O操做。V4L支持的ioctl命令一般須要幾個參數,通常狀況下,第一個參數fd表明打開的文件/設備,第二個參數cmd爲驅動程序的特殊命令,有的還有第3個或者更多的參數,根據不一樣的驅動程序不一樣。函數

    在V4L中定義了一些重要的宏,用來做爲ioctl函數的第二個參數,用來操縱設備,一些主要的宏定義說明以下:
1)VIDIOCGCAP:Ioctl操做獲得圖像設備的基本信息,保存在video_capability結構體中。
2)VIDIOCSCAP:Ioctl操做根據video_capability結構體中的值來設置圖像設備初始參數。
3)VIDIOCGPICT:Ioctl操做獲得圖像的基本信息,保存在video_picture中,包括圖像的亮度、色度、對比度等。
4)VIDIOCSPICT:Iocfl操做根據video_picture結構體中的值來設置採集圖像的初始參數。
5)VIDIOCGMBUF:Ioctl操做獲取攝像頭存儲緩衝區的幀信息。
6)VIDIOCAMCAPTURE:獲取視頻圖像。
7)VIDIOCGWIN:loctl操做獲得採集圖像區域的基本信息,保存在video_window結構體中。設置目標窗口的屬性。
8)VIDIOCSWIN:Ioctl操做根據video_window結構體中的值來設置採集圖像區域大小等參數。
9)VIDIOSYNC:Ioctl操做判斷視頻圖像是否截取成功。.net

視頻採集函數採用自頂向下的操做方式,首先規劃並定義這些函數。
1)v41_open() :開啓視頻採集的設備函數
       首先定義了一個默認的設備文件路徑,若是能夠開啓「/dev/video0"則將取回的信息放到vd中。設計

[cpp]  view plain  copy
 
  1. #define DEFAULT_DEVICE "dev/video0"  
  2. int v4l_open(char *dev,v4l_device *vd)  
  3. {  
  4.  if(!dev)  
  5.  dev=DEFAULT_DEVICE;  
  6.  if((vd->fd=open(dev,O_RDWR))<0)  
  7.  {  
  8.    perror("camera open:");  
  9.    return-1;  
  10.  }  
  11.    if(v4l_get_capability(vd))  
  12.    return -1;  
  13.    if(v41_get_picture(vd))  
  14.    return -1;  
  15.    return 0;  
  16. }  

當應用程序輸入的dev設備文件參數不存在時,就使用「/dev/video0」這個值。Linux下對設備文件的打開是經過open()函數完成的。指針

[cpp]  view plain  copy
 
  1. if((vd->fd=open(dev,O_RDWR))<0)  
  2. {  
  3.    perror("camera_open:");  
  4.    return-1;  
  5. }  

設備文件開啓後,將回傳的文件描述符放到vd->fd裏。在應用程序中使用v4l_open()函數時,須要先聲明一個video_device類型的設備變量,而後經過調用v4l_open()函數將設備打開。若是能夠正常打開設備文件,設備變量也就得到了相應的回傳信息。v41_open()函數被調用的程序以下:orm

[cpp]  view plain  copy
 
  1. video_device vd;  
  2. if(camera_open('‘/dev/video0」,&vd))  
  3. {  
  4.   return-1;  
  5. }  

2)v4l_get_capability():獲取設備信息
成功開啓設備後,首先取得設備信息,v4l_get_capability()函數調用ioctl()取得設備文件相關信息,而且將取得的信息放到視頻

[cpp]  view plain  copy
 
  1. video_capability結構體中。  
  2. int v4l_get_capability(v4l_device *vd)  
  3. {  
  4.  if(ioctl(vd->fd,VIDEOCGCAP,&(vd->capability))<0)  
  5.  {  
  6.    perror("camera_get_capability:");  
  7.    return-1;  
  8.  }  
  9.  return 0;  
  10. }  

這個函數的主要內容是ioctl(vd->fd,VIDEOCGCAP,&(vd->capability))。vd->fd是由v4l_open()傳回來的文件描述符,而傳遞VIDIOCGCAP給ioctl()則會傳回設備相關信息,存放與vd->capability。
3)v4l_get_picture():圖像初始化
       取得設備信息後,還要取得圖像信息。所謂圖像信息是指輸入到視頻捕捉設備的圖像格式。

[cpp]  view plain  copy
 
  1. int v4l_get_picture(v4l_device *vd)  
  2. {  
  3.   if(ioctl(vd->fd,VIDIOCGPICT,&(vd->picture))<0)  
  4.   {  
  5.     perror(「camera_get_picture:");  
  6.     return-1;  
  7.    }  
  8.     return 0;  
  9. }  

4)改變video_picture中的份量的值,先爲份量賦新值,再傳遞VIDIOCSPICT給ioctl()函數進行設置。

[cpp]  view plain  copy
 
  1. vd->picture.colour=65535;  
  2. if(ioctl(vd->fd,VIDIOCSPICT,&(vd->picture))<0)  
  3. {  
  4.   perror("VIDIOCSPICT");  
  5.   return-1;  
  6. }  

5)v4l_get_channels():初始化channel

[cpp]  view plain  copy
 
  1. int v4l_get_channels(camera_device *vd)  
  2. {  
  3. int i;  
  4. for(i=0;i < vd->capability.channels;i++){  
  5. vd->channel[i].channel=i;  
  6. if(ioctl(vd->fd,VIDIOCGCHAN,&(vd->channel[i]))<0)  
  7. {  
  8. perror("camera_get_channel:");  
  9. return -1;  
  10. }}  
  11. return 0;  
  12. }  

6)v4l_close():關閉設備

[cpp]  view plain  copy
 
  1. int v4l_close(camera_device *vd)  
  2. {  
  3.   close(vd->fd);  
  4.   return 0;  
  5. }  

3.圖像採集流程

        從V4L的數據結構看出,完成基於V4L的USB視頻數據採集,先要得到相關的視頻採集的設備的信息和圖像信息,並對採集窗口、顏色模式、幀狀態初始化,而後才能進行視頻圖像的採集。
具體操做流程以下:
       1)打開設備進行設定,經過使用標準的文件打開函數操做;
       2)查詢圖像緩衝區信息並設定;VIDIOCSFBUF主要是設定圖像緩衝區的基地址和緩衝區大小;
       3)查詢圖像截取窗口信息並設定:VIDIOCGWIN和VIDIOCSWIN主要設定截取圖像尺寸和位置;
       4)查詢通道信息並設定,能夠從一個或者多個通道捕獲數據,來進行通道的查詢設定函數VIDEOCGCHAN和VIDEOCSCHAN;
       5)獲取圖像並存放在緩衝區。

在V4L中圖像截取方式有兩種:
      1)經過調用read具備阻塞功能的函數,將輸出圖像數據複製到預先設定好的數據緩衝區中,就能夠實現對每幀圖像數據的讀取;
      2)用mmap方式,直接將設備文件/dev/video0映射到內存中,不須要額外的對數據緩衝區進行復制工做,加快了圖像信息的捕捉速度。另外,mmap()系統調用使得進程之間經過映射同一文件實現共享內存,各個進程能夠像訪問普通內存同樣對文件進行訪問,訪問時只須要使用指針而不用調用文件操做函數。

   視頻採集的流程以下圖所示。

 鑑於mmap()以上優勢,本文采用的第2種方式,利用mmap()方式對視頻進行採集與裁剪的操做以下:
        1)先使用ioctl(vd->fd,VIDIOCGMBUF,&vd->mbuf)數得到攝像頭存儲緩衝區的幀信息,以後修改video_map中的設置,例如從新設置圖像幀的垂直及水平分辨率、彩色顯示格式以及當前幀的狀態。可利用以下語句

[cpp]  view plain  copy
 
  1. vd->mmap.height=240;  
  2. vd->mmap.width=320;  
  3. vd->mmap.format=VIDEO_PALETTE_RGB24;  
  4. vd->framestat[0]=vd->framestat[1]=0;  
  5. vd->flame=0;  

      2)接着把攝像頭對應的設備文件映射到內存區,命令爲

[cpp]  view plain  copy
 
  1. vd->map=(unsignedchar *)mmap(0,vd->mbuf.size,PROT_READ|PROT_WRITE,MAP_SHARED,vd->fd,0)   

          這樣設備文件的內容就映射到內存區,映射內容區可讀可寫而且不一樣進程間可共享。該函數成功時返回映像到內存區的指針,失敗時返回1。mmap()函數中第一個參數表示共享內存的起始地址,在此外設爲0表示由系統分配。第二個參數表示映射到調用進程地址空間的字節數,此值爲vd->mbuf.size。第三個參數指定共享內存的訪問權限,它有如下幾個值PORT_READ(可讀),PROT_WRITE(可寫),PROT_EXEC(可執行),此處設爲可讀和可寫。第四個參數指定共享內存的屬性,有MAP_SHARED、MAP _PRIVATE、MAP_FIXED三種屬性,通常狀況下從中選一個使用。

       3)視頻截取。把視頻映射到內存後就可進行視頻的截取。命令爲

[cpp]  view plain  copy
 
  1. ioctl(vd->fd,VIDIOCMCAPTRUE,&(vd->mmap));  

若調用成功,開始一幀的截取,該操做是非阻塞的,是否截取完畢留給VIDIOCSYNC來判斷。

      4)調用VIDEOCSYNC等待一幀截取結束。

[cpp]  view plain  copy
 
  1. if(ioctl(vd->fd,VIDIOCSYNC,&frame)<0)  
  2. {  
  3.    perror("VIDIOCSYNC ERRORI!!」);  
  4.    return -1;  
  5. }  

函數調用成功,代表一幀圖像截取完成,能夠開始進行下一次視頻截取。其中frame是當前截取的幀的序號。

      5)因爲採集是採用雙緩衝的方式,這樣在處理一幀時能夠採集另外一幀。frame表示當前採集的是哪一幀圖像,framestat[2]表示幀的狀態(如未開始採集和等待採集結束)。幀在內存中的地址由vd->map+vd->mbuf.offsets[vd->frame]肯定。採集結束後調用munmap命令取消映射:munmap(vd->map,vd->mbuf.size)。

     以上詳細分析了圖像採集流程,下面給出圖像採集的具體實現。

4.攝像頭採集模塊的具體實現 

視頻採集的具體實現過程以下:

第一部分,設備初始化。
(1)首先:必須聲明包含2個頭文件:

[cpp]  view plain  copy
 
  1. #include<sys/types.h>  
  2. #include<linux/videodev.h>  

(2)而後,進行設備初始化,打開視頻設備,攝像頭在系統中對應的設備文件爲/dev/video0,經過調用驅動程序中定義的open操做來完成。以下:

[cpp]  view plain  copy
 
  1. int fd=open("/dev/video」,O_RDW-R);  

其中fd是設備打開後返回的文件描述符,系統調用函數可以使用它對設備文件進行操做。

(3)接着經過調用ioctl VIDIOCGCAP操做讀取struct video_capability中有關攝像頭信息。以下:

[cpp]  view plain  copy
 
  1. struct video_capability grab_capability;  
  2. ioctl(fd,VIDIOCGCAP,&grab_capability);/*得到struct video_capability中的攝像頭的信息*/  

(4) 而後經過調用ioctl VIDIOCSFBUF設置內存緩衝區的相關信息,緩衝區的信息能夠經過printf函數輸出;

(5)經過調用Ioctl VIDIOCSWIN來完成對視頻窗口的設置,如窗口寬度,高度等;

(6)接着經過調用loctl VIDIOCGPICT操做讀取struct video_capability中有關圖像信息。以下:

[cpp]  view plain  copy
 
  1. struct video_picture grab_picture;  
  2. ioctl(fd,VIDIOCGPICT,&grab_picture);/*獲取圖像信息*/  

(7)在獲取圖像信息後,還能夠根據須要改變這些信息,例如對比度、亮度、調色板等,具體作法是先給video_picture中的相應變量賦新值,再利用Ioctl VIDIOCSPICT函數進行設置。以下:

[cpp]  view plain  copy
 
  1. grab_picture.colour=65535;  
  2. if(ioctl(fd,VIDIOCSPICT,&grab_picture))<0)  
  3. {  
  4.   perror("VIDIOCSPICT」);  
  5.   return-1;  
  6. }  

(8)接着初始化channel,必須先完成vd->capability中的信息調用,使用下列函數:

[cpp]  view plain  copy
 
  1. int v4l_get_channels(v4l_device *vd)  

第二部分,使用mmap方式截取視頻。
(1)首先調用ioctl(fd,VIDIOCGMBUF,&grab_vm)函數獲取攝像頭存儲緩衝區的幀信息,以後初始化video_mbuf修改video_mmap中的設置,從新設置圖像信息如幀的垂直及水平分辨率、彩色顯示格式等。以下:

[cpp]  view plain  copy
 
  1. structvideo mmap grab_buf ;  //如下爲設置圖像幀緩衝區信息  
  2. grab_buf.frame=0;   //一次只採集一幀  
  3. grab_buf.height= 240;  //圖像高度  
  4. grab_buf.width=320;   //圖像寬度  
  5. grab_buf.format=VIDEO_PALETTE_RGB24;   //圖像的調色板格式,24位真彩色  
  6. unsigned char *data=mmap(0,240*320*3,PROT_READ| PROT_WRITE,MAP_SHARD,fd,0); //內存映射  
  7. ioctl(grab fd,VIDIOCMCAPTURE,&grab_buf);  //採集圖像  

(2)而後調用ioctl(grab fd,VIDIOCSYNC,&frame)函數,該函數成功返回則表示採集完畢,採集到的圖像數據放到以data爲起始地址,長度爲240*320*3的內存區域中,讀取該內存中的數據即可獲得圖像數據。
(3)在此基礎上一樣可實現連續幀的採集,即一次採集連續多幀圖像的數據。此時首先要設置grab_bur.frame爲要採集的幀數。在循環語句中,也是使用ioctl VIOCMCAPTURE和ioctl VIDIOCSYNC操做完成每幀讀取,可是要給採集到的每幀圖像賦地址爲data+grab_vm.offsets[frame],而後保存文件格式。其中grab_vm爲video_mbuf結構體變量的一個聲明,利用ioctl(fd,VIDIOCGMBUF,
&grab_vm)即可得到grab 的信息。

(4)若要繼續採集可再加一個外循環,在外循環語句中只要給原來的內循環再賦伊grab_buf.frame=0便可。

http://blog.csdn.net/wangrunmin/article/details/7766221

相關文章
相關標籤/搜索