Linux基于V4L2的视频捕捉

简介

linux环境使用V4l2实现摄像头捕捉,界面流畅播放并可以保存图片到本地。

代码

void VideoCapture::run()
{
    qDebug() << "VideoCapture start";

    // 打开设备
    int fd = open("/dev/video0", O_RDWR);
    if(fd < 0)
    {
        qDebug("video设备打开失败\n");
        return;
    }
    else
    {
        qDebug("video设备打开成功\n");
    }

    //查看设备是否为视频采集设备
    struct v4l2_capability vcap;
    ioctl(fd, VIDIOC_QUERYCAP, &vcap);
    if (!(V4L2_CAP_VIDEO_CAPTURE & vcap.capabilities))
    {
        qDebug("No capture video device!\n");
        return;
    }

    // 枚举帧格式
    struct v4l2_fmtdesc fmtdesc;
    fmtdesc.index = 0;
    fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    qDebug("Video支持所有格式如下:");
    while(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0)
    {
        qDebug("v4l2_format %d:%s",fmtdesc.index, fmtdesc.description);
        fmtdesc.index++;
    }

    // 枚举分辨率
    struct v4l2_frmsizeenum frmsize;
    frmsize.index = 0;
    frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    qDebug("Video支持分辨率如下:");
    while(ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0)
    {
        qDebug("%d frame_size<%d*%d %d>", frmsize.index, frmsize.discrete.width, frmsize.discrete.height, frmsize.pixel_format);
        frmsize.index++;
    }
    // 枚举某分辨率下的帧速率
    int pixel_width = 1280;
    int pixel_height = 720;

    struct v4l2_frmivalenum frmival;
    frmival.index = 0;
    frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    frmival.pixel_format = V4L2_PIX_FMT_MJPEG;
    frmival.width = pixel_width;
    frmival.height = pixel_height;
    while(ioctl(fd,VIDIOC_ENUM_FRAMEINTERVALS, &frmival) == 0)
    {
        qDebug("<%d*%d> support frame_size %dfps", frmival.width, frmival.height, frmival.discrete.denominator / frmival.discrete.numerator);
        frmival.index++;
    }

    // 设置采集格式
    struct v4l2_format vfmt;
    vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vfmt.fmt.pix.width = pixel_width;
    vfmt.fmt.pix.height = pixel_height;
    vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    if(ioctl(fd, VIDIOC_S_FMT, &vfmt) < 0)
    {
        qDebug("video设置格式失败\n");
        return;
    }
    // 检查设置参数是否生效
    if(ioctl(fd, VIDIOC_G_FMT, &vfmt) < 0)
    {
        qDebug("video获取格式失败\n");
        return;
    }

    // 获取帧信息
    struct v4l2_streamparm streamparm;
    streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ioctl(fd, VIDIOC_G_PARM, &streamparm);
    qDebug("current frameRate <%d * %d>\n", streamparm.parm.capture.timeperframe.numerator, streamparm.parm.capture.timeperframe.denominator);
    // 设置帧信息
    if(V4L2_CAP_TIMEPERFRAME & streamparm.parm.capture.capability)
    {
        streamparm.parm.capture.timeperframe.numerator = 1;
        streamparm.parm.capture.timeperframe.denominator = 30;
//        if(ioctl(fd, VIDIOC_S_PARM, &streamparm) < 0)
//        {
//            qDebug("video设置帧率失败 <%d * %d>", streamparm.parm.capture.timeperframe.numerator, streamparm.parm.capture.timeperframe.denominator);
//        }
    }

    // 申请缓冲区空间
    struct v4l2_requestbuffers reqbuf;
    reqbuf.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.count = m_frameCount;
    reqbuf.memory = V4L2_MEMORY_MMAP;
    if(ioctl(fd, VIDIOC_REQBUFS, &reqbuf) < 0)
    {
        qDebug("video申请缓冲区失败\n");
        return;
    }

    // 将帧缓冲映射到进程地址空间
    struct v4l2_buffer buf;
    buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    // 将每一帧对应的缓冲区的起始地址保存在m_userVideoBuf数组中,读取采集数据时,只需直接读取映射区即可
    for(buf.index=0; buf.index<m_frameCount; buf.index++)
    {
        ioctl(fd, VIDIOC_QUERYBUF, &buf);
        m_userVideoBuf[buf.index] = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
        m_userVideoBufSize[buf.index] = buf.length;
        if(m_userVideoBuf[buf.index] == MAP_FAILED)
        {
            qDebug("video mmap failed\n");
            return;
        }

        // 入队操作
        if(ioctl(fd, VIDIOC_QBUF, &buf) < 0)
        {
            qDebug("入队失败\n");
            return;
        }
    }

    // 开始采集
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMON, &type) < 0)
    {
        qDebug("video开始采集失败");
        m_isRun = false;
    }

    //持续读取图像数据 使用select监听数据
    fd_set fds;
    struct timeval tv;
    tv.tv_sec  = 2;
    tv.tv_usec = 0;

    //图片文件缓存
    char *fileBuf = new char[3*1024*1024];

    while(m_isRun)
    {
        FD_ZERO(&fds);
        FD_SET(fd, &fds);
        int ret = select(fd+1, &fds, NULL, NULL, &tv);
        if(ret < 0)
        {
            qDebug("select io error\n");
            break;
        }

        // 读取帧
        struct v4l2_buffer readbuffer;
        readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        readbuffer.memory = V4L2_MEMORY_MMAP;
        if(ioctl(fd, VIDIOC_DQBUF, &readbuffer) < 0)
        {
            qDebug("读取帧失败\n");
        }

//        qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss:zzz");

        //重点:帧数据处理
        const uchar *data = (const uchar *)m_userVideoBuf[readbuffer.index];
        int length = readbuffer.length;

        //构造并显示
        QImage image = QImage::fromData(data, length);
        emit sigUpdateImage(image.copy());

        // 再次入队
        if(ioctl(fd, VIDIOC_QBUF, &readbuffer) < 0)
        {
            qDebug("再次入队失败\n");
        }
    }

    // 停止采集
    if (ioctl(fd, VIDIOC_STREAMOFF, &type) < 0)
    {
        qDebug("停止采集失败\n");
    }

    // 释放映射
    for(uint i=0; i<m_frameCount; i++)
    {
        munmap(m_userVideoBuf[i], m_userVideoBufSize[i]);
    }

    //关闭文件
    emit sigUpdateImage(QImage());
    close(fd);
    delete[] fileBuf;
    qDebug() << "VideoCapture stop";
}

