最近在做关于摄像头测试程序相关的一些开发,主要是想要实现在摄像头采集视频的过程中,通过按键来实现拍照,然后将拍照得到的数据保存到一个文件中,通过了解V4L2的一些相关操作,原来,拍照后的数据是保存在一个buf里,而这股数据流便是熟悉的YUYV,要看到相应的图片,还需要特定的转换才能将YUYV的数据转换成bmp图片。
那么,在摄像头中,是怎么获取到图片的呢?
通过V4L2的官方手册,我们得知是该控制命令VIDIOC_DQBUF
那么这个命令的作用是什么呢?
功能: 从视频缓冲区的输出队列中取得一个已经保存有一帧视频数据的视频缓冲区;
参数说明:参数类型为V4L2缓冲区数据结构类型 struct v4l2_buffer ;
返回值说明: 执行成功时,函数返回值为 0;函数执行成功后,相应的内核视频缓冲区中保存有当前拍摄到的视频数据,应用程序可以通过访问用户空间来读取该视频数据。
举个简单的例子:
struct v4l2_buffer cfilledbuffer1; cfilledbuffer1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cfilledbuffer1.memory = V4L2_MEMORY_OVERLAY; if (ioctl(iCamFd, VIDIOC_DQBUF, &cfilledbuffer1) < 0) { printf("%s VIDIOC_DQBUF Failed!!! \n",__FUNCTION__); err = -1; goto exit; }
上面的这段代码中,cfilledbuffer1就是相应的视频缓冲区,这里面就保存了当前拍摄到的视频数据。
那么,应用程序是怎么访问用户空间的呢?
原来,在应用程序设计中通过调VIDIOC_QUERYBUF来获取内核空间的视频缓冲区信息,然后调用函数mmap把内核空间地址映射到用户空间,这样应用程序才能够访问位于内核空间的视频缓冲区。
那么这个参数的具体作用是什么呢?
参数说明:参数类型为V4L2缓冲区数据结构类型 struct v4l2_buffer ;
返回值说明: 执行成功时,函数返回值为 0;struct v4l2_buffer结构体变量中保存了指令的缓冲区的相关信息;
一般情况下,应用程序中调用VIDIOC_QUERYBUF取得了内核缓冲区信息后,紧接着调用mmap函数把内核空间地址映射到用户空间,方便用户空间应用程序的访问。
举个例子:
m_v4l2Buffer[0] =(unsigned char *) mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, iPmemFd, 0);//映射 if (m_v4l2Buffer[0] == MAP_FAILED) { printf(" m_v4l2Buffer[0] mmap failed\n"); err = -1; goto exit2; }
struct v4l2_buffer buffer; buffer.type = creqbuf.type; buffer.memory = creqbuf.memory; buffer.index = i;// 要获取内核视频缓冲区的信息编号 if (ioctl(iCamFd, VIDIOC_QUERYBUF, &buffer) < 0) { printf("%s VIDIOC_QUERYBUF Failed\n",__FUNCTION__); goto fail_loop; }
以上就是我本次做这个项目需要了解的东西,同时,通过参考一些写过V4L2的大神们写过的一些文章,我摘取了其中的部分优秀的文段作为总结,并了解V4L2驱动摄像头的基本流程:
一般操作流程(视频设备):
1. 打开设备文件。 int fd=open("/dev/video0",O_RDWR);
2. 取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。VIDIOC_QUERYCAP,struct v4l2_capability
3. 选择视频输入,一个视频设备可以有多个视频输入。VIDIOC_S_INPUT,struct v4l2_input
4. 设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。
VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format
5. 向驱动申请帧缓冲,一般不超过5个。struct v4l2_requestbuffers
6. 将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。mmap
7. 将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer
8. 开始视频的采集。VIDIOC_STREAMON
9. 出队列以取得已采集数据的帧缓冲,取得原始采集数据。VIDIOC_DQBUF
10. 将缓冲重新入队列尾,这样可以循环采集。VIDIOC_QBUF
11. 停止视频的采集。VIDIOC_STREAMOFF
12. 关闭视频设备。close(fd);