首先來看?Read/Write?,如果?VIDIOC_QUERYCAP?調用返回的?v4l2_capability?參數中,?V4L2_CAP_READWRITE?被設置成真了的話,就說明支持?Read/Write I/O?。這是最簡單最原始的方法,它需要進行數據?的拷貝?(?而不是像memory map?那樣只需要進行指針的交換?)?,而且不會交換元數據?(?比如說幀計數器和時間戳之類的可用于識別幀丟失和進行幀同步?)?,雖然它是最原始的方法,但因為其簡單,所以對于簡單的應用?程序比如只需要?capture靜態圖像是很有用的?。
如果使用?Read/Write?方法支持的話,必須同時支持另外兩個函數?select()?和?poll()?,這兩個函數用來進行?I/0?的多路復用。
對于?streaming?它有兩種方式,?driver?對兩種方式的支持要使用?VIDIOC_REQBUFS?來確定:
int ioctl(int fd, int request, struct v4l2_requestbuffers *argp);
對于?memory mapped?方式,?Memory mapped buffers?是通過?VIDIOC_REQBUFS?在?device memory?中申請的,而且必須在?map?進應用程序虛擬地址空間?之前就申請好。而對于?User pointers?,?User buffers?是在應用程序自己開辟的,只是通過?VIDIOC_REQBUFS?將驅動轉化到?user pointer?的?I/O?模式下。這兩種方式都不會拷貝數據,而只是?buffer?指針的交互。
首先來看一下?v4l2_requestbuffers?這個數據結構:
__u32 count
//?要申請的?buffer?的數量,只有當?memory?被設置成?V4L2_MEMORY_MMAP?的時候才會設置這個參數
enum v4l2_buf_type type
enum v4l2_memory memory
//?要么是?V4L2_MEMORY_MMAP?,要么是?V4L2_MEMORY_USERPTR
對于?memory mapped?模式,要在?device memory?下申請?buffer?,應用程序必須初始化上面的?3?個參數,驅動最后返回的?buffer?的個數可能等于?count?,也可能少于或者多于?count?,少于可能是因為內存不足,多于則可能是驅動為更好地完成相應功能增加的?buffer?。如果?driver?不支持?memory mapped?調用這個?ioctl?就會返回?EINVAL。
因為?memory map?模式下分配的是實實在在的物理內存,不是虛擬內存,所以使用完以后一定要使用?munmap()釋放。
應用程序可以重新調用?VIDICO_REQBUFS?來改變?buffer?的個數,但前提是必須先釋放已經?mapped?的?buffer?,可以先?munmap?,然后設置參數?count?為?0?來釋放所有的?buffer?。
對于?User pointer I/O?,應用程序只需設置上面的?type?和?memory?類型就可以了。
申請好?buffer?后在進行?memory mapped?之前,首先要使用?VIDIOC_QUERYBUF?來獲得分配的?buffer?信息,以傳給函數?mmap()?來進行?map?:
int ioctl(int fd, int request, struct v4l2_buffer *argp);
VIDIOC_QUERYBUF?是?memory mapped?這種模式下使用的方法,在?User pointer?模式下不需要使用這個函數,在調用之前應用程序需要設定?v4l2_buffer?中的兩個參數,一個是?buffer?類型,另外一個是?index number(?有效值從0?到申請的?buffer?數目減?1)?,調用這個?ioctl?會將相應?buffer?中的?flag?:?V4L2_BUF_FLAG_MAPPED, V4L2_BUF_FLAG_QUEUED?和?V4L2_BUF_FLAG_DONE?設置為有效。下面我們來仔細看看?v4l2_buffer?這個數據結構:
__u32 index
//?應用程序來設定,僅僅用來申明是哪個?buffer
enum v4l2_buf_type type
__u32 bytesused
//buffer?中已經使用的?byte?數,如果是?input stream?由?driver?來設定,相反則由應用程序來設定
__u32 flags
//?定義了?buffer?的一些標志位,來表明這個?buffer?處在哪個隊列,比如輸入隊列或者輸出隊列(V4L2_BUF_FLAG_QUEUED V4L2_BUF_FLAG_DONE)?,是否關鍵幀等等,具體可以參照?spec
enum v4l2_memory memory
//V4L2_MEOMORY_MMAP?/?V4L2_MEMORY_USERPTR?/?V4L2_MEMORY_OVERLAY
union m
__u32 offset
//?當?memory?類型是?V4L2_MEOMORY_MMAP?的時候,主要用來表明?buffer?在?device momory?中相對起始位置的偏移,主要用在?mmap()?參數中,對應用程序沒有左右
unsigned long userptr
//?當?memory?類型是?V4L2_MEMORY_USERPTR?的時候,這是一個指向虛擬內存中?buffer?的指針,由應用程序來設定。
__u32 length
//buffer?的?size
在?driver?內部管理?著兩個?buffer queues?,一個輸入隊列,一個輸出隊列。對于?capture device?來說,當輸入隊列中的?buffer?被塞滿數據以后會自動變為輸出隊列,等待調用?VIDIOC_DQBUF?將數據進行處理以后重新調用VIDIOC_QBUF?將?buffer?重新放進輸入隊列;對于?output device?來說?buffer?被顯示以后自動變為輸出隊列。
剛初始化的所有?map?過的?buffer?開始都處于?dequeced?的狀態,由?driver?來管理對應用程序是不可訪問的。對于?capture?應用程序來說,首先是通過?VIDIOC_QBUF?將所有?map?過的?buffer?加入隊列,然后通過VIDIOC_STREAMON?開始?capture?,并進入?read loop?,在這里應用程序會等待直到有一個?buffer?被填滿可以從隊列中?dequeued?,當數據使用完后再?enqueue?進輸入隊列;對于?output?應用程序來說,首先應用程序會buffer?裝滿數據然后?enqueued?,當足夠的?buffer?進入隊列以后就調用?VIDIOC_STREAMON?將數據輸出。
有兩種方法來阻塞應用程序的執行,直到有?buffer?能被?dequeued?,默認的是當調用?VIDIOC_DQBUF?的時候會被阻塞,直到有數據在?outgoing queue?,但是如果打開設備文件?的時候使用了?O_NONBLOCK?,則當調用VIDIOC_DQBUF?而又沒有數據可讀的時候就會立即返回。另外一種方法是調用?select?和?poll?來對文件描述符進行監聽是否有數據可讀。
VIDIOC_STREAMON?和?VIDIOC_STREAMOFF?兩個?ioctl?用來開始和停止?capturing?或者?output?,而且VIDIOC_STREAMOFF?會刪除輸入和輸出隊列中的所有?buffer?。
因此?drvier?如果要實現?memory mapping I/O?必須支持?VIDIOC_REQBUFS, VIDIOC_QUERYBUF, VIDIOC_QBUF, VIDIOC_DQBUF, VIDIOC_STREAMON?和?VIDIOC_STREAMOFF ioctl, the mmap(), munmap(), select()?和?poll()?函數。
User Pointers?是一種綜合了?Read/Write?和?memory mappded?優勢的?I/O?方法,?buffer?是由應用程序自己申請的,可以是在虛擬內存或者共享內存中。在?capture?和?output?方面基本來說和?memory mapped?方式是相同的,在這里只提一下它申請內存的方式。
User pointer?方式下,申請的內存也?memory page size?為單位對齊,而且?buffersize?也有一定限制,例示代碼中是這樣計算?buffer size?的,暫時還不知道這樣分配?buffer size?的依據是什么,先簡單地這樣用就好了:
page_size = getpagesize ();
buffer_size = (buffer_size + page_size - 1) & ~(page_size – 1);
buffers[n_buffers].start = memalign ( page_size,
buffer_size);
3?、?start_capturing
經過上面的一系列的數據協商已經?buffer?的分配以后就可以調用?VIDIOC_QBUF?將?buffer?全部加入輸入隊列中,并調用?VIDIOC_STREAM0N?開始捕獲數據了:
int ioctl(int fd, int request, struct v4l2_buffer *argp);
//VIDIOC_QBUF VIDIOC_DQBUF
int ioctl(int fd, int request, const int *argp);
//VIDIOC_STREAM0N VIDIOC_STREAMOFF?(?int?參數是?buffer?類型)
4?、?mainloop
開始捕獲數據以后就會進入一個主循環,可以使用?select?或者?poll?來監聽文件描述符的狀態,一旦有數據可讀,就調用函數來讀取數據。
5?、?read_frame
讀取數據根據?I/O?方式的不同而不同:
Read/Write?方式直接從文件描述符中讀一個幀大小的數據;
Memory mapped?方式下先從輸出隊列中?dequeued?一個?buffer?,然后對幀數據進行處理,處理完成以后再放入輸入隊列。
User pointer?方式下也是首先從輸出隊列中?dequeued?一個?buffer?,然后對這個?buffer?進行判斷,看是否是應用程序開始申請的?buffer?,然后再對這個?buffer?進行處理,最后放入輸入隊列。
6?、?stop_capturing / uninit_device / close device
最后就是捕捉以及資源釋放并關閉?device
下面給出一個示例代碼:
#include?
#include?
#include?
#include??/*?low-level i/o?*/
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#define DEVICE?"/dev/video"
static struct v4l2_requestbuffers req;
struct buffer
{
void*?start;
unsigned?int?length;
};?
static struct buffer?*buffers;
static struct v4l2_buffer buf;
usb_camera.c
#include?"head.h"
int?main()
{
int?fd;
fd=open_device();
get_device_info(fd);
get_frame_fmt(fd);
get_current_frame_info(fd);
try_format_support(fd);
set_frame_format(fd);
apply_memory_buf(fd);
memory_mapping(fd);
buffer_enqueue(fd);
close(fd);
return 0;
}
int?open_device()
{
int?fd;
if(-1==(fd=open(DEVICE,O_RDWR)))
printf("info:Can't open video device\n");
else
printf("info:Open the device :%d\n",fd);
return fd;
}
int?get_device_info(int?fd)
{
struct v4l2_capability cap;
if(-1==ioctl(fd,VIDIOC_QUERYCAP,&cap))
printf("info:VIDIOC_QUERYCAP ERROR\n");
else
printf("info:Driver Name:%s....Card Name:%s....Bus info:%s....Driver Version:%u.%u.%u\n",
cap.driver,cap.card,cap.bus_info,(cap.version>>16)&0XFF,(cap.version>>8)&0XFF,cap.version&0XFF);
return 1;
}
int?get_frame_fmt(int?fd)
{
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index=0;
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("info:Support format:");
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
{
printf("\t%d.%s",fmtdesc.index+1,fmtdesc.description);
fmtdesc.index++;
}
printf("\n");
return 1;
}
int?get_current_frame_info(int?fd)
{
struct v4l2_format fmt;
fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd,VIDIOC_G_FMT,&fmt);
printf("info:Current data format information:\n\twidth:%d\n\theight:%d\n",fmt.fmt.pix.width,fmt.fmt.pix.height);
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index=0;
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
{
if(fmtdesc.pixelformat?&?fmt.fmt.pix.pixelformat)
{
printf("\tformat:%s\n",fmtdesc.description);
break;
}
fmtdesc.index++;
}?
return 1;
}
int?try_format_support(int?fd)
{
struct v4l2_format fmt;
fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
//fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_RGB32;
fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;
if(ioctl(fd,VIDIOC_TRY_FMT,&fmt)==-1)
if(errno==EINVAL)
printf("info:not support format RGB32!\n");?
return 1;
}
int?set_frame_format(int?fd)
{
struct v4l2_format fmt;
fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width=640;
fmt.fmt.pix.height=480;
fmt.fmt.pix.pixelformat?=?V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field?=?V4L2_FIELD_INTERLACED;?
if(ioctl(fd,VIDIOC_S_FMT,&fmt)==-1)
if(errno==EINVAL)
printf("info:set frame format error!\n");
return 1;
}
int?apply_memory_buf(int?fd)
{
//struct v4l2_requestbuffers req;
req.count=4;
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory=V4L2_MEMORY_MMAP;
if(-1==ioctl(fd,VIDIOC_REQBUFS,&req))
printf("info:VIDIOC_REQBUFS FAILED\n");
else
printf("info:VIDIOC_REQBUFS SUCCESS\n");
return 1;
}
int?memory_mapping(int?fd)
{
unsigned?int?n_buffers;
buffers?=?(struct buffer*)calloc(req.count,sizeof(struct buffer));
if?(!buffers)?{
fprintf?(stderr,?"Out of memory\n");
exit?(EXIT_FAILURE);
}
//?映射
for?(n_buffers?=?0;?n_buffers?
//struct v4l2_buffer buf;
memset(&buf,0,sizeof(buf));
buf.type?=?V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory?=?V4L2_MEMORY_MMAP;
buf.index?=?n_buffers;
//?查詢序號為n_buffers 的緩沖區,得到其起始物理地址和大小
if?(-1?==?ioctl?(fd,?VIDIOC_QUERYBUF,?&buf))
exit(-1);
buffers[n_buffers].length?=?buf.length;
//?映射內存
buffers[n_buffers].start?=mmap?(NULL,buf.length,PROT_READ?|?PROT_WRITE,MAP_SHARED,fd,?buf.m.offset);
if?(MAP_FAILED?==?buffers[n_buffers].start)
exit(-1);
}?
printf("info:memory mapping success\n");
return 1;
}
int?buffer_enqueue(int?fd)
{
unsigned?int?i;
enum v4l2_buf_type type;
//?將緩沖幀放入隊列
for?(i?=?0;?i?4;?++i)
{
struct v4l2_buffer buf;
buf.type?=?V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory?=?V4L2_MEMORY_MMAP;
buf.index?=?i;
if(-1==ioctl?(fd,?VIDIOC_QBUF,?&buf))
printf("buffer enqueue failed\n");
}
type?=?V4L2_BUF_TYPE_VIDEO_CAPTURE;
//open stream?
if(-1==ioctl?(fd,?VIDIOC_STREAMON,?&type))
printf("info:open stream failed\n");
else
printf("info:open stream success\n");
return 1;
?
評論
查看更多