V4L2驱动的移植与应用Word文档下载推荐.docx

上传人:b****4 文档编号:16711183 上传时间:2022-11-25 格式:DOCX 页数:30 大小:42.51KB
下载 相关 举报
V4L2驱动的移植与应用Word文档下载推荐.docx_第1页
第1页 / 共30页
V4L2驱动的移植与应用Word文档下载推荐.docx_第2页
第2页 / 共30页
V4L2驱动的移植与应用Word文档下载推荐.docx_第3页
第3页 / 共30页
V4L2驱动的移植与应用Word文档下载推荐.docx_第4页
第4页 / 共30页
V4L2驱动的移植与应用Word文档下载推荐.docx_第5页
第5页 / 共30页
点击查看更多>>
下载资源
资源描述

V4L2驱动的移植与应用Word文档下载推荐.docx

《V4L2驱动的移植与应用Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《V4L2驱动的移植与应用Word文档下载推荐.docx(30页珍藏版)》请在冰豆网上搜索。

V4L2驱动的移植与应用Word文档下载推荐.docx

.vidioc_g_fmt_vid_cap=vidioc_g_fmt_vid_cap,

.vidioc_try_fmt_vid_cap=vidioc_try_fmt_vid_cap,

.vidioc_s_fmt_vid_cap=vidioc_s_fmt_vid_cap,

.vidioc_reqbufs=vidioc_reqbufs,

.vidioc_querybuf=vidioc_querybuf,

.vidioc_qbuf=vidioc_qbuf,

.vidioc_dqbuf=vidioc_dqbuf,

.vidioc_s_std=vidioc_s_std,

.vidioc_enum_input=vidioc_enum_input,

.vidioc_g_input=vidioc_g_input,

.vidioc_s_input=vidioc_s_input,

.vidioc_queryctrl=vidioc_queryctrl,

.vidioc_g_ctrl=vidioc_g_ctrl,

.vidioc_s_ctrl=vidioc_s_ctrl,

.vidioc_streamon=vidioc_streamon,

.vidioc_streamoff=vidioc_streamoff,

#ifdefCONFIG_VIDEO_V4L1_COMPAT

.vidiocgmbuf=vidiocgmbuf,

#endif

3)vivi_template

staticstructvideo_devicevivi_template={

.name="

vivi"

.fops=&

vivi_fops,

.ioctl_ops=&

vivi_ioctl_ops,

.minor=-1,

.release=video_device_release,

.tvnorms=V4L2_STD_525_60,

.current_norm=V4L2_STD_NTSC_M,

其中函数vivi_xxx和vidioc_xxx都是在vivi.c中实现的。

如果要基于某个硬件来实现V4L2的接口,那这些函数就需要调用硬件的驱动去实现。

4)vivi_dev

struct 

vivi_dev{

 

list_head 

vivi_devlist;

//内核双向链表,在内核数据结构里有描述

semaphore 

lock;

//信号量,防止竞态访问

int 

users;

//用户数量计数

/* 

variousdeviceinfo*/

unsigned 

resources;

video_device 

video_dev;

//这个成员是这个结构的核心,用面向对象的话来说就是基类

vivi_dmaqueue 

vidq;

//DMA队列

Severalcounters*/

h,m,s,us,jiffies;

//定时器定义

char 

timestr[13];

//其它一些资源变量.

像这样变义的结构在LinuxC中很普遍,这也是利用C来实现面向对象编程的强大方法。

建立这个结构对象之后,所有的操作都是基于这个结构,或者这个结构派生出的来的其它结构。

5)vivi_fh

vivi_fh{

vivi_dev 

*dev;

videocapture*/

vivi_fmt 

*fmt;

width,height;

videobuf_queue 

vb_vidq;

enum 

v4l2_buf_type 

type;

这个结构即是vivi_dev结构的更深层次封装,基于那个结构加入了更多的描述信息,如视频制式、视频画面大小、视频缓冲队列等等。

在open的时候,会把这个结构赋给file结构中的private_data域。

在释放设备时注销.其它的像ioctl,mmap,read,write等等都会用到这个结构,其实整个模块的编写的cdev差不多。

只是视频设备的基类是video_device,而字符设备的基类是cdev而已。

2、数据传输方式:

在设备与应用程序之间有三种数据传输方式:

1)read与write这种方式,它像其它设备驱动一样,但是这种方式很慢,对于数据视频流不能满足其要求;

2)直接的内存访问,可以通过其映射方式来传输(IO数据流,交换指向缓冲区指针的方法);

这是视频设备通常用的方法,采用mmap()的方法,即有内核空间里开辟内存,再在程序里把这部分的内存映射到程序空间。

如果有设备内存,即直接映射到设备的内核,这种性能更高。

