Camera編程

V4L2(video 4 linux 2)  

可以支持多種設備,它可以有以下幾種接口:

  1. 視頻採集接口(video capture interface):這種應用的設備可以是高頻頭或者攝像頭.V4L2的最初設計就是應用於這種功能的.       

  2. 視頻輸出接口(video output interface):可以驅動計算機的外圍視頻圖像設備--像可以輸出電視信號格式的設備.       

  3. 直接傳輸視頻接口(video overlay interface):它的主要工作是把從視頻採集設備採集過來的信號直接輸出到輸出設備之上,而不用經過系統的CPU.       

  4. 視頻間隔消隱信號接口(VBI interface):它可以使應用可以訪問傳輸消隱期的視頻信號.       

  5. 收音機接口(radio interface):可用來處理從AM或FM高頻頭設備接收來的音頻流.

 

Video4linux下視頻編程的流程:

(1)打開視頻設備:

(2)讀取設備信息

(3)更改設備當前設置(沒必要的話可以不做)

(4)進行視頻採集,兩種方法:

        a.內存映射

        b.直接從設備讀取

(5)對採集的視頻進行處理

(6)關閉視頻設備。

/* 這樣的流程地球人都曉得 */ 

 

 

關鍵步驟介紹 :

(1)打開視頻:

          Open(”/dev/video0”,vdàfd);

關閉視頻設備用close(”/dev/video0”,vdàfd);

(2)讀video_capability中信息

          ioctl(vd->fd, VIDIOCGCAP, &(vd->capability))

成功後可讀取vd->capability各分量 eg.

(3)讀video_picture中信息

          ioctl(vd->fd, VIDIOCGPICT, &(vd->picture));

(4)改變video_picture中分量的值 (可以不做的)

先爲分量賦新值,再調用VIDIOCSPICT

Eg.

vd->picture.colour = 65535;

if(ioctl(vd->fd, VIDIOCSPICT, &(vd->picture)) < 0)

{

perror("VIDIOCSPICT");

return -1;

}

(5)初始化channel (可以不做的)

必須先做得到vd->capability中的信息

for (i = 0; i < vd->capability.channels; i++)

{

      vd->channel[i].channel = i;

      if (ioctl(vd->fd, VIDIOCGCHAN, &(vd->channel[i])) < 0)

      {

         perror("v4l_get_channel:");

         return -1;

      }

}

 /* 通過ioctl,將struct v4l_struct作爲參數的設備操作而已,那麼重點將是整個結構體:struct v4l_struct*/

 

typedef  struct v4l_struct

{

   int fd;

   struct   video_capability    capability;

   struct   video_channel      channel[4];

   struct   video_picture        picture;

   struct   video_window       window;

   struct   video_capture      capture;

   struct   video_buffer         buffer;

   struct   video_mmap         mmap;

   struct   video_mbuf           mbuf;   

   unsigned char                  *map;

   int frame;

   int framestat[2];

}vd;

 

 /* 那麼,對該結構體的瞭解便成了關鍵*/

 Video4linux支持的數據結構及其用途 

(1)video_capability 包含設備的基本信息(設備名稱、支持的最大最小分辨率、信號源信息等),包含的分量:

name[32]   //設備名稱

maxwidth ,

maxheight,

minwidth,

minheight

Channels   //信號源個數

type           //是否能capture,彩色還是黑白,是否能裁剪等等。值如VID_TYPE_CAPTURE等

(2)video_picture設備採集的圖象的各種屬性

brightness 0~65535

hue

colour

contrast

whiteness

depth             // 24

palette           // VIDEO_PALETTE_RGB24

(3)video_channel         關於各個信號源的屬性

    Channel       //信號源的編號

    name

    tuners

    Type            // VIDEO_TYPE_TV | IDEO_TYPE_CAMERA

    Norm制式

(4)video_window       //包含關於capture area的信息

    x     x windows 中的座標.

    y     x windows 中的座標.

    width    The width of the image capture.

    height   The height of the image capture.

    chromakey       A host order RGB32 value for the chroma key.

    flags                 Additional capture flags.

    clips                  A list of clipping rectangles. (Set only)

    clipcount           The number of clipping rectangles. (Set only)

(5)video_mbuf      //利用mmap進行映射的幀的信息

      size           //每幀大小

      Frames     //最多支持的幀數

      Offsets     //每幀相對基址的偏移

(6)video_buffer  最底層對buffer的描述  

       void *baseBase     // physical address of the buffer

     int height                // Height of the frame buffer

      int width                  // Width of the frame buffer

      int depth                 // Depth of the frame buffer

      int bytesperline       // Number of bytes of memory between the start of two adjacent lines

   實際顯示的部分一般比它描述的部分小

(7)video_mmap      //用於mmap

 

重點:

