V4L2视频采集 X264编码项目总结(上)

来源:互联网 发布:软件概要设计范文 编辑:程序博客网 时间:2024/04/30 13:14

做这个项目看了很多博客,也出现了很多问题,我几乎把视频技术论坛的帖都翻完了,还好最后终于都意想不到的解决了。先总结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方面的。






0 0
原创粉丝点击