linux上用v4l2函数接口获取视频主要是一个步骤流程,一步步做就很容易,现已我在qt下编写的一个读取摄像头视频的程序中的相关代码为例。
首先打开视频设备,比如/dev/video0,
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; }然后初始化设备
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
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; } }开始捕获视频
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; }获取一帧图像
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放回去
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; }停止视频捕捉
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; }卸载摄像头设备
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; }关闭视频设备文件
int VideoDevice::close_device() { if(-1 == close(fd)) { emit display_error(tr("close: %1").arg(QString(strerror(errno)))); return -1; } return 0; }这就是完整的采集视频的流程,当然可以多增加配置采集的视频格式的代码。