camera_V4L2: Fix time per frame capability

V4L2_CAP_TIMEPERFRAME should be from v4l2_streamparm instead of
v4l2_capability.

BUG=chromium:718278
TEST=use an external camera which supports to adjust frame rate.

Change-Id: I44cc7ca281802bc135838f1ddb9accc8c6940f83
Reviewed-on: https://chromium-review.googlesource.com/501619
Commit-Ready: Heng-ruey Hsu <henryhsu@google.com>
Tested-by: Heng-ruey Hsu <henryhsu@google.com>
Reviewed-by: Heng-ruey Hsu <henryhsu@google.com>
diff --git a/client/site_tests/camera_V4L2/src/media_v4l2_device.cc b/client/site_tests/camera_V4L2/src/media_v4l2_device.cc
index 08bf5e2..aa110d8 100644
--- a/client/site_tests/camera_V4L2/src/media_v4l2_device.cc
+++ b/client/site_tests/camera_V4L2/src/media_v4l2_device.cc
@@ -99,13 +99,8 @@
   }
 
   v4l2_format fmt;
-  memset(&fmt, 0, sizeof(fmt));
-  fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
-  if (-1 == DoIoctl(VIDIOC_G_FMT, &fmt)) {
-    printf("<<< Error: VIDIOC_G_FMT on %s.>>>\n", dev_name_);
+  if (!GetV4L2Format(&fmt))
     return false;
-  }
 
   fmt.fmt.pix.width = width;
   fmt.fmt.pix.height = height;
@@ -134,22 +129,24 @@
       return false;
   }
 
-  if (cap.capabilities & V4L2_CAP_TIMEPERFRAME) {
-    if (fps > 0)
+  v4l2_streamparm param;
+  if (!GetParam(&param))
+    return false;
+
+  if (param.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
+    if (fps > 0) {
       SetFrameRate(fps);
-    fps = GetFrameRate();
-  } else {
-    // TODO(jiesun): probably we should derive this from VIDIOC_G_STD
-    fps = 30.0;
+    } else {
+      printf("<<< Error: fps %f should be a positive number.>>>\n", fps);
+      return false;
+    }
   }
+  float actual_fps = GetFrameRate();
 
   printf("actual format for capture %dx%d %c%c%c%c picture at %.2f fps\n",
          fmt.fmt.pix.width, fmt.fmt.pix.height,
          (pixfmt >> 0) & 0xff, (pixfmt >> 8) & 0xff,
-         (pixfmt >> 16) & 0xff, (pixfmt >> 24 ) & 0xff, fps);
-  width_ = fmt.fmt.pix.width;
-  height_ = fmt.fmt.pix.height;
-  pixfmt_ = fmt;
+         (pixfmt >> 16) & 0xff, (pixfmt >> 24 ) & 0xff, actual_fps);
 
   bool ret = false;
   switch (io_) {
@@ -906,8 +903,6 @@
 
     if (cap->capabilities & V4L2_CAP_STREAMING)
       printf("<<< Info: %s support streaming i/o interface.>>>\n", dev_name_);
-    if (cap->capabilities & V4L2_CAP_TIMEPERFRAME)
-      printf("<<< Info: %s support flexible frame period.>>>\n", dev_name_);
   }
 
   return true;
@@ -954,6 +949,17 @@
       param.parm.capture.timeperframe.numerator;
 }
 
+bool V4L2Device::GetV4L2Format(v4l2_format* format) {
+  memset(format, 0, sizeof(v4l2_format));
+  format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+  if (-1 == DoIoctl(VIDIOC_G_FMT, format)) {
+    printf("<<< Error: VIDIOC_G_FMT on %s.>>>\n", dev_name_);
+    return false;
+  }
+  return true;
+}
+
 uint64_t V4L2Device::Now() {
   struct timespec ts;
   int res = clock_gettime(CLOCK_MONOTONIC, &ts);
diff --git a/client/site_tests/camera_V4L2/src/media_v4l2_device.h b/client/site_tests/camera_V4L2/src/media_v4l2_device.h
index ce4eb19..50116a0 100644
--- a/client/site_tests/camera_V4L2/src/media_v4l2_device.h
+++ b/client/site_tests/camera_V4L2/src/media_v4l2_device.h
@@ -36,6 +36,10 @@
 
   virtual bool OpenDevice();
   virtual void CloseDevice();
+  // After this function is called, the driver may adjust the settings if they
+  // are unsupported.  Caller can use GetV4L2Format() and GetFrameRate() to know
+  // the actual settings.  If V4L2_CAP_TIMEPERFRAME is unsupported, fps will be
+  // ignored.
   virtual bool InitDevice(IOMethod io,
                           uint32_t width,
                           uint32_t height,
@@ -73,21 +77,9 @@
       uint32_t index, uint32_t pixfmt, uint32_t width, uint32_t height,
       float* frame_rate);
   float GetFrameRate();
+  bool GetV4L2Format(v4l2_format* format);
   bool Stop();
 
-  // Getter.
-  int32_t GetActualWidth() {
-    return width_;
-  }
-
-  int32_t GetActualHeight() {
-    return height_;
-  }
-
-  v4l2_format& GetActualPixelFormat() {
-    return pixfmt_;
-  }
-
   static uint32_t MapFourCC(const char* fourcc);
 
   virtual void ProcessImage(const void* p);
@@ -109,9 +101,6 @@
   uint32_t min_buffers_;  // Minimum buffers requirement.
   bool stopped_;
 
-  // Valid only after |InitDevice()|.
-  uint32_t width_, height_;
-  v4l2_format pixfmt_;
   // Sets to true when buffers are initialized.
   bool initialized_;
 };
