linux v4l2 摄像头采集视频的方法

    xiaoxiao2021-04-12  30

    Linux上用v4l2函数接口获取视频主要是一个步骤流程,一步步做就很容易,现已我在qt下编写的一个读取摄像头视频的程序中的相关代码为例。

    首先打开视频设备,比如/dev/video0,

    [cpp]  view plain  copy fd = open(dev_name.toStdString().c_str(), O_RDWR/*|O_NONBLOCK*/, 0);          if(-1 == fd)       {           emit display_error(tr("open: %1").arg(QString(strerror(errno))));           return -1;       }   然后初始化设备 [cpp]  view plain  copy v4l2_capability cap;   v4l2_cropcap cropcap;   v4l2_crop crop;   v4l2_format fmt;      if(-1 == ioctl(fd, VIDIOC_QUERYCAP, &cap))   {   //查询设备功能       if(EINVAL == errno)       {           emit display_error(tr("%1 is no V4l2 device").arg(dev_name));       }       else       {           emit display_error(tr("VIDIOC_QUERYCAP: %1").arg(QString(strerror(errno))));       }       return -1;   }      if(!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))   {   //视频采集       emit display_error(tr("%1 is no video capture device").arg(dev_name));       return -1;   }      if(!(cap.capabilities & V4L2_CAP_STREAMING))   {   //视频流       emit display_error(tr("%1 does not support streaming i/o").arg(dev_name));       return -1;   }      CLEAR(cropcap);      cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;      if(0 == ioctl(fd, VIDIOC_CROPCAP, &cropcap))   {       CLEAR(crop);       crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;       crop.c = cropcap.defrect;          if(-1 == ioctl(fd, VIDIOC_S_CROP, &crop))       {           if(EINVAL == errno)           {               emit display_error(tr("VIDIOC_S_CROP not supported"));           }           else           {               emit display_error(tr("VIDIOC_S_CROP: %1").arg(QString(strerror(errno))));               return -1;           }       }   }   else   {       emit display_error(tr("VIDIOC_CROPCAP: %1").arg(QString(strerror(errno))));       return -1;   }      CLEAR(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;//YUV4:2:2   fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;//隔行扫描      if(-1 == ioctl(fd, VIDIOC_S_FMT, &fmt))   {  //设置视频格式       emit display_error(tr("VIDIOC_S_FMT").arg(QString(strerror(errno))));       return -1;   }      if(-1 == init_mmap())   {  //初始化mmap,内存映射       return -1;   }   初始化mmap    [cpp]  view plain  copy v4l2_requestbuffers req;   CLEAR(req);      req.count = 4;   req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   req.memory = V4L2_MEMORY_MMAP;      if(-1 == ioctl(fd, VIDIOC_REQBUFS, &req))   {   //请求buf       if(EINVAL == errno)       {           emit display_error(tr("%1 does not support memory mapping").arg(dev_name));           return -1;       }       else       {           emit display_error(tr("VIDIOC_REQBUFS %1").arg(QString(strerror(errno))));           return -1;       }   }      if(req.count < 2)   {       emit display_error(tr("Insufficient buffer memory on %1").arg(dev_name));       return -1;   }      buffers = (buffer*)calloc(req.count, sizeof(*buffers));//分配内存大小      if(!buffers)   {       emit display_error(tr("out of memory"));       return -1;   }      for(n_buffers = 0; n_buffers < req.count; ++n_buffers)   {       v4l2_buffer buf;       CLEAR(buf);          buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;       buf.memory = V4L2_MEMORY_MMAP;       buf.index = n_buffers;          if(-1 == ioctl(fd, VIDIOC_QUERYBUF, &buf))       {   //获取buf信息起始位置,长度等           emit display_error(tr("VIDIOC_QUERYBUF: %1").arg(QString(strerror(errno))));           return -1;       }          buffers[n_buffers].length = buf.length;       buffers[n_buffers].start =               mmap(NULL, // start anywhere                    buf.length,                    PROT_READ | PROT_WRITE,                    MAP_SHARED,                    fd, buf.m.offset);//映射          if(MAP_FAILED == buffers[n_buffers].start)       {           emit display_error(tr("mmap %1").arg(QString(strerror(errno))));           return -1;       }   }   开始捕获视频 [cpp]  view plain  copy int VideoDevice::start_capturing()   {       unsigned int i;       for(i = 0; i < n_buffers; ++i)       {           v4l2_buffer buf;           CLEAR(buf);              buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;           buf.memory =V4L2_MEMORY_MMAP;           buf.index = i;   //        fprintf(stderr, "n_buffers: %d\n", i);              if(-1 == ioctl(fd, VIDIOC_QBUF, &buf))           {   //把buf排成一列               emit display_error(tr("VIDIOC_QBUF: %1").arg(QString(strerror(errno))));               return -1;           }       }          v4l2_buf_type type;       type = V4L2_BUF_TYPE_VIDEO_CAPTURE;          if(-1 == ioctl(fd, VIDIOC_STREAMON, &type))       {           emit display_error(tr("VIDIOC_STREAMON: %1").arg(QString(strerror(errno))));           return -1;       }       return 0;   }   获取一帧图像 [cpp]  view plain  copy int VideoDevice::get_frame(void **frame_buf, size_t* len)   {       v4l2_buffer queue_buf;       CLEAR(queue_buf);          queue_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;       queue_buf.memory = V4L2_MEMORY_MMAP;          if(-1 == ioctl(fd, VIDIOC_DQBUF, &queue_buf))       {   //从队列中取出一个buf           switch(errno)           {           case EAGAIN:   //            perror("dqbuf");               return -1;           case EIO:               return -1 ;           default:               emit display_error(tr("VIDIOC_DQBUF: %1").arg(QString(strerror(errno))));               return -1;           }       }          *frame_buf = buffers[queue_buf.index].start;       *len = buffers[queue_buf.index].length;       index = queue_buf.index;          return 0;      }   获取完后,将这一帧图像的buf放回去 [cpp]  view plain  copy int VideoDevice::unget_frame()   {       if(index != -1)       {           v4l2_buffer queue_buf;           CLEAR(queue_buf);              queue_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;           queue_buf.memory = V4L2_MEMORY_MMAP;           queue_buf.index = index;              if(-1 == ioctl(fd, VIDIOC_QBUF, &queue_buf))           {   //将buf放入队列               emit display_error(tr("VIDIOC_QBUF: %1").arg(QString(strerror(errno))));               return -1;           }           return 0;       }       return -1;   }   停止视频捕捉 [cpp]  view plain  copy int VideoDevice::stop_capturing()   {       v4l2_buf_type type;       type = V4L2_BUF_TYPE_VIDEO_CAPTURE;          if(-1 == ioctl(fd, VIDIOC_STREAMOFF, &type))       {           emit display_error(tr("VIDIOC_STREAMOFF: %1").arg(QString(strerror(errno))));           return -1;       }       return 0;   }   卸载摄像头设备 [cpp]  view plain  copy int VideoDevice::uninit_device()   {       unsigned int i;       for(i = 0; i < n_buffers; ++i)       {           if(-1 == munmap(buffers[i].start, buffers[i].length))           {               emit display_error(tr("munmap: %1").arg(QString(strerror(errno))));               return -1;           }          }       free(buffers);       return 0;   }   关闭视频设备文件 [cpp]  view plain  copy int VideoDevice::close_device()   {       if(-1 == close(fd))       {           emit display_error(tr("close: %1").arg(QString(strerror(errno))));           return -1;       }       return 0;   }   这就是完整的采集视频的流程,当然可以多增加配置采集的视频格式的代码。
    转载请注明原文地址: https://ju.6miu.com/read-667742.html

    最新回复(0)