用mmap(內存映射)方式截取視頻

mmap( )系統調用使得進程之間通過映射同一個普通文件實現共享內存。普通文件被映射到進程地址空間後,進程可以向訪問普通內存一樣對文件進行訪問,不必再調用read(),write()等操作。

兩個不同進程A、B共享內存的意思是,同一塊物理內存被映射到進程A、B各自的進程地址空間。進程A可以即時看到進程B對共享內存中數據的更新,反之亦然

採用共享內存通信的一個顯而易見的好處是效率高,因爲進程可以直接讀寫內存,而不需要任何數據的拷貝

(1)設置picture的屬性

(2)初始化video_mbuf,以得到所映射的buffer的信息

          ioctl(vd->fd, VIDIOCGMBUF, &(vd->mbuf))

(3)可以修改video_mmap和幀狀態的當前設置

    Eg.     vd->mmap.format = VIDEO_PALETTE_RGB24

              vd->framestat[0] = vd->framestat[1] = 0; vd->frame = 0;

(4)將mmap與video_mbuf綁定

void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset )

    len     // 映射到調用進程地址空間的字節數,它從被映射文件開頭offset個字節開始算起

    Prot   // 指定共享內存的訪問權限 PROT_READ(可讀), PROT_WRITE (可寫), PROT_EXEC (可執行)

    flags  // MAP_SHARED   MAP_PRIVATE中必選一個 // MAP_ FIXED不推薦使用addr //共內存享的起始地址,一般設0,表示由系統分配

    Mmap( ) // 返回值是系統實際分配的起始地址

    if((vd->map = (unsigned char*)mmap(0, vd->mbuf.size, PROT_READ|PROT_WRITE, MAP_SHARED, vd->fd, 0)) < 0)

    {

        perror("v4l_mmap mmap:");

        return -1;

    }

(5)Mmap方式下真正做視頻截取的 VIDIOCMCAPTURE

       ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap)) ;

若調用成功,開始一幀的截取,是非阻塞的,

是否截取完畢留給VIDIOCSYNC來判斷

(6)調用VIDIOCSYNC等待一幀截取結束

if(ioctl(vd->fd, VIDIOCSYNC, &frame) < 0)

{

    perror("v4l_sync:VIDIOCSYNC");

    return -1;

}

若成功,表明一幀截取已完成。可以開始做下一次 VIDIOCMCAPTURE

frame是當前截取的幀的序號。

 

****關於雙緩衝:

video_mbuf mbuf.frames = 2;      //一幀被處理時可以採集另一幀

int frame;                     //當前採集的是哪一幀

int framestat[2];           //幀的狀態 沒開始採集 |  等待採集結束

幀的地址由 vd->map+  vd->mbuf.offsets[vd->frame]得到

採集工作結束後調用munmap取消綁定

munmap(vd->map, vd->mbuf.size)

 

/* 關於直接讀取的方式,太落伍,不再贅述 */

 

 

 

區分video爲 順序式和交錯式:

順序式video順序的傳輸video image所有的行,

交錯式video則把一個video劃分成兩個fields,分別保存video image的奇數行和偶數行,被稱作奇數field和偶數field. 

       陰極射線電視機需要交替的顯示行來組成一個完整的幀,交替的時延需要我們交替的傳輸奇數field和偶數field.

       這個奇怪的技術的引用是因爲:在刷新率接近電影時,圖片會消退的過快。使用field可以避免使用double buffer以及額外的帶寬需求。

 

首先要明確camera並不是在同一時間曝光一幀,camera通過許多fields來傳輸這些幀的,這些field是在不同瞬間拍照。屏幕上的一個對象因此會在兩個field之間產生動畫效果。這種情況下需要識別哪一幀更老一點,也稱作“瞬間序”

 

當驅動通過fields 提供或者接收images,應用層應該知道如何通過這些fields組合成幀,通過劃分爲top bottom field, “空間序”:

top field的第一行是幀的第一行,

bottom field的第一行是幀的第二行。

 /* 問題:多個fields和一個完整的幀的關係 */

 

然而因爲field是一個跟着一個拍的,爭論幀是由top還是bottom開始的是沒意義的,任何兩個相鄰的top bottom field, 或者 bottom top field都可以組成一個有效的幀。

 

與直覺相反top field不一定是older field, older filed是否包含top 或者bottom lines是由video標準決定的. 因此要區分瞬間序空間序。下面的圖會給出清晰的解釋。

 

所有的video capture和out devices必須彙報當前的field順序。 一些驅動可能允許選擇不同的序,end應用可以在調用VIDIOC_S_FMT前初始化struct v4l2_pix_format的 field成員。否則可以使用V4L2_FIELD_ANY

 

下面列出了可能的field類型

V4L2_FIELD_ANY0

