V4L2采集摄像头过程中的几点细节
来源:互联网 发布:centos更新python 编辑:程序博客网 时间:2024/04/30 03:29
最近打算做一个H.264的图像传输设备,第一步当然是采集图像,了解过相关知识后得知图像采集需要用到V4L2,于是在网上找关于V4L2的资料,昨天终于把V4L2看的差不多,并且把网上的程序封装成了符合我的习惯的一个C++类。关于V4L2采集图像在网上有很多博客讲的都很详细,而我也水平有限,就不写V4L2的详细采集过程了,现在分享几点学习V4L2采集图像时遇到的一些不容易懂或者易错的地方,相信对于初学者来说这些困惑点也会遇到。
首先放上一张V4L2采集图像的流程图,把这个图理解透了也就差不多了。
struct v4l2_capability里的capabilities的BIT0和BIT26很重要,这两个必须是1,否则是没法采集到图像的。可以使用这样的代码来验证一下:
//judge wherher or not to be a video-get deviceif (!(cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE)){ printf("The Current device is not a video capture device\n"); exit(EXIT_FAILURE);}//judge whether or not to supply the form of video streamif (!(cap.capabilities & V4L2_CAP_STREAMING)){ printf("The Current device does not support streaming i/o\n"); exit(EXIT_FAILURE);}
第二点要说的就是mmap这一步,这一步会建立起内核与用户之间的联系,在打开设备的视频流后,只要第四步里申请到的队列不是满的,设备就会采集图像并送到队列中,而用户是不能访问内核的,所以我们需要mmap来建立起这个联系,我们才能得到图像数据。当然可以用read来读取,但是操作IO效率是很慢的。
for (n_buffer = 0; n_buffer < reqbufs.count; n_buffer++){ //stand for a frame struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = n_buffer; //check the information of the kernel cache requested if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) { perror("xioctl VIDIOC_QUERYBUF"); exit(EXIT_FAILURE); } usr_buf[n_buffer].length = buf.length; usr_buf[n_buffer].start = (char *)mmap( NULL, buf.length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, buf.m.offset ); if (MAP_FAILED == usr_buf[n_buffer].start) { perror("mmap"); exit(EXIT_FAILURE); }}
在开始采集图像这一步里,程序所做的工作有两点。
一是把申请到的帧缓存区送到视频采集队列,也就是第四步申请到的帧缓冲区
//place the kernel cache to a queuefor (i = 0; i < n_buffer; i++){ struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)){ perror("xioctl VIDIOC_QBUF"); exit(EXIT_FAILURE); }}
二是打开视频流数据的采集,这个简单
enum v4l2_buf_type type;//start capture datatype = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)){ printf("i=%d.\n", i); perror("VIDIOC_STREAMON"); close(fd); exit(EXIT_FAILURE);}
在我们需要得到图像的时候,首先要从队列中取出一帧,得到信息后再把这一帧放回队列中就行。
int CameraClass::GetOneImage(struct buffer_type *Image){ struct v4l2_buffer buf; unsigned int i; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; //put cache from queue if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { perror("xioct VIDIOC_DQBUF"); exit(EXIT_FAILURE); } assert(buf.index < n_buffer); //read process space's data to a file (*Image).start = usr_buf[buf.index].start; (*Image).length = usr_buf[buf.index].length; if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) { perror("xioctl VIDIOC_QBUF"); exit(EXIT_FAILURE); } return 0;}
最后附上完整的代码,已经测试成功。刚刚入门嵌入式,还有许多地方不太清楚,真诚欢迎批评指正!
以下是cpp文件
#include "camera_v4l2.h"CameraClass::CameraClass(char *DevicePath, int Width, int Height){ memcpy(dev_name, DevicePath, strlen(DevicePath)); width = Width; height = Height;}CameraClass::~CameraClass(){}int CameraClass::xioctl(int fd, int request, void * arg) { int r; do r = ioctl(fd, request, arg); while (-1 == r && EINTR == errno); //如果是被信号打断就重新请求io return r;}int CameraClass::InitCamera(void){ fd = open(dev_name, O_RDWR); //打开设备文件 if (fd < 0) { perror("open dev_name"); exit(EXIT_FAILURE); } //decive fuction, such as video input struct v4l2_capability cap; //frame format struct v4l2_format tv_fmt; //detail control value struct v4l2_fmtdesc fmt; int ret; //get the format of video supply memset(&fmt, 0, sizeof(fmt)); fmt.index = 0; //supply to image capture fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // show all format of supply printf("Support format:\n"); while (-1 != xioctl(fd, VIDIOC_ENUM_FMT, &fmt)) { fprintf(stdout, "%d.%s\n", ++fmt.index, fmt.description); } //check video decive driver capability ret = xioctl(fd, VIDIOC_QUERYCAP, &cap); if (ret < 0){ perror("xioctl VIDIOC_QUERYCAP"); exit(EXIT_FAILURE); } fprintf(stdout, "driver is %s!\n", cap.driver); fprintf(stdout, "card is %s!\n", cap.card); fprintf(stdout, "bus_info is %s!\n", cap.bus_info); fprintf(stdout, "version is %d!\n", cap.version); fprintf(stdout, "capabilities is %d!\n", cap.capabilities); //judge wherher or not to be a video-get device if (!(cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE)) { printf("The Current device is not a video capture device\n"); exit(EXIT_FAILURE); } //judge whether or not to supply the form of video stream if (!(cap.capabilities & V4L2_CAP_STREAMING)) { printf("The Current device does not support streaming i/o\n"); exit(EXIT_FAILURE); } //set the form of camera capture data tv_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; tv_fmt.fmt.pix.width = width; tv_fmt.fmt.pix.height = height; tv_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; //此处应该与设备所支持的格式相匹配 tv_fmt.fmt.pix.field = V4L2_FIELD_ANY; if (xioctl(fd, VIDIOC_S_FMT, &tv_fmt)< 0) { perror("xioctl VIDIOC_S_FMT"); exit(EXIT_FAILURE); } ret = xioctl(fd, VIDIOC_G_FMT, &tv_fmt); //打印出来检测一下 if (ret < 0) { perror("xioctl VIDIO_G_FMT"); return ret; } // Print Stream Format printf("Stream Format Informations:\n"); printf(" type: %d\n", tv_fmt.type); printf(" width: %d\n", tv_fmt.fmt.pix.width); printf(" height: %d\n", tv_fmt.fmt.pix.height); char fmtstr[8]; memset(fmtstr, 0, 8); memcpy(fmtstr, &tv_fmt.fmt.pix.pixelformat, 4); printf(" pixelformat: %s\n", fmtstr); printf(" field: %d\n", tv_fmt.fmt.pix.field); printf(" bytesperline: %d\n", tv_fmt.fmt.pix.bytesperline); printf(" sizeimage: %d\n", tv_fmt.fmt.pix.sizeimage); printf(" colorspace: %d\n", tv_fmt.fmt.pix.colorspace); printf(" priv: %d\n", tv_fmt.fmt.pix.priv); printf(" raw_date: %s\n", tv_fmt.fmt.raw_data); struct v4l2_requestbuffers reqbufs; memset(&reqbufs, 0, sizeof(reqbufs)); reqbufs.count = 3; reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; reqbufs.memory = V4L2_MEMORY_MMAP; if (-1 == xioctl(fd, VIDIOC_REQBUFS, &reqbufs)){ perror("xioctl VIDIOC_REQBUFS"); exit(EXIT_FAILURE); } n_buffer = reqbufs.count; fprintf(stdout, "n_buffer = %d\n", n_buffer); usr_buf = (struct buffer_type*)calloc(reqbufs.count, sizeof(usr_buf)); if (usr_buf == NULL){ printf("Out of memory\n"); exit(-1); } //map kernel cache to user process for (n_buffer = 0; n_buffer < reqbufs.count; n_buffer++) { //stand for a frame struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = n_buffer; //check the information of the kernel cache requested if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) { perror("xioctl VIDIOC_QUERYBUF"); exit(EXIT_FAILURE); } usr_buf[n_buffer].length = buf.length; usr_buf[n_buffer].start = (char *)mmap( NULL, buf.length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, buf.m.offset ); if (MAP_FAILED == usr_buf[n_buffer].start) { perror("mmap"); exit(EXIT_FAILURE); } } return 0;}int CameraClass::StartCapture(void){ unsigned int i; enum v4l2_buf_type type; //place the kernel cache to a queue for (i = 0; i < n_buffer; i++) { struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)){ perror("xioctl VIDIOC_QBUF"); exit(EXIT_FAILURE); } } //start capture data type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) { printf("i=%d.\n", i); perror("VIDIOC_STREAMON"); close(fd); exit(EXIT_FAILURE); } return 0;}int CameraClass::GetOneImage(struct buffer_type *Image){ struct v4l2_buffer buf; unsigned int i; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; //put cache from queue if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { perror("xioct VIDIOC_DQBUF"); exit(EXIT_FAILURE); } assert(buf.index < n_buffer); //read process space's data to a file (*Image).start = usr_buf[buf.index].start; (*Image).length = usr_buf[buf.index].length; if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) { perror("xioctl VIDIOC_QBUF"); exit(EXIT_FAILURE); } return 0;}int CameraClass::StopCapture(void){ enum v4l2_buf_type type; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) { perror("xioctl VIDIOC_STREAMOFF"); exit(EXIT_FAILURE); } return 0;}int CameraClass::CloseCamera(void){ unsigned int i; for (i = 0; i < n_buffer; i++) { if (-1 == munmap(usr_buf[i].start, usr_buf[i].length)) { exit(EXIT_FAILURE); } } if (-1 == close(fd)) { perror("close"); exit(EXIT_FAILURE); } return 0;}
以下是头文件
#ifndef __CAMERA_V4L2_H_#define __CAMERA_V4L2_H_#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <sys/ioctl.h>#include <stdlib.h>#include <linux/types.h>#include <linux/videodev2.h>#include <malloc.h>#include <math.h>#include <string.h>#include <sys/mman.h>#include <errno.h>#include <assert.h>struct buffer_type { void *start; //用户空间与文件映射的地址 size_t length; //由Width和Height决定};class CameraClass{public: CameraClass(char *DevicePath, int Width, int Height); ~CameraClass();public: int InitCamera(void); //初始化,包括打开文件,查询设备能力,映射至内核空间 int StartCapture(void); //开始采集图像,在内核中建立对应buf int GetOneImage(struct buffer_type *Image); int StopCapture(void); int CloseCamera(void); private: char dev_name[50]; //设备路径 int fd; int width; //图像长 int height; //图像宽 struct buffer_type *usr_buf; //用户空间的缓存 unsigned int n_buffer; //FIFO长度 int xioctl(int fd, int request, void *arg); //避免信号打断导致ioctl失败};#endif
以下是测试用文件
#include "camera_v4l2.h"#include "stdio.h"int main(int argc, char *argv[]){ if(argc != 2) { fprintf(stderr, "Input picture name:\n"); return -1; } CameraClass *MyLogitech = new CameraClass("/dev/video4", 1280, 960); if(0 == MyLogitech->InitCamera()) fprintf(stdout, "InitCamera succeed!\n"); if(0 == MyLogitech->StartCapture()) fprintf(stdout, "StartCapture succeed!\n"); struct buffer_type Image; if(0 == MyLogitech->GetOneImage(&Image)) fprintf(stdout, "GetOneImage succeed!\n"); fprintf(stdout, "Image ptr is %p, length is %d\n", Image.start, Image.length); int fd = creat(argv[1], 0644); if(-1 == fd) { perror("creat"); } else { int ret = write(fd, Image.start, Image.length); if(ret == -1) { perror("write"); } ret = close(fd); if(-1 == ret) { perror("close"); } } if(0 == MyLogitech->StopCapture()) fprintf(stdout, "StopCapture succeed!\n"); if(0 == MyLogitech->CloseCamera()) fprintf(stdout, "CloseCamera succeed!\n"); return 0;}
1 0
- V4L2采集摄像头过程中的几点细节
- 摄像头v4l2采集中的mmap
- V4L2采集摄像头数据
- V4L2摄像头采集数据
- V4L2摄像头采集数据
- V4L2摄像头采集数据
- V4L2摄像头视频采集
- Linux V4L2摄像头采集
- Linux V4L2 摄像头视频采集
- Linux V4L2 摄像头视频采集
- Linux V4L2 摄像头视频采集
- Linux V4L2 摄像头视频采集
- Linux V4L2 摄像头视频采集
- Linux V4L2 摄像头视频采集
- Linux V4L2 摄像头视频采集
- Linux V4L2 摄像头视频采集
- V4L2通过摄像头采集图片
- Linux V4L2 摄像头视频采集
- 安装phpredis插件出现的问题
- 剑指offer: 丑数
- 云平台仿真框架CloudSim
- 0319
- Java关键字 super和this
- V4L2采集摄像头过程中的几点细节
- mysql sql order
- 为什么要重写hashCode()方法和equals()方法以及如何进行重写
- ACM程序设计 书中题目U(美丽的数字)
- 走进 Redis 的世界
- 图形化升级单机oracle 11.2.0.1 到 11.2.0.4
- duilib开发基础:创建自定义控件的过程
- Location对象
- 编程及应用中的一些快捷键(持续更新中)