Linux系统中,摄像头驱动程序安装好后,为了进行视频采集必须加入Video4Linux模块,从而可以通过Video4Linux模块提供的编程接口(API)从摄像头设备中获取图像帧。下面具体研究基于V4L的视频采集程序设计。
1 Video4Linux概述
     Video4Linux是Linux中关于视频设备的内核驱动,为针对视频设备的应用程序编程提供一系列接口函数,在Linux下,视频采集设备的正常使用依赖Video4-Linux标准的支持,在编译内核时要选中Video4Linux项,对应的设备文件是/dev/video。对于USB口摄像头,其驱动程序中需要提供基本的I/O操作接口函数open、read、write、close的实现。对中断的处理实现,内存映射功能以及对I/O通道的控制接口函数ioctl的实现等,并把它们定义在structfile operations中。这样当应用程序对设备文件进行诸如open、read、write、close等系统调用时,Linux内核将通过file operations结构访问驱动程序提供的函数。

2.视频编程的数据结构及函数

Linux下V4L视频采集,主要是调用V4L模块参数进行视频原始数据采集。使用的一些主要参数和函数定义在系统/include/linux/videodev.h文件中。下面是videodev.h中定义的几个重要的数据结构如下:

  1. typedef struct v4l_struct
  2. {
  3. int   fd;
  4. struct video_capability capability;
  5. struct video_channel channel[4];
  6. struct video_picture picture;
  7. struct video_window window;
  8. struct video_capture capture;
  9. struct video_buffer buffer;
  10. struct video_mmap mmap;
  11. struct video_mbuf mbuf;
  12. unsigned char *map;
  13. int framestat[2];
  14. }v4l_device;

video_capability结构包含设备的基本信息,如设备名称、支持的最大最小分辨率、信号源信息等。它包含以下分量:name[32]指定设备名称,maxwidth,maxheight,minwidth,minheight指定摄像头能捕捉的最大和最小的图像尺寸。Channels指定信号源个数,type表示所截取的图像是否能被捕捉,是彩色还是黑白,是否能裁剪,值如VID TYPE CAPTURE等。
video_channel结构指定各个信号源的属性,它包含以下分量:channel指定信号源的编号,name为每个信号源的名称,Type指定所输入的信号类型,包含两种类型:VIDEO_TYPE_TV表示为电视信号;VIDEO_TYPE_CAMERA表示为摄像头信号。Norm表示电视信号所使用的制式,有VIDEO_MODE_PAL、VIDEO_MODE_NTSC、VIDEO_MODE_SECAM、VIDEO_MODE_AUTO等几
种选项。
video_picture结构用来设置采集图像的各种属性。它包括以下几个分量:brightness、hue、colour、contrast来为采集的图像提供类似电视信号的控制,whiteness来为灰度图像提供额外控制。
video_window结构设置采集的图像的显示方式。它包括以下几个分量:X用来描述窗口左上方的X坐标,Y用来描述左上方的Y坐标,width用来描述采集图像的宽度,height用来描述采集图像的高度,当采用色度键时chromakey用来表示颜色,用RGB32格式表示的。
video_mbuf结构用来描述利用mmap进行映射的帧的信息,实际上是输入到摄像头存储器缓冲区中的帧信息。它包含以下几个分量:size表示每帧大小,frames用来表示最多支持的帧数,offsets表示每帧相对基址的偏移。
video_buffer结构用来对最底层的buffer进行描述,它包含以下几个含量:void *baseBase描述buffer的物理地址,height描述frame buffer的高度,width描述frame buffer的宽度,depth描述frame buffer的深度,bytesperline描述每两个相邻的线之间的存储器字节数。
video mmap用于内存映射。

在V4L编程中,与图像设备交互主要是依靠系统调用ioctl函数来实现的。Ioctl(input/output control)函数是通过打开的文件描述符对各种文件尤其是字符设备文件进行控制,完成特定的I/O操作。V4L支持的ioctl命令通常需要几个参数,一般情况下,第一个参数fd代表打开的文件/设备,第二个参数cmd为驱动程序的特殊命令,有的还有第3个或者更多的参数,根据不同的驱动程序不同。

