Fix errors in camera_V4L2

1. REQBUFS ioctl should be after munmap. Otherwise, device is busy.
2. Frame rate should be float. Some devices may support (2/15) frame rate.

BUG=chromium:718278
TEST=test on nyan_big and pass the test.

Change-Id: I7dc1cb5f16a8c772ea2e38a47066175a97218ac8
Reviewed-on: https://chromium-review.googlesource.com/495947
Commit-Ready: Heng-ruey Hsu <henryhsu@google.com>
Tested-by: Heng-ruey Hsu <henryhsu@google.com>
Reviewed-by: Hung-yu Wu <hywu@chromium.org>
diff --git a/client/site_tests/camera_V4L2/src/camera_characteristics.cc b/client/site_tests/camera_V4L2/src/camera_characteristics.cc
index fa8eec0..cc315d8 100644
--- a/client/site_tests/camera_V4L2/src/camera_characteristics.cc
+++ b/client/site_tests/camera_V4L2/src/camera_characteristics.cc
@@ -79,7 +79,7 @@
   const base::FilePath path(kCameraCharacteristicsConfigFile);
   FILE* file = base::OpenFile(path, "r");
   if (!file) {
-    LOG(ERROR) << __func__ << ": Can't open file "
+    LOG(INFO) << __func__ << ": Can't open file "
                << kCameraCharacteristicsConfigFile
                << ". Use default characteristics instead";
     DeviceInfos device_infos;
diff --git a/client/site_tests/camera_V4L2/src/common_types.h b/client/site_tests/camera_V4L2/src/common_types.h
index f16d143..6dcd505 100644
--- a/client/site_tests/camera_V4L2/src/common_types.h
+++ b/client/site_tests/camera_V4L2/src/common_types.h
@@ -52,7 +52,7 @@
   // pixelformat. This is not sorted. For example, suppose width, height, and
   // fourcc are 640x480 YUYV. If frameRates are 15.0 and 30.0, the camera
   // supports outputting  640X480 YUYV in 15fps or 30fps.
-  std::vector<uint32_t> frame_rates;
+  std::vector<float> frame_rates;
 };
 
 typedef std::vector<SupportedFormat> SupportedFormats;
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 447585b..08bf5e2 100644
--- a/client/site_tests/camera_V4L2/src/media_v4l2_device.cc
+++ b/client/site_tests/camera_V4L2/src/media_v4l2_device.cc
@@ -83,7 +83,7 @@
                             uint32_t width,
                             uint32_t height,
                             uint32_t pixfmt,
-                            uint32_t fps) {
+                            float fps) {
   io_ = io;
   // Crop/Format setting could live across session.
   // We should always initialized them when supported.
@@ -140,10 +140,10 @@
     fps = GetFrameRate();
   } else {
     // TODO(jiesun): probably we should derive this from VIDIOC_G_STD
-    fps = 30;
+    fps = 30.0;
   }
 
