首先來(lái)看?Read/Write?,如果?VIDIOC_QUERYCAP?調(diào)用返回的?v4l2_capability?參數(shù)中,?V4L2_CAP_READWRITE?被設(shè)置成真了的話(huà),就說(shuō)明支持?Read/Write I/O?。這是最簡(jiǎn)單最原始的方法,它需要進(jìn)行數(shù)據(jù)?的拷貝?(?而不是像memory map?那樣只需要進(jìn)行指針的交換?)?,而且不會(huì)交換元數(shù)據(jù)?(?比如說(shuō)幀計(jì)數(shù)器和時(shí)間戳之類(lèi)的可用于識(shí)別幀丟失和進(jìn)行幀同步?)?,雖然它是最原始的方法,但因?yàn)槠浜?jiǎn)單,所以對(duì)于簡(jiǎn)單的應(yīng)用?程序比如只需要?capture靜態(tài)圖像是很有用的?。
如果使用?Read/Write?方法支持的話(huà),必須同時(shí)支持另外兩個(gè)函數(shù)?select()?和?poll()?,這兩個(gè)函數(shù)用來(lái)進(jìn)行?I/0?的多路復(fù)用。
對(duì)于?streaming?它有兩種方式,?driver?對(duì)兩種方式的支持要使用?VIDIOC_REQBUFS?來(lái)確定:
int ioctl(int fd, int request, struct v4l2_requestbuffers *argp);
對(duì)于?memory mapped?方式,?Memory mapped buffers?是通過(guò)?VIDIOC_REQBUFS?在?device memory?中申請(qǐng)的,而且必須在?map?進(jìn)應(yīng)用程序虛擬地址空間?之前就申請(qǐng)好。而對(duì)于?User pointers?,?User buffers?是在應(yīng)用程序自己開(kāi)辟的,只是通過(guò)?VIDIOC_REQBUFS?將驅(qū)動(dòng)轉(zhuǎn)化到?user pointer?的?I/O?模式下。這兩種方式都不會(huì)拷貝數(shù)據(jù),而只是?buffer?指針的交互。
首先來(lái)看一下?v4l2_requestbuffers?這個(gè)數(shù)據(jù)結(jié)構(gòu):
__u32 count
//?要申請(qǐng)的?buffer?的數(shù)量,只有當(dāng)?memory?被設(shè)置成?V4L2_MEMORY_MMAP?的時(shí)候才會(huì)設(shè)置這個(gè)參數(shù)
enum v4l2_buf_type type
enum v4l2_memory memory
//?要么是?V4L2_MEMORY_MMAP?,要么是?V4L2_MEMORY_USERPTR
對(duì)于?memory mapped?模式,要在?device memory?下申請(qǐng)?buffer?,應(yīng)用程序必須初始化上面的?3?個(gè)參數(shù),驅(qū)動(dòng)最后返回的?buffer?的個(gè)數(shù)可能等于?count?,也可能少于或者多于?count?,少于可能是因?yàn)閮?nèi)存不足,多于則可能是驅(qū)動(dòng)為更好地完成相應(yīng)功能增加的?buffer?。如果?driver?不支持?memory mapped?調(diào)用這個(gè)?ioctl?就會(huì)返回?EINVAL。
因?yàn)?memory map?模式下分配的是實(shí)實(shí)在在的物理內(nèi)存,不是虛擬內(nèi)存,所以使用完以后一定要使用?munmap()釋放。
應(yīng)用程序可以重新調(diào)用?VIDICO_REQBUFS?來(lái)改變?buffer?的個(gè)數(shù),但前提是必須先釋放已經(jīng)?mapped?的?buffer?,可以先?munmap?,然后設(shè)置參數(shù)?count?為?0?來(lái)釋放所有的?buffer?。
對(duì)于?User pointer I/O?,應(yīng)用程序只需設(shè)置上面的?type?和?memory?類(lèi)型就可以了。
申請(qǐng)好?buffer?后在進(jìn)行?memory mapped?之前,首先要使用?VIDIOC_QUERYBUF?來(lái)獲得分配的?buffer?信息,以傳給函數(shù)?mmap()?來(lái)進(jìn)行?map?:
int ioctl(int fd, int request, struct v4l2_buffer *argp);
VIDIOC_QUERYBUF?是?memory mapped?這種模式下使用的方法,在?User pointer?模式下不需要使用這個(gè)函數(shù),在調(diào)用之前應(yīng)用程序需要設(shè)定?v4l2_buffer?中的兩個(gè)參數(shù),一個(gè)是?buffer?類(lèi)型,另外一個(gè)是?index number(?有效值從0?到申請(qǐng)的?buffer?數(shù)目減?1)?,調(diào)用這個(gè)?ioctl?會(huì)將相應(yīng)?buffer?中的?flag?:?V4L2_BUF_FLAG_MAPPED, V4L2_BUF_FLAG_QUEUED?和?V4L2_BUF_FLAG_DONE?設(shè)置為有效。下面我們來(lái)仔細(xì)看看?v4l2_buffer?這個(gè)數(shù)據(jù)結(jié)構(gòu):
__u32 index
//?應(yīng)用程序來(lái)設(shè)定,僅僅用來(lái)申明是哪個(gè)?buffer
enum v4l2_buf_type type
__u32 bytesused
//buffer?中已經(jīng)使用的?byte?數(shù),如果是?input stream?由?driver?來(lái)設(shè)定,相反則由應(yīng)用程序來(lái)設(shè)定
__u32 flags
//?定義了?buffer?的一些標(biāo)志位,來(lái)表明這個(gè)?buffer?處在哪個(gè)隊(duì)列,比如輸入隊(duì)列或者輸出隊(duì)列(V4L2_BUF_FLAG_QUEUED V4L2_BUF_FLAG_DONE)?,是否關(guān)鍵幀等等,具體可以參照?spec
enum v4l2_memory memory
//V4L2_MEOMORY_MMAP?/?V4L2_MEMORY_USERPTR?/?V4L2_MEMORY_OVERLAY
union m
__u32 offset
//?當(dāng)?memory?類(lèi)型是?V4L2_MEOMORY_MMAP?的時(shí)候,主要用來(lái)表明?buffer?在?device momory?中相對(duì)起始位置的偏移,主要用在?mmap()?參數(shù)中,對(duì)應(yīng)用程序沒(méi)有左右
unsigned long userptr
//?當(dāng)?memory?類(lèi)型是?V4L2_MEMORY_USERPTR?的時(shí)候,這是一個(gè)指向虛擬內(nèi)存中?buffer?的指針,由應(yīng)用程序來(lái)設(shè)定。
__u32 length
//buffer?的?size
在?driver?內(nèi)部管理?著兩個(gè)?buffer queues?,一個(gè)輸入隊(duì)列,一個(gè)輸出隊(duì)列。對(duì)于?capture device?來(lái)說(shuō),當(dāng)輸入隊(duì)列中的?buffer?被塞滿(mǎn)數(shù)據(jù)以后會(huì)自動(dòng)變?yōu)檩敵鲫?duì)列,等待調(diào)用?VIDIOC_DQBUF?將數(shù)據(jù)進(jìn)行處理以后重新調(diào)用VIDIOC_QBUF?將?buffer?重新放進(jìn)輸入隊(duì)列;對(duì)于?output device?來(lái)說(shuō)?buffer?被顯示以后自動(dòng)變?yōu)檩敵鲫?duì)列。
剛初始化的所有?map?過(guò)的?buffer?開(kāi)始都處于?dequeced?的狀態(tài),由?driver?來(lái)管理對(duì)應(yīng)用程序是不可訪問(wèn)的。對(duì)于?capture?應(yīng)用程序來(lái)說(shuō),首先是通過(guò)?VIDIOC_QBUF?將所有?map?過(guò)的?buffer?加入隊(duì)列,然后通過(guò)VIDIOC_STREAMON?開(kāi)始?capture?,并進(jìn)入?read loop?,在這里應(yīng)用程序會(huì)等待直到有一個(gè)?buffer?被填滿(mǎn)可以從隊(duì)列中?dequeued?,當(dāng)數(shù)據(jù)使用完后再?enqueue?進(jìn)輸入隊(duì)列;對(duì)于?output?應(yīng)用程序來(lái)說(shuō),首先應(yīng)用程序會(huì)buffer?裝滿(mǎn)數(shù)據(jù)然后?enqueued?,當(dāng)足夠的?buffer?進(jìn)入隊(duì)列以后就調(diào)用?VIDIOC_STREAMON?將數(shù)據(jù)輸出。
有兩種方法來(lái)阻塞應(yīng)用程序的執(zhí)行,直到有?buffer?能被?dequeued?,默認(rèn)的是當(dāng)調(diào)用?VIDIOC_DQBUF?的時(shí)候會(huì)被阻塞,直到有數(shù)據(jù)在?outgoing queue?,但是如果打開(kāi)設(shè)備文件?的時(shí)候使用了?O_NONBLOCK?,則當(dāng)調(diào)用VIDIOC_DQBUF?而又沒(méi)有數(shù)據(jù)可讀的時(shí)候就會(huì)立即返回。另外一種方法是調(diào)用?select?和?poll?來(lái)對(duì)文件描述符進(jìn)行監(jiān)聽(tīng)是否有數(shù)據(jù)可讀。
VIDIOC_STREAMON?和?VIDIOC_STREAMOFF?兩個(gè)?ioctl?用來(lái)開(kāi)始和停止?capturing?或者?output?,而且VIDIOC_STREAMOFF?會(huì)刪除輸入和輸出隊(duì)列中的所有?buffer?。
因此?drvier?如果要實(shí)現(xiàn)?memory mapping I/O?必須支持?VIDIOC_REQBUFS, VIDIOC_QUERYBUF, VIDIOC_QBUF, VIDIOC_DQBUF, VIDIOC_STREAMON?和?VIDIOC_STREAMOFF ioctl, the mmap(), munmap(), select()?和?poll()?函數(shù)。
User Pointers?是一種綜合了?Read/Write?和?memory mappded?優(yōu)勢(shì)的?I/O?方法,?buffer?是由應(yīng)用程序自己申請(qǐng)的,可以是在虛擬內(nèi)存或者共享內(nèi)存中。在?capture?和?output?方面基本來(lái)說(shuō)和?memory mapped?方式是相同的,在這里只提一下它申請(qǐng)內(nèi)存的方式。
User pointer?方式下,申請(qǐng)的內(nèi)存也?memory page size?為單位對(duì)齊,而且?buffersize?也有一定限制,例示代碼中是這樣計(jì)算?buffer size?的,暫時(shí)還不知道這樣分配?buffer size?的依據(jù)是什么,先簡(jiǎn)單地這樣用就好了:
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
經(jīng)過(guò)上面的一系列的數(shù)據(jù)協(xié)商已經(jīng)?buffer?的分配以后就可以調(diào)用?VIDIOC_QBUF?將?buffer?全部加入輸入隊(duì)列中,并調(diào)用?VIDIOC_STREAM0N?開(kāi)始捕獲數(shù)據(jù)了:
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?參數(shù)是?buffer?類(lèi)型)
4?、?mainloop
開(kāi)始捕獲數(shù)據(jù)以后就會(huì)進(jìn)入一個(gè)主循環(huán),可以使用?select?或者?poll?來(lái)監(jiān)聽(tīng)文件描述符的狀態(tài),一旦有數(shù)據(jù)可讀,就調(diào)用函數(shù)來(lái)讀取數(shù)據(jù)。
5?、?read_frame
讀取數(shù)據(jù)根據(jù)?I/O?方式的不同而不同:
Read/Write?方式直接從文件描述符中讀一個(gè)幀大小的數(shù)據(jù);
Memory mapped?方式下先從輸出隊(duì)列中?dequeued?一個(gè)?buffer?,然后對(duì)幀數(shù)據(jù)進(jìn)行處理,處理完成以后再放入輸入隊(duì)列。
User pointer?方式下也是首先從輸出隊(duì)列中?dequeued?一個(gè)?buffer?,然后對(duì)這個(gè)?buffer?進(jìn)行判斷,看是否是應(yīng)用程序開(kāi)始申請(qǐng)的?buffer?,然后再對(duì)這個(gè)?buffer?進(jìn)行處理,最后放入輸入隊(duì)列。
6?、?stop_capturing / uninit_device / close device
最后就是捕捉以及資源釋放并關(guān)閉?device
下面給出一個(gè)示例代碼:
#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;
//?查詢(xún)序號(hào)為n_buffers 的緩沖區(qū),得到其起始物理地址和大小
if?(-1?==?ioctl?(fd,?VIDIOC_QUERYBUF,?&buf))
exit(-1);
buffers[n_buffers].length?=?buf.length;
//?映射內(nèi)存
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;
//?將緩沖幀放入隊(duì)列
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;
?
評(píng)論
查看更多