diff --git a/client/site_tests/camera_V4L2/src/media_v4l2_test.cc b/client/site_tests/camera_V4L2/src/media_v4l2_test.cc
index 0b76e84..d5c1570 100644
--- a/client/site_tests/camera_V4L2/src/media_v4l2_test.cc
+++ b/client/site_tests/camera_V4L2/src/media_v4l2_test.cc
@@ -4,6 +4,8 @@
 
 #include <getopt.h>
 
+#include <cmath>
+#include <limits>
 #include <memory>
 #include <string>
 #include <unordered_map>
@@ -169,7 +171,7 @@
   return true;
 }
 
-// Test all required resolutions with all fps which are larger than 30.
+// Test all required resolutions with 30 fps.
 bool TestResolutions(const std::string& dev_name,
                      bool check_1280x960,
                      bool check_1600x1200) {
@@ -189,16 +191,24 @@
   }
   SupportedFormat max_resolution = GetMaximumResolution(supported_formats);
 
+  const float kFrameRate = 30.0;
   SupportedFormats required_resolutions;
-  required_resolutions.push_back(SupportedFormat(320, 240, 0, 30.0));
-  required_resolutions.push_back(SupportedFormat(640, 480, 0, 30.0));
-  required_resolutions.push_back(SupportedFormat(1280, 720, 0, 30.0));
-  required_resolutions.push_back(SupportedFormat(1920, 1080, 0, 30.0));
+  required_resolutions.push_back(SupportedFormat(320, 240, 0, kFrameRate));
+  required_resolutions.push_back(SupportedFormat(640, 480, 0, kFrameRate));
+  required_resolutions.push_back(SupportedFormat(1280, 720, 0, kFrameRate));
+  required_resolutions.push_back(SupportedFormat(1920, 1080, 0, kFrameRate));
   if (check_1600x1200) {
-    required_resolutions.push_back(SupportedFormat(1600, 1200, 0, 30.0));
+    required_resolutions.push_back(SupportedFormat(1600, 1200, 0, kFrameRate));
   }
   if (check_1280x960) {
-    required_resolutions.push_back(SupportedFormat(1280, 960, 0, 30.0));
+    required_resolutions.push_back(SupportedFormat(1280, 960, 0, kFrameRate));
+  }
+
+  v4l2_streamparm param;
+  if (!device->GetParam(&param)) {
+    printf("[Error] Can not get stream param on device '%s'\n",
+        dev_name.c_str());
+    return false;
   }
 
   for (const auto& test_resolution : required_resolutions) {
@@ -216,39 +226,45 @@
       return false;
     }
 
