V4L2接口编程
V4L2接口编程
官方文档连接:LinuxTV文档中心
参考链接:3-1.V4l2接口编程
基本使用为以下几个步骤:
-
打开设备
1
2
3
4
5
6int fd = open("/dev/video0", O_RDWR);
if(fd < 0)
{
perror("打开设备失败");
return -1;
} -
通过
ioctl
获取支持格式1
2
3
4
5
6
7
8
9
10
11
12
13
14struct v4l2_fmtdesc v4fmt;
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//指定类型
v4fmt.index = 0;//指定获取格式的序列号(有的摄像头可能有多个可支持的格式)
int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);//VIDIOC_ENUM_FMT命令,获得支持格式
if(ret < 0)
{
perror("获取失败");
}
printf("index=%d\n", v4fmt.index);
printf("flags=%d\n", v4fmt.flags);
printf("description=%s\n", v4fmt.description);
unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
printf("pixelformat=%c%c%c%c\n", p[0],p[1],p[2],p[3]);
printf("reserved=%d\n", v4fmt.reserved[0]); -
通过
ioctl
配置摄像头采集格式1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26struct v4l2_format vfmt;
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集
vfmt.fmt.pix.width = 640;//设置宽(不能任意,需要硬件支持)
vfmt.fmt.pix.height = 480;//设置高
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//设置视频采集格式
int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);//VIDIOC_S_FMT:设置摄像头采集格式
if(ret < 0)
{
perror("设置格式失败");
}
memset(&vfmt, 0, sizeof(vfmt));
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_G_FMT, &vfmt);//VIDIOC_G_FMT:获取摄像头采集格式
if(ret < 0)
{
perror("获取格式失败");
}
//对比
if(vfmt.fmt.pix.width == 640 && vfmt.fmt.pix.height == 480 &&
vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
{
printf("设置成功\n");
}else
{
printf("设置失败\n");
} -
通过
ioctl
申请内核缓冲区队列1
2
3
4
5
6
7
8
9struct v4l2_requestbuffers reqbuffer;
reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuffer.count = 4; //申请4个缓冲区
reqbuffer.memory = V4L2_MEMORY_MMAP ;//映射方式
ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer);//VIDIOC_REQBUFS:申请内核缓冲区队列
if(ret < 0)
{
perror("申请队列空间失败");
} -
把内核的缓冲区队列映射到用户空间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22unsigned char *mptr[4];//保存映射后用户空间的首地址
unsigned int size[4];//记录映射的长度,便于后期释放
struct v4l2_buffer mapbuffer;
//初始化type, index
mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
for(int i=0; i<4; i++)
{
mapbuffer.index = i;
ret = ioctl(fd, VIDIOC_QUERYBUF, &mapbuffer);//从内核空间中查询一个空间做映射
if(ret < 0)
{
perror("查询内核空间队列失败");
}
mptr[i] = (unsigned char *)mmap(NULL, mapbuffer.length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, mapbuffer.m.offset);//mmap映射
size[i]=mapbuffer.length;//记录映射长度
ret = ioctl(fd, VIDIOC_QBUF, &mapbuffer);//通知使用完毕--‘放回去’
if(ret < 0)
{
perror("放回失败");
}
} -
准备开始采集
1
2
3
4
5
6int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_STREAMON, &type);//开始采集写数据到队列中
if(ret < 0)
{
perror("开启失败");
} -
采集数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//从队列中提取一帧数据
struct v4l2_buffer readbuffer;
readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_DQBUF, &readbuffer);//告诉内核要某一个数据,内核不可以修改
if(ret < 0)
{
perror("提取数据失败");
}
FILE *file=fopen("my.jpg", "w+");
//mptr[readbuffer.index]
fwrite(mptr[readbuffer.index], readbuffer.length, 1, file);
fclose(file);
//通知内核已经使用完毕
ret = ioctl(fd, VIDIOC_QBUF, &readbuffer);//告诉内核已经使用完毕
if(ret < 0)
{
perror("放回队列失败");
} -
停止采集
1
ret = ioctl(fd, VIDIOC_STREAMOFF, &type);//停止采集-不在向队列中写数据
-
释放内存
1
2for(int i=0; i<4; i)
munmap(mptr[i], size[i]); -
关闭设备
1
close(fd);
综合例子:
1 |
|