V4L2 API:

https://linuxtv.org/downloads/v4l-dvb-apis/userspace-api/v4l/v4l2.html

视频采集流程

V4L2视频采集的简化流程:

打开设备 -> 设置输入源 -> 设置视频帧格式 -> 设置帧率等参数 -> 申请BUF及映射 -> 启动流 -> 获取数据 -> 停止视频流 -> 关闭设备

其他一些细节就省略了,这里只记录V4L2相关的 ioctl 操作流程。

打开设备

fd = open(v4l_device, O_RDWR, 0);

获取设备信息

VIDIOC_QUERYCAP

struct v4l2_capability cam_cap;  
ret = ioctl(fd, VIDIOC_QUERYCAP, &cam_cap);
printf("Driver Name: %s\nCard Name: %s\nBus info: %s\nDriver Version: %u.%u.%u\n",
           cam_cap.driver,
           cam_cap.card,
           cam_cap.bus_info,
           (cam_cap.version >> 16) & 0XFF, (cam_cap.version >> 8) & 0XFF, cam_cap.version & 0XFF);

设置或获取输入源

VIDIOC_G_INPUT
VIDIOC_S_INPUT

/* set video input */ 
ioctl(fd, VIDIOC_S_INPUT, &channel);

/* get current video input */
ioctl(fd, VIDIOC_G_INPUT, &channel);

/* enum input */
ioctl(fd, VIDIOC_ENUMINPUT, &input);

枚举支持的视频帧格式

VIDIOC_ENUM_FMT

struct v4l2_fmtdesc vfd = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
ioctl(fd, VIDIOC_ENUM_FMT, &vfd);

枚举设备支持的所有标准

VIDIOC_ENUMSTD

struct v4l2_standard standard;
ioctl(fd, VIDIOC_ENUMSTD, &standard);

获取和设置当前的视频帧格式

VIDIOC_G_FMT

struct v4l2_format fmt = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
ioctl(fd, VIDIOC_G_FMT, &fmt);
printf("    width : %d  height : %d\n\
    pixelformat : %d\n\
    field : %d\n\
    bytesperline : %d\n\
    sizeimage : %d\n\
    colorspace : %d\n\
    priv : %d\n",\
    fmt.pix.width,\
    fmt.pix.height,\
    fmt.pix.pixelformat,\
    fmt.pix.field, \
    fmt.pix.bytesperline, \
    fmt.pix.sizeimage, \
    fmt.pix.colorspace, \
    fmt.pix.priv);

VIDIOC_S_FMT

/*设置帧格式,长,宽,采样类型等*/
struct v4l2_format fmt = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
fmt.fmt.pix.width = *width;
fmt.fmt.pix.height = *height;
fmt.fmt.pix.pixelformat = pixelformat;
fmt.fmt.pix.field = V4L2_FIELD_ANY;
ioctl(fd, VIDIOC_S_FMT, &fmt);

获取和设置参数

VIDIOC_G_PARM

struct v4l2_streamparm parm = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
ioctl(fd, VIDIOC_G_PARM, &parm);

VIDIOC_S_PARM

/*设置帧率fps等*/
parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
parm.parm.capture.timeperframe.numerator = 1;
parm.parm.capture.timeperframe.denominator = fps;
parm.parm.capture.capturemode = mode;
ioctl(fd, VIDIOC_S_PARM, &parm);

申请BUF

VIDIOC_REQBUFS

struct v4l2_requestbuffers req = {
    .type   = V4L2_BUF_TYPE_VIDEO_CAPTURE,
    .count  = desired_video_buffers,
    .memory = V4L2_MEMORY_MMAP
};
ioctl(fd, VIDIOC_REQBUFS, &req);

三种交换数据的方法:

  • 直接 read/write、
  • 内存映射(V4L2_MEMORY_MMAP)
  • 用户指针(V4L2_MEMORY_USERPTR)

获取缓存信息并内存映射:

VIDIOC_QUERYBUF

for (i = 0; i < req.count; i++) {
    struct v4l2_buffer buf = {
        .type   = V4L2_BUF_TYPE_VIDEO_CAPTURE,
        .index  = i,
        .memory = V4L2_MEMORY_MMAP
    };

    if(ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) {
        printf("VIDIOC_QUERYBUF error!\n");
        return -1;
    }

    buf_len[i].length = buf.length;
    buf_start[i].start = mmap(NULL, buf.length,
                            PROT_READ | PROT_WRITE, MAP_SHARED, 
                            fd, buf.m.offset);

    if(buf_start[i].start == MAP_FAILED) {
        printf("v4l_start_capturing mmap error!\n");
        return -1;
    }
}

将缓存放入队列

VIDIOC_QBUF

for (i = 0; i < req.count; i++) {
    struct v4l2_buffer buf = {
        .type   = V4L2_BUF_TYPE_VIDEO_CAPTURE,
        .index  = i,
        .memory = V4L2_MEMORY_MMAP
    };

    if(ioctl(fd, VIDIOC_QBUF, &buf) < 0) {
        printf("VIDIOC_QBUF error!\n");
        return -1;
    }
}

启动数据流

VIDIOC_STREAMON

enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMON, &type);

从队列中获取数据

VIDIOC_DQBUF #从队列中取出帧

struct v4l2_buffer buf = {
    .type   = V4L2_BUF_TYPE_VIDEO_CAPTURE,
    .memory = V4L2_MEMORY_MMAP
};

ioctl(fd, VIDIOC_DQBUF, &buf);

停止数据流

VIDIOC_STREAMOFF

enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMOFF, &type);

取消映射

for(i = 0; i < req.count; i++) {
	if(buf_start[i].start > 0)
		munmap(buf_start[i].start, buf_len[i].length);
}

关闭设备

close(dev_fd);

参考:

https://github.com/Jin992/robo_vision_mavalink/blob/c10d67cafcd65ac9efbf7bd84b2b117e00764fbf/Video/VideoCapture.cpp