基于V4L2的摄像头图像采集

一、V4L2的定义

V4L2(Video For Linux Two) 是内核提供给应用程序访问音、视频驱动的统一接口,在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/videoN下,N可能为0,1,2,3... 一般为0。

二、工作流程

Step1:初始化摄像头。打开设备->检查和设置设备属性->设置帧格式

Step2:启动采集命令。申请帧缓冲->内存映射->帧缓冲入队列->开始采集->帧缓冲出队列->重新入队列循环采集

Step3:停止采集命令

Step4:关闭摄像头

三、常用的结构体(参见/usr/include/linux/videodev2.h)

struct v4l2_requestbuffers reqbufs;//向驱动申请帧缓冲的请求,里面包含申请的个数
struct v4l2_capability cap;//这个设备的功能,比如是否是视频输入设备
struct v4l2_input input; //视频输入
struct v4l2_standard std;//视频的制式,比如PAL,NTSC
struct v4l2_format fmt;//帧的格式,比如宽度,高度等

struct v4l2_buffer buf;//代表驱动中的一帧
v4l2_std_id stdid;//视频制式,例如:V4L2_STD_PAL_B
struct v4l2_queryctrl query;//查询的控制
struct v4l2_control control;//具体控制的值

四、常用的控制命令

VIDIOC_REQBUFS:分配内存
VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址
VIDIOC_QUERYCAP:查询驱动功能
VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式
VIDIOC_S_FMT:设置当前驱动的频捕获格式
VIDIOC_G_FMT:读取当前驱动的频捕获格式
VIDIOC_TRY_FMT:验证当前驱动的显示格式
VIDIOC_CROPCAP:查询驱动的修剪能力
VIDIOC_S_CROP:设置视频信号的边框
VIDIOC_G_CROP:读取视频信号的边框
VIDIOC_QBUF:把数据从缓存中读取出来
VIDIOC_DQBUF:把数据放回缓存队列
VIDIOC_STREAMON:开始视频显示函数
VIDIOC_STREAMOFF:结束视频显示函数
VIDIOC_QUERYSTD:检查当前视频设备支持的标准,例如PAL或NTSC。

五、详细的开发流程

1.打开视频设备

在V4L2中,视频设备被看做一个文件。使用open函数打开这个设备:

// 用非阻塞模式打开摄像头设备

int fd;

fd = open(“/dev/video0″, O_RDWR | O_NONBLOCK, 0);

// 如果用阻塞模式打开摄像头设备,上述代码变为:

fd = open(”/dev/video0″, O_RDWR, 0);

关于阻塞模式和非阻塞模式:

应用程序能够使用阻塞模式或非阻塞模式打开视频设备,如果使用非阻塞模式调用视频设备,即使尚未捕获到信息,驱动依旧会把缓存(DQBUFF)里的东西返回给应用程序。

2.检查和设置设备属性

(1)设置视频设备属性通过ioctl来进行设置,ioctl有三个参数,分别是fd, cmd,和parameter,表示设备描述符,控制命令和控制命令参数。

(2)查询设备功能: VIDIOC_QUERYCAP 

相关函数:

ioctl(fd, VIDIOC_QUERYCAP, &cap)

相关结构体:

struct v4l2_capability

{

__u8 driver[16]; // 驱动名字

__u8 card[32]; // 设备名字

__u8 bus_info[32]; // 设备在系统中的位置

__u32 version; // 驱动版本号

__u32 capabilities; // 设备支持的操作

__u32 reserved[4]; // 保留字段

};


(3)获取视频设备支持的视频格式:VIDIOC_ENUM_FMT

相关函数:ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)

相关结构体:struct v4l2_fmtdesc

(4)设置视频设备的视频数据格式:VIDIOC_S_FMT

相关函数:ioctl(fd, VIDIOC_S_FMT, &fmt)

相关结构体:struct v4l2_format

3.采集图像

(1)申请帧缓冲:VIDIOC_REQBUFS

相关函数:ioctl(fd,VIDIOC_REQBUFS,&req)

相关结构体:struct v4l2_requestbuffers

(2)查询帧缓冲的相关信息,包括视频缓冲区的使用状态、在内核空间的偏移地址、缓冲区长度等:VIDIOC_QUERYBUF

相关函数:ioctl (fd, VIDIOC_QUERYBUF, &buf)

相关结构体:struct v4l2_buffer

(3)内存映射

应用程序中调用VIDIOC_QUERYBUF取得了内核缓冲区信息后,紧接着调用mmap函数把内核空间地址映射到用户空间,方便用户空间应用程序的访问。

操作系统一般把系统使用的内存划分成用户空间和内核空间,分别由应用程序管理和操作系统管理。应用程序可以直接访问内存的地址,而内核空间存放的是 供内核访问的代码和数据,用户不能直接访问。v4l2捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地 址。

一共有三种视频采集方式:使用read、write方式;内存映射方式和用户指针模式。

read、write方式:在用户空间和内核空间不断拷贝数据,占用了大量用户内存空间,效率不高。

内存映射方式:把设备里的内存映射到应用程序中的内存控件,直接处理设备内存,这是一种有效的方式。上面的mmap函数就是使用这种方式。

用户指针模式:内存片段由应用程序自己分配。这点需要在v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR。

(4)帧缓冲入队列:VIDIOC_QBUF

相关函数:ioctl(fd, VIDIOC_QBUF, &buf);

(5)启动采集命令:VIDIOC_STREAMON

(6)帧缓冲出队列:VIDIOC_DQBUF

相关函数:ioctl(fd, VIDIOC_DQBUF, &buf);

4.停止采集命令:VIDIOC_STREAMOFF

5.关闭设备:close(fd)


六、采集结果



基于V4L2的摄像头图像采集

                        图1 终端程序运行


基于V4L2的摄像头图像采集

                         图2 采集的图像文件


基于V4L2的摄像头图像采集

                                                                               图3   左摄像头采集的图像



基于V4L2的摄像头图像采集

                                                                                         图4  右摄像头采集的图像