以capture.c爲例子解析V4L2過程

轉載時請註明出處和作者聯繫方式
文章出處:http://blog.csdn.net/jack0106
作者聯繫方式:馮牮
[email protected]

 

      V4L2 V4L 有較大的改動,並已成爲 2.6 的標準接口,函蓋 video\ dvb \FM… ,多數驅動都在向 V4l2 遷移 V4L2 採用流水線的方式,操作更簡單直觀。 一般來說,需要用到的函數就是 open() close() ioctl ()

 

      推薦的參考資源:

      (1) Video for Linux Two API Specification---Revision 2.6.32

      http:// linuxtv.org/downloads/video4linux/API/V4L2_API/spec-single/v4l2.html

      (2) capture.c 官方示例程序

 

      說明:強烈建議閱讀這兩個參考資源!!!如果是初次接觸,建議可以先瀏覽一下第一個參考文檔,但是capture.c程序一定要仔細的看一下,至少得先讓它能夠跑起來(正常運行情況下,在終端下會不斷的輸出一個 ".")。

      下面的講解,都將是基於capture.c程序。但是,capture.c程序有一點點小的不足,後面的內容中會介紹到,在末尾,會附上我稍微修改過的一個版本。

 

      對照capture.c程序,值得我們注意的主要是以下幾點:  

(1)3 I/O 方式

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

     (1.2)mmap 把硬件設備 裏的內存映射 到位於用戶空間的應用程序中的內存地址上, 直接處理設備內存,這是一種有效的 方式

      (1.3) userptr 內存由用戶空間的應用程序分配,並把地址傳遞到內核中的驅動程序, 然後由 v4l2 驅動程序直接將數據填充到用戶空間的內存中。

 

       第一種方式效率是最低的,後面兩種方法都能提高執行的效率,但是對於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是不能被交換到虛擬內存中,雖然這種方法不怎麼影響讀寫效率,但是它一直佔用着內核空間中的內存,當系統的內存有限的 時候,如果同時運行有大量的進程,則對系統的整體性能會有一定的影響。如果對於這裏描述的這些概念還不是很清楚的話,沒事,先記住下面給出的結論就行了, 以後再詳細的去了解)。

       所以,對於 I/O 方法的選擇,推薦的順序是 userptr mmap read-write

 

(2) 當使用 mmap userptr 方式的時候,有一個環形緩衝隊列的概念,這個隊列中,有 n buffer ,驅動程序採集到的視頻幀數據,就是存儲在每個buffer 中。

      在每次用 VIDIOC_DQBUF 取出一個 buffer ,並且處理完數據後,一定要用 VIDIOC_QBUF 將這個 buffer 再次放回到環形緩衝隊列中

      環形緩衝隊列,也使得這兩種 I/O 方式的效率高於直接 read/write

 

 

( 3 ) 採集視頻的分辨率

       (3.1) Cropping ,裁剪 ( 並非所有的攝像頭都支持裁剪功能 )

       (3.2) Scaling ,縮放

  仔細理解這個圖,對理解v4l2工作原理很有幫助:

      下面有個參考圖片,注意圖片中的 4 個矩形:

video4linux(3) - jinsuo2007 - 嵌入式工程師
 

 

紅色 bounds ,是 最大的能捕捉到的圖像 範圍。這個是攝像頭本身的硬件屬性,比如攝像頭CCD的分辨率。

藍色 defrect ,是 我們的設備能夠得到的最大的 範圍。要區別於紅色的部分,這是在CCD分辨率的基礎上,我們的系統通過驅動軟件能夠獲得的最大的分辨率。defrect和bounds可能會有小的差別,也可能是重合的。

綠色 crop ,是我們希望裁剪的部分 ( 區別裁剪和縮放 ) 。也就是我我們希望獲取CCD中的某個矩形部分。

紫色 fmt ,伸縮,這一部分是我們最終獲得的圖像的大小。在使用 VIDIOC_S_FMT 時,驅動程序會計算出圖像幀數據的大小,並且返回給我們。 ( 後面還會提到這個 )

 

       如果硬件設備不支持 crop ,則相當於就是直接在 defrect 矩形上面設置大小。可以簡單的理解爲直接設置期望的視頻分辨率。普通的攝像頭,通常都不支持crop,在capture.c程序中也能看到對crop的處理方式--如果有crop功能,則crop,否則就跳過,直接使用VIDIOC_S_FMT。

   