Application 可以請求使用這個參數,如果V4L2_FIELD_NONE, V4L2_FIELD_TOP, V4L2_FIELD_BOTTOM V4L2_FIELD_INTERLACE 中任何一個格式都支持.驅動選擇使用哪一個格式依賴於硬件能力,以及請求的image尺寸,驅動選擇一個然後返回這個格式。struct_buffer的field成員不可以爲V4L2_FIELD_ANY.

 

V4L2_FIELD_NONE1

Images是順序式格式,驅動可以指定這種order,當驅動無法區分V4L2_FIELD_TOP和V4L2_FIELD_BOTTOM

 

V4L2_FIELD_TOP2

Images僅僅包含top field

 

V4L2_FIELD_BOTTOM3

Images僅僅包含bottom field. 應用可能希望防止設備捕獲interlaced的圖片,因爲這種圖片會在運動物體周圍產生毛狀特效

 

V4L2_FIELD_INTERLACED4

Images包含top和bottom field, 隔行交替,fields的瞬間序依賴於當前video的標準。M/NTSC首先傳輸bottom field, 其他的則先傳輸top field

 

V4L2_FIELD_SEQ_TB5

Images包含top和bottom field, top field的行首先存放在memory中,然後緊跟着bottom field的行。 Fields一直以瞬間序存儲,較老的放在內存前面。Images的尺寸和幀相關,而不是field

 

V4L2_FIELD_SEQ_BT6

Images包含top和bottom field, bottom field的行首先存放在memory中,然後緊跟着top field的行。 Fields一直以瞬間序存儲,較老的放在內存前面。Images的尺寸和幀相關,而不是field

 

V4L2_FIELD_ALTERATE7

 一個幀的兩個field分別放在不同的buffer, 按照瞬間序,也就是說老的一個是第一個。driver或者應用指明field的奇偶性(奇偶性:當前的field是top 還是bottom field). 任何兩個連續的field構成一個frame,是否兩個field是連續的,不需要drop掉他們,可以通過v4l2_buffer中的sequence 成員判定。Images的尺寸和frame而不是fields相關


 

V4L2_FIELD_INTERLACED_TB8

Images 包含top和bottom field, 每行交替, top field在前面。top field首先傳送


 

V4L2_FIELD_INTERLACED_BT9Images 包含top和bottom field, 每行交替, bottom field在前面。bottom field首先傳送

 

 

Field Order, Top Field First Transmitted

 


Field Order, Bottom Field First Transmitted


 


from:  http://blog.csdn.net/kickxxx/article/details/6367669

相關鏈接:

http://blog.csdn.net/xiao888lin/article/details/5727446

===========================================================================================================

 

   ***採集程序實現過程***

   首先打開視頻設備,攝像頭在系統中對應的設備文件爲/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) 函數讀取攝像頭緩衝中video_picture信息。在用戶空間程序中可以改變這些信息,具體方法爲先給分量賦新值,再調用VIDIOCSPICT ioctl函數,例如:

   grab_pic.depth=3;

   if(ioctl(grab_fd, VIDIOCSPICT, &grab_pic)<0)

   {

               perror("VIDIOCSP[1]ICT");

               return -1;

         };

   完成以上初始化設備工作後,就可以對視頻圖像截取了,有兩種方法:

               一種是read()直接讀取;         // 通過內核緩衝 區來讀取數據

               另外一種mmap()內存映射。

/* mmap()通過把設備文件映射到內存中,繞過了內核緩衝區,最快的磁盤訪問往往還是慢於最慢的內存訪問,所以mmap()方式加速了 I/O訪問。

另外,mmap()系統調用使得進程之間通過映射同一文件實現共享內存,各進程可以像訪問普通內存一樣對文件進行訪問,訪問時只需要使用指針 而不用調用文件操作函數。因爲mmap()的以上優點,所以在程序實現中採用了內存映射方式,即mmap()方式。 */

 

   利用mmap()方式視頻裁取具體進行操作如下。

   ①先使用ioctl(grab_fd,  VIDIOCGMBUF, &grab_vm) 函數獲得攝像頭存儲緩衝區的幀信息,

                 之後修改video_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) 函數判斷該幀圖像是否截取完畢,成功返回表示截取完畢,之後就可把圖像數據保存成文件的形式。

                       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即可。

------------------------------------------------------------------------------------------------------------------------------------

 

http://blog.csdn.net/evilcode/article/category/932884

博客掃蕩

---------------------------------------------------------------------------------------------------------------

原文地址: http://www.eoeandroid.com/thread-34671-1-1.html

作者的博客:http://blog.csdn.net/wxzking

關於V4L2驅動的code,可以去下面兩個路徑下查看:
/kernel/drivers/media/video/
/kernel/include/media/
關於camera的code,可以去下面路徑查看:
/frameworks/base/camera/libcameraservice/
/frameworks/base/incude/ui/
/hardware/mx5x/libcamera/

 

 

