#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define
#define
#define
#define
#define
#define
#define
static
int
{
}
int
{
int
char
char
FILE
struct
struct
struct
struct
struct
unsigned
usb_camera_fd
//
if
{
fprintf(stderr,"Can't
return
}
-----------------------------video_capability-----------------------------
if
//
{
perror("Couldn't
return
}
if
{
printf("video
printf("video_caps.channels
printf("video_caps.type
printf("video
printf("video
printf("video
printf("video
}
-----------------------------video_channel-----------------------------
//
if
{
perror("ioctl
return
}
if
{
printf("video
printf("video
printf("video
}*/
-----------------------------video_picture-----------------------------
if
{
perror("ioctl
return
}
屏蔽了?屏蔽了?
if
{
printf("video_pic.brightness
printf("video_pic.colour
printf("video_pic.contrast
printf("video_pic.depth
printf("video_pic.hue
printf("video_pic.whiteness
printf("video_pic.palette
}
-----------------------------video_mbuf-----------------------------
memset(&video_mbuffer,0,sizeof(video_mbuffer));
//初始化video_mbuf,以得到所映射的buffer的信息
if
{
perror("ioctl
return
}
if
{
printf("video_mbuffer.frames
printf("video_mbuffer.offsets[0]
printf("video_mbuffer.size
}
等一等,对照v4l.docx看到这儿了
//将mmap与video_mbuf绑定
mapaddr=(unsigned
if
{
perror("v4l
return
}
-----------------------------video_mmap-----------------------------
vid_mmap.width
vid_mmap.height
vid_mmap.frame
vid_mmap.format
-----------------------------start
//若调用成功,开始一帧的截取,是非阻塞的
if
{
perror("ioctl
return
}
-----------------------------wait
//若成功,表明一帧截取已完成。可以开始做下一次
//frame是当前截取的帧的序号
framesize=320*240>>2;//实际采集到的jpeg图像的大小最多也就十几KB
//获取刚刚采集到的图像数据帧的地址
framebuffer
//获取图像的大小
//jpegsize
if
{
printf("Can't
return
}
//分配空间,准备将destbuffer
destbuffer
if
{
printf("malloc
return
}
memcpy(destbuffer,framebuffer,video_mbuffer.size);
fp
if
{
printf("Can't
//return
}
fwrite(destbuffer,video_mbuffer.size,1,fp);//写入文件
fclose(fp);//关闭文件
free(destbuffer);//释放空间
munmap(mapaddr,video_mbuffer.size);//取消绑定
close(usb_camera_fd);
return
}
一、介绍
原文网址:http://lwn.net/Articles/203924/
笔者最近有机会写了一个摄像头的驱动,是“One
V4L2有一段历史了。大约在1998的秋天,它的光芒第一次出现在Bill
V4L2在设计时,是要支持很多广泛的设备的,它们之中只有一部分在本质上是真正的视频设备:
video
video
捕获接口还有一个变体,存在于video
VBI
radio
也可能出现其它种类的设备。V4L2
视频驱动与其他驱动不同之处,在于它的配置方式多种多样。因此大部分V4L2驱动都有一些特定的代码,好让应用可以知道给定的设备有什么功能,并配置设备,使其按期望的方式工作。V4L2的API定义了几十个回调函数,用来配置如调谐频率、窗口和裁剪、帧速率、视频压缩、图像参数(亮度、对比度…)、视频标准、视频格式等参数。这一系列文章的很大部分都要用来考察这些配置的过程。
然后,还有一个小任务,就是有效地在视频频率下进行I/O操作。V4L2定义了三种方法来在用户空间和外设之间移动视频数据,其中有些会比较复杂。视频I/O和视频缓冲层,将会分成两篇文章来写,它们是用来处量一些共性的任务的。
随后的文章每几周发一篇,共会加入到下面的列表中。
二、注册和打开
原文网址:http://lwn.net/Articles/204545/
这篇文章是LWN写V4L2接口的设备驱动系列文章的第二篇。没看过介绍篇的,也许可以从那篇开始看。这一期文章将关注Video
开始之前,有必要提一点,那就是对于搞视频驱动的人来说,有两份资料是非常有价值的。
TheV4L2
内核代码中的vivi驱动,即drivers/media/video/vivi.c.这是一个虚拟驱动。它可以用来测试,却不使用任何实际硬件。这样,它就成一个教人如何写V4L2驱动的非常好的实例。
首先,每个V4L2驱动都要包含一个必须的头文件:
1 | |
大部分所需的信息都在这里。作为一个驱动作者,当挖掘头文件的时候,你可能也得看看include/media/v4l2-dev.h,它定义了许多你将来要打交道的结构体。
一个视频驱动很可能要有处理PCI总线,或USB总线的部分。这里我们不会花什么时间还接触这些东西。通常会有一个内部一I2C接口,我们在这一系列的后续文章中会接触到它。然后还有一个V4L2的子系统接口。这个子系统是围绕video_device这个结构体建立的,它代表的是一个V4L2设备。讲解进入这个结构体的一切,将会是这个系列中几篇文章的主题。这里我们先有一个概览。
video_device结构体的name字段是这一类设备的名字,它会出现在内核日志和sysfs中出现。这个名字通常与驱动的名字相同。
所表示的设备有两个字段来描述。第一个字段(type)似乎是从V4L1的API中遗留下来的,它可以下列四个值之一:
VFL_TYPE_GRABBER
VFL_TYPE_VBI
VFL_TYPE_RADIO
VFL_TYPE_VTX
如果你的设备支持上面提到的不只一种功能,那就要为每个功能注册一个V4L2设备。然而在V4L2中,注册的每个设备都可以用作它实际支持的各种模式(就是说,你要为一个物理设备创建不多个设备节点,但你却可以调用任意一个设备节点,来实现这个物理设备支持的任意功能)。实质上的问题是,在V4L2中,你实际上只需一个设备,注册多个V4l2设备只是为了与V4l1兼容。
第二个字段是type2,它以掩码的形式对设备的功能提供了更详尽的描述。它可以包含以下值:
VID_TYPE_CAPTURE
VID_TYPE_TUNER
VID_TYPE_TELETEXT
VID_TYPE_OVERLAY
VID_TYPE_CHROMAKEY
VID_TYPE_CLIPPING
VID_TYPE_FRAMERAM
VID_TYPE_SCALES
VID_TYPE_MONOCHROME
VID_TYPE_SUBCAPTURE
VID_TYPE_MPEG_DECODER
VID_TYPE_MPEG_ENCODER
VID_TYPE_MJPEG_DECODER
VID_TYPE_MJPEG_ENCODER
V4L2驱动还要初始化的一个字段是minor,它是你想要的子设备号。通常这个值都设为-1,这样会让video4linux子系统在注册时自动分配一个子设备号。
在video_device结构体中,还有三组不同的函数指针集。第一组只包含一个函数,那就是release(),如果驱动没有release()函数,内核就会抱怨(笔者发现一个件有趣的事,就是这个抱怨涉及到冒犯一篇LWN文章的作者)。release()函数很重要:由于多种原因,对video_device的引用可以在最后一个应用关闭文件描述符后很长一段时间依然保持。它们甚至可以在设备己经注销后依然保持。因此,在release()函数调用前,释放这个结构体是不安全的。所以这个函数通常要包含一个简单的kfree()调用。
video_device的file_operations结构体包含都是常规的函数指针。视频设备通常都包括open()和release()函数。注意:这里所说的release函数并非上面所讲到的同名的release()函数,这个release()
第三组函数存在于video_device结构体本身里面,它们是V4L2
最后,从一开始就要知道的一个字段就是debug.可以把它设成是V4L2_DEBUG_IOCTL或V4L2_DEBUG_IOCTL_ARG(或是两个都设,这是个掩码),可以生成很多的调试信息,它们可以帮助一个迷糊的程序员找到毛病,知道为什么驱动和应用谁也不知道对方在说什么。
视频设备注册
一旦video_device己经配置好,就可以下面的函数注册了:
1 | int |
这里vfd是设备的结构体(video_device),type的值与它的type字段值相同,nr也是一样,想要的子设备号(为-1则注册时自动分配)。返回值当为0,若返加的是负的
出错码,则表明出错了,和通常一样,我们要知道,设备一旦注册,它的函数可能就会立即调用,所以不到一切准备就绪,不要调用video_register_device();
设备的注销方法为:
1 | void |
open()
int
open()函数要做的第一件事是通过给定的inode找到内部设备,这是通过找到inode中存存储的子设备号来完成的。这里还可以实现一定数量的初始化,如果有关闭电源选项的话,这个时间恰好可以用来开启硬件电源。
V4L2规范还定义了一些相关的惯例。其一是:根据其设计,文件描述符可以在给定的任何时间重复打开。这样设定的目的是当一个应用在显示(或是产生)视频信号时,另一个应用可以改变控制值。所以,虽然某些操作是独占性质的(特别是数据读、写等),但是设备总体本身是要支持描述符复用的。
另一个值得一提的惯例是:open()函数,总体上讲,不可以改变硬件中现行的操作参数。有些时候可能会有这样的情况:通过命令行程序,根据一组参数(分辨率,视频格式等)来改变摄像头配置,然后运行一个完全不同的程序来,比如说,从摄像头获取上帧图像。如果摄像头在设置在中途改变了,这种模式就不好用。所以除非应用明确表示要改变设置(这种情况当然不包括在open函数中)V4L2驱动要尽量保持设定不变。
release()函数做一些必要清理工作。因为文件描述符可以重复打开,所以release函数中减小引用计数,并在彻底退出之前做检查。如果关闭的文件描述符是用来传输数据的,release函数很可能要关掉DMA,并做一些其他的清理工作。
本系列的下一篇文章我们将进入查询设备功能和设定系统模式的冗长过程之中,请续断关注。
1. 控制命令VIDIOC_QUERYCAP
功能:查询设备驱动的功能 ;
参数说明:参数类型为V4L2的能力描述类型struct v4l2_capability;
struct v4l2_capability {
};
返回值说明: 执行成功时,函数返回值为 0;
函数执行成功后,struct v4l2_capability 结构体变量中的返回当前视频设备所支持的功能;
例如支持视频捕获功能V4L2_CAP_VIDEO_CAPTURE、 V4L2_CAP_STREAMING等。
使用举例:
-------------------------------------------------------------------------------------------------------
struct v4l2_capability cap;
iret = ioctl(fd_usbcam, VIDIOC_QUERYCAP,&cap);
if(iret < 0){
}
------------------------------------------------------------------------------------------------------
执行完VIDIOC_QUERYCAP命令后,cap变量中包含了该视频设备的能力信息,程序中通过检查cap中的设备能力信息来判断设备是否支持某项功能。
三、基本I/O处理
如果有人在video
传统上来讲,视频驱动中包含的ioctl()函数一般会长得像一部小说,而函数所得到的结论也往往比小说更令人满意来,他们往往在中间拖了很多(这句话完全不明白什么意思)。所以V4L2的API在2.6.18版本的内核开始做出了改变。冗长的ioctl函数被替换成了一个大的回调函数的集合,每个回调函数实现自己的ioctl函数。实际上,在2.6.19-rc3中,有79个这样的回调函数。而幸运的是,多数驱动并不需实现所有的回调函数,甚至都不是大部分回调函数。
在ioctl()函数中发生的事情都放到了drivers/media/video/videodev.c里面。这部分代码处理数据在内核和用户空间之间的传输并把ioctl调用发送给驱动。要使用它的话,只要把video_device中的video_ioctl2()做为ioctl()来调用就行了。实际上,多数驱动也要把它当成unlocked_ioctl()来用。Video4Linux2层的锁可以对其进行处理,而且驱动也应该在合适的地方加锁。(这一段没看明白,乱写的)
四、输入输出
输入和输出
这是不定期发布的关于写视频驱动程序的LWN系统文章的第四篇.没有看过介绍篇的,也许想从这里开始.本周的文章介绍的是应用程序如何确定在特定适配器上哪些输入和输出可用,并且它们之间做出选择。
在很多情况下,视频适配器并不能提供很多的输入输出选项.比如说摄像头控制器,可能只是提供摄像头,而没什么别的功能.然而,在一些其他的情况下,事情将变得很复杂.一个电视卡可能对应板上不用的接头有不同的输入.他甚至可能有可以独立发挥其功能的多路调谐器.有时,那些输入会有不同的特性;有些调谐器可以支持比其他的更广泛的视频标准.对于输出来说,也有同样的问题.
很明显,若想一个应用可以有效地利用视频适配器,它必须有能力找到可用的输入和输出,而且他必须能找到他想操作的那一个.为此,Video4Linux2
这三个(对于硬件支持的每一个功能)驱动都要支持.虽然如此,对于简单的硬件而言,代码还是很简单的.驱动也要提供一此启动时的默认值.然而,驱动不应该做的是,在应用退出时重置输入输出信息.对于其他视频参数,在多次打开之间,参数应维持不变.
视频标准
在我们进入输入输出的细节之前,我们应该先了解一下视频标准
V4L2使用v4l2_std_id来代表视频标准,它是一个64位的掩码。每个标准变种在掩码中就是一位。所以标准NTSC就是V4L2_STD_NTSC_M,
(0x2000)。如果一个设备可以处理所以有NTSC变种,它就可以设为V4L2_STD_NTSC,它可以所有相关位置位。对PAL和SECAM标准,也存在一组类似的位集。Seethis
对于用户空间而言,V4L2提供一个ioctl()命令(VIDIOC_ENUMSTD),它允许应用查询设备实现了哪些标准。驱动却无需直接回答查询,而是将video_device结构体的tvnorm字段设置为它所支持的所有标准。然后V4L2层会向应用输出所支持的标准。VIDIOC_G_STD命令,可以用来查询现在哪种标准是激活的,它也是在V4L2层通过返回video_device结构的current_norm字段来处理的,驱动程序应在启动时,初始化current_norm来反应现实情况。有些应用即使他并没有设置过标准,发现标准没有设置也会感到困惑。
当某个应用想要申请某个标准的时候,会发出一个VIDIOC_S_STD调用,该调用通过下面的函数传到驱动:
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
驱动要对硬件编程,以使用给定的标准,并返回0(或是负的出错编码).V4L2层需要把current_norm设为新的值。
应用可能想要知道硬件所看到的是何种信号,答案可以通过VIDIOC_QUERYSTD找到,它到了驱动里面就是:
---------------------------------------------------------------------------------------------------------------int
------------------------------------------------------------------------------------------------------------驱动要尽可能地在这个字段填写详细信息。如果硬件没有提供足够的信息,std字段就会暗示任何可能出现的标准。
这里还有一点值得一提:所以的视频设备必须支持(或是声明支持)至少一种视频标准。视频标准对于摄像头来说没什么意义,它不与任何监管制度绑定。但是也不存一个标准说“我是个摄像头,我什么都能做”,所以V4L2层有很多摄像头声明可以返回PAL或NTSC数据(实际只是如些声明而己)。