v4l2编程经典.docx
《v4l2编程经典.docx》由会员分享,可在线阅读,更多相关《v4l2编程经典.docx(45页珍藏版)》请在冰豆网上搜索。
v4l2编程经典
#include
#defineUSB_VIDEO_DEV"/dev/video0"
#defineFILE_NAME"/tmp/1.jpg"
#defineSTILL_IMAGE-1
#defineVIDEO_START0
#defineVIDEO_STOP1
#defineVIDEO_PALETTE_RAW_JPEG20
#defineVIDEO_PALETTE_JPEG21
staticintdebug=1;
intget_jpegsize(unsignedchar*buf,intsize)
{
inti;
for(i=1024;i { if((buf[i]==0xFF)&&(buf[i+1]==0xD9))returni+2;//jpeg文件格式中是以0xFF0xD9结尾的, //以此判断文件大小 } return-1;}intmain(intargc,char*argv[]){ intusb_camera_fd=-1,framesize=0,jpegsize=0;char*usb_video_dev=USB_VIDEO_DEV;char*filename=FILE_NAME;FILE*fp;structvideo_capabilityvideo_caps;structvideo_channelvideo_chan;structvideo_picturevideo_pic; structvideo_mbufvideo_mbuffer;structvideo_mmapvid_mmap;unsignedchar*mapaddr=NULL,*framebuffer=NULL,*destbuffer=NULL;usb_camera_fd=open(usb_video_dev,O_RDWR);//打开设备,可读写if(usb_camera_fd==-1){fprintf(stderr,"Can'topendevice%s",usb_video_dev);return1;} /**********************video_capability**************/if(ioctl(usb_camera_fd,VIDIOCGCAP,&video_caps)==-1)//getvideodevicecapability获取设备基本信息{perror("Couldn'tgetvideodevicecapability");return-1;}if(debug){printf("videoname:%s\n",video_caps.name);printf("video_caps.channels:%d\n",video_caps.channels);printf("video_caps.type:0x%x\n",video_caps.type);printf("videomaxwidth:%d\n",video_caps.maxwidth);printf("videomaxheight:%d\n",video_caps.maxheight);printf("videominwidth:%d\n",video_caps.minwidth);printf("videominheight:%d\n",video_caps.minheight); }/************************video_channel****************//*video_chan.channel=video_caps.channels;if(ioctl(usb_camera_fd,VIDIOCGCHAN,&video_chan)==-1)//获取信号源的属性{perror("ioctl(VIDIOCGCAP)");return-1;}if(debug){printf("videochannel:%d\n",video_chan.channel);printf("videochannelname:%s\n",video_chan.name);printf("videochanneltype:%d\n",video_chan.type);}*//*************************video_picture****************/if(ioctl(usb_camera_fd,VIDIOCGPICT,&video_pic)==-1)//获取设备采集的图象的各种属性{perror("ioctl(VIDIOCGPICT)");return-1;}/*video_pic.palette=VIDEO_PALETTE_JPEG;video_pic.depth=8;video_pic.hue=50110;video_pic.whiteness=41380;if(ioctl(usb_camera_fd,VIDIOCSPICT,&video_pic)==-1)//设置设备采集的图象的属性{perror("ioctl(VIDIOCSPICT)");//return-1;}*/if(debug){printf("video_pic.brightness:%d\n",video_pic.brightness);printf("video_pic.colour:%d\n",video_pic.colour);printf("video_pic.contrast:%d\n",video_pic.contrast);printf("video_pic.depth:%d\n",video_pic.depth);printf("video_pic.hue:%d\n",video_pic.hue);printf("video_pic.whiteness:%d\n",video_pic.whiteness);printf("video_pic.palette:%d\n",video_pic.palette);}/************************video_mbuf**********************/memset(&video_mbuffer,0,sizeof(video_mbuffer));//初始化video_mbuf,以得到所映射的buffer的信息if(ioctl(usb_camera_fd,VIDIOCGMBUF,&video_mbuffer)==-1)//video_mbuf{perror("ioctl(VIDIOCGMBUF)");return-1;}if(debug){printf("video_mbuffer.frames:%d\n",video_mbuffer.frames);printf("video_mbuffer.offsets[0]:%d\nvideo_mbuffer.offsets[1]:%d\n",video_mbuffer.offsets[0],video_mbuffer.offsets[1]);printf("video_mbuffer.size:%d\n",video_mbuffer.size); }//将mmap与video_mbuf绑定mapaddr=(unsignedchar*)mmap(0,video_mbuffer.size,PROT_READ,MAP_SHARED,usb_camera_fd,0);if(mapaddr<0){perror("v4lmmap");return-1;}/********************video_mmap*************************/vid_mmap.width=320;vid_mmap.height=240;vid_mmap.frame=0;//单帧采集vid_mmap.format=VIDEO_PALETTE_JPEG;/**********************startcapture********************///Mmap方式下真正开始视频截取//若调用成功,开始一帧的截取,是非阻塞的是否截取完毕留给VIDIOCSYNC来判断if(ioctl(usb_camera_fd,VIDIOCCAPTURE,VIDEO_START)==-1){perror("ioctl(VIDIOCCAPTURE)");return-1;}/***********************waitforready*******************///调用VIDIOCSYNC等待一帧截取结束//若成功,表明一帧截取已完成。可以开始做下一次VIDIOCMCAPTURE//frame是当前截取的帧的序号/*if(ioctl(usb_camera_fd,VIDIOCSYNC,&vid_mmap.frame)==-1) {perror("ioctl(VIDIOCSYNC)");//return-1;}*/ framesize=320*240>>2;//实际采集到的jpeg图像的大小最多也就十几KB//获取刚刚采集到的图像数据帧的地址framebuffer=mapaddr+video_mbuffer.offsets[vid_mmap.frame];//获取图像的大小//jpegsize=get_jpegsize(framebuffer,framesize);if(jpegsize<0){printf("Can'tgetsizeofjpegpicture\n");return1;}//分配空间,准备将destbuffer缓冲区中的图像数据写入文件destbuffer=(unsignedchar*)malloc(video_mbuffer.size);if(destbuffer==NULL){printf("mallocmemoryfordestbuffererror\n");return-1;}memcpy(destbuffer,framebuffer,video_mbuffer.size);fp=fopen(filename,"wb");//打开文件if(!fp){printf("Can'topenfile%s",filename);//return-1;}fwrite(destbuffer,video_mbuffer.size,1,fp);//写入文件fclose(fp);//关闭文件free(destbuffer);//释放空间munmap(mapaddr,video_mbuffer.size);//取消绑定close(usb_camera_fd);return1;}v4l2驱动编写篇一--介绍原文网址:笔者最近有机会写了一个摄像头的驱动,是“Onelaptopperchild”项目的中摄像头专用的。这个驱动使用了为此目的而设计的内核API:theVideo4Linux2API。在写这个驱动的过程中,笔者发现了一个惊人的问题:这个API的文档工作做得并不是很好,而用户层的文档则写的,实际上,相当不错。为了补救现在的状况,LWN将在未来的内个月里写一系列文章,告诉大家如何写V4L2接口的驱动。V4L2有一段历史了。大约在1998的秋天,它的光芒第一次出现在BillDirks的眼中。经过长足的发展,它于2002年11月,发布2.5.46时,融入了内核主干之中。然而直到今天,仍有一部分内核驱不支持新的API,这种新旧API的转换工作仍在进行。同时,V4L2API也在发展,并在2.6.18版本中进行了一些重大的改变。支持V4L2的应用依旧相对较少。V4L2在设计时,是要支持很多广泛的设备的,它们之中只有一部分在本质上是真正的视频设备:∙videocaptureinterface(影像捕获接口)从调谐器或是摄像头上获取视频数据。对很多人来讲,影像捕获(videocapture)是V4L2的基本应用。由于笔者在这方面的经验是强项,这一系列文章也趋于强调捕获API,但V4L2不止这些。∙videooutputinterface(视频输出接口)允许应用使用PC的外设,让其提供视频图像。有可能是通过电视信号的形式。∙捕获接口还有一个变体,存在于videooverlayinterface(视频覆盖接口)之中。它的工作是方便视频显示设备直接从捕获设备上获取数据。视频数据直接从捕获设备传到显示设备,无需经过CPU。∙VBIinterfaces(Verticalblankingintervalinterface,垂直消隐接口)提供垂直消隐期的数据接入。这个接口包括raw和sliced两种接口,其分别在于硬件中处理的VBI数据量。(为什么要在消隐期间接入数据呢?看这里)∙radiointerface(广播接口)用于从AM或FM调谐器中获得音频数据。也可能出现其它种类的设备。V4L2API中还有一些关于编译码和效果设备的stub,他们都用来转换视频数据流。然而这块的东西尚未完成确定,更不说应用了。还有“teletext”和”radiodatasystem”的接口,他们目前在V4L1API中实现。他们没有移动到V4L2的API中来,而且目前也没有这方面的计划。视频驱动与其他驱动不同之处,在于它的配置方式多种多样。因此大部分V4L2驱动都有一些特定的代码,好让应用可以知道给定的设备有什么功能,并配置设备,使其按期望的方式工作。V4L2的API定义了几十个回调函数,用来配置如调谐频率、窗口和裁剪、帧速率、视频压缩、图像参数(亮度、对比度…)、视频标准、视频格式等参数。这一系列文章的很大部分都要用来考察这些配置的过程。然后,还有一个小任务,就是有效地在视频频率下进行I/O操作。V4L2定义了三种方法来在用户空间和外设之间移动视频数据,其中有些会比较复杂。视频I/O和视频缓冲层,将会分成两篇文章来写,它们是用来处量一些共性的任务的。随后的文章每几周发一篇,共会加入到下面的列表中。v4l2驱动编写篇二--注册和打开原文网址:这篇文章是LWN写V4L2接口的设备驱动系列文章的第二篇。没看过介绍篇的,也许可以从那篇开始看。这一期文章将关注VideoforLinux驱动的总体结构和设备注册过程。开始之前,有必要提一点,那就是对于搞视频驱动的人来说,有两份资料是非常有价值的。∙TheV4L2APISpecification.(V4L2API说明)这份文档涵盖了用户空间视角下的API,但在很大程度上,V4L2驱动直接实现的就是那些API。所以大部分结构体是相同的,而且V4L2调用的语义也表述很很明了。打印一份出来(可以考虑去掉自由文本协议的文本内容,以保护树木[前面是作者原文,节省纸张就是保护树木嘛]),放在容易够到的地方。∙内核代码中的vivi驱动,即drivers/media/video/vivi.c.这是一个虚拟驱动。它可以用来测试,却不使用任何实际硬件。这样,它就成一个教人如何写V4L2驱动的非常好的实例。首先,每个V4L2驱动都要包含一个必须的头文件:1 #include大部分所需的信息都在这里。作为一个驱动作者,当挖掘头文件的时候,你可能也得看看include/media/v4l2-dev.h,它定义了许多你将来要打交道的结构体。一个视频驱动很可能要有处理PCI总线,或USB总线的部分。这里我们不会花什么时间还接触这些东西。通常会有一个内部一I2C接口,我们在这一系列的后续文章中会接触到它。然后还有一个V4L2的子系统接口。这个子系统是围绕video_device这个结构体建立的,它代表的是一个V4L2设备。讲解进入这个结构体的一切,将会是这个系列中几篇文章的主题。这里我们先有一个概览。video_device结构体的name字段是这一类设备的名字,它会出现在内核日志和sysfs中出现。这个名字通常与驱动的名字相同。所表示的设备有两个字段来描述。第一个字段(type)似乎是从V4L1的API中遗留下来的,它可以下列四个值之一:∙VFL_TYPE_GRABBER表明是一个图像采集设备–包括摄像头、调谐器,诸如此类。∙VFL_TYPE_VBI代表的设备是从视频消隐的时间段取得信息的设备。∙VFL_TYPE_RADIO代表无线电设备。∙VFL_TYPE_VTX代表视传设备。如果你的设备支持上面提到的不只一种功能,那就要为每个功能注册一个V4L2设备。然而在V4L2中,注册的每个设备都可以用作它实际支持的各种模式(就是说,你要为一个物理设备创建不多个设备节点,但你却可以调用任意一个设备节点,来实现这个物理设备支持的任意功能)。实质上的问题是,在V4L2中,你实际上只需一个设备,注册多个V4l2设备只是为了与V4l1兼容。第二个字段是type2,它以掩码的形式对设备的功能提供了更详尽的描述。它可以包含以下值:∙VID_TYPE_CAPTURE它可以捕获视频数据∙VID_TYPE_TUNER它可以接收不同的频率∙VID_TYPE_TELETEXT它可以抓取字幕∙VID_TYPE_OVERLAY它可以将视频数据直接覆盖到显示设备的帧缓冲区∙VID_TYPE_CHROMAKEY一种特殊的覆盖能力,覆盖的仅是帧缓冲区中像素值为某特定值的区域∙VID_TYPE_CLIPPING它可以剪辑覆盖数据∙VID_TYPE_FRAMERAM它使用帧缓冲区中的存储器∙VID_TYPE_SCALES它可以缩放视频数据∙VID_TYPE_MONOCHROME这个是一个纯灰度设备∙VID_TYPE_SUBCAPTURE它可以捕获图像的子区域∙VID_TYPE_MPEG_DECODER它支持mpeg码流解码∙VID_TYPE_MPEG_ENCODER它支持编码mpeg码流∙VID_TYPE_MJPEG_DECODER它支持mjpeg解码∙VID_TYPE_MJPEG_ENCODER它支持mjpeg编码V4L2驱动还要初始化的一个字段是minor,它是你想要的子设备号。通常这个值都设为-1,这样会让video4linux子系统在注册时自动分配一个子设备号。在video_device结构体中,还有三组不同的函数指针集。第一组只包含一个函数,那就是release(),如果驱动没有release()函数,内核就会抱怨(笔者发现一个件有趣的事,就是这个抱怨涉及到冒犯一篇LWN文章的作者)。release()函数很重要:由于多种原因,对video_device的引用可以在最后一个应用关闭文件描述符后很长一段时间依然保持。它们甚至可以在设备己经注销后依然保持。因此,在release()函数调用前,释放这个结构体是不安全的。所以这个函数通常要包含一个简单的kfree()调用。video_device的file_operations结构体包含都是常规的函数指针。视频设备通常都包括open()和release()函数。注意:这里所说的release函数并非上面所讲到的同名的release()函数,这个release()函数只要设备关闭就要调用。通常都还要有read()和write()函数,这取决于设备的功能是输入还是输出。然而我们要注意的是,对于视频流设备而言,传输数据还有别的方法。多数处理视频流数据的设备还需要实现poll()和mmap();而且每个V4L2设备都要有ioctl()函数,但是也可以使用V4L2子系统的video_ioctl2();第三组函数存在于video_device结构体本身里面,它们是V4L2API的核心。这组函数有几十个,处理不同的设备配置操作、流输入输出和其他操作。最后,从一开始就要知道的一个字段就是debug.可以把它设成是V4L2_DEBUG_IOCTL或V4L2_DEBUG_IOCTL_ARG(或是两个都设,这是个掩码),可以生成很多的调试信息,它们可以帮助一个迷糊的程序员找到毛病,知道为什么驱动和应用谁也不知道对方在说什么。视频设备注册一旦video_device己经配置好,就可以下面的函数注册了:1intvideo_register_device(structvideo_device*vfd,inttype,intnr);这里vfd是设备的结构体(video_device),type的值与它的type字段值相同,nr也是一样,想要的子设备号(为-1则注册时自动分配)。返回值当为0,若返加的是负的出错码,则表明出错了,和通常一样,我们要知道,设备一旦注册,它的函数可能就会立即调用,所以不到一切准备就绪,不要调用video_register_device();设备的注销方法为:1voidvideo_unregister_device(structvideo_device*vfd);请继续关注本系列的下篇文章,我们将会看看这些函数的具体实现。open()和release()每个V4L2设备都需要open()函数,其原型也与常规的相同。int(*open)(structinode*inode,
if((buf[i]==0xFF)&&(buf[i+1]==0xD9))returni+2;//jpeg文件格式中是以0xFF0xD9结尾的,
//以此判断文件大小
}
return-1;
intmain(intargc,char*argv[])
intusb_camera_fd=-1,framesize=0,jpegsize=0;
char*usb_video_dev=USB_VIDEO_DEV;
char*filename=FILE_NAME;
FILE*fp;
structvideo_capabilityvideo_caps;
structvideo_channelvideo_chan;
structvideo_picturevideo_pic;
structvideo_mbufvideo_mbuffer;
structvideo_mmapvid_mmap;
unsignedchar*mapaddr=NULL,*framebuffer=NULL,*destbuffer=NULL;
usb_camera_fd=open(usb_video_dev,O_RDWR);//打开设备,可读写
if(usb_camera_fd==-1)
fprintf(stderr,"Can'topendevice%s",usb_video_dev);
return1;
/**********************video_capability**************/
if(ioctl(usb_camera_fd,VIDIOCGCAP,&video_caps)==-1)//getvideodevicecapability获取设备基本信息
perror("Couldn'tgetvideodevicecapability");
if(debug)
printf("videoname:
%s\n",video_caps.name);
printf("video_caps.channels:
%d\n",video_caps.channels);
printf("video_caps.type:
0x%x\n",video_caps.type);
printf("videomaxwidth:
%d\n",video_caps.maxwidth);
printf("videomaxheight:
%d\n",video_caps.maxheight);
printf("videominwidth:
%d\n",video_caps.minwidth);
printf("videominheight:
%d\n",video_caps.minheight);
/************************video_channel****************/
/*video_chan.channel=video_caps.channels;
if(ioctl(usb_camera_fd,VIDIOCGCHAN,&video_chan)==-1)//获取信号源的属性
perror("ioctl(VIDIOCGCAP)");
printf("videochannel:
%d\n",video_chan.channel);
printf("videochannelname:
%s\n",video_chan.name);
printf("videochanneltype:
%d\n",video_chan.type);
}*/
/*************************video_picture****************/
if(ioctl(usb_camera_fd,VIDIOCGPICT,&video_pic)==-1)//获取设备采集的图象的各种属性
perror("ioctl(VIDIOCGPICT)");
/*video_pic.palette=VIDEO_PALETTE_JPEG;
video_pic.depth=8;
video_pic.hue=50110;
video_pic.whiteness=41380;
if(ioctl(usb_camera_fd,VIDIOCSPICT,&video_pic)==-1)//设置设备采集的图象的属性
perror("ioctl(VIDIOCSPICT)");
//return-1;
printf("video_pic.brightness:
%d\n",video_pic.brightness);
printf("video_pic.colour:
%d\n",video_pic.colour);
printf("video_pic.contrast:
%d\n",video_pic.contrast);
printf("video_pic.depth:
%d\n",video_pic.depth);
printf("video_pic.hue:
%d\n",video_pic.hue);
printf("video_pic.whiteness:
%d\n",video_pic.whiteness);
printf("video_pic.palette:
%d\n",video_pic.palette);
/************************video_mbuf**********************/
memset(&video_mbuffer,0,sizeof(video_mbuffer));
//初始化video_mbuf,以得到所映射的buffer的信息
if(ioctl(usb_camera_fd,VIDIOCGMBUF,&video_mbuffer)==-1)//video_mbuf
perror("ioctl(VIDIOCGMBUF)");
printf("video_mbuffer.frames:
%d\n",video_mbuffer.frames);
printf("video_mbuffer.offsets[0]:
%d\nvideo_mbuffer.offsets[1]:
%d\n",video_mbuffer.offsets[0],video_mbuffer.offsets[1]);
printf("video_mbuffer.size:
%d\n",video_mbuffer.size);
//将mmap与video_mbuf绑定
mapaddr=(unsignedchar*)mmap(0,video_mbuffer.size,PROT_READ,MAP_SHARED,usb_camera_fd,0);
if(mapaddr<0)
perror("v4lmmap");
/********************video_mmap*************************/
vid_mmap.width=320;
vid_mmap.height=240;
vid_mmap.frame=0;//单帧采集
vid_mmap.format=VIDEO_PALETTE_JPEG;
/**********************startcapture********************///Mmap方式下真正开始视频截取
//若调用成功,开始一帧的截取,是非阻塞的是否截取完毕留给VIDIOCSYNC来判断
if(ioctl(usb_camera_fd,VIDIOCCAPTURE,VIDEO_START)==-1)
perror("ioctl(VIDIOCCAPTURE)");
/***********************waitforready*******************///调用VIDIOCSYNC等待一帧截取结束
//若成功,表明一帧截取已完成。
可以开始做下一次VIDIOCMCAPTURE
//frame是当前截取的帧的序号
/*if(ioctl(usb_camera_fd,VIDIOCSYNC,&vid_mmap.frame)==-1)
perror("ioctl(VIDIOCSYNC)");
framesize=320*240>>2;//实际采集到的jpeg图像的大小最多也就十几KB
//获取刚刚采集到的图像数据帧的地址
framebuffer=mapaddr+video_mbuffer.offsets[vid_mmap.frame];
//获取图像的大小
//jpegsize=get_jpegsize(framebuffer,framesize);
if(jpegsize<0)
printf("Can'tgetsizeofjpegpicture\n");
//分配空间,准备将destbuffer缓冲区中的图像数据写入文件
destbuffer=(unsignedchar*)malloc(video_mbuffer.size);
if(destbuffer==NULL)
printf("mallocmemoryfordestbuffererror\n");
memcpy(destbuffer,framebuffer,video_mbuffer.size);
fp=fopen(filename,"wb");//打开文件
if(!
fp)
printf("Can'topenfile%s",filename);
fwrite(destbuffer,video_mbuffer.size,1,fp);//写入文件
fclose(fp);//关闭文件
free(destbuffer);//释放空间
munmap(mapaddr,video_mbuffer.size);//取消绑定
close(usb_camera_fd);
v4l2驱动编写篇一--介绍
原文网址:
笔者最近有机会写了一个摄像头的驱动,是“Onelaptopperchild”项目的中摄像头专用的。
这个驱动使用了为此目的而设计的内核API:
theVideo4Linux2API。
在写这个驱动的过程中,笔者发现了一个惊人的问题:
这个API的文档工作做得并不是很好,而用户层的文档则写的,实际上,相当不错。
为了补救现在的状况,LWN将在未来的内个月里写一系列文章,告诉大家如何写V4L2接口的驱动。
V4L2有一段历史了。
大约在1998的秋天,它的光芒第一次出现在BillDirks的眼中。
经过长足的发展,它于2002年11月,发布2.5.46时,融入了内核主干之中。
然而直到今天,仍有一部分内核驱不支持新的API,这种新旧API的转换工作仍在进行。
同时,V4L2API也在发展,并在2.6.18版本中进行了一些重大的改变。
支持V4L2的应用依旧相对较少。
V4L2在设计时,是要支持很多广泛的设备的,它们之中只有一部分在本质上是真正的视频设备:
∙videocaptureinterface(影像捕获接口)从调谐器或是摄像头上获取视频数据。
对很多人来讲,影像捕获(videocapture)是V4L2的基本应用。
由于笔者在这方面的经验是强项,这一系列文章也趋于强调捕获API,但V4L2不止这些。
∙videooutputinterface(视频输出接口)允许应用使用PC的外设,让其提供视频图像。
有可能是通过电视信号的形式。
∙捕获接口还有一个变体,存在于videooverlayinterface(视频覆盖接口)之中。
它的工作是方便视频显示设备直接从捕获设备上获取数据。
视频数据直接从捕获设备传到显示设备,无需经过CPU。
∙VBIinterfaces(Verticalblankingintervalinterface,垂直消隐接口)提供垂直消隐期的数据接入。
这个接口包括raw和sliced两种接口,其分别在于硬件中处理的VBI数据量。
(为什么要在消隐期间接入数据呢?
看这里)
∙radiointerface(广播接口)用于从AM或FM调谐器中获得音频数据。
也可能出现其它种类的设备。
V4L2API中还有一些关于编译码和效果设备的stub,他们都用来转换视频数据流。
然而这块的东西尚未完成确定,更不说应用了。
还有“teletext”和”radiodatasystem”的接口,他们目前在V4L1API中实现。
他们没有移动到V4L2的API中来,而且目前也没有这方面的计划。
视频驱动与其他驱动不同之处,在于它的配置方式多种多样。
因此大部分V4L2驱动都有一些特定的代码,好让应用可以知道给定的设备有什么功能,并配置设备,使其按期望的方式工作。
V4L2的API定义了几十个回调函数,用来配置如调谐频率、窗口和裁剪、帧速率、视频压缩、图像参数(亮度、对比度…)、视频标准、视频格式等参数。
这一系列文章的很大部分都要用来考察这些配置的过程。
然后,还有一个小任务,就是有效地在视频频率下进行I/O操作。
V4L2定义了三种方法来在用户空间和外设之间移动视频数据,其中有些会比较复杂。
视频I/O和视频缓冲层,将会分成两篇文章来写,它们是用来处量一些共性的任务的。
随后的文章每几周发一篇,共会加入到下面的列表中。
v4l2驱动编写篇二--注册和打开
这篇文章是LWN写V4L2接口的设备驱动系列文章的第二篇。
没看过介绍篇的,也许可以从那篇开始看。
这一期文章将关注VideoforLinux驱动的总体结构和设备注册过程。
开始之前,有必要提一点,那就是对于搞视频驱动的人来说,有两份资料是非常有价值的。
∙TheV4L2APISpecification.(V4L2API说明)这份文档涵盖了用户空间视角下的API,但在很大程度上,V4L2驱动直接实现的就是那些API。
所以大部分结构体是相同的,而且V4L2调用的语义也表述很很明了。
打印一份出来(可以考虑去掉自由文本协议的文本内容,以保护树木[前面是作者原文,节省纸张就是保护树木嘛]),放在容易够到的地方。
∙内核代码中的vivi驱动,即drivers/media/video/vivi.c.这是一个虚拟驱动。
它可以用来测试,却不使用任何实际硬件。
这样,它就成一个教人如何写V4L2驱动的非常好的实例。
首先,每个V4L2驱动都要包含一个必须的头文件:
1
大部分所需的信息都在这里。
作为一个驱动作者,当挖掘头文件的时候,你可能也得看看include/media/v4l2-dev.h,它定义了许多你将来要打交道的结构体。
一个视频驱动很可能要有处理PCI总线,或USB总线的部分。
这里我们不会花什么时间还接触这些东西。
通常会有一个内部一I2C接口,我们在这一系列的后续文章中会接触到它。
然后还有一个V4L2的子系统接口。
这个子系统是围绕video_device这个结构体建立的,它代表的是一个V4L2设备。
讲解进入这个结构体的一切,将会是这个系列中几篇文章的主题。
这里我们先有一个概览。
video_device结构体的name字段是这一类设备的名字,它会出现在内核日志和sysfs中出现。
这个名字通常与驱动的名字相同。
所表示的设备有两个字段来描述。
第一个字段(type)似乎是从V4L1的API中遗留下来的,它可以下列四个值之一:
∙VFL_TYPE_GRABBER表明是一个图像采集设备–包括摄像头、调谐器,诸如此类。
∙VFL_TYPE_VBI代表的设备是从视频消隐的时间段取得信息的设备。
∙VFL_TYPE_RADIO代表无线电设备。
∙VFL_TYPE_VTX代表视传设备。
如果你的设备支持上面提到的不只一种功能,那就要为每个功能注册一个V4L2设备。
然而在V4L2中,注册的每个设备都可以用作它实际支持的各种模式(就是说,你要为一个物理设备创建不多个设备节点,但你却可以调用任意一个设备节点,来实现这个物理设备支持的任意功能)。
实质上的问题是,在V4L2中,你实际上只需一个设备,注册多个V4l2设备只是为了与V4l1兼容。
第二个字段是type2,它以掩码的形式对设备的功能提供了更详尽的描述。
它可以包含以下值:
∙VID_TYPE_CAPTURE它可以捕获视频数据
∙VID_TYPE_TUNER它可以接收不同的频率
∙VID_TYPE_TELETEXT它可以抓取字幕
∙VID_TYPE_OVERLAY它可以将视频数据直接覆盖到显示设备的帧缓冲区
∙VID_TYPE_CHROMAKEY一种特殊的覆盖能力,覆盖的仅是帧缓冲区中像素值为某特定值的区域
∙VID_TYPE_CLIPPING它可以剪辑覆盖数据
∙VID_TYPE_FRAMERAM它使用帧缓冲区中的存储器
∙VID_TYPE_SCALES它可以缩放视频数据
∙VID_TYPE_MONOCHROME这个是一个纯灰度设备
∙VID_TYPE_SUBCAPTURE它可以捕获图像的子区域
∙VID_TYPE_MPEG_DECODER它支持mpeg码流解码
∙VID_TYPE_MPEG_ENCODER它支持编码mpeg码流
∙VID_TYPE_MJPEG_DECODER它支持mjpeg解码
∙VID_TYPE_MJPEG_ENCODER它支持mjpeg编码
V4L2驱动还要初始化的一个字段是minor,它是你想要的子设备号。
通常这个值都设为-1,这样会让video4linux子系统在注册时自动分配一个子设备号。
在video_device结构体中,还有三组不同的函数指针集。
第一组只包含一个函数,那就是release(),如果驱动没有release()函数,内核就会抱怨(笔者发现一个件有趣的事,就是这个抱怨涉及到冒犯一篇LWN文章的作者)。
release()函数很重要:
由于多种原因,对video_device的引用可以在最后一个应用关闭文件描述符后很长一段时间依然保持。
它们甚至可以在设备己经注销后依然保持。
因此,在release()函数调用前,释放这个结构体是不安全的。
所以这个函数通常要包含一个简单的kfree()调用。
video_device的file_operations结构体包含都是常规的函数指针。
视频设备通常都包括open()和release()函数。
注意:
这里所说的release函数并非上面所讲到的同名的release()函数,这个release()函数只要设备关闭就要调用。
通常都还要有read()和write()函数,这取决于设备的功能是输入还是输出。
然而我们要注意的是,对于视频流设备而言,传输数据还有别的方法。
多数处理视频流数据的设备还需要实现poll()和mmap();而且每个V4L2设备都要有ioctl()函数,但是也可以使用V4L2子系统的video_ioctl2();
第三组函数存在于video_device结构体本身里面,它们是V4L2API的核心。
这组函数有几十个,处理不同的设备配置操作、流输入输出和其他操作。
最后,从一开始就要知道的一个字段就是debug.可以把它设成是V4L2_DEBUG_IOCTL或V4L2_DEBUG_IOCTL_ARG(或是两个都设,这是个掩码),可以生成很多的调试信息,它们可以帮助一个迷糊的程序员找到毛病,知道为什么驱动和应用谁也不知道对方在说什么。
视频设备注册
一旦video_device己经配置好,就可以下面的函数注册了:
intvideo_register_device(structvideo_device*vfd,inttype,intnr);
这里vfd是设备的结构体(video_device),type的值与它的type字段值相同,nr也是一样,想要的子设备号(为-1则注册时自动分配)。
返回值当为0,若返加的是负的
出错码,则表明出错了,和通常一样,我们要知道,设备一旦注册,它的函数可能就会立即调用,所以不到一切准备就绪,不要调用video_register_device();
设备的注销方法为:
voidvideo_unregister_device(structvideo_device*vfd);
请继续关注本系列的下篇文章,我们将会看看这些函数的具体实现。
open()和release()每个V4L2设备都需要open()函数,其原型也与常规的相同。
int(*open)(structinode*inode,
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1