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


1. 初始化 Memory Mapping 或 User Pointer I/O.
int ioctl(int fd, int requestbuf, struct 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;     //V4L2_MEMORY_MMAP時有效。表明要申請的buffer個數。
 enum v4l2_buf_type    type;       //Stream 或Buffer。此處為V4L2_BUF_TYPE_VIDEO_CAPTURE
 enum v4l2_memory     memory; //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信息。
 
我們首先通過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;  //0到count-1
解釋一下:在調用ioctl- VIDIOC_REQBUFS(向設備申請緩存區)  時,建立了count個Buffer。 所以,這里index的有效范圍是:0到count-1.
 
 
在調用ioctl- VIDIOC_QUERYBUF(獲取緩存幀的地址、長度)后,Driver會填充v4l2_buffer 結構體內所有信息供用戶使用。則:
1. flags中:V4L2_BUF_FLAG_MAPPED, V4L2_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。取值 :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(停止視頻數據流)
 
非常簡單的調用。就是開始和結束。
 
 

常用的VIDIOC命令:
 1. VIDIOC_QUERYCAP (查詢設備屬性)
 2. VIDIOC_ENUM_FMT (顯示所有支持的格式)
 3. VIDIOC_S_FMT (設置視頻捕獲格式)
 4. VIDIOC_G_FMT (獲取硬件現在的視頻捕獲格式)
 5. VIDIOC_TRY_FMT (檢查是否支持某種幀格式)
 6. VIDIOC_ENUM_FRAMESIZES (枚舉設備支持的分辨率信息)
 7. VIDIOC_ENUM_FRAMEINTERVALS (獲取設備支持的幀間隔)
 8. VIDIOC_S_PARM && VIDIOC_G_PARM (設置和獲取流參數)
 9. VIDIOC_QUERYCAP (查詢驅動的修剪能力)
 10. VIDIOC_S_CROP (設置視頻信號的邊框)
 11. VIDIOC_G_CROP (讀取設備信號的邊框)
 12. VIDIOC_REQBUFS (向設備申請緩存區)
 13. VIDIOC_QUERYBUF (獲取緩存幀的地址、長度)
 14. VIDIOC_QBUF (把幀放入隊列)
 15. VIDIOC_DQBUF (從隊列中取出幀)
 16. VIDIOC_STREAMON && VIDIOC_STREAMOFF (啟動/停止視頻數據流)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM