V4L2是V4L的升級版本,為linux下視頻設(shè)備程序提供了一套接口規(guī)范。包括一套數(shù)據(jù)結(jié)構(gòu)和底層V4L2驅(qū)動接口。
1、常用的結(jié)構(gòu)體在內(nèi)核目錄include/linux/videodev2.h中定義
struct v4l2_requestbuffers //申請幀緩沖,對應(yīng)命令VIDIOC_REQBUFS
struct v4l2_capability //視頻設(shè)備的功能,對應(yīng)命令VIDIOC_QUERYCAP
struct v4l2_input //視頻輸入信息,對應(yīng)命令VIDIOC_ENUMINPUT
struct v4l2_standard //視頻的制式,比如PAL,NTSC,對應(yīng)命令VIDIOC_ENUMSTD
struct v4l2_format //幀的格式,對應(yīng)命令VIDIOC_G_FMT、VIDIOC_S_FMT等
struct v4l2_buffer //驅(qū)動中的一幀圖像緩存,對應(yīng)命令VIDIOC_QUERYBUF
struct v4l2_crop //視頻信號矩形邊框
v4l2_std_id //視頻制式
2、常用的IOCTL接口命令也在include/linux/videodev2.h中定義
VIDIOC_REQBUFS //分配內(nèi)存
VIDIOC_QUERYBUF //把VIDIOC_REQBUFS中分配的數(shù)據(jù)緩存轉(zhuǎn)換成物理地址
VIDIOC_QUERYCAP //查詢驅(qū)動功能
VIDIOC_ENUM_FMT //獲取當(dāng)前驅(qū)動支持的視頻格式
VIDIOC_S_FMT //設(shè)置當(dāng)前驅(qū)動的頻捕獲格式
VIDIOC_G_FMT //讀取當(dāng)前驅(qū)動的頻捕獲格式
VIDIOC_TRY_FMT //驗證當(dāng)前驅(qū)動的顯示格式
VIDIOC_CROPCAP //查詢驅(qū)動的修剪能力
VIDIOC_S_CROP //設(shè)置視頻信號的矩形邊框
VIDIOC_G_CROP //讀取視頻信號的矩形邊框
VIDIOC_QBUF //把數(shù)據(jù)從緩存中讀取出來
VIDIOC_DQBUF //把數(shù)據(jù)放回緩存隊列
VIDIOC_STREAMON //開始視頻顯示函數(shù)
VIDIOC_STREAMOFF //結(jié)束視頻顯示函數(shù)
VIDIOC_QUERYSTD //檢查當(dāng)前視頻設(shè)備支持的標(biāo)準(zhǔn),例如PAL或NTSC。
3、操作流程
V4L2提供了很多訪問接口,你可以根據(jù)具體需要選擇操作方法。需要注意的是,很少有驅(qū)動完全實現(xiàn)了所有的接口功能。所以在使用時需要參考驅(qū)動源碼,或仔細(xì)閱讀驅(qū)動提供者
的使用說明。
下面列舉出一種操作的流程,供參考。
3.1 打開設(shè)備文件
int fd = open(Devicename,mode);
// Devicename:/dev/video0、/dev/video1 ……
// Mode:O_RDWR [| O_NONBLOCK]
如果使用非阻塞模式調(diào)用視頻設(shè)備,則當(dāng)沒有可用的視頻數(shù)據(jù)時,不會阻塞,而立刻返回。
3.2 取得設(shè)備的capability
struct v4l2_capability capability;
int ret = ioctl(fd, VIDIOC_QUERYCAP, &capability);
看看設(shè)備具有什么功能,比如是否具有視頻輸入特性。
3.3 選擇視頻輸入
struct v4l2_input input;
…… //初始化input
int ret = ioctl(fd, VIDIOC_QUERYCAP, &input);
一個視頻設(shè)備可以有多個視頻輸入。如果只有一路輸入,這個功能可以沒有。
3.4 檢測視頻支持的制式
v4l2_std_id std;
do {
ret = ioctl(fd, VIDIOC_QUERYSTD, &std);
} while (ret == -1 && errno == EAGAIN);
switch (std)
{
case V4L2_STD_NTSC:
//……
case V4L2_STD_PAL:
//……
}
3.5 設(shè)置視頻捕獲格式
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
fmt.fmt.pix.height = height;
fmt.fmt.pix.width = width;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
ret = ioctl(fd, VIDIOC_S_FMT, &fmt);
if(ret)
{
perror(“VIDIOC_S_FMT\n”);
close(fd);
return -1;
}
3.6 向驅(qū)動申請幀緩存
struct v4l2_requestbuffers req;
if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1)
{
return -1;
}
v4l2_requestbuffers結(jié)構(gòu)中定義了緩存的數(shù)量,驅(qū)動會據(jù)此申請對應(yīng)數(shù)量的視頻緩存。多個緩存可以用于建立FIFO,來提高視頻采集的效率。
3.7 獲取每個緩存的信息,并mmap到用戶空間
typedef struct VideoBuffer
{
void *start;
size_t length;
} VideoBuffer;
VideoBuffer* buffers = calloc( req.count, sizeof(*buffers) ); //申請 req.count 個內(nèi)存
struct v4l2_buffer buf;
for (numBufs = 0; numBufs 《 req.count; numBufs++) //映射所有的緩存
{
memset( &buf, 0, sizeof(buf) );
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = numBufs;
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1)
{ //獲取到對應(yīng)index的緩存信息,此處主要利用length信息及offset信息來完成后面的mmap操作。
return -1;
}
buffers[numBufs].length = buf.length;
// 轉(zhuǎn)換成相對地址
buffers[numBufs].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
if (buffers[numBufs].start == MAP_FAILED)
{
return -1;
}
}
3.8 開始采集視頻
int buf_type= V4L2_BUF_TYPE_VIDEO_CAPTURE;
int ret = ioctl(fd, VIDIOC_STREAMON, &buf_type);
3.9 取出FIFO緩存中已經(jīng)采樣的幀緩存
struct v4l2_buffer buf;
memset(&buf,0,sizeof(buf));
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
buf.index=0; //此值由下面的ioctl返回
if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1)
{
return -1;
}
根據(jù)返回的buf.index找到對應(yīng)的mmap映射好的緩存,取出視頻數(shù)據(jù)。
3.10 將剛剛處理完的緩沖重新入隊列尾,這樣可以循環(huán)采集
if (ioctl(fd, VIDIOC_QBUF, &buf) == -1)
{
return -1;
}
3.11 停止視頻的采集
int ret = ioctl(fd, VIDIOC_STREAMOFF, &buf_type);
3.12 關(guān)閉視頻設(shè)備
close(fd);
評論
查看更多