在Linux中应用层获取图像的数据都是通过固定的框架实现的。首先试想一下,应用层获取一帧一帧的图像数据,需要做哪些步骤?如果我们想从Linux的内核中获取数据,最直接的方法就是mmap内存映射获取一帧图像数据在应用层的操作空间的首地址,这样应用层就可以直接读取这个首地址就获取到了图像的数据了。
代码参考:https://www.jianshu.com/p/0ac427d267d4
这里主要是讲解v4l2中应用层怎样从内核中获取数据的方法。至于之前的初始化中的获取camera的属性,或者视频格式的设置等。请参考:https://blog.csdn.net/u010299133/article/details/103737645
1.就像上面分析的那样,首先需要将位于内核的首地址映射到用户空间,并且内核以多大的缓冲空间存取视频帧的数据,是需要应用层决定的,操作方法如下:
struct v4l2_requestbuffers req;
//申请帧缓冲
req.count=FRAME_NUM;//这个需要用户决定,在内核中申请的帧缓冲的个数,一般以应用层能够处理的过来即可。4个或者5个
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory=V4L2_MEMORY_MMAP;
if(ioctl(fd,VIDIOC_REQBUFS,&req)==-1){
printf("request for buffers error\n");
return FALSE;
}
2.通过上面的操作,我们已经在内核中申请好了数个帧缓冲的空间,接下来我们还需要将内核中的每个帧缓冲空间的地址映射到用户空间,以供用户空间的应用程序读取等操作。
struct v4l2_buffer buf;//内核帧缓冲结构体
//以下为映射的过程
for (n_buffers = 0; n_buffers < FRAME_NUM; n_buffers++) // FRAME_NUM即为上面步骤中的申请的内核帧缓冲的个数
{
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers;
//查询
if (ioctl (fd, VIDIOC_QUERYBUF, &buf) == -1)//通过这个ioctl操作后,就可以查询相对应的内核帧缓冲空间的信息。如:帧的内核空间的地址,帧的大小
{
printf("query buffer error\n");
return FALSE;
}
//映射
buffers[n_buffers].length = buf.length;//一帧空间的长度
buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ|PROT_WRITE, MAP_SHARED, fd, buf.m.offset);//将内核空间的地址映射到用户空间。长度:buf.length,内核的空间起点:buf.m.offset
if (buffers[n_buffers].start == MAP_FAILED)
{
printf("buffer map error\n");
return FALSE;
}
}
3.到了这个时候,应用层时不时就可以就可以直接读取这些首地址以获取视频帧数据了呢?当然还不行的。我们还需要将我们上面第一步申请到的内核的帧缓冲的空间做一个入队操作。
//入队就是将含有FRAME_NUM多个的内核的帧缓冲做成队列的形式
for (n_buffers = 0; n_buffers < FRAME_NUM; n_buffers++)
{
buf.index = n_buffers;
ioctl(fd, VIDIOC_QBUF, &buf);
}
4.开始采集
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMON, &type);
5.接下来就可以获取数据了,获取每一帧数据的同时需要做一个出队的操作,然后从上面第2步中映射的地址中读取视频帧数据,然后再做入队的操作,以便接下来轮流获取视频数据。
for(n_buffers = 0; n_buffers < FRAME_NUM; n_buffers++)
{
//出队
buf.index = n_buffers;//队列中帧的编号
ioctl(fd, VIDIOC_DQBUF, &buf);
//可以获取时间戳
buffers[n_buffers].timestamp = buf.timestamp.tv_sec*1000000+buf.timestamp.tv_usec;
//可以将获取到的帧数据写入文件,也可以将这些数据拷贝到一个缓冲中,以供其他线程使用
FILE *fp2 = fopen(file_name, "wb");
if(!fp2)
{
printf("open %s error\n",file_name);
return(FALSE);
}
fwrite(buffers[n_buffers].start, IMAGEHEIGHT*IMAGEWIDTH*2,1,fp2);
fclose(fp2);
//入队循环
ioctl(fd, VIDIOC_QBUF, &buf);
}
6.应用程序退出时的处理
主要是关闭视频流:
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMON, &type);
来源:CSDN
作者:酸菜鱼的鱼
链接:https://blog.csdn.net/u010299133/article/details/104009691