V4L2 API詳解 Buffer的準備和數據讀取

作者: Sam (甄峯)  [email protected]

前面主要介紹的是:V4L2 的一些設置接口,如亮度,飽和度,曝光時間,幀數,增益,白平衡等。今天看看V4L2 得到數據的幾個關鍵ioctl,Buffer的申請和數據的抓取。


1. 初始化 Memory Mapping 或 User Pointer I/O.
申請數據Buffer。
int ioctl(int fd, int requestbufstruct v4l2_requestbuffers * argp);
參數一:open()所產生的句柄。
參數二:VIDIOC_REQBUFS
參數三:in/out結構體。
struct v4l2_requestbuffers
{
 __u32 count;
 enum v4l2_buf_type type;
 enum v4l2_memory memory; //Applications set this field to V4L2_MEMORY_MMAP or V4L2_MEMORY_USERPTR
 __u32 reserved[2];
};

注意,有兩種方式的I/O。 Memory Mapping 和User Pointer。
Memory Mapping的Buffer由Driver申請爲物理連續的內存空間(Kernel空間)。在此ioctl調用時被分配,需要早於mmap()動作將他們映射到用戶空間。

1.1:Memory Mapping模式詳解:
在使用Memory Mapping模式時,參數三中結構體內每個field都需要設置。


 __u32 count;   //當memory=V4L2_MEMORY_MMAP時,此處纔有效。表明要申請的buffer個數。
 enum v4l2_buf_type type;  //Stream 或者Buffer的類型。此處肯定爲V4L2_BUF_TYPE_VIDEO_CAPTURE
 enum v4l2_memory memory;  //既然是Memory Mapping模式,則此處設置爲:V4L2_MEMORY_MMAP


注意:count是個輸入輸出函數。因爲你所申請到的Buffer個數不一定就是你所輸入的Number。所以在ioctl執行後,driver會將真實申請到的buffer個數填充到此field. 這個數目有可能大於你想要申請的,也可能小與,甚至可能是0個。
應用程序可以再次調用ioctl--VIDIOC_REQBUFS 來修改buffer個數。但前提是必須先釋放已經 mapped  buffer ,可以先 munmap ,然後設置參數 count  0 來釋放所有的 buffer


支持Memory  Mapping  I/O方式的前提是:v4l2_capability  中支持V4L2_CAP_STREAMING。
在這個模式下,數據本身不會被Copy,只是在Kernel和用戶態之間交換。在應用程序想要訪問到這些數據之前,它必須調用mmap()影射到用戶態。

同時也要注意,通過ioctl申請的內存,是物理內存,無法被交換入Disk,所以一定要釋放:munmap()



1.2:User Pointer模式:
User Pointer模式時,應用程序實現申請。
只需要填充Type=V4L2_BUF_TYPE_VIDEO_CAPTURE, memory=V4L2_MEMORY_USERPTR




2. 詢問Buffer狀態:
int ioctl(int fd, int request, struct v4l2_buffer* argp);
參數一:open()所產生的句柄。
參數二:VIDIOC_QUERYBUF
參數三:v4l2_buffer 結構體。(IN/OUT參數)

注意,此ioctl是Memory Mapping的I/O方法之一。User Pointer模式不需要。在Buffer在ioctl-VIDIOC_REQBUFS執行時創建後,隨時都可以調用此Ioctl得到buffer信息。
視頻緩衝區的使用狀態、在內核空間的偏移地址、緩衝區長度等。在應用程序設計中通過調VIDIOC_QUERYBUF來獲取內核空間的視頻緩衝區信息,然後調用函數mmap把內核空間地址映射到用戶空間,這樣應用程序才能夠訪問位於內核空間的視頻緩衝區

我們首先通過v4l2_buffer結構體看看參數三這個輸入輸出參數需要輸入些什麼,以及能夠得到什麼信息。

struct v4l2_buffer
{
 __u32 index;
 enum v4l2_buf_type type;
 __u32 bytesused;
 __u32 flags;
 enum v4l2_field field;
 struct timeval timestamp;
 struct v4l2_timecode timecode;
 __u32 sequence;