在V4L中定义了一些重要的宏,用来作为ioctl函数的第二个参数,用来操纵设备,一些主要的宏定义说明如下:
1)VIDIOCGCAP:Ioctl操作得到图像设备的基本信息,保存在video_capability结构体中。
2)VIDIOCSCAP:Ioctl操作根据video_capability结构体中的值来设置图像设备初始参数。
3)VIDIOCGPICT:Ioctl操作得到图像的基本信息,保存在video_picture中,包括图像的亮度、色度、对比度等。
4)VIDIOCSPICT:Iocfl操作根据video_picture结构体中的值来设置采集图像的初始参数。
5)VIDIOCGMBUF:Ioctl操作获取摄像头存储缓冲区的帧信息。
6)VIDIOCAMCAPTURE:获取视频图像。
7)VIDIOCGWIN:loctl操作得到采集图像区域的基本信息,保存在video_window结构体中。设置目标窗口的属性。
8)VIDIOCSWIN:Ioctl操作根据video_window结构体中的值来设置采集图像区域大小等参数。
9)VIDIOSYNC:Ioctl操作判断视频图像是否截取成功。

视频采集函数采用自顶向下的操作方式,首先规划并定义这些函数。
1)v41_open() :开启视频采集设备函数
       首先定义了一个默认的设备文件路径,如果可以开启“/dev/video0"则将取回的信息放到vd中。

  1. #define DEFAULT_DEVICE "dev/video0"
  2. int v4l_open(char *dev,v4l_device *vd)
  3. {
  4. if(!dev)
  5. dev=DEFAULT_DEVICE;
  6. if((vd->fd=open(dev,O_RDWR))<0)
  7. {
  8. perror("camera open:");
  9. return-1;
  10. }
  11. if(v4l_get_capability(vd))
  12. return -1;
  13. if(v41_get_picture(vd))
  14. return -1;
  15. return 0;
  16. }

当应用程序输入的dev设备文件参数不存在时,就使用“/dev/video0”这个值。Linux下对设备文件的打开是通过open()函数完成的。

  1. if((vd->fd=open(dev,O_RDWR))<0)
  2. {
  3. perror("camera_open:");
  4. return-1;
  5. }

设备文件开启后,将回传的文件描述符放到vd->fd里。在应用程序中使用v4l_open()函数时,需要先声明一个video_device类型的设备变量,然后通过调用v4l_open()函数将设备打开。如果可以正常打开设备文件,设备变量也就获得了相应的回传信息。v41_open()函数被调用的程序如下:

  1. video_device vd;
  2. if(camera_open('‘/dev/video0”,&vd))
  3. {
  4. return-1;
  5. }

2)v4l_get_capability():获取设备信息
成功开启设备后,首先取得设备信息,v4l_get_capability()函数调用ioctl()取得设备文件相关信息,并且将取得的信息放到

  1. video_capability结构体中。
  2. int v4l_get_capability(v4l_device *vd)
  3. {
  4. if(ioctl(vd->fd,VIDEOCGCAP,&(vd->capability))<0)
  5. {
  6. perror("camera_get_capability:");
  7. return-1;
  8. }
  9. return 0;
  10. }

这个函数的主要内容是ioctl(vd->fd,VIDEOCGCAP,&(vd->capability))。vd->fd是由v4l_open()传回来的文件描述符,而传递VIDIOCGCAP给ioctl()则会传回设备相关信息,存放与vd->capability。
3)v4l_get_picture():图像初始化
       取得设备信息后,还要取得图像信息。所谓图像信息是指输入到视频捕捉设备的图像格式。

  1. int v4l_get_picture(v4l_device *vd)
  2. {
  3. if(ioctl(vd->fd,VIDIOCGPICT,&(vd->picture))<0)
  4. {
  5. perror(“camera_get_picture:");
  6. return-1;
  7. }
  8. return 0;
  9. }

4)改变video_picture中的分量的值,先为分量赋新值,再传递VIDIOCSPICT给ioctl()函数进行设置。

  1. vd->picture.colour=65535;
  2. if(ioctl(vd->fd,VIDIOCSPICT,&(vd->picture))<0)
  3. {
  4. perror("VIDIOCSPICT");
  5. return-1;
  6. }

5)v4l_get_channels():初始化channel

  1. int v4l_get_channels(camera_device *vd)
  2. {
  3. int i;
  4. for(i=0;i < vd->capability.channels;i++){
  5. vd->channel[i].channel=i;
  6. if(ioctl(vd->fd,VIDIOCGCHAN,&(vd->channel[i]))<0)
  7. {
  8. perror("camera_get_channel:");
  9. return -1;
  10. }}
  11. return 0;
  12. }

