V4L2采集摄像头过程中的几点细节

来源:互联网 发布:centos更新python 编辑:程序博客网 时间:2024/04/30 03:29

 最近打算做一个H.264的图像传输设备,第一步当然是采集图像,了解过相关知识后得知图像采集需要用到V4L2,于是在网上找关于V4L2的资料,昨天终于把V4L2看的差不多,并且把网上的程序封装成了符合我的习惯的一个C++类。关于V4L2采集图像在网上有很多博客讲的都很详细,而我也水平有限,就不写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
原创粉丝点击