V4L2驱动的移植与应用Word文档下载推荐.docx
- 文档编号:16711183
- 上传时间:2022-11-25
- 格式:DOCX
- 页数:30
- 大小:42.51KB
V4L2驱动的移植与应用Word文档下载推荐.docx
《V4L2驱动的移植与应用Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《V4L2驱动的移植与应用Word文档下载推荐.docx(30页珍藏版)》请在冰豆网上搜索。
.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个参数该传递什么值?
考虑
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- V4L2 驱动 移植 应用