v4l2应用

来源:互联网 发布:百度新西兰导航软件 编辑:程序博客网 时间:2024/05/22 06:37

V4L2 介绍
      V4L(Video for Linux )是Linux 内核中关于视频设备的子系统,它为linux 下的视频驱动提供了统一的接口,使得应用程序可以使用统一的API 函数操作不同的视频设备,极大地简化了视频系统的开发和维护。
由于 V4L 有很多缺陷,Bill Dirks 等人对其进行了重新设计,并取名为Video for LinuxTwo(V4L2),最早出现于Linux2.5.x 版本。V4L2 相比于V4L 有更好的扩展性和灵活性,并且支持的硬件设备更多。但是需要注意的是,V4L2 对V4L 进行了彻底的改造,因而两者并不兼容[1]。
      Linux 系统中,所有的外部设备都被看成一种特殊的文件,称之为设备文件。应用程序可以通过访问这些特殊文件实现对应设备的控制。V4L2 视频设备的设备文件为/dev 目录下的videoN(N 为0~63)文件,其主设备号为81,次设备号为N(N 为0~63)。
2.2 ioctl 系统调用
      V4L2 的绝大部分功能是通过ioctl 系统调用完成的,其语法为:

ioctl(int fd, int request, void *argp)
其中fd 为设备文件描述符,通过open()函数获得;request 为系统调用类型,用于告诉系统要做什么;  argp 为用户数据指针,用于传递参数或接收数据。以下是几种常用的命令类型,这些类型都在videodev.h 中进行了定义:
VIDIOC_G_FMT/VIDIOC_S_FMT,获取/设置数据格式,主要是图片的宽高、每像素所占bit 和像素格式(比如YUV4:2:0 或RGB24 等)。数据类型为struct v4l2_format。
VIDIOC_G_CROP/VIDIOC_S_CROP,获取/设置图片剪裁矩形框,主要是设置相机的视角大小和位置。数据类型为struct v4l2_crop。
VIDIOC_REQBUFS,用于内存映射方式,该命令可以在驱动层申请多个缓冲区存放采集到的图片。需要注意的是用户需要使用mmap 将其映射到用户空间才能访问。数据类型为v4l2_requestbuffers。
VIDIOC_QUERYBUF,用于内存映射方式,该命令可以查询由VIDIOC_REQBUFS 申请到的缓冲区的大小和偏移地址,这两个参量会作为参数传给mmap。数据类型为structv4l2_buffer。
VIDIOC_QBUF,用于内存映射方式,该命令的作用是将VIDIOC_REQBUFS 申请的缓冲区放入采集队列,只有这样驱动才会将采集到的数据写入缓冲区。数据结构为structv4l2_buffer。
VIDIOC_DQBUF,用于内存映射方式,该命令的作用是将填充数据的一个缓冲区取出队列,这样驱动就不会再向这个缓冲区写数据,应用程序可以对该缓 冲区的数据进行处理。需要注意的是,应用程序处理完数据后,需要使用VIDIOC_QBUF 重新将该缓冲区放入采集队列。
VIDIOC_STREAMON/VIDIOC_STREAMOFF,这两个命令用于启动/停止视频数据的采集或输出。数据结构为enum v4l2_buf_type。
2.3 V4L2 数据交换模式
V4L2 支持三种数据交换模式,分别是:直接读取设备文件方式(read/write)用户指针方式(userptr)以及mmap 映射方式
(1)直接读取设备文件方式
直接调用 read()、write()函数进行数据的读入和输出,该方法一般配合select()使用。
(2)用户指针方式
首先由应用程序申请一段缓冲区,然后将缓冲区传给驱动,驱动将其作为缓冲区,从而实现了内存共享。
(3)mmap 映射方式
这种方式与用户指针方式类似,只是采用了逆向操作。首先在内核空间申请多个缓冲区,然后将每个缓冲区通过mmap 映射到用户空间,这样驱动和应用程序共享这些缓冲区,在进行数据处理时不需要进行拷贝,大大提高了效率。

V4L2 图片采集过程分为打开设备、设置图片格式、分配缓冲区、读取数据、关闭设备等步骤。
1)打开设备,获得文件描述符int fd=open(“/dev/video0”, O_RDWR,0)
应用程序能够使用阻塞模式或非阻塞模式打开视频设备。如果使用非阻塞模式,即使数据还未准备充分,驱动依旧会把缓存的数据返回给应用程序,但是缓存的数据 可能并非所需有效数据,这样可能造成错误。如果使用阻塞模式,只有数据准备充分时驱动才可以把缓存的数据返回给应用程序。该系统保持默认的阻塞模式
2)设置图片格式
将图片宽设为640,高设为480,像素格式设为YUYV。其他参数保持默认。
struct v4l2_format fmt;
memset ( &fmt, 0, sizeof(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;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1)
{
return -1;
}
3)分配缓冲区
首先向驱动申请三个缓冲区,为mmap 映射做准备。
struct v4l2_requestbuffers req;
req.count = 3; //申请三个缓冲区
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1)
{
return -1;
}
然后通过调用ioctl(fd, VIDIOC_QUERYBUF, &buf)获得缓冲区的长度(buf.length)和偏移地址(fd, buf.m.offset),将这两个参量作为参数传给mmap 函数buffers[num].start =mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,fd, buf.m.offset);
其中buffers 的定义为
struct
{
void *start;
int length;
}buffers[MAX_BUFFER_CNT];
该结构用于管理缓冲区在用户空间的地址。应用程序对start 所指向的存储区进行操作等同于对视频缓冲区进行操作。
缓冲区分配好了以后,驱动并不会向里面写入数据,还需要将每个缓冲区放入视频采集队列。
ioctl(fd, VIDIOC_QBUF, &buf);
4)启动采集过程,读取数据
以上步骤完成了视频采集的准备工作,但驱动还没有启动采集过程,应用程序需要调用VIDIOC_STREAMON ioctl 系统调用驱动才会开始采集数据。
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl (fd, VIDIOC_STREAMON, &type);
采集过程开始以后,驱动会不停地将数据写入分配的缓冲区内,当一个缓冲区的数据准备就绪后,驱动就会将其放入输出队列,等待应用程序的处理。当所有的缓冲区都进入输出队列后,驱动将停止采集,并等待缓冲区重新放入采集队列。
读取数据时,首先需要将一个缓冲区出队列:
struct v4l2_buffer buf;
ioctl (fd, VIDIOC_DQBUF, &buf);
驱动会从输出队列取出一个缓冲区,并将其序号赋值给buf.index,应用程序可以通过buffers[buf.index].start 来访问缓冲区的数据。当处理完成后,需要将其重新放入采集队列。
ioctl(fd, VIDIOC_QBUF, &buf);
5)停止采集
首先停止采集过程ioctl (fd, VIDIOC_STREAMOFF, &type),然后使用munmap 函数删除映射,最后调用close(fd)函数关闭设备。

原创粉丝点击