您的位置 首页 电源

学习V4L2应用程序结构时要注意的事项

学习V4L2应用程序框架时要注意的事项-V4L2采用流水线的方式,操作更简单直观,基本遵循打开视频设备、设置格式、处理数据、关闭设备,更多的具体操作通过ioctl函数来实现。

  V4L2是V4L的晋级版别,linux下视频设备程序供给了一套接口规范。

  常用的结构体在内核目录include/linux/videodev2.h中界说

  struct v4l2_requestbuffers //恳求帧缓冲,对应指令VIDIOC_REQBUFS

  struct v4l2_capability //视频设备的功用,对应指令VIDIOC_QUERYCAP

  struct v4l2_input //视频输入信息,对应指令VIDIOC_ENUMINPUT

  struct v4l2_standard //视频的制式,比方PAL,NTSC,对应指令VIDIOC_ENUMSTD

  struct v4l2_format //帧的格局,对应指令VIDIOC_G_FMT、VIDIOC_S_FMT等

  struct v4l2_buffer //驱动中的一帧图画缓存,对应指令VIDIOC_QUERYBUF

  struct v4l2_crop //视频信号矩形边框

  v4l2_std_id //视频制式

  V4L2选用流水线的办法,操作更简略直观,根本遵从翻开视频设备、设置格局、处理数据、封闭设备,更多的具体操作经过ioctl函数来完成。

  1.翻开视频设备

  在V4L2中,视频设备被看做一个文件。运用open函数翻开这个设备:

  // 用非堵塞形式翻开摄像头设备

  int cameraFd;

  cameraFd = open(“/dev/video0”, O_RDWR | O_NONBLOCK, 0);

  // 假如用堵塞形式翻开摄像头设备,上述代码变为:

  //cameraFd = open(“/dev/video0”, O_RDWR, 0);

  运用程序能够运用堵塞形式或非堵塞形式翻开视频设备,假如运用非堵塞形式调用视频设备,即便没有捕获到信息,驱动仍旧会把缓存(DQBUFF)里的东西回来给运用程序。

  2. 设定特点及收集办法

  翻开视频设备后,能够设置该视频设备的特点,例如裁剪、缩放等。这一步是可选的。在Linux编程中,一般运用ioctl函数来对设备的I/O通道进行办理:

  int ioctl (int __fd, unsigned long int __request, 。../*args*/) ;

  在进行V4L2开发中,常用的指令标志符如下(some are optional):

  • 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。

  2.1查看当时视频设备支撑的规范

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

  v4l2_std_id std;

  do {

  ret = ioctl(fd, VIDIOC_QUERYSTD, &std);

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

  switch (std) {

  case V4L2_STD_NTSC:

  //……

  case V4L2_STD_PAL:

  //……

  }

  2.2 设置视频捕获格局

  当检测完视频设备支撑的规范后,还需求设定视频捕获格局,结构如下:

  struct v4l2_format fmt;

  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结构如下:

  struct v4l2_format {

  enum v4l2_buf_type type; //数据流类型,有必要永远是V4L2_BUF_TYPE_VIDEO_CAPTURE

  union

  {

  struct v4l2_pix_format pix;

  struct v4l2_window win;

  struct v4l2_vbi_format vbi;

  __u8 raw_data[200];

  } fmt;

  };

  struct v4l2_pix_format {

  __u32 width; // 宽,有必要是16的倍数

  __u32 height; // 高,有必要是16的倍数

  __u32 pixelformat; // 视频数据存储类型,例如是YUV4:2:2仍是RGB

  enum v4l2_field field;

  __u32 bytesperline;

  __u32 sizeimage;

  enum v4l2_colorspace colorspace;

  __u32 priv;

  };

  2.3 分配内存

  接下来能够为视频捕获分配内存:

  struct v4l2_requestbuffers req;

  req.count = BUFFER_COUNT;

  req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

  req.memory = V4L2_MEMORY_MMAP;

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

  return -1;

  }

  v4l2_requestbuffers 结构如下:

  struct v4l2_requestbuffers {

  u32 count;//缓存数量,也便是说在缓存行列里坚持多少张相片

  enum v4l2_buf_type type; //数据流类型,有必要永远是V4L2_BUF_TYPE_VIDEO_CAPTURE

  enum v4l2_memory memory;//V4L2_MEMORY_MMAP或V4L2_MEMORY_USERPTR

  u32 reserved[2];

  };

  2.4 获取并记载缓存的物理空间

  运用VIDIOC_REQBUFS,咱们获取了req.count个缓存,下一步经过调用VIDIOC_QUERYBUF指令来获取这些缓存的地址,然后运用mmap函数转化成运用程序中的肯定地址,最终把这段缓寄存入缓存行列:

  typedef struct VideoBuffer {

  void *start;

  size_t length;

  } VideoBuffer;

  v4l2_buffer 结构如下:

  struct v4l2_buffer {

  __u32 index;

  enum v4l2_buf_type type;

  __u32 bytesused;

  __u32 flags;

  enum v4l2_field field;

  struct TImeval TImestamp;

  struct v4l2_TImecode TImecode;

  __u32 sequence;

  /* memory location */

  enum v4l2_memory memory;

  union {

  __u32 offset;

  unsigned long userptr;

  } m;

  __u32 length;

  __u32 input;

  __u32 reserved;

  };

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

  struct v4l2_buffer buf;

  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) {

  return -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) {

  return -1;

  }

  // 放入缓存行列

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

  return -1;

  }

  }

  2.5 视频收集办法

  操作体系一般把体系运用的内存划分红用户空间和内核空间,分别由运用程序办理和操作体系办理。运用程序能够直接拜访内存的地址,而内核空间寄存的是供内核拜访的代码和数据,用户不能直接拜访。v4l2捕获的数据,开端是寄存在内核空间的,这意味着用户不能直接拜访该段内存,有必要经过某些手法来转化地址。

  一共有三种视频收集办法:运用read/write办法;内存映射办法和用户指针形式。

  read、write办法,在用户空间和内核空间不断复制数据,占用了很多用户内存空间,功率不高。

  内存映射办法:把设备里的内存映射到运用程序中的内存控件,直接处理设备内存,这是一种有用的办法。上面的mmap函数便是运用这种办法。

  用户指针形式:内存片段由运用程序自己分配。这点需求在v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR。

  2.6 处理收集数据

  V4L2有一个数据缓存,寄存req.count数量的缓存数据。数据缓存选用FIFO的办法,当运用程序调用缓存数据时,缓存行列将最早收集到的视频数据缓存送出,并从头收集一张视频数据。这个进程需求用到两个ioctl指令,VIDIOC_DQBUF和VIDIOC_QBUF:

  struct v4l2_buffer buf;

  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)

  {

  return -1;

  }

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

  //从头放入缓存行列

  if (ioctl(cameraFd, VIDIOC_QBUF, &buf) == -1) {

  return -1;

  }

  3. 封闭视频设备

  运用close函数封闭一个视频设备

  close(cameraFd)

  假如运用mmap,最终还需求运用munmap办法。

  下面是damo程序(经过实践验证,修正了网上的例程的过错)

  ———————————————————————————————————–

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #define CAMERA_DEVICE “/dev/video0”

  #define CAPTURE_FILE “frame.jpg”

  #define VIDEO_WIDTH 640

  #define VIDEO_HEIGHT 480

  #define VIDEO_FORMAT V4L2_PIX_FMT_YUYV

  #define BUFFER_COUNT 4

  typedef struct VideoBuffer {

  void *start;

  size_t length;

  } VideoBuffer;

  int main()

  {

  int i, ret;

  // 翻开设备

  int fd;

  fd = open(CAMERA_DEVICE, O_RDWR, 0);

  if (fd 《 0) {

  printf(“Open %s failed\n”, CAMERA_DEVICE);

  return -1;

  }

  // 获取驱动信息

  struct v4l2_capability cap;

  ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);

  if (ret 《 0) {

  printf(“VIDIOC_QUERYCAP failed (%d)\n”, ret);

  return ret;

  }

  // Print capability infomations

  printf(“Capability Informations:\n”);

  printf(“ driver: %s\n”, cap.driver);

  printf(“ card: %s\n”, cap.card);

  printf(“ bus_info: %s\n”, cap.bus_info);

  printf(“ version: %08X\n”, cap.version);

  printf(“ capabilities: %08X\n”, cap.capabilities);

  // 设置视频格局

  struct v4l2_format fmt;

  memset(&fmt, 0, sizeof(fmt));

  fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

  fmt.fmt.pix.width = VIDEO_WIDTH;

  fmt.fmt.pix.height = VIDEO_HEIGHT;

  fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;

  fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

  ret = ioctl(fd, VIDIOC_S_FMT, &fmt);

  if (ret 《 0) {

  printf(“VIDIOC_S_FMT failed (%d)\n”, ret);

  return ret;

  }

  // 获取视频格局

  ret = ioctl(fd, VIDIOC_G_FMT, &fmt);

  if (ret 《 0) {

  printf(“VIDIOC_G_FMT failed (%d)\n”, ret);

  return ret;

  }

  // Print Stream Format

  printf(“Stream Format Informations:\n”);

  printf(“ type: %d\n”, fmt.type);

  printf(“ width: %d\n”, fmt.fmt.pix.width);

  printf(“ height: %d\n”, fmt.fmt.pix.height);

  char fmtstr[8];

  memset(fmtstr, 0, 8);

  memcpy(fmtstr, &fmt.fmt.pix.pixelformat, 4);

  printf(“ pixelformat: %s\n”, fmtstr);

  printf(“ field: %d\n”, fmt.fmt.pix.field);

  printf(“ bytesperline: %d\n”, fmt.fmt.pix.bytesperline);

  printf(“ sizeimage: %d\n”, fmt.fmt.pix.sizeimage);

  printf(“ colorspace: %d\n”, fmt.fmt.pix.colorspace);

  printf(“ priv: %d\n”, fmt.fmt.pix.priv);

  printf(“ raw_date: %s\n”, fmt.fmt.raw_data);

  // 恳求分配内存

  struct v4l2_requestbuffers reqbuf;

  reqbuf.count = BUFFER_COUNT;

  reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

  reqbuf.memory = V4L2_MEMORY_MMAP;

  ret = ioctl(fd , VIDIOC_REQBUFS, &reqbuf);

  if(ret 《 0) {

  printf(“VIDIOC_REQBUFS failed (%d)\n”, ret);

  return ret;

  }

  // 获取空间

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

  struct v4l2_buffer buf;

  for (i = 0; i 《 reqbuf.count; i++)

  {

  buf.index = i;

  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

  buf.memory = V4L2_MEMORY_MMAP;

  ret = ioctl(fd , VIDIOC_QUERYBUF, &buf);

  if(ret 《 0) {

  printf(“VIDIOC_QUERYBUF (%d) failed (%d)\n”, i, ret);

  return ret;

  }

  // mmap buffer

  framebuf[i].length = buf.length;

  framebuf[i].start = (char *) mmap(0, buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, buf.m.offset);

  if (framebuf[i].start == MAP_FAILED) {

  printf(“mmap (%d) failed: %s\n”, i, strerror(errno));

  return -1;

  }

  // Queen buffer

  ret = ioctl(fd , VIDIOC_QBUF, &buf);

  if (ret 《 0) {

  printf(“VIDIOC_QBUF (%d) failed (%d)\n”, i, ret);

  return -1;

  }

  printf(“Frame buffer %d: address=0x%x, length=%d\n”, i, (unsigned int)framebuf[i].start, framebuf[i].length);

  }

  // 开端录制

  enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

  ret = ioctl(fd, VIDIOC_STREAMON, &type);

  if (ret 《 0) {

  printf(“VIDIOC_STREAMON failed (%d)\n”, ret);

  return ret;

  }

  // Get frame

  ret = ioctl(fd, VIDIOC_DQBUF, &buf);

  if (ret 《 0) {

  printf(“VIDIOC_DQBUF failed (%d)\n”, ret);

  return ret;

  }

  // Process the frame

  FILE *fp = fopen(CAPTURE_FILE, “wb”);

  if (fp 《 0) {

  printf(“open frame data file failed\n”);

  return -1;

  }

  fwrite(framebuf[buf.index].start, 1, buf.length, fp);

  fclose(fp);

  printf(“Capture one frame saved in %s\n”, CAPTURE_FILE);

  // Re-queen buffer

  ret = ioctl(fd, VIDIOC_QBUF, &buf);

  if (ret 《 0) {

  printf(“VIDIOC_QBUF failed (%d)\n”, ret);

  return ret;

  }

  // Release the resource

  for (i=0; i《 4; i++)

  {

  munmap(framebuf[i].start, framebuf[i].length);

  }

  close(fd);

  printf(“Camera test Done.\n”);

  return 0;

  }

  ———————————————————————————————————–

  附件:

  void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

  int munmap(void *start, size_t length);

  参数阐明:

  ——start:映射区的开端地址。

  ——length:映射区的长度。

  ——prot:希望的内存保护标志,不能与文件的翻开形式抵触。是以下的某个值,能够经过or运算合理地组合在一同

  —PROT_EXEC //页内容能够被履行

  —PROT_READ //页内容能够被读取

  —PROT_WRITE //页能够被写入

  —PROT_NONE //页不行拜访

  ——flags:指定映射目标的类型,映射选项和映射页是否能够同享。它的值能够是一个或许多个以下位的组合体

  —MAP_FIXED //运用指定的映射开始地址,假如由start和len参数指定的内存区堆叠于现存的映射空间,堆叠部分将会被丢掉。假如指定的开始地址不行用,操作将会失利。而且开始地址有必要落在页的鸿沟上。

  —MAP_SHARED //与其它所有映射这个目标的进程同享映射空间。对同享区的写入,相当于输出到文件。直到msync()或许munmap()被调用,文件实践上不会被更新。

  —MAP_PRIVATE //树立一个写入时复制的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能运用其间一个。

  —MAP_DENYWRITE //这个标志被疏忽。

  —MAP_EXECUTABLE //同上

  —MAP_NORESERVE //不要为这个映射保存交流空间。当交流空间被保存,对映射区修正的可能会得到确保。当交流空间不被保存,一同内存不足,对映射区的修正会引起段违例信号。

  —MAP_LOCKED //定映射区的页面,然后避免页面被交流出内存。

  —MAP_GROWSDOWN //用于仓库,告知内核VM体系,映射区能够向下扩展。

  —MAP_ANONYMOUS //匿名映射,映射区不与任何文件相关。

  —MAP_ANON //MAP_ANONYMOUS的别称,不再被运用。

  —MAP_FILE //兼容标志,被疏忽。

  —MAP_32BIT //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被疏忽。当时这个标志只在x86-64渠道上得到支撑。

  —MAP_POPULATE //为文件映射经过预读的办法准备好页表。随后对映射区的拜访不会被页违例堵塞。

  —MAP_NONBLOCK //仅和MAP_POPULATE一同运用时才有含义。不履行预读,只为已存在于内存中的页面树立页表进口。

  ——fd:有用的文件描述词。假如MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1。

  ——offset:被映射目标内容的起点。

  回来值:

  成功履行时,mmap()回来被映射区的指针,munmap()回来0。

  失利时,mmap()回来MAP_FAILED[其值为(void *)-1],munmap回来-1。errno被设为以下的某个值。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/dianyuan/90144.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部