V4L2,即 video for linux 2,V4L的第二版,linux下的视频库,非常方便用于采集摄像头数据,使用方法一般有以下流程
打开设备——(检查设备属性)——设置帧格式——(设置采集速度即帧率)——设置缓冲区管理方式——开始采集图像——获取图像数据——处理数据——关闭设备
1. 打开设备
linux下一切皆文件,硬件设备也有其文件节点,故摄像头打开设备和打开文件一样
int fd;
fd = open("/dev/video0", O_RDWR)
其中“/dev/video0为摄像头设备
2. 检查设备属性
此为可选,如果不确定摄像头是否支持某个功能,可先检查再使用
//query camera capabilities
struct v4l2_capability cap;
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1)
{
perror("VIDIOC_QUERYCAP failed!\n");
return false;
}
结果将保存再cap结构体中,其中比较重要的是capabilities,从这一项可以检查是否支持某项功能
主要包括类型,摄像头永远是capture,采集图像的宽高,格式等
//set format
struct v4l2_format fmt;
memset(&fmt, 0, sizeof(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)
{
perror("VIDIOC_S_FMT failed!\n");
return false;
}
VIDIOC_S_FMT用于设置格式,VIDIOC_G_FMT用于读取格式,可以检查设置是否成功
//get format
if (ioctl(fd, VIDIOC_G_FMT, &fmt) == -1)
{
perror("VIDIOC_G_FMT failed!\n");
return false;
}
4. 设置帧率
// set stream parameter
struct v4l2_streamparm parm;
parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
parm.parm.capture.timeperframe.numerator = 1;
parm.parm.capture.timeperframe.denominator = 30;
parm.parm.capture.capturemode = 0;
if (ioctl(fd, VIDIOC_S_PARM, &parm) == -1)
{
perror("VIDIOC_S_PARM failed\n");
return false;
}
以上代码设置为30fps,这个数值不是随便设,看摄像头是否支持,一般15,30都会支持,高端摄像头可到60
同样VIDIOC_G_PARM用于读取相关参数
5. 设置缓冲区管理方式
一般有内存映射方式和用户指针方式
下面的代码向驱动程序申请缓冲区
//request memory allocation
struct v4l2_requestbuffers reqbuf;
reqbuf.count = 4;
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) == -1)
{
perror("VIDIOC_REQBUFS failed!\n");
return false;
}
接下来将申请到的缓冲区映射到用户程序中,以便在用户程序访问,方便处理
typedef struct __video_buffer
{
void *start;
size_t length;
} video_buf_t;
video_buf_t *framebuf;
framebuf = (video_buf_t *)calloc(reqbuf.count, sizeof(video_buf_t));
struct v4l2_buffer buf;
for (int i = 0; i < reqbuf.count; i++)
{
buf.index = i;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1)
{
perror("VIDIOC_QUERYBUF failed!\n");
return false;
}
//mmap buffer
framebuf[i].length = buf.length;
framebuf[i].start = mmap(NULL, buf.length,
PROT_READ | PROT_WRITE,
MAP_SHARED, fd, buf.m.offset);
if (framebuf[i].start == MAP_FAILED)
{
perror("mmap failed!\n");
return false;
}
//buffer queue
if (ioctl(fd, VIDIOC_QBUF, &buf) == -1)
{
perror("VIDIOC_QBUF failed!\n");
return false;
}
}
6. 开始采集图像
//start camera capture
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_STREAMON, &type) == -1)
{
perror("VIDIOC_STREAMON failed!\n");
return false;
}
7. 获取和处理数据
该过程一般为循环过程
从申请的缓冲队列中取出一项,包含了一帧图像数据,进行处理,再将缓冲区放回队列中
struct v4l2_buffer buf;
while (!quit)
{
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1)
{
perror("VIDIOC_DQBUF failed!\n");
usleep(10000);
retry++;
if (retry > 10)
quit = true;
continue;
}
//Do something,process frame data
if (ioctl(fd, VIDIOC_QBUF, &buf) == -1)
{
perror("VIDIOC_QBUF failed!\n");
continue;
}
}
8. 关闭设备
和打开设备一样,直接操作文件即可
close(fd);
参考:
http://www.cnblogs.com/emouse/archive/2013/03/04/2943243.html
http://blog.csdn.net/arm11082/article/details/51851017