原创
【转帖】V4L的使用
2008-5-30 11:51
4049
10
10
分类:
工程师职场
/* Video4Linux是Linux下用于获取视频和音频数据的API接口,在这篇文章中,着重阐述如何利用Video4Linux获取摄像头数据,以实现连续影像的播放。 */
/* 1. 摄像头的安装 */
/* 在Linux下常用的摄像头驱动是spca5xx, 这是一个通用驱动,读者可以在以下网站下到这个驱动 http://mxhaard.free.fr/download.html。这个网站还给出了这款驱动支持的摄像头的种类。另外,ov511芯片直接就支持Linux,使用者款芯片的摄像头有网眼V2000。我使用的是网眼V2000的摄像头,和Z-Star 301p+现代7131R芯片的摄像头。后一种需要spca5xx的驱动。关于spca5xx的安装方法,网上有很多介绍,这里就不说了。 */
/* 2. 摄像头的调试 */
/* 安装好摄像头后,为了测试摄像头能否正常工作,可以用一下软件。比较著名的是xawtv,在网上搜以下可以下载到。安装好后,打开xawtv则可以调试摄像头。 */
/* 3. Video4Linux 编程获取数据 */
/* 现有的video4linux有两个版本,v4l和v4l2。本文主要是关于v4l的编程。利用v4l API获取视频图像一般有以下几步: */
/* a> 打开设备 */
/* b> 设置设备的属性,比如图像的亮度,对比度等等 */
/* c> 设定传输格式和传输方式 */
/* d> 开始传输数据,一般是一个循环,用以连续的传输数据 */
/* e> 关闭设备 */
/* 下面具体介绍v4l编程的过程。首先指出,在video4linux编程时要包含头文件,其中包含了video4linux的数据结构和函数定义。 */
/* 1)v4l的数据结构 */
/* 在video4linux API中定义了如下数据结构,详细的数据结构定义可以参考v4l API的文档,这里就编程中经常使用的数据结构作出说明。 */
/* 首先我们定义一个描述设备的数据结构,它包含了v4l中定义的所有数据结构: */
typedef struct _v4ldevice
{
int fd;//设备号
struct video_capability capability;
struct video_channel channel[10];
struct video_picture picture;
struct video_clip clip;
struct video_window window;
struct video_capture capture;
struct video_buffer buffer;
struct video_mmap mmap;
struct video_mbuf mbuf;
struct video_unit unit;
unsigned char *map;//mmap方式获取数据时,数据的首地址
pthread_mutex_t mutex;
int frame;
int framestat[2];
int overlay;
}v4ldevice;
/* 下面解释上面这个数据结构中包含的数据结构,这些结构的定义都在中。 */
/* * struct video_capability */
/* name[32] Canonical name for this interface */
/* type Type of interface */
/* channels Number of radio/tv channels if appropriate */
/* audios Number of audio devices if appropriate */
/* maxwidth Maximum capture width in pixels */
/* maxheight Maximum capture height in pixels */
/* minwidth Minimum capture width in pixels */
/* minheight Minimum capture height in pixels */
/* 这一个数据结构是包含了摄像头的属性,name是摄像头的名字,maxwidth maxheight是摄像头所能获取的最大图像大小,用像素作单位。 */
/* 在程序中,通过ioctl函数的VIDIOCGCAP控制命令读写设备通道已获取这个结构,有关ioctl的使用,比较复杂,这里就不说了。下面列出获取这一数据结构的代码: */
int v4lgetcapability(v4ldevice *vd)
{
if(ioctl(vd->fd, VIDIOCGCAP, &(vd->capability)) < 0) {
v4lperror("v4lopen:VIDIOCGCAP");
return -1;
}
return 0;
}
/* * struct video_picture */
/* brightness Picture brightness */
/* hue Picture hue (colour only) */
/* colour Picture colour (colour only) */
/* contrast Picture contrast */
/* whiteness The whiteness (greyscale only) */
/* depth The capture depth (may need to match the frame buffer depth) */
/* palette Reports the palette that should be used for this image */
/* 这个数据结构主要定义了图像的属性,诸如亮度,对比度,等等。这一结构的获取通过ioctl发出VIDIOCGPICT控制命令获取。 */
/* * struct video_mbuf */
/* size The number of bytes to map */
/* frames The number of frames */
/* offsets The offset of each frame */
/* 这个数据结构在用mmap方式获取数据时很重要: */
/* size表示图像的大小,如果是640*480的彩色图像,size=640*480*3 */
/* frames表示帧数 */
/* offsets表示每一帧在内存中的偏移地址,通过这个值可以得到数据在图像中的地址。 */
/* 得到这个结构的数据可以用ioctl的VIDIOCGMBUF命令。源码如下: */
int v4lgetmbuf(v4ldevice *vd)
{
if(ioctl(vd->fd, VIDIOCGMBUF, &(vd->mbuf))<0) {
v4lperror("v4lgetmbuf:VIDIOCGMBUF");
return -1;
}
return 0;
}
/* 而数据的地址可以有以下方式计算: */
unsigned char *v4lgetaddress(v4ldevice *vd)
{
return (vd->map + vd->mbuf.offsets[vd->frame]);
}
/* 2)获取影像mmap方式。 */
/* 在video4linux下获取影像有两种方式:overlay和mmap。由于我的摄像头不支持overlay方式,所以这里只谈mmap方式。 */
/* mmap方式是通过内存映射的方式获取数据,系统调用ioctl的VIDIOCMCAPTURE后,将图像映射到内存中,然后可以通过前面的 v4lgetmbuf(vd)函数和v4lgetaddress(vd)函数获得数据的首地址,这是你可以选择是将它显示出来还是放到别的什么地方。 */
/* 下面给出获取连续影像的最简单的方法(为了简化,将一些可去掉的属性操作都去掉了): */
char* devicename="/dev/video0";
char* buffer;
v4ldevice device;
int width = 640;
int height = 480;
int frame = 0;
v4lopen("/dev/video0",&device);//打开设备
v4lgrabinit(&device,width,height);//初始化设备,定义获取的影像的大小
v4lmmap(&device);//内存映射
v4lgrabstart(&device,frame);//开始获取影像
while(1){
v4lsync(&device,frame);//等待传完一帧
frame = (frame+1)%2;//下一帧的frame
v4lcapture(&device,frame);//获取下一帧
buffer = (char*)v4lgetaddress(&device);//得到这一帧的地址
//buffer给出了图像的首地址,你可以选择将图像显示或保存......
//图像的大小为 width*height*3
..........................
}
/* 为了更好的理解源码,这里给出里面的函数的实现,这里为了简化,将所有的出错处理都去掉了。 */
int v4lopen(char *name, v4ldevice *vd)
{
int i;
if((vd->fd = open(name,O_RDWR)) < 0) {
return -1;
}
if(v4lgetcapability(vd))
return -1;
}
int v4lgrabinit(v4ldevice *vd, int width, int height)
{
vd->mmap.width = width;
vd->mmap.height = height;
vd->mmap.format = vd->picture.palette;
vd->frame = 0;
vd->framestat[0] = 0;
vd->framestat[1] = 0;
return 0;
}
int v4lmmap(v4ldevice *vd)
{
if(v4lgetmbuf(vd)<0)
return -1;
if((vd->map = mmap(0, vd->mbuf.size, PROT_READ|PROT_WRITE, MAP_SHARED, vd->fd, 0)) < 0) {
return -1;
}
return 0;
}
int v4lgrabstart(v4ldevice *vd, int frame)
{
vd->mmap.frame = frame;
if(ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap)) < 0) {
return -1;
}
vd->framestat[frame] = 1;
return 0;
}
int v4lsync(v4ldevice *vd, int frame)
{
if(ioctl(vd->fd, VIDIOCSYNC, &frame) < 0) {
return -1;
}
vd->framestat[frame] = 0;
return 0;
}
int v4lcapture(v4ldevice *vd, int frame)
{
vd->mmap.frame = frame;
if(ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap)) < 0) {
return -1;
}
vd->framestat[frame] = 1;
return 0;
}
文章评论(0条评论)
登录后参与讨论