-  printf("actual format for capture %dx%d %c%c%c%c picture at %d fps\n",
+  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);
@@ -169,24 +169,32 @@
 }
 
 bool V4L2Device::UninitDevice() {
+  v4l2_requestbuffers req;
+  memset(&req, 0, sizeof(req));
+  req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   switch (io_) {
     case IO_METHOD_MMAP:
-      v4l2_requestbuffers req;
-      memset(&req, 0, sizeof(req));
-      req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-      req.memory = V4L2_MEMORY_MMAP;
-      if (-1 == DoIoctl(VIDIOC_REQBUFS, &req)) {
-        printf("<<< Error: VIDIOC_REQBUFS failed on %s.>>>\n", dev_name_);
-        return false;
-      }
-
       for (uint32_t i = 0; i < num_buffers_; ++i)
         if (-1 == munmap(v4l2_buffers_[i].start, v4l2_buffers_[i].length)) {
           printf("<<< Error: munmap() on %s failed.>>>\n", dev_name_);
           return false;
         }
+
+      req.memory = V4L2_MEMORY_MMAP;
+      if (-1 == DoIoctl(VIDIOC_REQBUFS, &req)) {
+        printf("<<< Error: VIDIOC_REQBUFS for MMAP failed on %s: %s.>>>\n",
+            dev_name_, strerror(errno));
+        return false;
+      }
       break;
     case IO_METHOD_USERPTR:
+      req.memory = V4L2_MEMORY_USERPTR;
+      if (-1 == DoIoctl(VIDIOC_REQBUFS, &req)) {
+        printf("<<< Error: VIDIOC_REQBUFS for USERPTR failed on %s.: %s>>>\n",
+            dev_name_, strerror(errno));
+        return false;
+      }
+
       for (uint32_t i = 0; i < num_buffers_; ++i)
         free(v4l2_buffers_[i].start);
       break;
@@ -424,7 +432,8 @@
     if (EINVAL == errno)
       printf("<<< Error: mmap() io is not supported on %s.>>>\n", dev_name_);
     else
-      printf("<<< Error: VIDIOC_REQBUFS failed on %s.>>>\n", dev_name_);
+      printf("<<< Error: VIDIOC_REQBUFS for MMAP(%d) failed on %s: %s.>>>\n",
+          min_buffers_, dev_name_, strerror(errno));
     return false;
   }
 
@@ -476,7 +485,8 @@
     if (EINVAL == errno)
       printf("<<< Error: user pointer is not supported on %s.>>>\n", dev_name_);
     else
-      printf("<<< Error: VIDIOC_REQBUFS failed on %s.>>>\n", dev_name_);
+      printf("<<< Error: VIDIOC_REQBUFS for USERPTR(%d) failed on %s: %s.>>>\n",
+          min_buffers_, dev_name_, strerror(errno));
     return false;
   }
 
@@ -801,7 +811,7 @@
 
 bool V4L2Device::GetFrameInterval(
     uint32_t index, uint32_t pixfmt, uint32_t width, uint32_t height,
-    uint32_t* frame_rate) {
+    float* frame_rate) {
   v4l2_frmivalenum frm_interval;
   memset(&frm_interval, 0, sizeof(frm_interval));
   frm_interval.pixel_format = pixfmt;
@@ -819,13 +829,7 @@
   }
 
   if (frame_rate) {
-    if (frm_interval.discrete.denominator %
-        frm_interval.discrete.numerator) {
-      printf("<<< Error: frame rate is a floating point %d/%d.>>>\n",
-          frm_interval.discrete.denominator, frm_interval.discrete.numerator);
-      return false;
-    }
-    *frame_rate = frm_interval.discrete.denominator /
+    *frame_rate = static_cast<float>(frm_interval.discrete.denominator) /
         frm_interval.discrete.numerator;
   }
   return true;
@@ -931,21 +935,23 @@
   return true;
 }
 