相关推荐

  1. Linux基于V4L2视频捕捉

    2024-06-08 06:42:03       33 阅读
  2. 视频V4L2应用

    2024-06-08 06:42:03       41 阅读
  3. Linux多媒体基础 - v4l2 vb2_queue用法

    2024-06-08 06:42:03       31 阅读
  4. V4L2驱动

    2024-06-08 06:42:03       30 阅读
  5. 汽车IVI中控开发入门及进阶(十二):V4L2视频

    2024-06-08 06:42:03       58 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-08 06:42:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-08 06:42:03       100 阅读
  3. 在Django里面运行非项目文件

    2024-06-08 06:42:03       82 阅读
  4. Python语言-面向对象

    2024-06-08 06:42:03       91 阅读

热门阅读

  1. Unity3D DOTS 10W GPU Intancing 动画与合批优化详解

    2024-06-08 06:42:03       28 阅读
  2. Excel中的SUMPRODUCT函数:使用方法与案例分析

    2024-06-08 06:42:03       30 阅读
  3. 【MyBatisPlus条件构造器】

    2024-06-08 06:42:03       29 阅读
  4. [DT] 翻译笔记

    2024-06-08 06:42:03       24 阅读
  5. uniapp vue 隐藏button的边框

    2024-06-08 06:42:03       31 阅读
  6. 零、测试开发前置知识

    2024-06-08 06:42:03       28 阅读
  7. 【常用工具系列】Git 教程——从入门到大师

    2024-06-08 06:42:03       45 阅读
  8. Freemarker

    2024-06-08 06:42:03       27 阅读
  9. MySQL学习——获取数据库和表格的信息

    2024-06-08 06:42:03       30 阅读
  10. solidity的modifier修饰符

    2024-06-08 06:42:03       22 阅读
  11. 数据分析------统计学知识点(一)

    2024-06-08 06:42:03       35 阅读