 enum v4l2_memory memory;
 union {
 __u32 offset;
 unsigned long userptr;
 } m;
 __u32 length;
 __u32 input;
 __u32 reserved;
};

在調用ioctl--VIDIOC_QUERYBUF時,需要寫入的項目有:
enum v4l2_buf_type type; //V4L2_BUF_TYPE_VIDEO_CAPTURE
__u32 index;  // 這裏需要解釋一下,因爲在調用ioctl-VIDIOC_REQBUFS時,建立了count個Buffer。所以,這裏index的有效範圍是:0到count-1.


在調用ioctl-VIDIOC_QUERYBUF後,Driver會填充v4l2_buffer 結構體內所有信息供用戶使用。
如果一些正常:
1. flags 中:V4L2_BUF_FLAG_MAPPEDV4L2_BUF_FLAG_QUEUED and V4L2_BUF_FLAG_DONE被設置。
2. memory中,V4L2_MEMORY_MMAP被設置。
3. m.offset中,從將要mapping 的device memory頭到數據頭的offset.
4. length 中,填充當前Buffer長度。
5。其它的Field有可能設置,也有可能不被設置。


這樣,mmap()想要有的信息就全了。而mmap()之後,Device Driver 申請的或者Device Memory就能映射到用戶空間。數據就可以被應用程序使用了。這纔是ioctl-VIDIOC_QUERYBUF的關鍵作用。



3.和Driver交換buffer: 
對Camera這樣的捕獲設備來說,Device將數據放到Buffer中,用戶得到數據。Device再次將數據放到Buffer中。
那麼Device Driver 怎樣知道哪個Buffer是可以存放數據的呢?這就用到當前這兩個ioctl-VIDIOC_QBUF, ioctl-VIDIOC_DQBUF.

ioctl-VIDIOC_QBUF: 將指定的Buffer放到輸入隊列中,即向Device表明這個Buffer可以存放東西。
ioctl-VIDIOC_DQBUF: 將輸出隊列中的數據 buffer取出。

 driver 內部管理着兩個 buffer queues ,一個輸入隊列,一個輸出隊列。對於 capture device 來說,當輸入隊列中的 buffer 被塞滿數據以後會自動變爲輸出隊列,等待調用 VIDIOC_DQBUF 將數據進行處理以後重新調用 VIDIOC_QBUF  buffer 重新放進輸入隊列.


用法:
ioctl--VIDIOC_QBUF:
int ioctl(int fd, int request, struct v4l2_buffer* argp);
參數一:open()所產生的句柄。
參數二:VIDIOC_QBUF
參數三:v4l2_buffer 結構體。(IN/OUT參數)

參數三是IN/OUT 參數。需要填充
enum v4l2_buf_type type; //V4L2_BUF_TYPE_VIDEO_CAPTURE
__u32 index;  // 這裏需要解釋一下,因爲在調用ioctl-VIDIOC_REQBUFS時,建立了count個Buffer。所以,這裏index的有效範圍是:0到count-1. 
memory: V4L2_MEMORY_MMAP.

則這個結構體指明的buffer被送入輸出隊列,表明此Buffer可以被device 填充數據。

用法:
ioctl--VIDIOC_DQBUF:
int ioctl(int fd, int request, struct v4l2_buffer* argp);
參數一:open()所產生的句柄。
參數二:VIDIOC_DQBUF
參數三:v4l2_buffer 結構體。(IN/OUT參數)

從輸出隊列中取出一個有數據的Buffer。這個Buffer中的數據被處理後,此Buffer可以通過ioctl-VIDIOC_QBUF再次放入輸入隊列中去。




4. 開始和結束捕獲:
ioctl--VIDIOC_STREAMON. ioctl--VIDIOC_STREAMOFF

非常簡單的調用。就是開始和結束。
發佈了55 篇原創文章 · 獲贊 22 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章