/*
* 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 <malloc.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>
#include <opencv2/opencv.hpp>

#include <jpeglib.h>
#include <iostream>
#include <boost/thread/thread.hpp>
#define CLEAR(x) memset (&(x), 0, sizeof (x))

#define Img_WIDTH 1504
#define Img_HEIGHT 1504
#define YUYV    //YUYV  MJEGP
#define ShowImg false //YUYV  MJEGP
#define FrameRate 30

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);// ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的调用个数如下
  while (-1 == r && EINTR == errno);
  return r;
}
static void process_image(const void * p)
{
  fputc('.',stdout);
  fputc('.',stdout);
  fflush(stdout);
}
double t_old; 
static void process_image(const void * p,int length)
{
  // std::cout<<"length = "<<length<<std::endl;  
  double t = ((double)cv::getTickCount()-t_old)/cv::getTickFrequency();
  // std::cout<<" t = "<<t*1000<<" ms"<<std::endl;
  t_old = (double)cv::getTickCount();

#ifdef YUYV
  cv::Mat M(Img_HEIGHT,Img_WIDTH,CV_8U); 
  uchar *data = (uchar*)M.data;
  for(int i=0;i<Img_HEIGHT*Img_WIDTH;i++)
  {
      data[i] = *((uchar*)p+2*i);
  } 
  if(ShowImg)
  {
    cv::imshow("img_gray",M);
    cv::waitKey(1);
  }
#endif
#ifdef MJPEG
  cv::Mat M(Img_HEIGHT,Img_WIDTH*2,CV_8U); 
  uchar *data = (uchar*)M.data;
  for(int i=0;i<Img_HEIGHT*Img_WIDTH*2;i++)
  {
    data[i] = *((uchar*)p+i);
  } 
  cv::Mat A = cv::imdecode(M,cv::IMREAD_GRAYSCALE);
  cv::imshow("img_gray",A);
  cv::waitKey(1);
  if(ShowImg)
  {
    cv::imshow("img_gray",M);
    cv::waitKey(1);
  }
#endif
}

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,buffers[0].length);
      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,buffers[buf.index].length);
      {
        FILE *fp = NULL;
        // fp = fopen("/home/huajun/hj/test.jpg", "w");
        if (fp != NULL)
        {
                // fwrite(buffers[buf.index].start, 1, buffers[buf.index].length, fp);
                sync();
                // fclose(fp);
        }
      }

      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 (1) {
    for (;;) {
      fd_set fds;
      struct timeval tv;
      int r;
      FD_ZERO (&fds); //将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。
      FD_SET (fd, &fds); //用于在文件描述符集合中增加一个新的文件描述符。 

      // std::cout<<" fd = "<<fd<<std::endl;
      /* Timeout. */
      tv.tv_sec = 4;
      tv.tv_usec = 0;
      // t_old = (double)cv::getTickCount();
      r = select (fd + 1, &fds, NULL, NULL, &tv); //用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型
      // double t = ((double)cv::getTickCount()-t_old)/cv::getTickFrequency();
      // std::cout<<" t = "<<t*1000<<" ms"<<std::endl;
      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.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 = (buffer*)calloc (1, sizeof (*buffers));//内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针
  if (!buffers) {
    fprintf (stderr, "Out of memory\n");
    exit (EXIT_FAILURE);
  }
  buffers[0].length = buffer_size;
  buffers[0].start = malloc (buffer_size); //malloc 向系统申请分配指定size个字节的内存空间。返回类型是 void* 类型
  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 = (buffer*)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_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 = (buffer*)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)) { //使用ioctl VIDIOC_QUERYCAP 来查询当前driver是否合乎规范 因为V4L2要求所有driver 和Device都支持这个Ioctl
    if (EINVAL == errno) {  /* Invalid argument */
      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 */  //c是表示采集窗口的大小的结构体,
    if (-1 == xioctl (fd, VIDIOC_S_CROP, &crop)) {
      switch (errno) {
        case EINVAL:
        /* Cropping not supported. */
        break;
        default:
        /* Errors ignored. */
        break;
      }
    }
  } 
  else {
    /* Errors ignored. */
  }

  int ret;
  struct v4l2_fmtdesc fmt_my;
  memset(&fmt_my, 0, sizeof(fmt_my));
  fmt_my.index = 0;
  fmt_my.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  while ((ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmt_my)) == 0) {  //查询支持的格式
    printf("{ pixelformat = '%c%c%c%c', description = '%s' }\n",
        fmt_my.pixelformat & 0xFF, (fmt_my.pixelformat >> 8) & 0xFF,
        (fmt_my.pixelformat >> 16) & 0xFF, (fmt_my.pixelformat >> 24) & 0xFF,
        fmt_my.description);
        fmt_my.index++;
   }

  CLEAR (fmt);
  fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  fmt.fmt.pix.width = Img_WIDTH;
  fmt.fmt.pix.height = Img_HEIGHT;
  #ifdef YUYV
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_MJPEG;//
  #endif
  #ifdef MJPEG
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;//V4L2_PIX_FMT_YUYV;//
  #endif
  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;



    struct v4l2_streamparm Stream_Parm;
    memset(&Stream_Parm, 0, sizeof(struct v4l2_streamparm));  
    Stream_Parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
    Stream_Parm.parm.capture.timeperframe.denominator =FrameRate; //分母
    Stream_Parm.parm.capture.timeperframe.numerator = 1;  //分子
    if(-1 == xioctl(fd, VIDIOC_S_PARM, &Stream_Parm))  //设置帧数
    {
      printf("Set frame rate failed\n");
    }
    memset(&Stream_Parm, 0, sizeof(struct v4l2_streamparm));  
    Stream_Parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
    if(-1 == xioctl(fd, VIDIOC_G_PARM, &Stream_Parm))  //获取帧数
    {
      printf("Get frame rate failed\n");
    }
    int capability = Stream_Parm.parm.capture.capability;
    int capturemode = Stream_Parm.parm.capture.capturemode;
    int numerator = Stream_Parm.parm.capture.timeperframe.numerator;
    int denominator = Stream_Parm.parm.capture.timeperframe.denominator;
    std::cout<<" numerator = "<<numerator<<std::endl;
    std::cout<<" denominator = "<<denominator<<std::endl;
    std::cout<<" capability = "<<capability<<std::endl;  //4096 表示能设置帧率
    std::cout<<" capturemode = "<<capturemode<<std::endl;  //

  struct v4l2_queryctrl  Setting;
  Setting.id = V4L2_CID_GAIN;
  if(-1 == xioctl(fd, VIDIOC_QUERYCTRL, &Setting))  //查询支持的增益设置
  {
    printf(" Query GAIN settings failed \n");
  }
  else
    printf(" V4L2_CID_GAIN Setting \n minimum = '%d' maximum = '%d' step = '%d' default_value = '%d' \n",
        Setting.minimum, Setting.maximum,Setting.step,Setting.default_value);

  struct v4l2_control ctrl;
  ctrl.id = V4L2_CID_GAIN;
  ctrl.value = 4;
  if( -1 == ioctl(fd, VIDIOC_S_CTRL, &ctrl))  //设置增益
  {
    printf(" Set GAIN failed \n");
  }

   Setting.id = V4L2_CID_EXPOSURE_ABSOLUTE;
  if(-1 == xioctl(fd, VIDIOC_QUERYCTRL, &Setting))  //查询支持的曝光时间
  {
    printf(" Query EXPOSURE settings failed \n");
  }
  else
    printf(" V4L2_CID_EXPOSURE Setting \n minimum = '%d' maximum = '%d' step = '%d' default_value = '%d' \n",
        Setting.minimum, Setting.maximum,Setting.step,Setting.default_value);

  ctrl.id = V4L2_CID_EXPOSURE_AUTO;
  ctrl.value = V4L2_EXPOSURE_MANUAL;
  if(-1 == ioctl(fd, VIDIOC_S_CTRL, &ctrl))  //设置曝光模式
  {    
    printf("  set EXPOSURE Mode failed\n");
  }
  ctrl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
  ctrl.value = 300;
  if( -1 == ioctl(fd, VIDIOC_S_CTRL, &ctrl))   //设置曝光时间
  {
    printf("  set EXPOSURE Time failed\n");
  }

  Setting.id = V4L2_CID_BRIGHTNESS;
  if(-1 == xioctl(fd, VIDIOC_QUERYCTRL, &Setting))  //查询支持的亮度
  {
    printf(" Query BRIGHTNESS settings failed \n");
  }
  else
    printf(" V4L2_CID_BRIGHTNESS Setting \n minimum = '%d' maximum = '%d' step = '%d' default_value = '%d' \n",
        Setting.minimum, Setting.maximum,Setting.step,Setting.default_value);
  ctrl.id = V4L2_CID_BRIGHTNESS;
  ctrl.value = 0;
  if( -1 == ioctl(fd, VIDIOC_S_CTRL, &ctrl))   //设置亮度
  {
    printf("set BRIGHTNESS failed\n");
  }


  Setting.id = V4L2_CID_CONTRAST;
  if(-1 == xioctl(fd, VIDIOC_QUERYCTRL, &Setting))  //查询支持的对比度
  {
    printf(" Query CONTRAST settings failed \n");
  }
  else
    printf(" V4L2_CID_CONTRAST Setting \n minimum = '%d' maximum = '%d' step = '%d' default_value = '%d' \n",
        Setting.minimum, Setting.maximum,Setting.step,Setting.default_value);
  ctrl.id = V4L2_CID_CONTRAST;
  ctrl.value = 0;
  if( -1 == ioctl(fd, VIDIOC_S_CTRL, &ctrl))   //设置对比度
  {
    printf("set BRIGHTNESS failed\n");
  }


  Setting.id = V4L2_CID_SATURATION;
  if(-1 == xioctl(fd, VIDIOC_QUERYCTRL, &Setting))  //查询支持的饱和度
  {
    printf(" Query SATURATION settings failed \n");
  }
  else
    printf(" V4L2_CID_SATURATION Setting \n minimum = '%d' maximum = '%d' step = '%d' default_value = '%d' \n",
        Setting.minimum, Setting.maximum,Setting.step,Setting.default_value);
  ctrl.id = V4L2_CID_SATURATION;
  ctrl.value = 0;
  if( -1 == xioctl(fd, VIDIOC_S_CTRL, &ctrl))   //设置饱和度
  {
    printf("set BRIGHTNESS failed\n");
  }

    Setting.id = V4L2_CID_HUE;
  if(-1 == xioctl(fd, VIDIOC_QUERYCTRL, &Setting))  //查询支持的色彩
  {
    printf(" Query HUE settings failed \n");
  }
  else
    printf(" V4L2_CID_HUE Setting \n minimum = '%d' maximum = '%d' step = '%d' default_value = '%d' \n",
        Setting.minimum, Setting.maximum,Setting.step,Setting.default_value);
  ctrl.id = V4L2_CID_HUE;         
  ctrl.value = 0;
  if( -1 == xioctl(fd, VIDIOC_S_CTRL, &ctrl))   //设置色彩
  {
    printf("set HUE failed\n");
  }

    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)) { //将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);//打开设备 成功返回新分配的文件描述符  可读可写打开 对于设备文件,以O_NONBLOCK方式打开可以做非阻塞I/O(Nonblock I/O)。
  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;
}
Logo

更多推荐