V4L2应用编程

来源:互联网 发布:这样开淘宝网店 编辑:程序博客网 时间:2024/05/16 05:11

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

操作流程

常用数据结构

structv4l2_requestbuffers   //申请帧缓冲,对应命令VIDIOC_REQBUFSstruct v4l2_capability     //视频设备的功能,对应命令VIDIOC_QUERYCAP structv4l2_input       //视频输入信息,对应命令VIDIOC_ENUMINPUTstruct v4l2_standard     //视频的制式,比如PAL,NTSC,对应命令VIDIOC_ENUMSTD structv4l2_format       //帧的格式,对应命令VIDIOC_G_FMT、VIDIOC_S_FMT等structv4l2_buffer      //驱动中的一帧图像缓存,对应命令VIDIOC_QUERYBUFstructv4l2_crop       //视频信号矩形边框v4l2_std_id          //视频制式


 

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。

 

struct v4l2_capability   {       __u8 driver[16];//驱动名        __u8 card[32];//例如Hauppauge winTV        __u8 bus_info[32];//PCI总线信息        __u32 version;//内核版本        __u32 capabilities;//设备能力        __u32 reserved[4];   };   


 

struct v4l2_format   {       enum v4l2_buf_type type;//本结构的数据类型    };


 

struct v4l2_pix_format   {       __u32   width;//宽度        __u32   height;//高度    } 


 

struct v4l2_requestbuffers   {       __u32   count;//缓存数量        enum v4l2_buf_type type;//数据流类型    }  


 

enum v4l2_memory{      };  


操作模块分析

(1)打开设备文件


                  int fd = open(Devicename,mode);
                  Devicename:/dev/video0、/dev/video1 ……
                  Mode:O_RDWR [| O_NONBLOCK]

                  如果使用非阻塞模式调用视频设备,则当没有可用的视频数据时,不会阻塞,而立刻返回。

(2)设备初始化

 

          (1)取得设备的capability

                    struct v4l2_capability capability;
                  int ret = ioctl(fd, VIDIOC_QUERYCAP, &capability);

                   看看设备具有什么功能,比如是否具有视频输入特性。

           (2)选择视频输入

                    struct v4l2_input input;
                  ……初始化input
                  int ret = ioctl(fd, VIDIOC_QUERYCAP, &input);

                  一个视频设备可以有多个视频输入。如果只有一路输入,这个功能可以没有。

            (3)检测视频支持的制式

                 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:
                                //……
                }

             (4)设置视频捕获格式

                          struct v4l2_format fmt;
                            fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
                            fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
                            fmt.fmt.pix.height = height;
                            fmt.fmt.pix.width = width;
                            fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
                            ret = ioctl(fd, VIDIOC_S_FMT, &fmt);
                            if(ret) {
                                      perror("VIDIOC_S_FMT\n");
                                      close(fd);
                              return -1;
                           }

                 (5)向驱动申请帧缓存

                             struct v4l2_requestbuffers req;
                               if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
                                      return -1;
                            }

                    v4l2_requestbuffers结构中定义了缓存的数量,驱动会据此申请对应数量的视频缓存。多个缓存可以用于建立FIFO,来提高视频采集的效率。

              (6)获取每个缓存的信息,并mmap到用户空间

                       typedef struct VideoBuffer {
                                   void *start;
                                     size_t length;
                              } VideoBuffer;

                      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) {//获取到对应index的缓存信息,此处主要利用length信息及offset信息来完成后面的mmap操作。
                                  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;
                          }

  操作系统一般把系统使用的内存划分成用户空间和内核空间,分别由应用程序管理和操作系统管理。应用程序可以直接访问内存的地址,而内核空间存放的是供内核访问的代码和数据,用户不能直接访问。v4l2捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地址。

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

1)使用readwrite方式:直接使用readwrite函数进行读写。这种方式最简单,但是这种方式会在用户空间和内核空间不断拷贝数据,同时在用户空间和内核空间占用大量内存,效率不高。

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

3)用户指针模式:内存由用户空间的应用程序分配,并把地址传递到内核中的驱动程序,然后由v4l2驱动程序直接将数据填充到用户空间的内存中。这点需要在v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR

第一种方式效率是最低的,后面两种方法都能提高执行的效率,但是对于mmap方式,文档中有这样一句描述 --Remember the buffers are allocated in physical memory, as opposed to virtual memory which can be swapped out to disk. Applications should free the buffers as soon as possible with the munmap () function .(使用mmap方法的时候,buffers相当于是在内核空间中分配的,这种情况下,这些buffer是不能被交换到虚拟内存中,虽然这种方法不怎么影响读写效率,但是它一直占用着内核空间中的内存,当系统的内存有限的时候,如果同时运行有大量的进程,则对系统的整体性能会有一定的影响。)

      所以,对于三种视频采集方式的选择,推荐的顺序是userptrmmapread-write。当使用 mmap userptr方式的时候,有一个环形缓冲队列的概念,这个队列中,有 n buffer,驱动程序采集到的视频帧数据,就是存储在每个 buffer中。在每次用VIDIOC_DQBUF取出一个buffer,并且处理完数据后,一定要用VIDIOC_QBUF将这个buffer再次放回到环形缓冲队列中。环形缓冲队列,也使得这两种视频采集方式的效率高于直接read/write

