Advertisement
themoebius

frame_count.cpp

Mar 10th, 2017
117
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.96 KB | None | 0 0
  1. /*
  2.  *  V4L2 video capture example
  3.  *
  4.  *  This program can be used and distributed without restrictions.
  5.  *
  6.  *      This program is provided with the V4L2 API
  7.  * see https://linuxtv.org/docs.php for more information
  8.  */
  9.  
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <assert.h>
  14.  
  15. #include <getopt.h>             /* getopt_long() */
  16.  
  17. #include <fcntl.h>              /* low-level i/o */
  18. #include <unistd.h>
  19. #include <errno.h>
  20. #include <sys/stat.h>
  21. #include <sys/types.h>
  22. #include <sys/time.h>
  23. #include <sys/mman.h>
  24. #include <sys/ioctl.h>
  25.  
  26. #include <linux/videodev2.h>
  27.  
  28. #include <vector>
  29. #include <string>
  30.  
  31. #define CLEAR(x) memset(&(x), 0, sizeof(x))
  32. #define MIN_WARMUP_TIME 10.0
  33.  
  34. struct buffer {
  35.         void   *start;
  36.         size_t  length;
  37. };
  38.  
  39. double time_diff(const struct timeval& t1, const struct timeval& t0)
  40. {
  41.     double dt = (t1.tv_sec - t0.tv_sec) + (t1.tv_usec - t0.tv_usec) / 1000000.0;
  42. }
  43.  
  44. class CameraStats {
  45.     public:
  46.         CameraStats() :
  47.             max_gap(0),
  48.             total_num_frames(0),
  49.             total_counted_frames(0),
  50.             effective_frame_rate(0),
  51.             num_slow_10_pct(0),
  52.             num_slow_100_pct(0),
  53.             num_slow_200_pct(0),
  54.             expected_frame_rate(30),
  55.             expected_frame_time(1.0 / 30.0)
  56.         {
  57.             last_read_time.tv_sec = 0;
  58.             last_read_time.tv_usec = 0;
  59.  
  60.             first_read_time.tv_sec = 0;
  61.             first_read_time.tv_usec = 0;
  62.         }
  63.  
  64.         void set_frame_rate(double fps)
  65.         {
  66.             expected_frame_rate = fps;
  67.             expected_frame_time = 1.0 / fps;
  68.         }
  69.  
  70.         void count_frame()
  71.         {
  72.             struct timeval now;
  73.             gettimeofday(&now, NULL);
  74.            
  75.             if(total_num_frames == 0)
  76.             {
  77.                 first_read_time = now;
  78.             }
  79.             else if(time_diff(now, first_read_time) > MIN_WARMUP_TIME)
  80.             {
  81.                 double dt = time_diff(now, last_read_time);
  82.                 if(dt > max_gap) {
  83.                     max_gap = dt;
  84.                 }
  85.  
  86.                 if(dt < 1.1 * expected_frame_time) {
  87.                     fprintf(stderr, ".");
  88.                 }
  89.                 else if(dt < 2.0 * expected_frame_time) {
  90.                     fprintf(stderr, "+");
  91.                     num_slow_10_pct++;
  92.                 }
  93.                 else if(dt < 3.0 * expected_frame_time) {
  94.                     fprintf(stderr, "*");
  95.                     num_slow_100_pct++;
  96.                 }
  97.                 else {
  98.                     fprintf(stderr, "!");
  99.                     num_slow_200_pct++;
  100.                 }
  101.                 total_counted_frames++;
  102.                 effective_frame_rate =
  103.                     total_counted_frames / time_diff(now, first_read_time);
  104.             }
  105.             total_num_frames++;
  106.             last_read_time = now;
  107.         }
  108.        
  109.         void print_report()
  110.         {
  111.             double capture_time = time_diff(last_read_time, first_read_time);
  112.             fprintf(stdout, "capture time: %f\n", capture_time);
  113.             fprintf(stdout, "effective frame rate: %f\n", effective_frame_rate);
  114.             fprintf(stdout, "number 10%% slow frames: %d\n", num_slow_10_pct);
  115.             fprintf(stdout, "number 100%% slow frames: %d\n", num_slow_100_pct);
  116.             fprintf(stdout, "number >200%% slow frames: %d\n", num_slow_200_pct);
  117.             fprintf(stdout, "maximum gap between frames: %f\n", max_gap);
  118.             //fprintf(stderr, "biggest gap: %f\n", );
  119.             //fprintf(stderr, "number of slow frames: %d\n", num_slow_frames);
  120.  
  121.         }
  122.  
  123.     private:
  124.         double  max_gap;
  125.         int     total_num_frames;
  126.         int     total_counted_frames;
  127.         double  effective_frame_rate;
  128.         int num_slow_10_pct;
  129.         int num_slow_100_pct;
  130.         int num_slow_200_pct;
  131.         struct  timeval last_read_time;
  132.         struct  timeval first_read_time;
  133.         double  expected_frame_rate;
  134.         double  expected_frame_time;
  135.  
  136. };
  137.  
  138. class Camera {
  139.         public:
  140.                 std::string             dev_name_;
  141.                 int                     fd_;
  142.                 struct buffer           *buffers_;
  143.                 unsigned int     n_buffers_;
  144.  
  145.         Camera();
  146.         ~Camera();
  147.  
  148.                 void open_device(const std::string& dev_name);
  149.                 void close_device();
  150.        
  151.         void init_device();
  152.         void uninit_device();
  153.  
  154.         void start_capturing();
  155.         void stop_capturing();
  156.  
  157.         int read_frame();
  158.  
  159.     private:
  160.         bool open_;
  161.         bool initialized_;
  162.         bool capturing_;
  163.  
  164.         void init_mmap();
  165. };
  166.  
  167. static void errno_exit(const char *s)
  168. {
  169.         fprintf(stderr, "%s error %d, %s\\n", s, errno, strerror(errno));
  170.         exit(EXIT_FAILURE);
  171. }
  172.  
  173. static int xioctl(int fh, int request, void *arg)
  174. {
  175.         int r;
  176.  
  177.         do {
  178.                 r = ioctl(fh, request, arg);
  179.         } while (-1 == r && EINTR == errno);
  180.  
  181.         return r;
  182. }
  183.  
  184. Camera::Camera()
  185.     :
  186.     dev_name_(""),
  187.     fd_(-1),
  188.     buffers_(NULL),
  189.     n_buffers_(0),
  190.     open_(false),
  191.     initialized_(false)
  192. {
  193. }
  194.  
  195. Camera::~Camera()
  196. {
  197.     if(capturing_)
  198.         stop_capturing();
  199.     if(initialized_)
  200.         uninit_device();
  201.     if(open_)
  202.         close_device();
  203. }
  204.  
  205. void Camera::close_device(void)
  206. {
  207.         if (-1 == close(fd_))
  208.                 errno_exit("close");
  209.  
  210.         fd_ = -1;
  211.     open_ = false;
  212. }
  213.  
  214. void Camera::open_device(const std::string& dev_name)
  215. {
  216.         struct stat st;
  217.  
  218.         if (-1 == stat(dev_name.c_str(), &st)) {
  219.                 fprintf(stderr, "Cannot identify '%s': %d, %s\n",
  220.                                 dev_name.c_str(), errno, strerror(errno));
  221.                 exit(EXIT_FAILURE);
  222.         }
  223.  
  224.         if (!S_ISCHR(st.st_mode)) {
  225.                 fprintf(stderr, "%s is no devicen", dev_name.c_str());
  226.                 exit(EXIT_FAILURE);
  227.         }
  228.  
  229.         fd_ = open(dev_name.c_str(), O_RDWR /* required */ | O_NONBLOCK, 0);
  230.  
  231.         if (-1 == fd_) {
  232.                 fprintf(stderr, "Cannot open '%s': %d, %s\n",
  233.                                 dev_name.c_str(), errno, strerror(errno));
  234.                 exit(EXIT_FAILURE);
  235.         }
  236.  
  237.         dev_name_ = std::string(dev_name.c_str());
  238.     open_ = true;
  239. }
  240.  
  241. void Camera::init_device()
  242. {
  243.         struct v4l2_capability cap;
  244.         struct v4l2_cropcap cropcap;
  245.         struct v4l2_crop crop;
  246.         struct v4l2_format fmt;
  247.         unsigned int min;
  248.  
  249.         if (-1 == xioctl(fd_, VIDIOC_QUERYCAP, &cap)) {
  250.                 if (EINVAL == errno) {
  251.                         fprintf(stderr, "%s is no V4L2 device\n",
  252.                                         dev_name_.c_str());
  253.                         exit(EXIT_FAILURE);
  254.                 } else {
  255.                         errno_exit("VIDIOC_QUERYCAP");
  256.                 }
  257.         }
  258.  
  259.         if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
  260.                 fprintf(stderr, "%s is no video capture device\n",
  261.                                 dev_name_.c_str());
  262.                 exit(EXIT_FAILURE);
  263.         }
  264.  
  265.         if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
  266.                 fprintf(stderr, "%s does not support streaming i/o\n",
  267.                                 dev_name_.c_str());
  268.                 exit(EXIT_FAILURE);
  269.         }
  270.  
  271.  
  272.         /* Select video input, video standard and tune here. */
  273.  
  274.  
  275.         CLEAR(cropcap);
  276.  
  277.         cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  278.  
  279.         if (0 == xioctl(fd_, VIDIOC_CROPCAP, &cropcap)) {
  280.                 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  281.                 crop.c = cropcap.defrect; /* reset to default */
  282.  
  283.                 if (-1 == xioctl(fd_, VIDIOC_S_CROP, &crop)) {
  284.                         switch (errno) {
  285.                                 case EINVAL:
  286.                                         /* Cropping not supported. */
  287.                                         break;
  288.                                 default:
  289.                                         /* Errors ignored. */
  290.                                         break;
  291.                         }
  292.                 }
  293.         } else {
  294.                 /* Errors ignored. */
  295.         }
  296.  
  297.  
  298.         CLEAR(fmt);
  299.  
  300.         fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  301.  
  302. #if 0
  303.         if (force_format) {
  304.                 fmt.fmt.pix.width       = 640;
  305.                 fmt.fmt.pix.height      = 480;
  306.                 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
  307.                 fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
  308.  
  309.                 if (-1 == xioctl(fd_, VIDIOC_S_FMT, &fmt))
  310.                         errno_exit("VIDIOC_S_FMT");
  311.  
  312.                 /* Note VIDIOC_S_FMT may change width and height. */
  313.         }
  314.     else
  315. #endif
  316.     {
  317.                 /* Preserve original settings as set by v4l2-ctl for example */
  318.                 if (-1 == xioctl(fd_, VIDIOC_G_FMT, &fmt))
  319.                         errno_exit("VIDIOC_G_FMT");
  320.         }
  321.  
  322.         /* Buggy driver paranoia. */
  323.         min = fmt.fmt.pix.width * 2;
  324.         if (fmt.fmt.pix.bytesperline < min)
  325.                 fmt.fmt.pix.bytesperline = min;
  326.         min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
  327.         if (fmt.fmt.pix.sizeimage < min)
  328.                 fmt.fmt.pix.sizeimage = min;
  329. #warning "DISABLE"
  330.         init_mmap();
  331.  
  332.     initialized_ = true;
  333. }
  334.  
  335. void Camera::init_mmap()
  336. {
  337.     struct v4l2_requestbuffers req;
  338.  
  339.     CLEAR(req);
  340.  
  341.     req.count = 4;
  342.     req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  343.     req.memory = V4L2_MEMORY_MMAP;
  344.  
  345.     if (-1 == xioctl(fd_, VIDIOC_REQBUFS, &req)) {
  346.         if (EINVAL == errno) {
  347.             fprintf(stderr, "%s does not support "
  348.                     "memory mapping", dev_name_.c_str());
  349.             exit(EXIT_FAILURE);
  350.         } else {
  351.             errno_exit("VIDIOC_REQBUFS");
  352.         }
  353.     }
  354.  
  355.     if (req.count < 2) {
  356.         fprintf(stderr, "Insufficient buffer memory on %s\\n",
  357.                 dev_name_.c_str());
  358.         exit(EXIT_FAILURE);
  359.     }
  360.  
  361.     buffers_ = (buffer *)calloc(req.count, sizeof(*buffers_));
  362.  
  363.     if (!buffers_) {
  364.         fprintf(stderr, "Out of memory\\n");
  365.         exit(EXIT_FAILURE);
  366.     }
  367.  
  368.     for (n_buffers_ = 0; n_buffers_ < req.count; ++n_buffers_) {
  369.         struct v4l2_buffer buf;
  370.  
  371.         CLEAR(buf);
  372.  
  373.         buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  374.         buf.memory      = V4L2_MEMORY_MMAP;
  375.         buf.index       = n_buffers_;
  376.  
  377.         if (-1 == xioctl(fd_, VIDIOC_QUERYBUF, &buf))
  378.             errno_exit("VIDIOC_QUERYBUF");
  379.  
  380.         buffers_[n_buffers_].length = buf.length;
  381.         buffers_[n_buffers_].start =
  382.             mmap(NULL /* start anywhere */,
  383.                     buf.length,
  384.                     PROT_READ | PROT_WRITE /* required */,
  385.                     MAP_SHARED /* recommended */,
  386.                     fd_, buf.m.offset);
  387.  
  388.         if (MAP_FAILED == buffers_[n_buffers_].start)
  389.             errno_exit("mmap");
  390.     }
  391.  
  392. }
  393.  
  394. void Camera::uninit_device(void)
  395. {
  396.         unsigned int i;
  397.  
  398.         for (i = 0; i < n_buffers_; ++i)
  399.                 if (-1 == munmap(buffers_[i].start, buffers_[i].length))
  400.                         errno_exit("munmap");
  401.  
  402.         free(buffers_);
  403.  
  404.     initialized_ = false;
  405. }
  406.  
  407. void Camera::start_capturing()
  408. {
  409.         unsigned int i;
  410.         enum v4l2_buf_type type;
  411.  
  412.         for (i = 0; i < n_buffers_; ++i) {
  413.                 struct v4l2_buffer buf;
  414.  
  415.                 CLEAR(buf);
  416.                 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  417.                 buf.memory = V4L2_MEMORY_MMAP;
  418.                 buf.index = i;
  419.  
  420.                 if (-1 == xioctl(fd_, VIDIOC_QBUF, &buf))
  421.                         errno_exit("VIDIOC_QBUF");
  422.         }
  423.         type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  424.         if (-1 == xioctl(fd_, VIDIOC_STREAMON, &type))
  425.                 errno_exit("VIDIOC_STREAMON");
  426.  
  427.     capturing_ = true;
  428. }
  429.  
  430. void Camera::stop_capturing()
  431. {
  432.         enum v4l2_buf_type type;
  433.  
  434.         type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  435.         if (-1 == xioctl(fd_, VIDIOC_STREAMOFF, &type))
  436.                 errno_exit("VIDIOC_STREAMOFF");
  437.  
  438.     capturing_ = false;
  439. }
  440.  
  441. int Camera::read_frame()
  442. {
  443.     struct v4l2_buffer buf;
  444.     unsigned int i;
  445.  
  446.     CLEAR(buf);
  447.  
  448.     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  449.         buf.memory = V4L2_MEMORY_MMAP;
  450.  
  451.         if (-1 == xioctl(fd_, VIDIOC_DQBUF, &buf)) {
  452.                 switch (errno) {
  453.                         case EAGAIN:
  454.                                 return 0;
  455.  
  456.                         case EIO:
  457.                                 /* Could ignore EIO, see spec. */
  458.  
  459.                                 /* fall through */
  460.  
  461.                         default:
  462.                                 errno_exit("VIDIOC_DQBUF");
  463.                 }
  464.         }
  465.  
  466.         assert(buf.index < n_buffers_);
  467.  
  468.         //process_image(buffers_[buf.index].start, buf.bytesused);
  469.  
  470.         if (-1 == xioctl(fd_, VIDIOC_QBUF, &buf))
  471.                 errno_exit("VIDIOC_QBUF");
  472.  
  473.         return 1;
  474. }
  475.  
  476. static void usage(FILE *fp, int argc, char **argv,
  477.     const std::string& dev, const int run_time)
  478. {
  479.         fprintf(fp,
  480.                         "Usage: %s [options]\n\n"
  481.                         "Version 1.3\n"
  482.                         "Options:\n"
  483.                         "-d | --device name   Video device name [%s]\n"
  484.                         "-h | --help          Print this message\n"
  485.                         "-f | --format        Force format to 640x480 YUYV\n"
  486.                         "-t | --time          Number of seconds to grab [%i]\n"
  487.                         "",
  488.                         argv[0], dev.c_str(), run_time);
  489. }
  490.  
  491. static const char short_options[] = "d:hft:";
  492.  
  493. static const struct option
  494. long_options[] = {
  495.         { "device", required_argument, NULL, 'd' },
  496.         { "help",   no_argument,       NULL, 'h' },
  497.         { "format", no_argument,       NULL, 'f' },
  498.         { "time",  required_argument,  NULL, 't' },
  499.         { 0, 0, 0, 0 }
  500. };
  501.  
  502.  
  503. void run(const std::vector<Camera*>& cameras, const int num_seconds)
  504. {
  505.     struct timeval t_start, t_now;
  506.     struct timeval timeout;
  507.     double running_time;
  508.     std::vector<CameraStats> stats;
  509.  
  510.     fd_set master;
  511.     int max_fd = -1;
  512.     FD_ZERO(&master);
  513.  
  514.     for(std::vector<Camera*>::const_iterator cam_it = cameras.begin();
  515.         cam_it != cameras.end(); ++cam_it)
  516.     {
  517.         (*cam_it)->start_capturing();
  518.         if((*cam_it)->fd_ > max_fd)
  519.         {
  520.             max_fd = (*cam_it)->fd_;
  521.         }
  522.         FD_SET((*cam_it)->fd_, &master);
  523.         fprintf(stderr, "%d\n", (*cam_it)->fd_);
  524.  
  525.         CameraStats stat;
  526.         stats.push_back(stat);
  527.     }
  528.  
  529.     gettimeofday(&t_start, NULL);
  530.     running_time = 0;
  531.     while(running_time < (double)num_seconds)
  532.     {
  533.         //fprintf(stderr, "x");
  534.         //fflush(stderr);
  535.         fd_set dup = master;
  536.         //FD_ZERO(&dup);
  537.  
  538.         timeout.tv_sec = 10;
  539.         timeout.tv_usec = 0;
  540.  
  541.         int ret = select(max_fd+1, &dup, NULL, NULL, &timeout);
  542.        
  543.         if(ret < 0) {
  544.             if (EINTR == errno)
  545.                 continue;
  546.             errno_exit("select");
  547.         }
  548.  
  549.         if(ret == 0) {
  550.             fprintf(stderr, "select timeout\n");
  551.             exit(EXIT_FAILURE);
  552.         }
  553.    
  554.         std::vector<CameraStats>::iterator stat_it = stats.begin();
  555.         std::vector<Camera*>::const_iterator cam_it = cameras.begin();
  556.  
  557.         for(; cam_it != cameras.end(); ++cam_it, ++stat_it)
  558.         {
  559.             if(FD_ISSET((*cam_it)->fd_, &dup)) {
  560.                 (*cam_it)->read_frame();
  561.                 stat_it->count_frame();
  562.  
  563.                 fflush(stdout);
  564.                 fprintf(stdout, "%d", (*cam_it)->fd_);
  565.                 fflush(stdout);
  566.             }
  567.         }
  568.  
  569.         gettimeofday(&t_now, NULL);
  570.         running_time = (t_now.tv_sec - t_start.tv_sec) + (t_now.tv_usec - t_start.tv_usec) / (1000000.0);
  571.     }
  572.    
  573.     std::vector<CameraStats>::iterator stat_it = stats.begin();
  574.     std::vector<Camera*>::const_iterator cam_it = cameras.begin();
  575.     for(; cam_it != cameras.end(); ++cam_it, ++stat_it)
  576.     {
  577.         (*cam_it)->stop_capturing();
  578.         fprintf(stdout, "%s\n", (*cam_it)->dev_name_.c_str());
  579.         stat_it->print_report();
  580.     }
  581.  
  582. }
  583.  
  584.  
  585. int main(int argc, char** argv)
  586. {
  587.     std::string dev_name;
  588.     int run_time = 5;  
  589.  
  590.     std::vector<std::string> dev_names;
  591.  
  592.     for (;;) {
  593.         int idx;
  594.         int c;
  595.  
  596.         c = getopt_long(argc, argv,
  597.                 short_options, long_options, &idx);
  598.  
  599.         if (-1 == c)
  600.             break;
  601.  
  602.         switch (c) {
  603.             case 0: /* getopt_long() flag */
  604.                 break;
  605.  
  606.             case 'd':
  607.                 dev_name = optarg;
  608.                 dev_names.push_back(dev_name);
  609.                 break;
  610.  
  611.             case 'h':
  612.                 usage(stdout, argc, argv, dev_name, run_time);
  613.                 exit(EXIT_SUCCESS);
  614.  
  615.             case 't':
  616.                 errno = 0;
  617.                 run_time = strtol(optarg, NULL, 0);
  618.                 if (errno)
  619.                     errno_exit(optarg);
  620.                 break;
  621.  
  622.             default:
  623.                 usage(stderr, argc, argv, dev_name, run_time);
  624.                 exit(EXIT_FAILURE);
  625.         }
  626.     }
  627.  
  628.     if(dev_names.empty())
  629.     {
  630.         dev_names.push_back(std::string("/dev/video0"));
  631.     }
  632.  
  633.     std::vector<Camera*> cameras;
  634.     Camera* c = NULL;  
  635.  
  636.     for(std::vector<std::string>::iterator name_it = dev_names.begin();
  637.         name_it != dev_names.end(); ++name_it)
  638.     {
  639.         c = new Camera();
  640.         c->open_device(*name_it);
  641.         c->init_device();
  642.        
  643.         cameras.push_back(c);
  644.     }
  645.    
  646.     run(cameras, run_time);
  647.  
  648.     for(std::vector<Camera*>::iterator cam_it = cameras.begin();
  649.         cam_it != cameras.end(); ++cam_it)
  650.     {
  651.         (*cam_it)->uninit_device();
  652.         (*cam_it)->close_device();
  653.         delete (*cam_it);
  654.         (*cam_it) = NULL;
  655.     }
  656.  
  657.     return 0;
  658. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement