折腾了一天,总算弄好了:),就简单记录一下遇到的一些问题吧。
首先,可以通过 ls /dev | grep video
来查看相机设备有哪些。
但这里有一个大坑,每个实体相机会有两个/dev/video*文件,如/dev/video0 和/dev/video1, 然而其中只有一个可以用来读取视频信息。
解决方案是v4l2-ctl -d /dev/video0 --all
(把0换成其他数字),它会列出设备的具体信息,包括支持的格式。对于可用的和不可用的设备文件,该命令的输出完全不同,可以自己试试。
具体的流程如下图所示(来自V4L2框架解析|极客笔记)
-
打开设备
要注意使用上面说的可用的设备,我就因为这个浪费了一个上午,emo
open("/dev/video0", O_RDWR | O_NONBLOCK);
还有这里打开的模式O_RDWR
要和mmap中的模式一样, 否则会出现Permission error. 为什么要O_NONBLOCK
见4.讲帧缓冲区入队, 出队
2. 查看并设置设备
可以用ioctl VIDIOC_QUERYCAP
来查看设备能力, VIDIOC_ENUM_FMT
来查看支持的格式,其实信息都在v4l2-ctl -d /dev/video0 --all
里了。只是个可选项!
这一步中最主要的是用ioctl VIDIOC_S_FMT
来设置输出的宽度,高度,文件格式(一般只有V4L2_PIX_FMT_JPEG
和 V4L2_PIX_FMT_YYUV
. IMPORTANT, 这个设置不一定会生效,若设置了不支持的值,则会使用默认值!所以之后要VIDIOC_G_FMT
获取设置来查看是否成功
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /*v4l2_buf_typea,camera must use V4L2_BUF_TYPE_VIDEO_CAPTURE*/
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; /*V4L2_PIX_FMT_YYUV*/
fmt.fmt.pix.field = V4L2_FIELD_NONE; /*V4L2_FIELD_NONE*/
if (ioctl(fd, VIDIOC_S_FMT, &fmt)< 0)
{
fprintf(stderr,"VIDIOC_S_FMT set err\n");
exit(EXIT_FAILURE);
}
3.申请帧缓冲区
先通过VIDIOC_REQBUFS
来申请缓冲区,暂时申请到的缓冲区在内核地址空间,程序访问不到 , 之后再用mmap讲缓冲区映射到用户地址空间。代码在picture_capture.c
中,自己看,流程是固定的,很容易懂。
4.讲帧缓冲区入队, 出队
通过VIDIOC_QBUF
使得一个缓冲区加入帧队列,等待系统载入帧数据,VIDIOC_DQBUF
获取一个已经载人帧数据的缓冲区,并从帧队列中删除。IMPORTANT,就行read和write会阻塞一样,VIDIOC_DQBUF
在帧队列没有已经载入帧数据的缓冲区时也会阻塞,因此需要用select
等io复用函数来避免阻塞带来的影响。具体就是可以VIDIOC_DQBUF
时fd可读。将其加入select readset就行了。
5.对数据的处理
我现在只会拍照处理,直接讲读取到的帧数据保存为jpg文件就行。其他的正在学
详情请见picture_capture.c