-bool V4L2Device::SetFrameRate(uint32_t fps) {
+bool V4L2Device::SetFrameRate(float fps) {
   v4l2_streamparm param;
   if (!GetParam(&param))
     return false;
-  param.parm.capture.timeperframe.numerator = 1;
-  param.parm.capture.timeperframe.denominator = fps;
+
+  const int kFrameRatePrecision = 10000;
+  param.parm.capture.timeperframe.numerator = kFrameRatePrecision;
+  param.parm.capture.timeperframe.denominator = fps * kFrameRatePrecision;
   return SetParam(&param);
 }
 
-uint32_t V4L2Device::GetFrameRate() {
+float V4L2Device::GetFrameRate() {
   v4l2_streamparm param;
   if (!GetParam(&param))
     return -1;
-  return (param.parm.capture.timeperframe.denominator /
-          param.parm.capture.timeperframe.numerator);
+  return static_cast<float>(param.parm.capture.timeperframe.denominator) /
+      param.parm.capture.timeperframe.numerator;
 }
 
 uint64_t V4L2Device::Now() {
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 a890302..ce4eb19 100644
--- a/client/site_tests/camera_V4L2/src/media_v4l2_device.h
+++ b/client/site_tests/camera_V4L2/src/media_v4l2_device.h
@@ -40,7 +40,7 @@
                           uint32_t width,
                           uint32_t height,
                           uint32_t pixfmt,
-                          uint32_t fps);
+                          float fps);
   virtual bool UninitDevice();
   virtual bool StartCapture();
   virtual bool StopCapture();
@@ -65,14 +65,14 @@
   bool SetCrop(v4l2_crop* crop);
   bool GetParam(v4l2_streamparm* param);
   bool SetParam(v4l2_streamparm* param);
-  bool SetFrameRate(uint32_t fps);
+  bool SetFrameRate(float fps);
   bool GetPixelFormat(uint32_t index, uint32_t* pixfmt);
   bool GetFrameSize(
       uint32_t index, uint32_t pixfmt, uint32_t *width, uint32_t *height);
   bool GetFrameInterval(
       uint32_t index, uint32_t pixfmt, uint32_t width, uint32_t height,
-      uint32_t* frame_rate);
-  uint32_t GetFrameRate();
+      float* frame_rate);
+  float GetFrameRate();
   bool Stop();
 
   // Getter.
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 ad11f8e..8288b01 100644
--- a/client/site_tests/camera_V4L2/src/media_v4l2_test.cc
+++ b/client/site_tests/camera_V4L2/src/media_v4l2_test.cc
@@ -32,7 +32,7 @@
 
 int RunTest(V4L2Device* device, V4L2Device::IOMethod io,
             uint32_t buffers, uint32_t capture_time_in_sec, uint32_t width,
-            uint32_t height, uint32_t pixfmt, uint32_t fps) {
+            uint32_t height, uint32_t pixfmt, float fps) {
   int32_t retcode = 0;
   if (!device->InitDevice(io, width, height, pixfmt, fps))
     retcode = 1;
@@ -84,7 +84,7 @@
       };
 
       format.frame_rates.clear();
-      uint32_t frame_rate;
+      float frame_rate;
       for (uint32_t k = 0; k < num_frame_rate; ++k) {
         if (!device->GetFrameInterval(k, format.fourcc, format.width,
                                       format.height, &frame_rate)) {
@@ -92,7 +92,7 @@
           return false;
         };
         // All supported resolution should have at least 1 fps.
-        if (frame_rate == 0) {
+        if (frame_rate < 1.0) {
           printf("[Error] Frame rate should be at least 1.\n");
           return false;
         }
@@ -137,7 +137,7 @@
   uint32_t width = 640;
   uint32_t height = 480;
   uint32_t pixfmt = V4L2_PIX_FMT_YUYV;
-  uint32_t fps = 30;
+  float fps = 30.0;
   uint32_t time_to_capture = 3;  // The unit is second.
   bool check_1280x960 = false;
 
@@ -188,13 +188,13 @@
   SupportedFormat max_resolution = GetMaximumResolution(supported_formats);
 
   SupportedFormats required_resolutions;
-  required_resolutions.push_back(SupportedFormat(320, 240, 0, 30));
-  required_resolutions.push_back(SupportedFormat(640, 480, 0, 30));
-  required_resolutions.push_back(SupportedFormat(1280, 720, 0, 30));
-  required_resolutions.push_back(SupportedFormat(1920, 1080, 0, 30));
-  required_resolutions.push_back(SupportedFormat(1600, 1200, 0, 30));
+  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(1600, 1200, 0, 30.0));
   if (check_1280x960) {
-    required_resolutions.push_back(SupportedFormat(1280, 960, 0, 30));
+    required_resolutions.push_back(SupportedFormat(1280, 960, 0, 30.0));
   }
 
   for (const auto& test_resolution : required_resolutions) {
@@ -222,9 +222,9 @@
       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) in %s\n",
-            test_format->width, test_format->height, test_format->fourcc,
-            dev_name.c_str());
+        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;
       }
 
@@ -234,9 +234,9 @@
           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) failed in %s\n",
+        printf("[Error] Capture test %dx%d (%08X) %.2f fps failed in %s\n",
             test_format->width, test_format->height, test_format->fourcc,
-            dev_name.c_str());
+            frame_rate, dev_name.c_str());
       }
     }
     if (!frame_rate_tested) {