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

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!\n");
        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!\n");
        return false;
    }

VIDIOC_S_FMT用于设置格式,VIDIOC_G_FMT用于读取格式,可以检查设置是否成功

    //get format
    if (ioctl(fd, VIDIOC_G_FMT, &fmt) == -1)
    {
        perror("VIDIOC_G_FMT failed!\n");
        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\n");
        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!\n");
        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!\n");
            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!\n");
            return false;
        }
        //buffer queue
        if (ioctl(fd, VIDIOC_QBUF, &buf) == -1)
        {
            perror("VIDIOC_QBUF failed!\n");
            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!\n");
        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!\n");
            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!\n"); 
        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










你可能感兴趣的:(Linux)