3)异步IO口访问,但是这种方法在V4L2模块中还没有实现。

(重要:

需要确认)

vivi中的mmap是利用第二种方法来实现的,这也是视频设备常用的方法:

static 

int

vivi_mmap(struct 

file*file,struct 

vm_area_struct*vma)

{

vivi_fh*fh=file->

private_data;

ret;

dprintk(1,"

mmapcalled,vma=0x%08lx\n"

(unsigned 

long)vma);

ret=videobuf_mmap_mapper(&

fh->

vb_vidq,vma);

vmastart=0x%08lx,size=%ld,ret=%d\n"

(unsigned 

long)vma->

vm_start,

vm_end-(unsigned 

ret);

return 

}

videobuf_mmap_mapper(&

 

这个核心函数把设备的I/O内存或者设备内存映射到系统为它开辟的虚拟内存。

3、操控设备的实现:

ioctl

vivi_ioctl(struct 

inode*inode,struct 

file*file,unsigned 

cmd,unsigned 

long 

arg)

video_usercopy(inode,file,cmd,arg,vivi_do_ioctl);

vivi_do_ioctl这个函数里调用一些命令来设备V4L2模块中的一些结构参数来改变或者获取设备的参数。

二、V4L2的应用

下面简单介绍一下V4L2驱动的应用流程。

1、视频采集的基本流程

一般的,视频采集都有如下流程:

2、打开视频设备

在V4L2中,视频设备被看做一个文件。

使用open函数打开这个设备:

//用非阻塞模式打开摄像头设备

intcameraFd;

cameraFd=open("

/dev/video0"

O_RDWR|O_NONBLOCK,0);

//如果用阻塞模式打开摄像头设备,上述代码变为:

//cameraFd=open("

O_RDWR,0);

关于阻塞模式和非阻塞模式:

应用程序能够使用阻塞模式或非阻塞模式打开视频设备,如果使用非阻塞模式调用视频设备,即使尚未捕获到信息,驱动依旧会把缓存(DQBUFF)里的东西返回给应用程序。

3、设定属性及采集方式

打开视频设备后,可以设置该视频设备的属性,例如裁剪、缩放等。

这一步是可选的。

在Linux编程中,一般使用ioctl函数来对设备的I/O通道进行管理:

externintioctl(int__fd,unsignedlongint__request,...)__THROW;

__fd:

设备的ID,例如刚才用open函数打开视频通道后返回的cameraFd;

__request:

具体的命令标志符。

在进行V4L2开发中,一般会用到以下的命令标志符:

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。

这些IO调用,有些是必须的,有些是可选择的。

4、检查当前视频设备支持的标准

在亚洲,一般使用PAL(720X576)制式的摄像头,而欧洲一般使用NTSC(720X480),使用VIDIOC_QUERYSTD来检测:

v4l2_std_idstd;

do{

ret=ioctl(fd,VIDIOC_QUERYSTD,&

std);

}while(ret==-1&

&

errno==EAGAIN);