6)v4l_close():关闭设备

  1. int v4l_close(camera_device *vd)
  2. {
  3. close(vd->fd);
  4. return 0;
  5. }

3.图像采集流程

从V4L的数据结构看出,完成基于V4L的USB视频数据采集,先要获得相关的视频采集设备的信息和图像信息,并对采集窗口、颜色模式、帧状态初始化,然后才能进行视频图像的采集。
具体操作流程如下:
       1)打开设备进行设定,通过使用标准的文件打开函数操作;
       2)查询图像缓冲区信息并设定;VIDIOCSFBUF主要是设定图像缓冲区的基地址和缓冲区大小;
       3)查询图像截取窗口信息并设定:VIDIOCGWIN和VIDIOCSWIN主要设定截取图像尺寸和位置;
       4)查询通道信息并设定,可以从一个或者多个通道捕获数据,来进行通道的查询设定函数VIDEOCGCHAN和VIDEOCSCHAN;
       5)获取图像并存放在缓冲区。

在V4L中图像截取方式有两种:
      1)通过调用read具有阻塞功能的函数,将输出图像数据复制到预先设定好的数据缓冲区中,就可以实现对每帧图像数据的读取;
      2)用mmap方式,直接将设备文件/dev/video0映射到内存中,不需要额外的对数据缓冲区进行复制工作,加快了图像信息的捕捉速度。另外,mmap()系统调用使得进程之间通过映射同一文件实现共享内存,各个进程可以像访问普通内存一样对文件进行访问,访问时只需要使用指针而不用调用文件操作函数。

视频采集的流程如下图所示。

鉴于mmap()以上优点,本文采用的第2种方式,利用mmap()方式对视频进行采集与裁剪的操作如下:
        1)先使用ioctl(vd->fd,VIDIOCGMBUF,&vd->mbuf)数获得摄像头存储缓冲区的帧信息,之后修改video_map中的设置,例如重新设置图像帧的垂直及水平分辨率、彩色显示格式以及当前帧的状态。可利用如下语句

  1. vd->mmap.height=240;
  2. vd->mmap.width=320;
  3. vd->mmap.format=VIDEO_PALETTE_RGB24;
  4. vd->framestat[0]=vd->framestat[1]=0;
  5. vd->flame=0;

2)接着把摄像头对应的设备文件映射到内存区,命令为

  1. vd->map=(unsignedchar *)mmap(0,vd->mbuf.size,PROT_READ|PROT_WRITE,MAP_SHARED,vd->fd,0)

这样设备文件的内容就映射到内存区,映射内容区可读可写并且不同进程间可共享。该函数成功时返回映像到内存区的指针,失败时返回1。mmap()函数中第一个参数表示共享内存的起始地址,在此外设为0表示由系统分配。第二个参数表示映射到调用进程地址空间的字节数,此值为vd->mbuf.size。第三个参数指定共享内存的访问权限,它有以下几个值PORT_READ(可读),PROT_WRITE(可写),PROT_EXEC(可执行),此处设为可读和可写。第四个参数指定共享内存的属性,有MAP_SHARED、MAP _PRIVATE、MAP_FIXED三种属性,一般情况下从中选一个使用。

       3)视频截取。把视频映射到内存后就可进行视频的截取。命令为

  1. ioctl(vd->fd,VIDIOCMCAPTRUE,&(vd->mmap));

若调用成功,开始一帧的截取,该操作是非阻塞的,是否截取完毕留给VIDIOCSYNC来判断。

4)调用VIDEOCSYNC等待一帧截取结束。

  1. if(ioctl(vd->fd,VIDIOCSYNC,&frame)<0)
  2. {
  3. perror("VIDIOCSYNC ERRORI!!”);
  4. return -1;
  5. }

函数调用成功,表明一帧图像截取完成,可以开始进行下一次视频截取。其中frame是当前截取的帧的序号。

5)由于采集是采用双缓冲的方式,这样在处理一帧时可以采集另一帧。frame表示当前采集的是哪一帧图像,framestat[2]表示帧的状态(如未开始采集和等待采集结束)。帧在内存中的地址由vd->map+vd->mbuf.offsets[vd->frame]确定。采集结束后调用munmap命令取消映射:munmap(vd->map,vd->mbuf.size)。

以上详细分析了图像采集流程,下面给出图像采集的具体实现。

4.摄像头采集模块的具体实现 

