V4L是 Video for Linux的缩写,它是Linux 内核中关于视频设备的子系统,它为linux 下的视频驱动提供了统一的接口,使得应用程序可以使用统一的API 函数操作不同的视频设备,极大地简化了视频系统的开发和维护。
由于早期的 V4L 有很多缺陷,Bill Dirks 等人对其进行了重新设计,并取名为Video for Linux 2(V4L2使用),最早出现于Linux2.5.x 版本。V4L2 相比于V4L 有更好的扩展性和灵活性,并且支持的硬件设备更多。
因此在应用程序V4L编程实际是指v4l2,我们这个系列的以V4L2为主,但由于历史的原因,V4L2一般兼容V4L.所以很多程序可以用V4L接口.
1.V4L支持设备
V4L2(video for linux) 可以支持多种设备,它可以有以下几种接口:
1. 视频采集接口(video capture interface):这种应用的设备可以是高频头或者摄像头.V4L2的最初设计就是应用于这种功能的.下面也是着重讲解这种应用.
2. 视频输出接口(video output interface):可以驱动计算机的外围视频图像设备--像可以输出电视信号格式的设备.
3. 直接传输视频接口(video overlay interface):它的主要工作是把从视频采集设备采集过来的信号直接输出到输出设备之上,而不用经过系统的CPU.
4. 视频间隔消隐信号接口(VBI interface):它可以使应用可以访问传输消隐期的视频信号.
5. 收音机接口(radio interface):可用来处理从AM或FM高频头设备接收来的音频流.
2.V4L处理基本流程
跟一般设备处理一样,大体上V4L处理有四个流程.
2.1 打开V4L设备结点
一般V4L设备结点名是 /dev/videoN.如第一个V4L设备是/dev/video0.
int fd = open("/dev/video0",O_RDWR |O_NONBLOCK);
2.2配置设备/查询设备属性
主要通过ioctl来操作,象V4L2 常见的的命令有
格式
int ioctl (int __fd, unsigned long int __request, .../*args*/) ;
__request是V4L2一些ioctl命令,常见如下.
-
- VIDIOC_REQBUFS:分配内存
- VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址
- VIDIOC_QUERYCAP:查询驱动功能
- VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式
- VIDIOC_S_FMT:设置当前驱动的频捕获格式
- VIDIOC_G_FMT:读取当前驱动的频捕获格式
- VIDIOC_TRY_FMT:验证当前驱动的显示格式
- VIDIOC_CROPCAP:查询驱动的修剪能力
- VIDIOC_S_CROP:设置视频信号的边框
- VIDIOC_G_CROP:读取视频信号的边框
- VIDIOC_QBUF:把数据从缓存中读取出来
- VIDIOC_DQBUF:把数据放回缓存队列
- VIDIOC_STREAMON:开始视频显示函数
- VIDIOC_STREAMOFF:结束视频显示函数
- VIDIOC_QUERYSTD:检查当前视频设备支持的标准,例如PAL或NTSC。
完整的IOCTL命令参见http://v4l2spec.bytesex.org/spec/r7624.htm
2.3 处理V4L视频数据
在V4L设备中,有的设备从硬件取出,送到应用程序处理,比如摄像头硬件取得视频数据后,通过V4L接口把视频数据发送应用程序, 比如显示屏幕或保存成为文件.
有的设备是从应用发往硬件处理,如电视接口.
在V4L接口,设定了三种应用程序与驱动的交互方式,分别是
直接读取设备文件方式(read/write)、用户指针方式(userptr)以及mmap 映射方式。
1)mmap方式,驱动将内部数据空间映射到应用程序空间上,双方直接在这个空间进行数据交换,是效果最高的方法,这也是最常用的方式之一
2)
直接读取设备文件方式 直接调用 read()、write()函数进行数据的读入和输出,该方法一般配合select()使用。
3)用户指针方式 首先由应用程序申请一段缓冲区,然后将缓冲区传给驱动,驱动将其作为缓冲区,从而实现了内存共享。这一方法用的较少.
2.4 关闭设备
调用close();如果是内存映射方式,在关闭前还需要调用munmap解除映射.
3.V4L两个版本区别
1.头文件不一样 V4L使用#include
V4L2使用 #include
2.IOCTL命令编号 ,V4L使用 VIDIOCXXXX的形式,而V4L2使用VIDIOC_XXXX 或 VIDIOC_G_XXXX形式.
如V4L中取设备属性命令是VIDIOCGCAP,而V4L2对应的是VIDIOC_QUERYCAP.
3.两者数据结构不一样,V4L以Video_为前缀,而V4L以v4l2_为前缀.如设备属性
V4l1--> struct video_capability video_cap
V4l2-->struct v4l2_capability
4.检测V4L设备版本
在V4L2中,规定必须实现 VIDIOC_QUERYCAP命令,而V4L1,规定必须实现VIDIOCGCAP,用这个方法可以判断设备的版本.参见如下代码.
/*
* Author: Andrew Huang
* detectd v4l2 device version
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
0 -- 不是v4l设备
1 -- v4l 设备
2 -- v4l2 设备
*/
int test_v4l_version(int fd)
{
int ret = 0;
char dummy[256];
if (-1 != ioctl(fd,VIDIOC_QUERYCAP,dummy)) {
ret = 2;
}
else if (-1 != ioctl(fd,VIDIOCGCAP,dummy)) {
ret = 1;
}
return ret;
}
int main(int argc,char * argv[])
{
char dev_name[64] = "/dev/video2";
int cam_fd =-1;
if(argc>1)
{
strncpy(dev_name,argv[1],sizeof(dev_name)-1);
}
printf("open device %s\n",dev_name);
cam_fd = open(dev_name,O_RDWR|O_NONBLOCK);
if(cam_fd == -1)
{
printf("open failure \n");
return -1;
}
switch(test_v4l_version(cam_fd))
{
case 0:
printf("%s:fd %d isn't v4l deivce\n",dev_name,cam_fd);
return -1;
break;
case 1:
printf("\n### video4linux device info [%s] ###\n",dev_name);
return -2;
break;
case 2:
printf("\n### v4l2 device info [%s] ###\n",dev_name);
break;
}
close(cam_fd);
return 0;
}
作者:
Andrew Huang [email protected]
转自: http://blog.chinaunix.net/uid-20587912-id-405264.html
PS:
错误:expected expression before ‘struct’
解决:#include