(3)视频采集

 

                 (1)开始采集

                   int buf_type= V4L2_BUF_TYPE_VIDEO_CAPTURE;
                  int ret = ioctl(fd, VIDIOC_STREAMON, &buf_type);

                 (2)取出FIFO缓存中已经采样的帧缓存

                      struct v4l2_buffer buf;
                  memset(&buf,0,sizeof(buf));
                  buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
                  buf.memory=V4L2_MEMORY_MMAP;
                  buf.index=0;//此值由下面的ioctl返回
                  if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1)
                  {
                          return -1;
                  }

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

                 (3)将刚刚处理完的缓冲重新入队列尾,这样可以循环采集

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

(4)采集停止

 

             int ret = ioctl(fd, VIDIOC_STREAMOFF, &buf_type);

(5)释放相关资源

          关闭视频设备

          close(fd);

实例代码

/*  *  V4L2 video capture example  *  *  This program can be used and distributed without restrictions.  */    #include <stdio.h>   #include <stdlib.h>   #include <string.h>   #include <assert.h>     #include <getopt.h>             /* getopt_long() */     #include <fcntl.h>              /* low-level i/o */   #include <unistd.h>   #include <errno.h>   #include <sys/stat.h>   #include <sys/types.h>   #include <sys/time.h>   #include <sys/mman.h>   #include <sys/ioctl.h>     #include <asm/types.h>          /* for videodev2.h */     #include <linux/videodev2.h>     #define CLEAR(x) memset (&(x), 0, sizeof (x))     typedef enum {      IO_METHOD_READ,      IO_METHOD_MMAP,      IO_METHOD_USERPTR,  } io_method;    struct buffer {          void *                  start;          size_t                  length;  };    static char *           dev_name        = NULL;  static io_method    io      = IO_METHOD_MMAP;  static int              fd              = -1;  struct buffer *         buffers         = NULL;  static unsigned int     n_buffers       = 0;    static void  errno_exit                      (const char *           s)  {          fprintf (stderr, "%s error %d, %s\n",                   s, errno, strerror (errno));            exit (EXIT_FAILURE);  }    static int  xioctl                          (int                    fd,                                   int                    request,                                   void *                 arg)  {          int r;            do r = ioctl (fd, request, arg);          while (-1 == r && EINTR == errno);            return r;  }    static void  process_image                   (const void *           p)  {          fputc ('.', stdout);          fflush (stdout);  }    static int  read_frame          (void)  {          struct v4l2_buffer buf;      unsigned int i;        switch (io) {      case IO_METHOD_READ:              if (-1 == read (fd, buffers[0].start, buffers[0].length)) {                      switch (errno) {                      case EAGAIN:                              return 0;                case EIO:                  /* Could ignore EIO, see spec. */                    /* fall through */                default:                  errno_exit ("read");              }          }                process_image (buffers[0].start);            break;        case IO_METHOD_MMAP:          CLEAR (buf);                    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;                  buf.memory = V4L2_MEMORY_MMAP;                if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {                      switch (errno) {                      case EAGAIN:                              return 0;                case EIO:                  /* Could ignore EIO, see spec. */                    /* fall through */                default:                  errno_exit ("VIDIOC_DQBUF");              }          }                    assert (buf.index < n_buffers);                process_image (buffers[buf.index].start);            if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))              errno_exit ("VIDIOC_QBUF");            break;        case IO_METHOD_USERPTR:          CLEAR (buf);                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;              buf.memory = V4L2_MEMORY_USERPTR;            if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {              switch (errno) {              case EAGAIN:                  return 0;                case EIO:                  /* Could ignore EIO, see spec. */                    /* fall through */                default:                  errno_exit ("VIDIOC_DQBUF");              }          }            for (i = 0; i < n_buffers; ++i)              if (buf.m.userptr == (unsigned long) buffers[i].start                  && buf.length == buffers[i].length)                  break;            assert (i < n_buffers);                process_image ((void *) buf.m.userptr);            if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))              errno_exit ("VIDIOC_QBUF");            break;      }        return 1;  }    static void  mainloop                        (void)  {      unsigned int count;            count = 100;            while (count-- > 0) {                  for (;;) {                          fd_set fds;                          struct timeval tv;                          int r;                            FD_ZERO (&fds);                          FD_SET (fd, &fds);                            /* Timeout. */                          tv.tv_sec = 2;                          tv.tv_usec = 0;                            r = select (fd + 1, &fds, NULL, NULL, &tv);                            if (-1 == r) {                                  if (EINTR == errno)                                          continue;                                    errno_exit ("select");                          }                            if (0 == r) {                                  fprintf (stderr, "select timeout\n");                                  exit (EXIT_FAILURE);                          }                if (read_frame ())                              break;                    /* EAGAIN - continue select loop. */                  }          }  }    static void  stop_capturing                  (void)  {          enum v4l2_buf_type type;        switch (io) {      case IO_METHOD_READ:          /* Nothing to do. */          break;        case IO_METHOD_MMAP:      case IO_METHOD_USERPTR:          type = V4L2_BUF_TYPE_VIDEO_CAPTURE;            if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type))              errno_exit ("VIDIOC_STREAMOFF");            break;      }  }    static void  start_capturing                 (void)  {          unsigned int i;          enum v4l2_buf_type type;        switch (io) {      case IO_METHOD_READ:          /* Nothing to do. */          break;        case IO_METHOD_MMAP:          for (i = 0; i < n_buffers; ++i) {                      struct v4l2_buffer buf;                    CLEAR (buf);                    buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;                  buf.memory      = V4L2_MEMORY_MMAP;                  buf.index       = i;                    if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))                              errno_exit ("VIDIOC_QBUF");          }                    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;            if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))              errno_exit ("VIDIOC_STREAMON");            break;        case IO_METHOD_USERPTR:          for (i = 0; i < n_buffers; ++i) {                      struct v4l2_buffer buf;                    CLEAR (buf);                    buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;                  buf.memory      = V4L2_MEMORY_USERPTR;              buf.index       = i;              buf.m.userptr   = (unsigned long) buffers[i].start;              buf.length      = buffers[i].length;                if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))                              errno_exit ("VIDIOC_QBUF");          }            type = V4L2_BUF_TYPE_VIDEO_CAPTURE;            if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))              errno_exit ("VIDIOC_STREAMON");            break;      }  }    static void  uninit_device                   (void)  {          unsigned int i;        switch (io) {      case IO_METHOD_READ:          free (buffers[0].start);          break;        case IO_METHOD_MMAP:          for (i = 0; i < n_buffers; ++i)              if (-1 == munmap (buffers[i].start, buffers[i].length))                  errno_exit ("munmap");          break;        case IO_METHOD_USERPTR:          for (i = 0; i < n_buffers; ++i)              free (buffers[i].start);          break;      }        free (buffers);  }    static void  init_read           (unsigned int       buffer_size)  {          buffers = calloc (1, sizeof (*buffers));            if (!buffers) {                  fprintf (stderr, "Out of memory\n");                  exit (EXIT_FAILURE);          }        buffers[0].length = buffer_size;      buffers[0].start = malloc (buffer_size);        if (!buffers[0].start) {              fprintf (stderr, "Out of memory\n");                  exit (EXIT_FAILURE);      }  }    static void  init_mmap           (void)  {      struct v4l2_requestbuffers req;            CLEAR (req);            req.count               = 4;          req.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;          req.memory              = V4L2_MEMORY_MMAP;        if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) {                  if (EINVAL == errno) {                          fprintf (stderr, "%s does not support "                                   "memory mapping\n", dev_name);                          exit (EXIT_FAILURE);                  } else {                          errno_exit ("VIDIOC_REQBUFS");                  }          }            if (req.count < 2) {                  fprintf (stderr, "Insufficient buffer memory on %s\n",                           dev_name);                  exit (EXIT_FAILURE);          }            buffers = calloc (req.count, sizeof (*buffers));            if (!buffers) {                  fprintf (stderr, "Out of memory\n");                  exit (EXIT_FAILURE);          }            for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {                  struct v4l2_buffer buf;                    CLEAR (buf);                    buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;                  buf.memory      = V4L2_MEMORY_MMAP;                  buf.index       = n_buffers;                    if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf))                          errno_exit ("VIDIOC_QUERYBUF");                    buffers[n_buffers].length = buf.length;                  buffers[n_buffers].start =                          mmap (NULL /* start anywhere */,                                buf.length,                                PROT_READ | PROT_WRITE /* required */,                                MAP_SHARED /* recommended */,                                fd, buf.m.offset);                    if (MAP_FAILED == buffers[n_buffers].start)                          errno_exit ("mmap");          }  }    static void  init_userp          (unsigned int       buffer_size)  {      struct v4l2_requestbuffers req;            CLEAR (req);            req.count               = 4;          req.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;          req.memory              = V4L2_MEMORY_USERPTR;            if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) {                  if (EINVAL == errno) {                          fprintf (stderr, "%s does not support "                                   "user pointer i/o\n", dev_name);                          exit (EXIT_FAILURE);                  } else {                          errno_exit ("VIDIOC_REQBUFS");                  }          }            buffers = calloc (4, sizeof (*buffers));            if (!buffers) {                  fprintf (stderr, "Out of memory\n");                  exit (EXIT_FAILURE);          }            for (n_buffers = 0; n_buffers < 4; ++n_buffers) {                  buffers[n_buffers].length = buffer_size;                  buffers[n_buffers].start = malloc (buffer_size);                    if (!buffers[n_buffers].start) {                  fprintf (stderr, "Out of memory\n");                      exit (EXIT_FAILURE);          }          }  }    static void  init_device                     (void)  {          struct v4l2_capability cap;          struct v4l2_cropcap cropcap;          struct v4l2_crop crop;          struct v4l2_format fmt;      unsigned int min;            if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) {                  if (EINVAL == errno) {                          fprintf (stderr, "%s is no V4L2 device\n",                                   dev_name);                          exit (EXIT_FAILURE);                  } else {                          errno_exit ("VIDIOC_QUERYCAP");                  }          }            if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {                  fprintf (stderr, "%s is no video capture device\n",                           dev_name);                  exit (EXIT_FAILURE);          }        switch (io) {      case IO_METHOD_READ:          if (!(cap.capabilities & V4L2_CAP_READWRITE)) {              fprintf (stderr, "%s does not support read i/o\n",                   dev_name);              exit (EXIT_FAILURE);          }            break;        case IO_METHOD_MMAP:      case IO_METHOD_USERPTR:          if (!(cap.capabilities & V4L2_CAP_STREAMING)) {              fprintf (stderr, "%s does not support streaming i/o\n",                   dev_name);              exit (EXIT_FAILURE);          }            break;      }              /* Select video input, video standard and tune here. */          CLEAR (cropcap);            cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;            if (0 == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) {                  crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;                  crop.c = cropcap.defrect; /* reset to default */                    if (-1 == xioctl (fd, VIDIOC_S_CROP, &crop)) {                          switch (errno) {                          case EINVAL:                                  /* Cropping not supported. */                                  break;                          default:                                  /* Errors ignored. */                                  break;                          }                  }          } else {                      /* Errors ignored. */          }              CLEAR (fmt);            fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;          fmt.fmt.pix.width       = 640;           fmt.fmt.pix.height      = 480;          fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;          fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;            if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt))                  errno_exit ("VIDIOC_S_FMT");            /* Note VIDIOC_S_FMT may change width and height. */        /* Buggy driver paranoia. */      min = fmt.fmt.pix.width * 2;      if (fmt.fmt.pix.bytesperline < min)          fmt.fmt.pix.bytesperline = min;      min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;      if (fmt.fmt.pix.sizeimage < min)          fmt.fmt.pix.sizeimage = min;        switch (io) {      case IO_METHOD_READ:          init_read (fmt.fmt.pix.sizeimage);          break;        case IO_METHOD_MMAP:          init_mmap ();          break;        case IO_METHOD_USERPTR:          init_userp (fmt.fmt.pix.sizeimage);          break;      }  }    static void  close_device                    (void)  {          if (-1 == close (fd))              errno_exit ("close");            fd = -1;  }    static void  open_device                     (void)  {          struct stat st;             if (-1 == stat (dev_name, &st)) {                  fprintf (stderr, "Cannot identify '%s': %d, %s\n",                           dev_name, errno, strerror (errno));                  exit (EXIT_FAILURE);          }            if (!S_ISCHR (st.st_mode)) {                  fprintf (stderr, "%s is no device\n", dev_name);                  exit (EXIT_FAILURE);          }            fd = open (dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);            if (-1 == fd) {                  fprintf (stderr, "Cannot open '%s': %d, %s\n",                           dev_name, errno, strerror (errno));                  exit (EXIT_FAILURE);          }  }    static void  usage                           (FILE *                 fp,                                   int                    argc,                                   char **                argv)  {          fprintf (fp,                   "Usage: %s [options]\n\n"                   "Options:\n"                   "-d | --device name   Video device name [/dev/video]\n"                   "-h | --help          Print this message\n"                   "-m | --mmap          Use memory mapped buffers\n"                   "-r | --read          Use read() calls\n"                   "-u | --userp         Use application allocated buffers\n"                   "",           argv[0]);  }    static const char short_options [] = "d:hmru";    static const struct option  long_options [] = {          { "device",     required_argument,      NULL,           'd' },          { "help",       no_argument,            NULL,           'h' },          { "mmap",       no_argument,            NULL,           'm' },          { "read",       no_argument,            NULL,           'r' },          { "userp",      no_argument,            NULL,           'u' },          { 0, 0, 0, 0 }  };    int  main                            (int                    argc,                                   char **                argv)  {          dev_name = "/dev/video";            for (;;) {                  int index;                  int c;                                    c = getopt_long (argc, argv,                                   short_options, long_options,                                   &index);                    if (-1 == c)                          break;                    switch (c) {                  case 0: /* getopt_long() flag */                          break;                    case 'd':                          dev_name = optarg;                          break;                    case 'h':                          usage (stdout, argc, argv);                          exit (EXIT_SUCCESS);                    case 'm':                          io = IO_METHOD_MMAP;              break;                    case 'r':                          io = IO_METHOD_READ;              break;                    case 'u':                          io = IO_METHOD_USERPTR;              break;                    default:                          usage (stderr, argc, argv);                          exit (EXIT_FAILURE);                  }          }            open_device ();            init_device ();            start_capturing ();            mainloop ();            stop_capturing ();            uninit_device ();            close_device ();            exit (EXIT_SUCCESS);            return 0;  }  