视频采集的具体实现过程如下:

第一部分,设备初始化。
(1)首先:必须声明包含2个头文件:

  1. #include<sys/types.h>
  2. #include<linux/videodev.h>

(2)然后,进行设备初始化,打开视频设备,摄像头在系统中对应的设备文件为/dev/video0,通过调用驱动程序中定义的open操作来完成。如下:

  1. int fd=open("/dev/video”,O_RDW-R);

其中fd是设备打开后返回的文件描述符,系统调用函数可使用它对设备文件进行操作。

(3)接着通过调用ioctl VIDIOCGCAP操作读取struct video_capability中有关摄像头信息。如下:

  1. struct video_capability grab_capability;
  2. ioctl(fd,VIDIOCGCAP,&grab_capability);/*获得struct video_capability中的摄像头的信息*/

(4) 然后通过调用ioctl VIDIOCSFBUF设置内存缓冲区的相关信息,缓冲区的信息可以通过printf函数输出;

(5)通过调用Ioctl VIDIOCSWIN来完成对视频窗口的设置,如窗口宽度,高度等;

(6)接着通过调用loctl VIDIOCGPICT操作读取struct video_capability中有关图像信息。如下:

  1. struct video_picture grab_picture;
  2. ioctl(fd,VIDIOCGPICT,&grab_picture);/*获取图像信息*/

(7)在获取图像信息后,还可以根据需要改变这些信息,例如对比度、亮度、调色板等,具体做法是先给video_picture中的相应变量赋新值,再利用Ioctl VIDIOCSPICT函数进行设置。如下:

  1. grab_picture.colour=65535;
  2. if(ioctl(fd,VIDIOCSPICT,&grab_picture))<0)
  3. {
  4. perror("VIDIOCSPICT”);
  5. return-1;
  6. }

(8)接着初始化channel,必须先完成vd->capability中的信息调用,使用下列函数:

  1. int v4l_get_channels(v4l_device *vd)

第二部分,使用mmap方式截取视频。
(1)首先调用ioctl(fd,VIDIOCGMBUF,&grab_vm)函数获取摄像头存储缓冲区的帧信息,之后初始化video_mbuf修改video_mmap中的设置,重新设置图像信息如帧的垂直及水平分辨率、彩色显示格式等。如下:

  1. structvideo mmap grab_buf ;  //以下为设置图像帧缓冲区信息
  2. grab_buf.frame=0;   //一次只采集一帧
  3. grab_buf.height= 240;  //图像高度
  4. grab_buf.width=320;   //图像宽度
  5. grab_buf.format=VIDEO_PALETTE_RGB24;   //图像的调色板格式,24位真彩色
  6. unsigned char *data=mmap(0,240*320*3,PROT_READ| PROT_WRITE,MAP_SHARD,fd,0); //内存映射
  7. ioctl(grab fd,VIDIOCMCAPTURE,&grab_buf);  //采集图像

(2)然后调用ioctl(grab fd,VIDIOCSYNC,&frame)函数,该函数成功返回则表示采集完毕,采集到的图像数据放到以data为起始地址,长度为240*320*3的内存区域中,读取该内存中的数据便可得到图像数据。
(3)在此基础上同样可实现连续帧的采集,即一次采集连续多帧图像的数据。此时首先要设置grab_bur.frame为要采集的帧数。在循环语句中,也是使用ioctl VIOCMCAPTURE和ioctl VIDIOCSYNC操作完成每帧读取,但是要给采集到的每帧图像赋地址为data+grab_vm.offsets[frame],然后保存文件格式。其中grab_vm为video_mbuf结构体变量的一个声明,利用ioctl(fd,VIDIOCGMBUF,
&grab_vm)便可获得grab 的信息。

(4)若要继续采集可再加一个外循环,在外循环语句中只要给原来的内循环再赋伊grab_buf.frame=0即可。

http://blog.csdn.net/wangrunmin/article/details/7766221

基于Video4Linux的视频采集模块开发(转)的更多相关文章

  1. 【转】基于V4L2的视频驱动开发

    编写基于V4L2视频驱动主要涉及到以下几个知识点:1> 摄像头方面的知识 要了解选用的摄像头的特性,包括访问控制方法.各种参数的配置方法.信号输出类型等.2> Camera解码器.控制器 ...

  2. 基于V4L2的视频驱动开发【转】

    转自:http://blog.chinaunix.net/uid-10747583-id-298573.html Tags:V4L2驱动框架.API.操作流程…… 原文地址:http://www.ee ...

  3. 基于DM642 RAW采集格式的视频驱动开发及应用

    摘 要:为解决C64X系列数字信号处理器(DSP)视频驱动不能应用于原始数据格式(RAW)采集格式的问题,设计了DM642和电耦合元件(CCD)高清传感器的数据传输接口,并分析.修改用于标准格式的视频 ...

  4. 嵌入式应用开发第四阶段-基于rk3399的视频监控系统

    一.需求分析 伴随着嵌入式技术.图像处理技术和无线网络传输技术的发展,传统模拟视频监控系统和基于PC的远程视频监控系统由于自身的不足,已经无法满足现代社会应用中不断涌现出来的新需求,于是基于嵌入式技术 ...

  5. 最简单的基于FFmpeg的直播系统开发移动端例子:IOS 视频解码器

    本文记录IOS平台下基于FFmpeg的视频解码器.该示例C语言的源代码来自于<最简单的基于FFMPEG+SDL的视频播放器>.相关的概念就不再重复记录了. 源代码 项目的目录结构如图所示. ...

  6. 基于Linux的视频传输系统(上大学时參加的一个大赛的论文)

    文件夹 1原创性声明----------------------------------------------------3 2 摘要-------------------------------- ...

  7. 《转》iOS音频视频初级开发

    代码改变世界 Posts - 73, Articles - 0, Comments - 1539 Cnblogs Dashboard Logout HOME CONTACT GALLERY RSS   ...

  8. 基于live555的视频直播 DM368IPNC RTSP分析

    因需要,从个人的理解顺序和需求角度对live555的分析与开发整理,包含RTSP Server与RTSP Client.如何直播H.264流与JPEG流等,均进行了探讨,对live555的初学者有一定 ...

  9. 基于python的互联网软件测试开发(自动化测试)-全集合

    基于python的互联网软件测试开发(自动化测试)-全集合 1   关键字 为了便于搜索引擎收录本文,特别将本文的关键字给强调一下: python,互联网,自动化测试,测试开发,接口测试,服务测试,a ...

随机推荐

  1. SPOJ 5152 Brute-force Algorithm EXTREME && HDU 3221 Brute-force Algorithm 快速幂,快速求斐波那契数列,欧拉函数,同余 难度:1

    5152. Brute-force Algorithm EXTREME Problem code: BFALG Please click here to download a PDF version ...

  2. 二十二、utl_inaddr(用于取得局域网或Internet环境中的主机名和IP地址)

    1.概述 作用:用于取得局域网或Internet环境中的主机名和IP地址. 2.包的组成 1).get_host_name作用:用于取得指定IP地址所对应的主机名语法:utl_inaddr.get_h ...

  3. CF 913 A

    题解: 首先判断n是否大于30 大于30输出m 否则输出m%2^n 代码: #include<bits/stdc++.h> using namespace std; int n,m; in ...

  4. django 自定义用户表替换系统默认表

    首先新建一个users应用,编写这个应用的models类. from django.contrib.auth.models import AbstractUser class UserProfile( ...

  5. Week08《Java程序设计》第八次学习总结

    Week08<Java程序设计>第八次学习总结 1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 答: 2.书面作业 1. ArrayList代码分析 1.1 解 ...

  6. 2018-2019-2 网络对抗技术 20165202 Exp6 信息搜集与漏洞扫描

    博客目录 一.实践目标 二.实践内容 各种搜索技巧的应用 DNS IP注册信息的查询 基本的扫描技术:主机发现.端口扫描.OS及服务版本探测.具体服务的查点 漏洞扫描:会扫,会看报告,会查漏洞说明,会 ...

  7. L162

    More than 250 corporate signatories joined together to try and deal with plastic pollution in an ann ...

  8. L161

    The robot arm made for gentle undersea explorationA soft robotic arm which will allow underwater sea ...

  9. TP5 volist

    VOLIST标签 volist标签通常用于查询数据集(select方法)的结果输出,通常模型的select方法返回的结果是一个二维数组,可以直接使用volist标签进行输出. 在控制器中首先对模版赋值 ...

  10. jfc在jsp页面画图,不将图片存在服务器端,只存入会话session(可用)

    jfc在jsp页面画图,不将图片存在服务器端,只存入会话session.其中主要用到jfc的一个servlet类. <%@ page contentType="text/html;ch ...