V4L的使用

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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值