others

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h>#include <getopt.h>  #include <fcntl.h>  #include <unistd.h>#include <errno.h>#include <sys/stat.h>#include <sys/types.h>#include <sys/time.h>#include <sys/mman.h>#include <sys/ioctl.h>#include <asm/types.h>#include <linux/videodev2.h>#include <linux/fb.h>#define CLEAR(x) memset (&(x), 0, sizeof (x)) struct buffer {    void * start;    size_t length;}; static char * dev_name = NULL;static int fd = -1;struct buffer * buffers = NULL;static unsigned int n_buffers = 0;static int time_in_sec_capture=5;static int fbfd = -1;static struct fb_var_screeninfo vinfo;static struct fb_fix_screeninfo finfo;static char *fbp=NULL;static long screensize=0; static void errno_exit (const char * s){    fprintf (stderr, "%s error %d, %s/n",s, errno, strerror (errno));    exit (EXIT_FAILURE);} static int xioctl (int fd,int request,void * arg){    int r;    do r = ioctl (fd, request, arg);    while (-1 == r && EINTR == errno);    return r;} inline int clip(int value, int min, int max) {    return (value > max ? max : value < min ? min : value);  } static void process_image (const void * p){         //ConvertYUVToRGB32    //1;    unsigned char* in=(char*)p;    int width=640;    int height=480;    int istride=1280;    int x,y,j;    int y0,u,y1,v,r,g,b;    long location=0;     for ( y = 100; y < height + 100; ++y) {        for (j = 0, x=100; j < width * 2 ; j += 4,x +=2) {          location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +            (y+vinfo.yoffset) * finfo.line_length;                      y0 = in[j];          u = in[j + 1] - 128;                          y1 = in[j + 2];                  v = in[j + 3] - 128;                   r = (298 * y0 + 409 * v + 128) >> 8;          g = (298 * y0 - 100 * u - 208 * v + 128) >> 8;          b = (298 * y0 + 516 * u + 128) >> 8;                  fbp[ location + 0] = clip(b, 0, 255);          fbp[ location + 1] = clip(g, 0, 255);          fbp[ location + 2] = clip(r, 0, 255);              fbp[ location + 3] = 255;               r = (298 * y1 + 409 * v + 128) >> 8;          g = (298 * y1 - 100 * u - 208 * v + 128) >> 8;          b = (298 * y1 + 516 * u + 128) >> 8;            fbp[ location + 4] = clip(b, 0, 255);          fbp[ location + 5] = clip(g, 0, 255);          fbp[ location + 6] = clip(r, 0, 255);              fbp[ location + 7] = 255;              }        in +=istride;       }} static int read_frame (void){    struct v4l2_buffer buf;    unsigned int i;     CLEAR (buf);    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;    buf.memory = V4L2_MEMORY_MMAP;     if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {        switch (errno) {        case EAGAIN:        return 0;        case EIO:                      default:            errno_exit ("VIDIOC_DQBUF");        }    }     assert (buf.index < n_buffers);    printf("v4l2_pix_format->field(%d)/n", buf.field);    //assert (buf.field ==V4L2_FIELD_NONE);    process_image (buffers[buf.index].start);    if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))        errno_exit ("VIDIOC_QBUF");     return 1;} static void run (void){    unsigned int count;    int frames;    frames = 30 * time_in_sec_capture;     while (frames-- > 0) {        for (;;) {            fd_set fds;            struct timeval tv;            int r;            FD_ZERO (&fds);            FD_SET (fd, &fds);                         tv.tv_sec = 2;            tv.tv_usec = 0;             r = select (fd + 1, &fds, NULL, NULL, &tv);             if (-1 == r) {                if (EINTR == errno)                    continue;                errno_exit ("select");            }             if (0 == r) {                fprintf (stderr, "select timeout/n");                exit (EXIT_FAILURE);            }             if (read_frame ())                break;                        }    }} static void stop_capturing (void){    enum v4l2_buf_type type;     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;    if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type))        errno_exit ("VIDIOC_STREAMOFF");} static void start_capturing (void){    unsigned int i;    enum v4l2_buf_type type;     for (i = 0; i < n_buffers; ++i) {        struct v4l2_buffer buf;        CLEAR (buf);         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;        buf.memory = V4L2_MEMORY_MMAP;        buf.index = i;         if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))            errno_exit ("VIDIOC_QBUF");        }     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;     if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))        errno_exit ("VIDIOC_STREAMON");    } static void uninit_device (void){    unsigned int i;     for (i = 0; i < n_buffers; ++i)        if (-1 == munmap (buffers[i].start, buffers[i].length))            errno_exit ("munmap");        if (-1 == munmap(fbp, screensize)) {          printf(" Error: framebuffer device munmap() failed./n");          exit (EXIT_FAILURE) ;        }        free (buffers);}  static void init_mmap (void){    struct v4l2_requestbuffers req;     //mmap framebuffer        fbp = (char *)mmap(NULL,screensize,PROT_READ | PROT_WRITE,MAP_SHARED ,fbfd, 0);        if ((int)fbp == -1) {            printf("Error: failed to map framebuffer device to memory./n");            exit (EXIT_FAILURE) ;        }    memset(fbp, 0, screensize);    CLEAR (req);     req.count = 4;    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;    req.memory = V4L2_MEMORY_MMAP;     if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) {        if (EINVAL == errno) {            fprintf (stderr, "%s does not support memory mapping/n", dev_name);            exit (EXIT_FAILURE);        } else {            errno_exit ("VIDIOC_REQBUFS");        }    }     if (req.count < 4) {    //if (req.count < 2)        fprintf (stderr, "Insufficient buffer memory on %s/n",dev_name);        exit (EXIT_FAILURE);    }     buffers = calloc (req.count, sizeof (*buffers));     if (!buffers) {        fprintf (stderr, "Out of memory/n");        exit (EXIT_FAILURE);    }     for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {        struct v4l2_buffer buf;         CLEAR (buf);         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;        buf.memory = V4L2_MEMORY_MMAP;        buf.index = n_buffers;         if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf))            errno_exit ("VIDIOC_QUERYBUF");         buffers[n_buffers].length = buf.length;        buffers[n_buffers].start =mmap (NULL,buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd, buf.m.offset);         if (MAP_FAILED == buffers[n_buffers].start)            errno_exit ("mmap");    } }   static void init_device (void){    struct v4l2_capability cap;    struct v4l2_cropcap cropcap;    struct v4l2_crop crop;    struct v4l2_format fmt;    unsigned int min;      // Get fixed screen information      if (-1==xioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {            printf("Error reading fixed information./n");            exit (EXIT_FAILURE);        }         // Get variable screen information     if (-1==xioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {            printf("Error reading variable information./n");            exit (EXIT_FAILURE);       }    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;      if (-1 == xioctl (fd, VIDIOC_QUERYCAP, ∩)) {        if (EINVAL == errno) {            fprintf (stderr, "%s is no V4L2 device/n",dev_name);            exit (EXIT_FAILURE);        } else {            errno_exit ("VIDIOC_QUERYCAP");          }    }     if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {        fprintf (stderr, "%s is no video capture device/n",dev_name);        exit (EXIT_FAILURE);    }     if (!(cap.capabilities & V4L2_CAP_STREAMING)) {        fprintf (stderr, "%s does not support streaming i/o/n",dev_name);        exit (EXIT_FAILURE);    }          CLEAR (cropcap);     cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;     if (0 == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) {        crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;        crop.c = cropcap.defrect;         if (-1 == xioctl (fd, VIDIOC_S_CROP, &crop)) {            switch (errno) {            case EINVAL:                break;            default:            break;            }        }    }else {     }     CLEAR (fmt);     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;    fmt.fmt.pix.width = 640;      fmt.fmt.pix.height = 480;    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;     if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt))        errno_exit ("VIDIOC_S_FMT");     init_mmap (); } static void close_device (void){    if (-1 == close (fd))    errno_exit ("close");    fd = -1;    close(fbfd);} static void open_device (void){    struct stat st;       if (-1 == stat (dev_name, &st)) {    fprintf (stderr, "Cannot identify '%s': %d, %s/n",dev_name, errno, strerror (errno));    exit (EXIT_FAILURE);    }     if (!S_ISCHR (st.st_mode)) {    fprintf (stderr, "%s is no device/n", dev_name);    exit (EXIT_FAILURE);    }     //open framebuffer        fbfd = open("/dev/fb0", O_RDWR);        if (fbfd==-1) {            printf("Error: cannot open framebuffer device./n");            exit (EXIT_FAILURE);        }     //open camera    fd = open (dev_name, O_RDWR| O_NONBLOCK, 0);     if (-1 == fd) {    fprintf (stderr, "Cannot open '%s': %d, %s/n",dev_name, errno, strerror (errno));    exit (EXIT_FAILURE);    }} static void usage (FILE * fp,int argc,char ** argv){fprintf (fp,"Usage: %s [options]/n/n""Options:/n""-d | --device name Video device name [/dev/video]/n""-h | --help Print this message/n""-t | --how long will display in seconds/n""",argv[0]);} static const char short_options [] = "d:ht:";static const struct option long_options [] = {{ "device", required_argument, NULL, 'd' },{ "help", no_argument, NULL, 'h' },{ "time", no_argument, NULL, 't' },{ 0, 0, 0, 0 }}; int main (int argc,char ** argv)    {    dev_name = "/dev/video0";     for (;;)          {        int index;        int c;         c = getopt_long (argc, argv,short_options, long_options,&index);        if (-1 == c)        break;         switch (c) {        case 0:        break;         case 'd':        dev_name = optarg;        break;         case 'h':        usage (stdout, argc, argv);        exit (EXIT_SUCCESS);        case 't':           time_in_sec_capture = atoi(optarg);             break;         default:        usage (stderr, argc, argv);        exit (EXIT_FAILURE);        }}     open_device ();     init_device ();     start_capturing ();     run ();     stop_capturing ();     uninit_device ();     close_device ();     exit (EXIT_SUCCESS); return 0;}
#ifndef _V4L_H#define _V4L_H#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <error.h>#include <assert.h>#include <fcntl.h>#include <sys/ioctl.h>#include <sys/types.h>#include <sys/mman.h>#include <linux/videodev.h>#include <sys/types.h>#include <string.h>/*采集的图像的最大长和宽*/#define MAX_WIDTH 400#define MAX_HEIGHT 300/*设备文件*/#define DEFAULT_DEVICE "/dev/video0"/*自定义数据结构,包含v4l 中用到的数据结构*/typedef struct v4l_struct{    int fd;/*设备号*/    struct video_capability capability; //包含设备的基本信息(设备名称、支持的最大最小分辨率、信号源信息等)    struct video_channel channel[8];//信号源个数    struct video_picture picture;//设备采集的图象的各种属性    struct video_mmap mmap;//用于mmap             struct video_mbuf mbuf;//利用mmap进行映射的帧的信息                unsigned char *buffer ;/*图像数据存放区*/    unsigned char *map;/*mmap方式获取数据时,数据的首地址*/    int frame_current;    int frame_using[2]; /*这个帧的状态0 表示可用,1表示不可用*/}v4l_device;/*************************************************************** 函数名:v4l_open* 功  能: 打开设备* 输  入: dev,vd* 输  出: 无* 返  回:  -1----失败  0----成功**************************************************************/int v4l_open( char *dev, v4l_device *vd ){    if( !dev )    {        dev=DEFAULT_DEVICE ;    }    if( ( vd->fd = open( dev, O_RDWR ) )  < 0 )    {        perror( "v4l_open error" );        return -1;    }    return 0;}/*************************************************************** 函数名: v4l_get_capability* 功  能: 获取设备属性* 输  入: vd* 输  出: 无* 返  回:  -1----失败 0----成功**************************************************************/int v4l_get_capability( v4l_device *vd ){    if( ioctl( vd->fd, VIDIOCGCAP, &( vd->capability ) ) <0 )    {        perror( "v4l_get_capability" );        return -1 ;    }    return 0; }/**************************************************************** 函数名:v4l_get_picture* 功  能:获取图片属性* 输  入: vd* 输  出: 无* 返  回:  -1----失败  0----成功***************************************************************/int v4l_get_picture( v4l_device *vd ){    if( ioctl( vd->fd,VIDIOCGPICT,&( vd->picture ) ) < 0 )    {        return -1;    }    return 0;}/*************************************************************** 函数名: v4l_set_picture* 功  能: 设置图片属性* 输  入: vd* 输  出: 无* 返  回: -1----失败 0----成功**************************************************************/int v4l_set_picture( v4l_device *vd ) {    if( ioctl( vd->fd, VIDIOCSPICT, &( vd->picture ) ) < 0 )    {        return -1;    }    return 0;}/************************************************************** 函数名:v4l_get_channels* 功  能:获取通道信息* 输  入: vd* 输  出: 无* 返  回:  -1----失败 0----成功*************************************************************/int v4l_get_channels( v4l_device *vd ){    int i;    for( i=0;i < vd->capability.channels ; i++ )    {        vd->channel[i].channel = i;               //确定通道        if( ioctl( vd->fd , VIDIOCGCHAN, &( vd->channel[i] ) ) <0 )        {            perror( "v4l_get_channel" );            return -1;        }    }    return 0;}/************************************************************** 函数名: v4l_get_mbuf* 功  能: 获取内存映射信息* 输  入: vd* 输  出: 无* 返  回:  -1----失败 0----成功**************************************************************/int v4l_get_mbuf( v4l_device *vd ){    if( ioctl ( vd->fd,VIDIOCGMBUF,&( vd->mbuf ) ) <0 )    {        perror( "get_mbuf:" );        return -1;    }    if ( ( vd->map = ( unsigned char * )mmap( 0, vd->mbuf.size, PROT_READ | PROT_WRITE, MAP_SHARED, vd->fd, 0 ) ) < 0 )    {        perror("v4l_mmap_init:mmap");        return -1;    }    return 0 ;}/************************************************************** 函数名: v4l_init_mbuff* 功  能: 初始化内存映射信息* 输  入: vd* 输  出: 无* 返  回: 0----成功**************************************************************/int v4l_init_mbuf(v4l_device *vd){    //vd->mmap.frame = 10 ; //不懂双帧是怎样设置的这个frame 该是当前帧的可mbuf 以又没有设置怎么确定是双帧不是单帧还是更多    vd->mmap.width = MAX_WIDTH;    vd->mmap.height = MAX_HEIGHT;    vd->mmap.format = vd->picture.palette;    vd->frame_current = 0;    vd->frame_using[0] = 0;    vd->frame_using[1] = 0;    return 0;}/*************************************************************** 函数名: v4l_get_address* 功  能: 获取数据在图像的地址***************************************************************/unsigned char *v4l_get_address(v4l_device *vd){    return (vd->map + vd->mbuf.offsets[vd->frame_current]);}/************************************************************** 函数名: v4l_grab_frame* 功  能: 捕获帧**************************************************************/int v4l_grab_frame(v4l_device *vd, int frame){    if (vd->frame_using[frame])     {        fprintf(stderr, "v4l_grab_frame: frame %d is already used./n", frame);        return -1;    }    vd->mmap.frame = frame;    if ( ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap ) ) < 0 )     {        perror( "v4l_grab_frame" );        return -1;    }    vd->frame_using[frame] = 1;    vd->frame_current = frame;    return 0;    }/*************************************************************** 函数名: v4l_grab_sync* 功  能:与内存映射捕获一致**************************************************************/int v4l_grab_sync(v4l_device *vd){      if (ioctl(vd->fd, VIDIOCSYNC, &(vd->frame_current)) < 0)     {        perror("v4l_grab_sync");    }    vd->frame_using[vd->frame_current] = 0;    return 0;}/**************************************************************** 函数名: v4l_munmap* 功  能:停止内存映射***************************************************************/int v4l_munmap( v4l_device *vd ){    if ( munmap( vd->map, vd->mbuf.size ) < 0 )     {        perror( "v4lmunmap:munmap" );        return -1;    }    return 0;}/**************************************************************** 函数名: v4l_close* 功  能:关闭设备***************************************************************/int v4l_close(v4l_device *vd){    close(vd->fd);    return 0;}#endif//简单的封装了关于SDL的相关操作"ScreenSurface.h"#ifndef SCREEN_SURFACE_H#define SCREEN_SURFACE_H#include <stdio.h>#include <stdlib.h>#include <SDL/SDL.h>class ScreenSurface {public:    ScreenSurface();    bool screen_init(int w, int h, int b = 0, Uint32 f = 0);//初始化    ~ScreenSurface();    SDL_Surface* point() const;    int screen_lock();    void screen_unlock();    void screen_quit();    void screen_set_caption( const char *str );//设置标题    bool flip( unsigned char * src) ;//显示    int startTV();//开始采集private:    static int screenNum;    int width;    int height;    int bpp;    Uint32 flags;    SDL_Surface* pScreen;};#endif//ScreenSurface.cpp#include "ScreenSurface.h"#include "qt_v4l.h"v4l_device  v4l_dev;/*************************************************************** 函数名: v4l_grab_movie* 功  能:捕获连续图像**************************************************************/void  v4l_grab_movie(){    v4l_grab_frame(&v4l_dev, v4l_dev.frame_current);/*获取下一 帧*/    v4l_grab_sync(&v4l_dev);/*等待传完一 帧*/    v4l_dev.buffer = v4l_get_address(&v4l_dev);/*得到这一帧的地址*/    v4l_dev.frame_current = (v4l_dev.frame_current+1)%2; /* 下一帧的frame*/}//构造函数。如果创建1个以上的screen surface,则会抛出异常ScreenSurface::ScreenSurface():width(640), height(480), bpp(32), flags(0){     pScreen = 0;    v4l_open(DEFAULT_DEVICE, &v4l_dev);/*打开设备*/    v4l_get_capability(&v4l_dev);    v4l_get_picture(&v4l_dev);    v4l_init_mbuf(&v4l_dev);/*初始化设备*/    v4l_get_mbuf(&v4l_dev);/*内存映射*/}bool ScreenSurface::screen_init(int w, int h, int b, Uint32 f){    width = w ;    height = h ;    bpp = b ;    flags = f ;        if(SDL_Init(SDL_INIT_VIDEO) < 0)    {        printf("SDL_Init Failed!/n");        return false;    }    //设置图象模式(宽*高 位数 标志 SDL_SWSURFACE | SDL_DOUBLEBUF)    pScreen = SDL_SetVideoMode(width, height, bpp, flags);    if ( pScreen == 0 )    {        printf("Could't set display mode /n");        SDL_Quit();        return false;    }    SDL_ShowCursor(SDL_DISABLE);    return true;}//析构函数。在对象消亡时,退出SDL系统。ScreenSurface::~ScreenSurface(){    }//返回screen surface中SDL_Surface结构的指针,主要提供给SDL的函数调用SDL_Surface* ScreenSurface::point() const{    return pScreen;}int ScreenSurface::screen_lock(){    if ( SDL_MUSTLOCK(pScreen))        return SDL_LockSurface(pScreen);    return 0;}void ScreenSurface::screen_unlock(){    if ( SDL_MUSTLOCK(pScreen))        SDL_UnlockSurface(pScreen); }void ScreenSurface::screen_quit(){    SDL_Quit();    v4l_munmap(&v4l_dev) ;    v4l_close(&v4l_dev); }void ScreenSurface::screen_set_caption( const char *str ){    SDL_WM_SetCaption( str, 0 );}//显示(弹出flip)screen surface到屏幕上bool ScreenSurface::flip( unsigned char * src ){    if ( screen_lock() < 0)        return false;    unsigned char  *dest;    dest = ( unsigned char * )pScreen->pixels;    memcpy( dest , src , width * height * 4 );    screen_unlock();        if ( SDL_Flip(pScreen) < 0 )        return false;    else         return true;   }int ScreenSurface::startTV(){    bool bFlag = true;    while(bFlag)    {        v4l_grab_movie();         unsigned char *buf= v4l_dev.buffer;        if (buf != NULL)        {            flip(buf);        }        SDL_Event event;        while(SDL_PollEvent(event))        {            if (event.type == SDL_QUIT)            {                //bFlag = false;                screen_quit();            }        }    }}//main.cpp#include "ScreenSurface.h"void main(){    ScreenSurface *m_pScreen;    m_pScreen = new ScreenSurface( );    m_pScreen->screen_init(400 , 300 , 32 , SDL_SWSURFACE | SDL_ANYFORMAT);    m_pScreen->screen_set_caption("DemoTV");       m_pScreen->startTV();}



 

 

 

原创粉丝点击