v4l2驱动编程,摄像头v4l2驱动编写

  v4l2驱动编程,摄像头v4l2驱动编写

  V4是V4L2的升级版,它为linux下的视频设备程序提供了一个windows sockets规范。包括一组数据结构和底层V4L2驱动程序接口。

  1.常见的结构定义在内核目录include/Linux/videodev2.h中。

  strutv 4 l 2 _ req Buffers//申请帧缓冲,对应命令VIDIOC_REQBUFS

  Struct v4l2_capability//视频设备的功能对应命令VIDIOC_QUERYCAP。

  strutv 4 l 2 _ input//视频输入信息,对应命令VIDIOC_ENUMINPUT

  strutv 4 l 2 _ standard//PAL、NTSC等视频标准对应的是命令VIDIOC_ENUMSTD。

  struct v4l2_format//frame的格式对应命令VIDIOC_G_FMT、VIDIOC_S_FMT等。

  struct v4l2_buffer//driver中的一帧图像缓存,对应命令VIDIOC_QUERYBUF。

  strutv 4 l 2 _ crop//视频信号的矩形边框

  V4l2_std_id//视频系统

  2.include/Linux/videodev2.h中也定义了常用的IOCTL接口命令。

  VIDIOC_REQBUFS //分配内存

  VIDIOC_QUERYBUF //将VIDIOC _ REQBUFS中分配的数据缓存转换为物理地址

  VIDIOC_QUERYCAP//查询驱动函数

  VIDIOC_ENUM_FMT//获取当前驱动支持的视频格式

  VIDIC _ 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。

  3.操作过程

  V4L2提供了很多访问接口,您可以根据自己的具体需求选择操作方式。应该注意的是,很少有驱动程序完全实现所有的接口功能。因此,使用时需要参考驱动程序源代码或仔细阅读驱动程序提供商的说明。

  以下是操作流程,供参考。

  (1)打开设备文件。

  int fd=open(Devicename,mode);

  设备名称:/dev/video0 、/dev/video1 ……

  模式:O_RDWR [ O_NONBLOCK]

  如果在非阻塞模式下调用视频设备,当没有可用的视频数据时,它将不会阻塞,并将立即返回。

  (2)获得设备的能力。

  struct v4l2_capability能力;

  int ret=ioctl(fd,VIDIOC_QUERYCAP,capability);

  看设备有什么功能,比如有没有视频输入功能。

  (3)选择视频输入。

  struct v4l2_input输入;

  .初始化输入

  int ret=ioctl(fd,VIDIOC_QUERYCAP,input);

  一个视频设备可以有多个视频输入。如果只有一个输入,该功能可以省略。

  (4)检测视频支持的系统

  v4l2 _ std _ id std

  做{

  ret=ioctl(fd,VIDIOC_QUERYSTD,STD);

  } while(ret==-1 errno==EAGAIN);

  开关(标准){

  案例V4L2_STD_NTSC:

  //……

  案例V4L2_STD_PAL:

  //……

  }

  (5)设置视频采集格式

  struct v4l2 _ format fmt

  fmt . TYPE=v4 L2 _ BUF _ TYPE _ VIDEO _ OUTPUT;

  fmt . fmt . PIX . pixel format=v4 L2 _ PIX _ FMT _ UYVY;

  fmt.fmt.pix.height=高度;

  fmt.fmt.pix.width=宽度;

  fmt . fmt . pix . FIELD=v4 L2 _ FIELD _ INTERLACED;

  ret=ioctl(fd,VIDIOC_S_FMT,fmt);

  if(ret) {

  佩罗尔(维迪奥克_ S _ FMT n );

  关闭(FD);

  return-1;

  }

  (6)向驱动程序申请帧缓冲。

  struct v4l2 _ requestbuffers req

  if (ioctl(fd,VIDIOC_REQBUFS,req)==-1) {

  return-1;

  }

  缓存的数量是在v4l2_requestbuffers结构中定义的,驱动会相应地申请相应数量的视频缓存。可以使用多个缓冲器来建立FIFO,以提高视频捕获的效率。

  (7)获取每个缓存的信息,并将其映射到用户空间。

  typedef结构VideoBuffer {

  void * start

  size_t长度;

  }视频缓冲区;

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

  struct v4l2 _ buffer buf

  for(numBufs=0;numBufs req.countnumufs){//映射所有缓存

  memset( buf,0,sizeof(buf));

  BUF . TYPE=v4 L2 _ BUF _ TYPE _ VIDEO _ CAPTURE;

  buf . MEMORY=v4l 2 _ MEMORY _ MMAP;

  buf.index=numBufs

  If (ioctl (fd,vidioc _ query buf,buf)==-1){//获取对应索引的缓存信息,这里主要用长度信息和偏移量信息来完成下面的mmap操作。

  return-1;

  }

  缓冲区[数字]。长度=buf.length

  //转换为相对地址

  缓冲区[数字]。start=mmap(NULL,buf.length,

  PROT _读 PROT _写,

  MAP_SHARED,

  fd,buf . m . offset);

  if(缓冲区[numBufs]。start==MAP_FAILED) {

  return-1;

  }

  (8)开始收集视频。

  int BUF _ TYPE=v4 L2 _ BUF _ TYPE _ VIDEO _ CAPTURE;

  int ret=ioctl(fd,VIDIOC_STREAMON,buf _ type);

  (9)取出FIFO缓冲器中的采样帧缓冲器。

  struct v4l2 _ buffer buf

  memset( buf,0,sizeof(buf));

  BUF . TYPE=v4 L2 _ BUF _ TYPE _ VIDEO _ CAPTURE;

  buf . MEMORY=v4l 2 _ MEMORY _ MMAP;

  buf . index=0;//此值由以下ioctl返回

  if (ioctl(fd,VIDIOC_DQBUF,buf)==-1)

  {

  return-1;

  }

  根据返回的buf.index找到对应的mmap映射缓存,取出视频数据。

  (10)将刚刚处理的缓冲区放回队列的末尾,以便循环回收。

  if (ioctl(fd,VIDIOC_QBUF,buf)==-1) {

  return-1;

  }

  (11)停止视频采集。

  int ret=ioctl(fd,VIDIOC_STREAMOFF,buf _ type);

  (12)关闭视频设备。

  关闭(FD);

  关于视频捕捉模式

  一般操作系统将系统使用的内存分为用户空间和内核空间,分别由应用程序和操作系统管理。应用程序可以直接访问内存的地址,而内核空间存储的是内核访问的代码和数据,用户无法直接访问。v4l2捕获的数据最初存储在内核空间,这意味着用户不能直接访问这个内存,必须通过某种方式改变地址。

  有三种视频采集方法:

  1)使用读写方法:直接使用读写函数进行读写。这种方法最简单,但是在用户空间和内核空间不断复制数据,占用用户空间和内核空间大量内存,效率低下。

  2)内存映射法(mmap):将设备中的内存映射到应用程序中的内存控件,直接处理设备的内存,是一种有效的方法。上面的mmap函数就是这样使用的。

  3)用户指针模式:内存由应用在用户空间分配,地址传递给内核中的驱动,然后v4l2驱动直接将数据填充到用户空间的内存中。这需要将v4l2_requestbuffers中的存储器域设置为V4L2_MEMORY_USERPTR。

  第一种方法效率最低,后两种方法可以提高执行效率,但是对于mmap方法,文档中有一个描述——请记住,缓冲区是在物理内存中分配的,而不是可以换出到磁盘的虚拟内存。应用程序应该使用munmap()函数尽快释放缓冲区。(使用mmap方法时,缓冲区分配在内核空间。在这种情况下,这些缓冲区不能交换到虚拟内存中。这种方法虽然不影响读写效率,但总是占用内核空间的内存。在系统内存有限的情况下,如果有大量的进程同时运行,会对系统的整体性能产生一定的影响。)

  所以对于三种视频拍摄方式的选择,推荐的顺序是userptr、mmap、读写。使用mmap或userptr时,有一个循环缓冲队列的概念。在这个队列中,有N个缓冲区,驱动程序采集的视频帧数据存储在每个缓冲区中。每次使用VIDIOC_DQBUF取出一个缓冲区并处理数据时,都必须使用VIDIOC_QBUF将这个缓冲区再次放回循环缓冲队列中。循环缓冲队列也使得这两种视频采集方式的效率高于直接方式。

  读/写.

v4l2驱动编程,摄像头v4l2驱动编写