(4) capture.c 程序中的 process_image 函數。

       capture.c 程序主要是用來演示怎樣使用 v4l2 接口,並沒有對採集到的視頻幀數據做任何實際的處理,僅僅用 process_image 函數表示了處理圖像的代碼位置。

       process_image 函數只有一個參數,就是存儲視頻幀的內存的地址指針,但是在真正的應用中,通常還需要知道該指針指向的數據的大小。

       因此可以修改函數,改成 void process_image ( const void * p, int len ) ,但是每次調用 process_image 的時候,第 2 個參數該傳遞什麼值?

 

考慮到程序中對 buffer 的定義

  struct buffer {

  void * start;

  size_t length};

 

       如果將 buffer.length 作爲第 2 個參數傳遞到修改後的 process_image 函數中,這樣做是不正確的。 process_image 需要的第二個參數應該是每幀圖像的大小,仔細閱讀代碼後會發現,

    注意:buffer.length 並不一定就等於圖像幀的大小。 (buffer 的大小,還需要考慮其他的一些因素,比如內存對齊等 )

(5) 圖像幀的大小和圖像的格式  

       首先要明確一點, RGB YUV 只是兩種很籠統的劃分方法,還需要知道具體的封裝方式,纔有辦法計算出視頻幀數據的實際大小。

      對於YUV而言, YUV 格式通常有兩大類:打包( packed )格式和平面( planar )格式。前者將 YUV 分量存放在同一個數組中,通常是幾個相鄰的像素組成一個宏像素( macro-pixel );而後者使用三個數組分開存放 YUV 三個分量,就像是一個三維平面一樣。

       以h.263爲例, H.263 編碼算法要求圖象被編碼爲一個亮度信號和兩個色差成分( Y Cb Cr ),可以記爲 YCbCr 亮度的取樣結構都是 dx 象素每行, dy 行每幅圖象。 兩個色差成分的取樣都是 dx/2 個象素每行, dy /2 行每幅 圖象。如下圖。

video4linux(3) - jinsuo2007 - 嵌入式工程師
 

      H.263編碼算法 要求的這種圖象格式對應到 v4l2 裏面,就是 V4L2_PIX_FMT_YUV420 (YUV YCbCr YVU YCrCb )

      V4L2_PIX_FMT_YUV420是一種平坦存儲格式,也就是說,在內存中,先存儲所有的 Y 值,然後是所有的 Cb 值,最後纔是 Cr 值。

      假設有一個 V 4L2_PIX_FMT_ Y UV 420 格式的圖像,分辨率是 4 × 4 像素,那麼該圖像幀在內存中存儲形式就是

  video4linux(3) - jinsuo2007 - 嵌入式工程師 

 

       根據前面的描述,可以看出一個公式,當使用 V4L2_PIX_FMT_YUV420 格式採集圖像的時候,如果圖像的寬度爲 width ,高度爲 height ,那麼圖像佔用的內存的大小就是 imagesize = width * height * 3 / 2。

        前面提到過, 使用 VIDIOC_S_FMT 時,驅動程序會計算出圖像幀數據的大小,並且返回給 我們。當 width = 640 height = 480 的時候,根據公式, imagesize = 640 * 480 * 3 / 2 = 460800

      同樣設置的時候,返回的 fmt.fmt.pix.sizeimage 也是 460800

      如果是使用其他的格式,都可以根據各種格式的定義來計算它們實際佔用的內存的大小以及他們在內 存中的存儲方式。

      建議將 Video for Linux Two API Specification 作爲手冊,裏面對視頻格式的介紹比較全面。

                                                                                                                                                    

 (6)下面給出修改過的capture.c代碼,只改動了一點點

  http://blog.csdn.net/jack0106/archive/2010/06/03/5644381.aspx

(7) 最後還想多說兩句,capture.c只是一個示例程序,僅僅是演示怎樣使用v4l2中最基本的接口。尤其是在main函數中的那幾個函數調用,表明了在 使用v4l2時的最基本的一個流程,包括 open_device,init_device,start_capturing,mainloop,stop_capturing,uninit_device,close_device。 在寫程序的時候,可以充分的利用這幾個基本模塊,把他們分散在不同的代碼位置上,靈活的調用,有興趣的可以看一下gstreamer中v4l2src的源 代碼或者其他的大型程序的相關部分。

    總之一句話,capture.c僅僅是一個演示程序,不要侷限於它的代碼結構,要靈活的使用。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章