zoukankan      html  css  js  c++  java
  • 2019年12月4日Linux开发手记

    OK,经过昨天对V4L2工作流程的学习,现在已经大体了解了V4L2的工作原理,现在开始对V4L2的API的学习,目标:1、打开摄像头 2、储存图像 3、关闭摄像头,API网址:Linux Media Infrastructure userspace API — The Linux Kernel documentation https://linuxtv.org/downloads/v4l-dvb-apis/media_uapi.html

    具体流程如下:

    1、打开设备:

    static void open_device()

    {

    dev_name = "/dev/video0";

    fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);

    }

    2、初始化设备:

    static void init_device()

    介于初始化设备函数过于复杂,这里就直接copy官方例程中的init_device()函数,此处就不多做叙述了,例程:https://blog.csdn.net/snow_rain_1314/article/details/85072669 。在初始化设备时主要完成了对用户空间的获取,图像格式以及I/O模式的确定等,这里我们的I/O模式为内存映射即Memory Mapping(MMAP)模式。

    3、捕获图像:

    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");

    }

    4、主循环

    static void mainloop(void)

        {

              unsigned int count;

              count = frame_count;//帧数70

              while (count-- > 0) {

                     for (;;) {/*select()机制中提供一fd_set的数据结构,实际上是一long类型的数组,

                               每一个数组元素都能与一打开的文件句柄(不管是socket句柄,还是其他文件或命名管道或设备句柄)建立联系,

                               当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一socket或文件可读*/

                            fd_set fds; 

                              

                            struct timeval tv;//时间值

                            int r;

                            FD_ZERO(&fds); /*将set清零使集合中不含任何fd*/

                            FD_SET(fd, &fds);/*将fd加入set集合*/

                            /* Timeout. */

                            tv.tv_sec = 2; //2s

                            tv.tv_usec = 0;//0微妙

                            r = select(fd + 1, &fds, NULL, NULL, &tv);//select操作,用于确定一个或多个套接口的状态,返回满足条件的套接口的数目,最多只等待两秒,

                            if (-1 == r) { //所有描述符集清零

                                   if (EINTR == errno)//如果错误类型为4(中断系统呼叫)

                                          continue;

                                   errno_exit("select");

                            }

                            if (0 == r) { //超时

                                   fprintf(stderr, "select timeout\n");

                                   exit(EXIT_FAILURE);

                            }

                            if (read_frame())//读取帧

                                   break;

                            /* EAGAIN - continue select loop. */

                     }

              }

        }

    5、在主循环的过程中读取帧

    static int read_frame(void)

        {

       struct v4l2_buffer buf;//v4l2中临时缓冲器

         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:

                                 /* Could ignore EIO, see spec. */

                                 /* fall through */

                          default:

                                 errno_exit("VIDIOC_DQBUF");

                          }

                   }

                   assert(buf.index < n_buffers);//断言,索引小于缓冲区个数

                   process_image(buffers[buf.index].start, buf.bytesused);//进程映射

                   if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))

                          errno_exit("VIDIOC_QBUF");

    }

    6、读取帧时开始存储图像

    static void process_image(const void *p, int size)

        {

                FILE *fp = fopen("a.jpg","w");

               if (out_buf)

                  {

                      fwrite(p, size, 1, fp);

                        fclose(fp);

                      //printf("1");

                  }

               fflush(stderr);

               fprintf(stderr, "Can not open it! ");

               fflush(stdout);

        }

    7、停止捕获

    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;

               }

        }

    8、释放内存

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

        }

    9、关闭设备

    static void close_device(void)

        {

               if (-1 == close(fd))

                      errno_exit("close");

               fd = -1;

        }

    10、输出异常

    fprintf(stderr, "\n");

    使用V4L2打开摄像头获取图像的流程就是这样,下一步就是使用python调用这个程序,取代cv2.imwrite。

  • 相关阅读:
    『转』MySQL存储过程语法例子
    搭建mysql cluster
    C++ typedef 两种用法
    C++符号重载
    C++ int const 和 const int 的区别
    C++ 结构和类的区别
    正式开始jQuery源码的学习
    c# in deep 之Lambda表达式于LINQ表达式结合后令人惊叹的简洁(2)
    c# in deep 之Lambda表达式
    一道有意思的导论问题
  • 原文地址:https://www.cnblogs.com/lvfengkun/p/11985882.html
Copyright © 2011-2022 走看看