做这个项目看了很多博客,也出现了很多问题,我几乎把视频技术论坛的帖都翻完了,还好最后终于都意想不到的解决了。先总结V4L2,然后在使用开源库x264编码的部分,会总结一下常见出现的系列问题和相应的解决方法。
一、V4L2视频采集
<1>V4L2介绍
<2>视频采集流程
1. 打开设备文件。 int fd=open(”/dev/video0″,O_RDWR);
2. 取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。VIDIOC_QUERYCAP,struct v4l2_capability
3. 选择视频输入,一个视频设备可以有多个视频输入。VIDIOC_S_INPUT,struct v4l2_input
4. 设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。
VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format
5. 向驱动申请帧缓冲,一般不超过5个。struct v4l2_requestbuffers
6. 将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。mmap
7. 将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer
8. 开始视频的采集。VIDIOC_STREAMON
9. 出队列以取得已采集数据的帧缓冲,取得原始采集数据。VIDIOC_DQBUF
10. 将缓冲重新入队列尾,这样可以循环采集。VIDIOC_QBUF
11. 停止视频的采集。VIDIOC_STREAMOFF
12. 关闭视频设备。close(fd);
<3>常用的结构体
struct v4l2_requestbuffers reqbufs;//向驱动申请帧缓冲的请求,里面包含申请的个数
struct v4l2_capability cap;//这个设备的功能,比如是否是视频输入设备
struct v4l2_input input; //视频输入
struct v4l2_standard std;//视频的制式,比如PAL,NTSC
struct v4l2_format fmt;//帧的格式,比如宽度,高度等
struct v4l2_buffer buf;//代表驱动中的一帧
v4l2_std_id stdid;//视频制式,例如:V4L2_STD_PAL_B
struct v4l2_queryctrl query;//查询的控制
struct v4l2_control control;//具体控制的值
<4>采集过程
1) 打开设备操作
在Linux操作系统中的任何设备都看做文件,对设备的操作就转换成对设备文件的操作。打开视频设备,调用函数fd = open
(dev_name, O_RDWR |O_NONBLOCK, 0),其中在main函数中定义了dev_name = "/dev/video3",/dev/video3就是USB摄像头对应的
设备文件,O_RDWR |O_NONBLOCK表明本文采用无阻噻模式打开摄像头设备。
2) 视频设备采集前的初始化设置
首先使用xioctl(fd, VIDIOC_QUERYCAP, &cap)获取有关摄像头的基本信息,查看是否支持视频输入等功能。然后设置视频捕获格
式,本系统选用的摄像头输出格式为YUV422,图像长、宽设置为176144,具体如下:
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; //YUV422
fmt.fmt.pix.height = 144;
fmt.fmt.pix.width =176;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if(-1== ioctl(fd, VIDIOC_S_FMT, &fmt);
3)向驱动申请帧缓存
struct v4l2_requestbuffers req; //一帧图像缓存
req.count = 4; //count 缓存数量
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { //向驱动申请帧缓存
4)获取每个缓存的信息,并mmap到用户空间
struct buffer * buffers = NULL;
buffers = calloc(req.count, sizeof(*buffers)); //申请物理内存
for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
struct v4l2_buffer buf;
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers; //缓存编号
if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) //获取缓存的地址
errno_exit("VIDIOC_QUERYBUF");
buffers[n_buffers].length = buf.length;
buffers[n_buffers].start=mmap(NULL ,buf.length,PROT_READ|PROT_WRITE ,MAP_SHARED , fd, buf.m.offset); } //转换成应用程序中
的绝对地址,放入缓存队列
5)打开数据流通道,开始采集视频
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) //打开数据流通道,开始采集
6)取出FIFO缓存中已经采样的帧缓存
enum v4l2_buf_type type;
for (i = 0; i < n_buffers; ++i) {
struct v4l2_buffer buf;
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) //把数据从缓存中读取出来
7)将采集到的视频按帧写入文件
process_image(buffers[count].start, cap_image_size);
static void process_image(const void * p,int len) {
if (len > 0)
fwrite(p, 1, len, outf); }
8)将刚刚处理完的缓冲重新入队列尾,这样可以循环采集
if (ioctl(fd, VIDIOC_QBUF, &buf) == -1)
9)停止视频的采集,释放之前申请的物理内存
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) //关闭数据流通道
errno_exit("VIDIOC_STREAMOFF");
free(buffers);
10)关闭视频设备
if (-1 == close(fd))
视频采集就总结到着,下篇总结有关264方面的。