V4L2的移植

vivi(drivers/media/video/vivi.c)講解一個V4L2驅動的編寫。注意它是一個虛擬的設備驅動,沒有與實際的硬件打交道。

    1、分析幾個重要數據結構:

     vivi.c包含頭文件v4l2-device.h和v4l2-ioctl.h,

                        其中v4l2-device.h中包含了v4l2-subdev.h,

                                                                    v4l2-subdev.h中又包含了v4l2-common.h,

                                                                                                             v4l2-common.h中包含了v4l2-dev.h。

 

   

關於視頻採集方式

操作系統一般把系統使用的內存劃分成用戶空間和內核空間,分別由應用程序管理和操作系統管理。應用程序可以直接訪問內存的地址,而內核空間存放的是 供內核訪問的代碼和數據,用戶不能直接訪問。v4l2捕獲的數據,最初是存放在內核空間的,這意味着用戶不能直接訪問該段內存,必須通過某些手段來轉換地址。

一共有三種視頻採集方式:

1)使用read、write方式:直接使用 read 和 write 函數進行讀寫。這種方式最簡單,但是這種方式會在 用戶空間和內核空間不斷拷貝數據 ,同時在用戶空間和內核空間佔用 了 大量內存,效率不高。

2)內存映射方式(mmap):把設備裏的內存映射到應用程序中的內存控件,直接處理設備內存,這是一種有效的方式。上面的mmap函數就是使用這種方式。

3)用戶指針模式:內存由用戶空間的應用程序分配,並把地址傳遞到內核中的驅動程序,然後由 v4l2 驅動程序直接將數據填充到用戶空間的內存中。這點需要在v4l2_requestbuffers裏將memory字段設置成V4L2_MEMORY_USERPTR。

 

第一種方式效率是最低的,後面兩種方法都能提高執行的效率,但是對於mmap 方式,文檔中有這樣一句描述 --Remember the buffers are allocated in physical memory, as opposed to virtual memory which can be swapped out to disk. Applications should free the buffers as soon as possible with the munmap () function .(使用mmap方法的時候,buffers相當於是在內核空間中分配的,這種情況下,這些buffer是不能被交換到虛擬內存中,雖然這種方法不怎麼影響讀寫效率,但是它一直佔用着內核空間中的內存,當系統的內存有限的時候,如果同時運行有大量的進程,則對系統的整體性能會有一定的影響。) 

       所以,對於三種視頻採集方式的選擇,推薦的順序是 userptr 、 mmap 、 read-write 。當使用 mmap 或 userptr 方式的時候,有一個環形緩衝隊列的概念,這個隊列中,有 n 個 buffer ,驅動程序採集到的視頻幀數據,就是存儲在每個 buffer 中。在每次用 VIDIOC_DQBUF 取出一個 buffer ,並且處理完數據後,一定要用 VIDIOC_QBUF 將這個 buffer 再次放回到環形緩衝隊列中。環形緩衝隊列,也使得這兩種視頻採集方式的效率高於直接 read/write 。

 

 ---------------------------------------------------------------------------------------------------------------------------------------------------------------

 

YUV信號有很多種,一般YUV420和YUV422用的比較多,

       YUV422格式 ,又分爲很多小類,按照U、V的排列可以有YUYV,  YVYU,  UYVY,  VYUY四種,其中,YUYV一般又稱作yuv2格式。

 

       而這四種YUV422格式 ,每種又可以分爲2小類,按Y和UV的排列可以有打包格式平面格式。 

       例如,一個640×480×2的YUV文件,

              打包格式    就是YUYVYUYV這樣一直排列下去,

              平面格式    就是先640×480個Y排列完,然後是 640×240個U,然後是640×240個V這樣排列,如下圖:

       
      假如有一幅640×480的圖片,用yuv422來表示,那麼,採樣方式就是每個像素採樣Y信號,U,V信號隔一個採樣,這樣算下來,就有640×480 個Y,640×240個U,640×240個V,一幅640×480大小的YUV圖片佔的總字節數爲640×480×2個字節,每像素2個字節,也就是 16位。

     在內存種這樣排列:Y0U0Y1V0 Y2U1Y3V1...

     第一個像素的YUV值爲: Y0   U0    V0

     第二個像素的YUV值爲: Y1   U0    V0

     第三個像素的YUV值爲: Y2   U1    V1

.....其他以此推類,也就是說每兩個像素是共用了UV的;在一行上來看,每個像素的YUV值種Y值被採樣,UV值採樣0後,跳到3,然後5,所以每行上Y有640個,U,V各320個


發佈了2 篇原創文章 · 獲贊 1 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章