-    bool frame_rate_tested = false;
+    bool frame_rate_30_supported = false;
     for (const auto& frame_rate : test_format->frame_rates) {
-      // If the fps is less than the fps that we want to test, skip it.
-      if (frame_rate < test_resolution.frame_rates[0]) {
-        continue;
-      }
-      frame_rate_tested = true;
-      if (RunTest(device.get(), io, buffers, time_to_capture,
-            test_format->width, test_format->height, test_format->fourcc,
-            frame_rate)) {
-        printf("[Error] Could not capture frames for %dx%d (%08X) %.2f fps in "
-            "%s\n", test_format->width, test_format->height,
-            test_format->fourcc, frame_rate, dev_name.c_str());
-        return false;
-      }
-
-      // Make sure the driver didn't adjust the format.
-      v4l2_format fmt = device->GetActualPixelFormat();
-      if (test_format->width != fmt.fmt.pix.width ||
-          test_format->height != fmt.fmt.pix.height ||
-          test_format->fourcc != fmt.fmt.pix.pixelformat ||
-          frame_rate != device->GetFrameRate()) {
-        printf("[Error] Capture test %dx%d (%08X) %.2f fps failed in %s\n",
-            test_format->width, test_format->height, test_format->fourcc,
-            frame_rate, dev_name.c_str());
+      if (std::fabs(frame_rate - kFrameRate) <=
+          std::numeric_limits<float>::epsilon()) {
+        frame_rate_30_supported = true;
+        break;
       }
     }
-    if (!frame_rate_tested) {
-      printf("[Error] Cannot test frame rate for %dx%d (%08X) failed in %s\n",
+    if (!frame_rate_30_supported) {
+      printf("[Error] Cannot test 30 fps for %dx%d (%08X) failed in %s\n",
           test_format->width, test_format->height, test_format->fourcc,
           dev_name.c_str());
       return false;
     }
+
+    if (RunTest(device.get(), io, buffers, time_to_capture,
+          test_format->width, test_format->height, test_format->fourcc,
+          kFrameRate)) {
+      printf("[Error] Could not capture frames for %dx%d (%08X) %.2f fps in "
+          "%s\n", test_format->width, test_format->height,
+          test_format->fourcc, kFrameRate, dev_name.c_str());
+      return false;
+    }
+
+    // Make sure the driver didn't adjust the format.
+    v4l2_format fmt;
+    if (!device->GetV4L2Format(&fmt)) {
+      return false;
+    }
+    if (test_format->width != fmt.fmt.pix.width ||
+        test_format->height != fmt.fmt.pix.height ||
+        test_format->fourcc != fmt.fmt.pix.pixelformat ||
+        std::fabs(kFrameRate - device->GetFrameRate()) >
+            std::numeric_limits<float>::epsilon()) {
+      printf("[Error] Capture test %dx%d (%08X) %.2f fps failed in %s\n",
+          test_format->width, test_format->height, test_format->fourcc,
+          kFrameRate, dev_name.c_str());
+      return false;
+    }
   }
   device->CloseDevice();
 
diff --git a/client/site_tests/camera_V4L2/src/media_v4l2_unittest.cc b/client/site_tests/camera_V4L2/src/media_v4l2_unittest.cc
index fcfbebd..53931d5 100644
--- a/client/site_tests/camera_V4L2/src/media_v4l2_unittest.cc
+++ b/client/site_tests/camera_V4L2/src/media_v4l2_unittest.cc
@@ -218,50 +218,17 @@
   if (!v4l2_dev.OpenDevice()) {
     printf("[Error] Can not open device '%s'\n", dev_name);
   }
-  v4l2_capability caps;
-  if (!v4l2_dev.ProbeCaps(&caps, true)) {
-    printf("[Error] Can not probe caps on device '%s'\n", dev_name);
+  v4l2_streamparm param;
+  if (!v4l2_dev.GetParam(&param)) {
+    printf("[Error] Can not get stream param on device '%s'\n", dev_name);
     exit(EXIT_FAILURE);
   }
   // we only try to adjust frame rate when it claims can.
-  if (caps.capabilities & V4L2_CAP_TIMEPERFRAME) {
-    v4l2_streamparm param;
-    if (!v4l2_dev.GetParam(&param)) {
-      printf("[Error] Can not get stream param on device '%s'\n", dev_name);
-      exit(EXIT_FAILURE);
-    }
+  if (param.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
     if (!v4l2_dev.SetParam(&param)) {
       printf("[Error] Can not set stream param on device '%s'\n", dev_name);
       exit(EXIT_FAILURE);
     }
-
-    if (!v4l2_dev.SetFrameRate(15)) {
-      printf("[Error] SetFrameRate failed on '%s'\n", dev_name);
-      exit(EXIT_FAILURE);
-    }
-    if (!v4l2_dev.GetParam(&param)) {
-      printf("[Error] Can not get stream param on device '%s'\n", dev_name);
-      exit(EXIT_FAILURE);
-    }
-    if (param.parm.capture.timeperframe.denominator !=
-              param.parm.capture.timeperframe.numerator * 15) {
-      printf("[Error] Can not set frame rate to 15 on '%s'\n", dev_name);
-      exit(EXIT_FAILURE);
-    }
-
-    if (!v4l2_dev.SetFrameRate(10)) {
-      printf("[Error] SetFrameRate failed on '%s'\n", dev_name);
-      exit(EXIT_FAILURE);
-    }
-    if (!v4l2_dev.GetParam(&param)) {
-      printf("[Error] Can not get stream param on device '%s'\n", dev_name);
-      exit(EXIT_FAILURE);
-    }
-    if (param.parm.capture.timeperframe.denominator !=
-              param.parm.capture.timeperframe.numerator * 10) {
-      printf("[Error] Can not set frame rate to 10 on '%s'\n", dev_name);
-      exit(EXIT_FAILURE);
-    }
   }
 
   v4l2_dev.CloseDevice();