V4L2采集视频操作基本按照:打开视频设备、设置视频格式、启动视频采集、循环处理视频数据、停止视频采集、关闭视频设备,具体操作通过ioctl等函数实现
一般操作流程如下:
1、打开视频设备文件
int fd = open("/dev/video0", O_RDWR);
2、查询设备的能力,比如是否具有视频输入、或者音频输入输出等
struct v4l2_capability {
__u8 driver[16]; /* i.e. "bttv" */ //驱动的名字
__u8 card[32]; /* i.e. "Hauppauge WinTV" */ //设备的名字
__u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */ //设备在系统中位置
__u32 version; /* should use KERNEL_VERSION() */ //驱动版本号
__u32 capabilities; /* Device capabilities */ //设备支持的操作
__u32 reserved[4];
};
struct v4l2_capability cap;
ioctl(fd, VIDIOC_QUERYCAP, &cap);
3、设置视频采集参数
1)设置视频的制式,制式包括PAL/NTSC,
typedef __u64 v4l2_std_id;
使用ioctl(fd, VIDIOC_S_STD, &std_id);
2)设置视频图像的采集窗口的类型和大小
struct v4l2_crop {
enum v4l2_buf_type type;//视频采集类型V4L2_BUF_TYPE_VIDEO_CAPTURE
struct v4l2_rect c; //表示采集窗口的大小的结构体
};
struct v4l2_crop crop;
使用ioctl(fd, VIDIOC_S_CROP, &crop);
3)显示所有的视频帧格式
struct v4l2_fmtdesc {
__u32 index; //格式序号
enum v4l2_buf_type type; //帧类型
__u32 flags; //是否为压缩格式
__u8 description[32]; //格式名称
__u32 pixelformat; /* Format fourcc */
__u32 reserved[4];
};
struct v4l2_fmtdesc fmtdesc;
ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc);可以通过循环使用这个函数遍历输出所有格式
4)设置视频帧格式,包括帧的点阵格式、宽度和高度等
struct v4l2_format {
enum v4l2_buf_type type;
union {
struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */
struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
__u8 raw_data[200]; /* user-defined */
} fmt;
};
struct v4l2_pix_format {
__u32 width;
__u32 height;
__u32 pixelformat; //{V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUYV ...}
enum v4l2_field field;
__u32 bytesperline; //表示每一行的字节数 /* for padding, zero if unused */
__u32 sizeimage; //表示图像所占的存储空间的大小
enum v4l2_colorspace colorspace;
__u32 priv; /* private data, depends on pixelformat */
};
struct v4l2_format fmt;
使用ioctl(fd, VIDIOC_S_FMT, &fmt);
5)设置视频的帧率
struct v4l2_streamparm {
enum v4l2_buf_type type; //V4L2_BUF_TYPE_VIDEO_CAPTURE
union {
struct v4l2_captureparm capture;
struct v4l2_outputparm output;
__u8 raw_data[200]; /* user-defined */
} parm;
};
struct v4l2_captureparm {
__u32 capability; /* Supported modes */
__u32 capturemode; //采集模式,采集高质量图片值为1,一般设为0
struct v4l2_fract timeperframe; /* Time per frame in .1us units */
__u32 extendedmode; /* Driver-specific extensions */
__u32 readbuffers; /* # of buffers for read */
__u32 reserved[4];
};
struct v4l2_streamparm parm;
使用ioctl(fd, VIDIOC_S_PARM,&parm);
6)设置视频的旋转方式,使用ioctl(fd, VIDIOC_S_CTRL, &ctrl);
4、向驱动申请视频流数据的帧缓冲区
1)申请若干个帧缓冲区,每个帧缓冲区存放一帧视频数据,这些帧缓冲区在内核空间
struct v4l2_requestbuffers {
__u32 count;
enum v4l2_buf_type type; //V4L2_BUF_TYPE_VIDEO_CAPTURE
enum v4l2_memory memory; //{V4L2_MEMORY_MMAP, V4L2_MEMORY_USERPTR, V4L2_MEMORY_OVERLAY}
__u32 reserved[2];
};
struct v4l2_requestbuffers req;
ioctl(fd, VIDIOC_REQBUFS, &req);
2)查询帧缓冲区在内核空间的长度和偏移量
struct v4l2_buffer {
__u32 index;
enum v4l2_buf_type type;
__u32 bytesused;
__u32 flags;
enum v4l2_field field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
/* memory location */
enum v4l2_memory memory;
union {
__u32 offset;
unsigned long userptr;
} m;
__u32 length;
__u32 input;
__u32 reserved;
};
struct v4l2_buffer req;
ioctl(fd, VIDIOC_QUERY_BUF, &req);
5、应用程序通过内存映射,将帧缓冲区的地址映射到用户空间,这样就可以直接操作采集到的帧数据流,而不必去复制
buffer[i].start = mmap(NULL, buffers[i].length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buffers[i].offset);
6、将申请到的帧缓冲全部放入视频采集输入队列,以便存放采集的数据。ioctl(fd, VIDIOC_QBUF, &buf);
7、开始视频流数据的采集。ioctl(fd, VIDIOC_STREAMON, &type);
在驱动程序处理视频的过程中,定义了两个队列:视频采集输入队列和视频采集输出队列,前者是等待驱动存放视频数据的队列,后者是驱动程序已经放入了视频数据的队列
应用程序需要将上述帧缓冲在视频采集输入队列排队,然后可以启动视频采集。
8、驱动将采集到的一帧视频数据存入输入队列第一个帧缓冲区,一帧数据采集完后,驱动程序将该帧缓冲区移至视频采集输出队列,等待应用程序从输出队列中取出,驱动
程序接下来采集下一帧数据,放入第二个帧缓冲区,同样帧缓冲区存满下一帧数据后被放入视频采集输出队列
驱动程序做的事,从输入队列中取一块buffer往里面填数据,填满之后将整块buffer移至输出队列(输入队列链表减小,输出链表变大)
应用程序做的事,DQBUF则从输出队列取出一块buffer(输出队列变小);QBUF则将一块buffer放入输入队列中(输入队列变大)。
9、应用程序从视频采集输出队列中取出已含有采集数据的帧缓冲区。ioctl(fd, VIDIOC_DQBUF, &buf);应用程序处理该帧缓冲区的原始视频数据
10、处理完后,应用程序将该帧缓冲区重新排入输入队列,这样便可以循环采集数据。ioctl(fd, VIDIOC_QBUF, &buf);
重复上述步骤8到10,直到停止采集数据。
11、停止视频的采集。ioctl(fd, VIDIOC_STREAMOFF, &type);
12、释放申请的视频缓冲区unmap,关闭视频设备文件close(fd);