Linux下用V4L2采集摄像头图像和视频

2019-07-13 08:33发布

V4L2,即 video for linux 2,V4L的第二版,linux下的视频库,非常方便用于采集摄像头数据,使用方法一般有以下流程 打开设备——(检查设备属性)——设置帧格式——(设置采集速度即帧率)——设置缓冲区管理方式——开始采集图像——获取图像数据——处理数据——关闭设备 1. 打开设备 linux下一切皆文件,硬件设备也有其文件节点,故摄像头打开设备和打开文件一样 int fd; fd = open("/dev/video0", O_RDWR)其中“/dev/video0为摄像头设备 2. 检查设备属性 此为可选,如果不确定摄像头是否支持某个功能,可先检查再使用 //query camera capabilities struct v4l2_capability cap; if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) { perror("VIDIOC_QUERYCAP failed! "); return false; }
结果将保存再cap结构体中,其中比较重要的是capabilities,从这一项可以检查是否支持某项功能
3. 设置帧格式 主要包括类型,摄像头永远是capture,采集图像的宽高,格式等 //set format struct v4l2_format fmt; memset(&fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = 640; fmt.fmt.pix.height = 480; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) { perror("VIDIOC_S_FMT failed! "); return false; } VIDIOC_S_FMT用于设置格式,VIDIOC_G_FMT用于读取格式,可以检查设置是否成功 //get format if (ioctl(fd, VIDIOC_G_FMT, &fmt) == -1) { perror("VIDIOC_G_FMT failed! "); return false; }4. 设置帧率 // set stream parameter struct v4l2_streamparm parm; parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; parm.parm.capture.timeperframe.numerator = 1; parm.parm.capture.timeperframe.denominator = 30; parm.parm.capture.capturemode = 0; if (ioctl(fd, VIDIOC_S_PARM, &parm) == -1) { perror("VIDIOC_S_PARM failed "); return false; } 以上代码设置为30fps,这个数值不是随便设,看摄像头是否支持,一般15,30都会支持,高端摄像头可到60 同样VIDIOC_G_PARM用于读取相关参数 5. 设置缓冲区管理方式 一般有内存映射方式和用户指针方式 下面的代码向驱动程序申请缓冲区 //request memory allocation struct v4l2_requestbuffers reqbuf; reqbuf.count = 4; reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; reqbuf.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) == -1) { perror("VIDIOC_REQBUFS failed! "); return false; } 接下来将申请到的缓冲区映射到用户程序中,以便在用户程序访问,方便处理 typedef struct __video_buffer { void *start; size_t length; } video_buf_t; video_buf_t *framebuf; framebuf = (video_buf_t *)calloc(reqbuf.count, sizeof(video_buf_t)); struct v4l2_buffer buf; for (int i = 0; i < reqbuf.count; i++) { buf.index = i; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) { perror("VIDIOC_QUERYBUF failed! "); return false; } //mmap buffer framebuf[i].length = buf.length; framebuf[i].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); if (framebuf[i].start == MAP_FAILED) { perror("mmap failed! "); return false; } //buffer queue if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) { perror("VIDIOC_QBUF failed! "); return false; } } 6. 开始采集图像 //start camera capture enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) { perror("VIDIOC_STREAMON failed! "); return false; }7. 获取和处理数据 该过程一般为循环过程 从申请的缓冲队列中取出一项,包含了一帧图像数据,进行处理,再将缓冲区放回队列中 struct v4l2_buffer buf; while (!quit) { buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) { perror("VIDIOC_DQBUF failed! "); usleep(10000); retry++; if (retry > 10) quit = true; continue; } //Do something,process frame data if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) { perror("VIDIOC_QBUF failed! "); continue; } } 8. 关闭设备 和打开设备一样,直接操作文件即可 close(fd);
代码:https://github.com/wlhe/videocapture 参考: http://www.cnblogs.com/emouse/archive/2013/03/04/2943243.html
http://blog.csdn.net/arm11082/article/details/51851017