switch(std){

caseV4L2_STD_NTSC:

//……

caseV4L2_STD_PAL:

5、设置视频捕获格式

当检测完视频设备支持的标准后,还需要设定视频捕获格式:

structv4l2_formatfmt;

memset(&

fmt,0,sizeof(fmt));

fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

fmt.fmt.pix.width=720;

fmt.fmt.pix.height=576;

fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;

fmt.fmt.pix.field=V4L2_FIELD_INTERLACED;

if(ioctl(fd,VIDIOC_S_FMT,&

fmt)==-1){

return-1;

v4l2_format结构体定义如下:

structv4l2_format

enumv4l2_buf_typetype;

//数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE

union

{

structv4l2_pix_formatpix;

structv4l2_windowwin;

structv4l2_vbi_formatvbi;

__u8raw_data[200];

}fmt;

structv4l2_pix_format

__u32width;

//宽,必须是16的倍数

__u32height;

//高,必须是16的倍数

__u32pixelformat;

//视频数据存储类型,例如是YUV4:

2:

2还是RGB

enumv4l2_fieldfield;

__u32bytesperline;

__u32sizeimage;

enumv4l2_colorspacecolorspace;

__u32priv;

6、分配内存

接下来可以为视频捕获分配内存:

structv4l2_requestbuffersreq;

if(ioctl(fd,VIDIOC_REQBUFS,&

req)==-1){

v4l2_requestbuffers定义如下:

structv4l2_requestbuffers

__u32count;

//缓存数量,也就是说在缓存队列里保持多少张照片

enumv4l2_memorymemory;

//V4L2_MEMORY_MMAP或V4L2_MEMORY_USERPTR

__u32reserved[2];

7、获取并记录缓存的物理空间

使用VIDIOC_REQBUFS,我们获取了req.count个缓存,下一步通过调用VIDIOC_QUERYBUF命令来获取这些缓存的地址,然后使用mmap函数转换成应用程序中的绝对地址,最后把这段缓存放入缓存队列:

typedefstructVideoBuffer{

void*start;

size_tlength;

}VideoBuffer;

VideoBuffer*buffers=calloc(req.count,sizeof(*buffers));

structv4l2_bufferbuf;

for(numBufs=0;

numBufs<

req.count;

numBufs++){

memset(&

buf,0,sizeof(buf));

buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory=V4L2_MEMORY_MMAP;

buf.index=numBufs;

//读取缓存

if(ioctl(fd,VIDIOC_QUERYBUF,&

buf)==-1){

}

buffers[numBufs].length=buf.length;

//转换成相对地址

buffers[numBufs].start=mmap(NULL,buf.length,

PROT_READ|PROT_WRITE,

MAP_SHARED,

fd,buf.m.offset);

if(buffers[numBufs].start==MAP_FAILED){

//放入缓存队列

if(ioctl(fd,VIDIOC_QBUF,&

8、关于视频采集方式

操作系统一般把系统使用的内存划分成用户空间和内核空间,分别由应用程序管理和操作系统管理。

应用程序可以直接访问内存的地址,而内核空间存放的是供内核访问的代码和数据,用户不能直接访问。

v4l2捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地址。

一共有三种视频采集方式:

1)使用read、write方式:

直接使用read和write函数进行读写。

这种方式最简单,但是这种方式会在用户空间和内核空间不断拷贝数据,同时在用户空间和内核空间占用了大量内存,效率不高。

2)内存映射方式(mmap):

把设备里的内存映射到应用程序中的内存控件,直接处理设备内存,这是一种有效的方式。

上面的mmap函数就是使用这种方式。

3)用户指针模式:

内存由用户空间的应用程序分配,并把地址传递到内核中的驱动程序,然后由v4l2驱动程序直接将数据填充到用户空间的内存中。

这点需要在v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR。

第一种方式效率是最低的,后面两种方法都能提高执行的效率,但是对于mmap方式,文档中有这样一句描述--Rememberthebuffersareallocatedinphysicalmemory,asopposedtovirtualmemorywhichcanbeswappedouttodisk.Applicationsshouldfreethebuffersassoonaspossiblewiththemunmap()function.(使用mmap方法的时候,buffers相当于是在内核空间中分配的,这种情况下,这些buffer是不能被交换到虚拟内存中,虽然这种方法不怎么影响读写效率,但是它一直占用着内核空间中的内存,当系统的内存有限的时候,如果同时运行有大量的进程,则对系统的整体性能会有一定的影响。

所以,对于三种视频采集方式的选择,推荐的顺序是userptr、mmap、read-write。

当使用mmap或userptr方式的时候,有一个环形缓冲队列的概念,这个队列中,有n个buffer,驱动程序采集到的视频帧数据,就是存储在每个buffer中。

在每次用VIDIOC_DQBUF取出一个buffer,并且处理完数据后,一定要用VIDIOC_QBUF将这个buffer再次放回到环形缓冲队列中。

环形缓冲队列,也使得这两种视频采集方式的效率高于直接read/write。

9、处理采集数据

V4L2有一个数据缓存,存放req.count数量的缓存数据。

数据缓存采用FIFO的方式,当应用程序调用缓存数据时,缓存队列将最先采集到的视频数据缓存送出,并重新采集一张视频数据。

这个过程需要用到两个ioctl命令,VIDIOC_DQBUF和VIDIOC_QBUF:

memset(&

buf,0,sizeof(buf));

buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory=V4L2_MEMORY_MMAP;

buf.index=0;

//读取缓存

if(ioctl(cameraFd,VIDIOC_DQBUF,&

buf)==-1)

//…………视频处理算法

//重新放入缓存队列

if(ioctl(cameraFd,VIDIOC_QBUF,&

10、关闭视频设备

使用close函数关闭一个视频设备

close(cameraFd)

三、V4L2的demo

capture.c是官方示例程序。

capture.c程序中的process_image函数:

capture.c程序主要是用来演示怎样使用v4l2接口,并没有对采集到的视频帧数据做任何实际的处理,仅仅用process_image函数表示了处理图像的代码位置。

process_image函数只有一个参数,就是存储视频帧的内存的地址指针,但是在真正的应用中,通常还需要知道该指针指向的数据的大小。

因此可以修改函数,改成voidprocess_image(constvoid*p,intlen),但是每次调用process_image的时候,第2个参数该传递什么值?

考虑

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工作范文 > 演讲主持

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1