一、V4L2应用层调用流程
二、V4L2设备注册
三、video设备初始化
四、V4L2 control结构框架图
五、v4l2 ctrl 函数初始化—增加标准接口v4l2_ctrl_new_std
六、v4l2 ctrl 函数初始化—增加自定义接口v4l2_ctrl_new_custom
七、V4L2 ioctl 标准接口 调用流程
八、V4L2 ioctl 控制接口 调用流程
1、打开设备
相机设备基本上都是videox节点,以/dev/video0节点为例
int fd = open("/dev/video0",O_RDWR);
2. 获取摄像支持的格式
struct v4l2_fmtdesc v4l2fmt;
v4l2fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
int i = 0;
while(1)
{
v4l2fmt.index = i++;
ioctl(fd, VIDIOC_ENUM_FMT, &v4l2fmt);
}
3.设置像素格式
这里设置输出的宽、高、像素格式
preview.format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
preview.format.fmt.pix.field = V4L2_FIELD_INTERLACED;
preview.format.fmt.pix.pixelformat = V4L2_PIX_FMT_NV21;
preview.format.fmt.pix.width = 1920;
preview.format.fmt.pix.height = 1080;
preview.format.fmt.pix.priv = 1;
ioctl(fd, VIDIOC_S_FMT, &preview.format);
4、查看设置的像素格式
ioctl(fd, VIDIOC_G_FMT, &preview.format);
printf("pix.pixelformat:\t%c%c%c%c\n",preview.format.fmt.pix.pixelformat & 0xFF, (preview.format.fmt.pix.pixelformat >> 8) & 0xFF,(preview.format.fmt.pix.pixelformat >> 16) & 0xFF, (preview.format.fmt.pix.pixelformat >> 24) & 0xFF);
printf("pix.height:\t\t%d\n",preview.format.fmt.pix.height);
printf("pix.width:\t\t%d\n",preview.format.fmt.pix.width);
printf("pix.field:\t\t%d\n",preview.format.fmt.pix.field);
5、请求队列大小
memset(&preview.rb,0,sizeof(preview.rb));
preview.rb.count=4;
preview.rb.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
preview.rb.memory=V4L2_MEMORY_MMAP;
ioctl(fd,VIDIOC_REQBUFS,&preview.rb);
6、内存映射到用户空间
根据请求的队列 buffer个数,把每一个buffer映射到用户空间对应的mem
for (i = 0; i < preview.rb.count; i++)
{
memset(&preview.buf,0,sizeof(preview.buf));
preview.buf.index = i;
preview.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
preview.buf.memory = V4L2_MEMORY_MMAP;
ioctl(fd,VIDIOC_QUERYBUF,&preview.buf);
mem[i].size = preview.buf.length;
mem[i].addr = mmap(NULL,mem[i].size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,preview.buf.m.offset);
}
7、请求到的buffer全部执行入队操作
for (i = 0; i < preview.rb.count; i++)
{
preview.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
preview.buf.memory = V4L2_MEMORY_MMAP;
preview.buf.index = i;
ioctl(fd, VIDIOC_QBUF, &preview.buf);
8、开始获取流数据
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMON, &type);
9、出队
preview.buf就保存了读到的数据信息,根据preview.buf.index的下标对应到用户的mem[preview.buf.index].addr 和mem[preview.buf.index].size就能获取到读到的数据
preview.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
preview.buf.memory = V4L2_MEMORY_MMAP;
ioctl(fd, VIDIOC_DQBUF, &preview.buf);
10、入队
把读完的buffer再进行入队操作
ioctl(fd, VIDIOC_QBUF, &preview.buf)
11、循环 入队 出队
如果需要抓连续的帧数据,就保持9、10两个步骤连续 入队出队即可
12、关闭流数据
当数据抓取完毕后,需要关闭流
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
for(i = 0; i < preview.rb.count; i++)
{
ret = munmap(mem[i].addr,mem[i].size);
if(ret < 0) {
printf("munmap fail \n");
}
}
ret = ioctl(fd, VIDIOC_STREAMOFF, &type);