Add tool for parsing camera config file in camera_V4L2

The three files are copied from arc-camera-service.
Only remove namespace and add constructor of SupportedFormat.

BUG=chromium:708396
TEST=test_that -b ${BOARD} ${IP} camera_V4L2

Change-Id: I3c9017289975358d8a61b3a329f0b0ebd5e68d10
Reviewed-on: https://chromium-review.googlesource.com/477734
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/Makefile b/client/site_tests/camera_V4L2/src/Makefile
index c44cc80..d3c88fa 100644
--- a/client/site_tests/camera_V4L2/src/Makefile
+++ b/client/site_tests/camera_V4L2/src/Makefile
@@ -2,20 +2,31 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-LDFLAGS =  -lrt -ldl
+PKG_CONFIG ?= pkg-config
+DEP_LIBS = libchrome-$(BASE_VER)
+CXXFLAGS += $(shell $(PKG_CONFIG) --cflags $(DEP_LIBS))
 
-LDFLAGS_UNITTEST = -lrt
+LDFLAGS = -lrt -ldl -std=c++11
+LDFLAGS += $(shell $(PKG_CONFIG) --libs $(DEP_LIBS))
+
+LDFLAGS_UNITTEST = -lrt -std=c++11
+LDFLAGS_UNITTEST += $(shell $(PKG_CONFIG) --libs $(DEP_LIBS))
 
 LDFLAGS_HELPER = -lrt
 
-SRC = media_v4l2_device.cc  \
-      media_v4l2_test.cc
+SRC = \
+	camera_characteristics.cc \
+	media_v4l2_device.cc  \
+	media_v4l2_test.cc
 
-SRC_UNITTEST = media_v4l2_device.cc \
-               media_v4l2_unittest.cc
+SRC_UNITTEST = \
+	camera_characteristics.cc \
+	media_v4l2_device.cc \
+	media_v4l2_unittest.cc
 
-SRC_HELPER = media_v4l2_device.cc \
-             media_v4l2_is_capture_device.cc
+SRC_HELPER = \
+	media_v4l2_device.cc \
+	media_v4l2_is_capture_device.cc
 
 TARGET = ../media_v4l2_test
 
diff --git a/client/site_tests/camera_V4L2/src/camera_characteristics.cc b/client/site_tests/camera_V4L2/src/camera_characteristics.cc
new file mode 100644
index 0000000..fa8eec0
--- /dev/null
+++ b/client/site_tests/camera_V4L2/src/camera_characteristics.cc
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "camera_characteristics.h"
+
+#include <ctype.h>
+
+#include <ios>
+#include <sstream>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+
+// /etc/camera/camera_characteristics.conf contains camera information which
+// driver cannot provide.
+static const char kCameraCharacteristicsConfigFile[] =
+    "/etc/camera/camera_characteristics.conf";
+
+/* Common parameters */
+static const char kLensFacing[] = "lens_facing";
+static const char kSensorOrientation[] = "sensor_orientation";
+static const char kUsbVidPid[] = "usb_vid_pid";
+static const char kLensInfoAvailableFocalLengths[] =
+    "lens_info_available_focal_lengths";
+static const char kLensInfoMinimumFocusDistance[] =
+    "lens_info_minimum_focus_distance";
+static const char kLensInfoOptimalFocusDistance[] =
+    "lens_info_optimal_focus_distance";
+
+/* HAL v1 parameters */
+static const char kHorizontalViewAngle_16_9[] = "horizontal_view_angle_16_9";
+static const char kHorizontalViewAngle_4_3[] = "horizontal_view_angle_4_3";
+static const char kVerticalViewAngle_16_9[] = "vertical_view_angle_16_9";
+static const char kVerticalViewAngle_4_3[] = "vertical_view_angle_4_3";
+
+/* HAL v3 parameters */
+static const char kLensInfoAvailableApertures[] =
+    "lens_info_available_apertures";
+static const char kSensorInfoPhysicalSize[] = "sensor_info_physical_size";
+static const char kSensorInfoPixelArraySize[] = "sensor_info_pixel_array_size";
+
+/* Special parameters */
+static const char kFramesToSkipAfterStreamon[] =
+    "frames_to_skip_after_streamon";
+static const char kResolution1280x960Unsupported[] =
+    "resolution_1280x960_unsupported";
+static const char kConstantFramerateUnsupported[] =
+    "constant_framerate_unsupported";
+
+static const struct DeviceInfo kDefaultCharacteristics = {
+  "",     // device_path
+  "",     // usb_vid
+  "",     // usb_pid
+  0,      // lens_facing
+  0,      // sensor_orientation
+  0,      // frames_to_skip_after_streamon
+  66.5,   // horizontal_view_angle_16_9
+  0.0,    // horizontal_view_angle_4_3
+  {1.6},  // lens_info_available_focal_lengths
+  0.3,    // lens_info_minimum_focus_distance
+  0.5,    // lens_info_optimal_focus_distance
+  42.5,   // vertical_view_angle_16_9
+  0.0,    // vertical_view_angle_4_3
+  false,  // resolution_1280x960_unsupported
+  false   // constant_framerate_unsupported
+};
+
+CameraCharacteristics::CameraCharacteristics() {
+}
+
+CameraCharacteristics::~CameraCharacteristics() {
+}
+
+const DeviceInfos CameraCharacteristics::GetCharacteristicsFromFile(
+    const std::unordered_map<std::string, std::string>& devices) {
+  const base::FilePath path(kCameraCharacteristicsConfigFile);
+  FILE* file = base::OpenFile(path, "r");
+  if (!file) {
+    LOG(ERROR) << __func__ << ": Can't open file "
+               << kCameraCharacteristicsConfigFile
+               << ". Use default characteristics instead";
+    DeviceInfos device_infos;
+    for (const auto& device : devices) {
+      device_infos.push_back(kDefaultCharacteristics);
+      size_t pos = device.first.find(":");
+      if (pos != std::string::npos) {
+        // If configuration file doesn't exist, the two attributes should be
+        // true.
+        device_infos.back().resolution_1280x960_unsupported = true;
+        device_infos.back().constant_framerate_unsupported = true;
+
+        device_infos.back().device_path = device.second;
+        device_infos.back().usb_vid = device.first.substr(0, pos - 1);
+        device_infos.back().usb_pid = device.first.substr(pos + 1);
+      } else {
+        LOG(ERROR) << __func__ << ": Invalid device: " << device.first;
+      }
+    }
+    return device_infos;
+  }
+
+  DeviceInfos tmp_device_infos;
+  char buffer[256], key[256], value[256];
+  uint32_t camera_id;
+  uint32_t module_id = -1;
+  std::string vid, pid;
+  while (fgets(buffer, sizeof(buffer), file)) {
+    // Skip comments and empty lines.
+    if (buffer[0] == '#' || buffer[0] == '\n') {
+      continue;
+    }
+
+    if (sscanf(buffer, "%[^=]=%s", key, value) != 2) {
+      LOG(ERROR) << __func__ << ": Illegal format: " << buffer;
+      continue;
+    }
+    std::vector<char*> sub_keys;
+    char* sub_key = strtok(key, ".");
+    while (sub_key) {
+      sub_keys.push_back(sub_key);
+      sub_key = strtok(NULL, ".");
+    }
+
+    if (sscanf(sub_keys[0], "camera%u", &camera_id) != 1) {
+      LOG(ERROR) << __func__ << ": Illegal format: " << sub_keys[0];
+      continue;
+    }
+    if (camera_id > tmp_device_infos.size()) {
+      // Camera id should be ascending by one.
+      LOG(ERROR) << __func__ << ": Invalid camera id: " << camera_id;
+      continue;
+    } else if (camera_id == tmp_device_infos.size()) {
+      tmp_device_infos.push_back(kDefaultCharacteristics);
+    }
+
+    uint32_t tmp_module_id;
+    // Convert value to lower case.
+    for (char* p = value; *p; ++p) *p = tolower(*p);
+
+    if (sscanf(sub_keys[1], "module%u", &tmp_module_id) != 1) {
+      AddPerCameraCharacteristic(
+          camera_id, sub_keys[1], value, &tmp_device_infos);
+    } else {
+      if (tmp_module_id != module_id) {
+        vid.clear();
+        pid.clear();
+        module_id = tmp_module_id;
+      }
+      if (strcmp(sub_keys[2], kUsbVidPid) == 0) {
+        char tmp_vid[256], tmp_pid[256];
+        if (sscanf(value, "%[0-9a-z]:%[0-9a-z]", tmp_vid, tmp_pid) != 2) {
+          LOG(ERROR) << __func__ << ": Invalid format: " << sub_keys[2];
+          continue;
+        }
+        vid = tmp_vid;
+        pid = tmp_pid;
+        const auto& device = devices.find(value);
+        if (device != devices.end()) {
+          tmp_device_infos[camera_id].usb_vid = vid;
+          tmp_device_infos[camera_id].usb_pid = pid;
+          tmp_device_infos[camera_id].device_path = device->second;
+        }
+
+        VLOG(1) << __func__ << ": Camera" << camera_id << " "
+                << kUsbVidPid << ": " << value;
+      } else if (!vid.empty() && !pid.empty()) {
+        // Some characteristics are module-specific, so only matched ones are
+        // selected.
+        if (tmp_device_infos[camera_id].usb_vid != vid ||
+            tmp_device_infos[camera_id].usb_pid != pid) {
+          VLOG(1) << __func__ << ": Mismatched module: "
+                  << "vid: " << vid << " pid: " << pid;
+          continue;
+        }
+        AddPerModuleCharacteristic(
+            camera_id, sub_keys[2], value, &tmp_device_infos);
+      } else {
+        // Characteristic usb_vid_pid should come before other module-specific
+        // characteristics.
+        LOG(ERROR) << __func__ << ": Illegal format."
+                   << " usb_vid_pid should come before: " << buffer;
+      }
+    }
+  }
+  base::CloseFile(file);
+
+  DeviceInfos device_infos;
+  // Some devices use the same camera_characteristics.conf and have different
+  // number of cameras.
+  for (size_t id = 0; id < tmp_device_infos.size(); ++id) {
+    if (tmp_device_infos[id].device_path.empty()) {
+      LOG(INFO) << __func__ << ": No matching module for camera" << id;
+    } else {
+      device_infos.push_back(tmp_device_infos[id]);
+    }
+  }
+  return device_infos;
+}
+
+void CameraCharacteristics::AddPerCameraCharacteristic(
+    uint32_t camera_id, const char* characteristic, const char* value,
+    DeviceInfos* device_infos) {
+  VLOG(1) << __func__ << ": " << characteristic << ": " << value;
+  if (strcmp(characteristic, kLensFacing) == 0) {
+    (*device_infos)[camera_id].lens_facing = strtol(value, NULL, 10);
+  } else if (strcmp(characteristic, kSensorOrientation) == 0) {
+    (*device_infos)[camera_id].sensor_orientation = strtol(value, NULL, 10);
+  } else if (strcmp(characteristic, kResolution1280x960Unsupported) == 0) {
+    std::istringstream is(value);
+    is >> std::boolalpha
+       >> (*device_infos)[camera_id].resolution_1280x960_unsupported;
+  } else if (strcmp(characteristic, kConstantFramerateUnsupported) == 0) {
+    std::istringstream is(value);
+    is >> std::boolalpha
+       >> (*device_infos)[camera_id].constant_framerate_unsupported;
+  } else {
+    LOG(ERROR) << __func__ << ": Unknown characteristic: " << characteristic
+               << " value: " << value;
+  }
+}
+
+void CameraCharacteristics::AddPerModuleCharacteristic(
+    uint32_t camera_id, const char* characteristic, const char* value,
+    DeviceInfos* device_infos) {
+  if (strcmp(characteristic, kFramesToSkipAfterStreamon) == 0) {
+    VLOG(1) << __func__ << ": " << characteristic << ": " << value;
+    (*device_infos)[camera_id].frames_to_skip_after_streamon =
+        strtol(value, NULL, 10);
+  } else if (strcmp(characteristic, kHorizontalViewAngle_16_9) == 0) {
+    AddFloatValue(value, kHorizontalViewAngle_16_9,
+                  &(*device_infos)[camera_id].horizontal_view_angle_16_9);
+  } else if (strcmp(characteristic, kHorizontalViewAngle_4_3) == 0) {
+    AddFloatValue(value, kHorizontalViewAngle_4_3,
+                  &(*device_infos)[camera_id].horizontal_view_angle_4_3);
+  } else if (strcmp(characteristic, kLensInfoAvailableFocalLengths) == 0) {
+    (*device_infos)[camera_id].lens_info_available_focal_lengths.clear();
+    char tmp_value[256];
+    strcpy(tmp_value, value);
+    char* focal_length = strtok(tmp_value, ",");
+    while (focal_length) {
+      float tmp_focal_length = strtof(focal_length, NULL);
+      if (tmp_focal_length != 0.0) {
+        VLOG(1) << __func__ << ": " << characteristic << ": "
+                << tmp_focal_length;
+        (*device_infos)[camera_id].lens_info_available_focal_lengths.push_back(
+            tmp_focal_length);
+      } else {
+        LOG(ERROR) << __func__ << ": Invalid " << characteristic << ": "
+                   << value;
+        (*device_infos)[camera_id].lens_info_available_focal_lengths.clear();
+        (*device_infos)[camera_id].lens_info_available_focal_lengths.push_back(
+            kDefaultCharacteristics.lens_info_available_focal_lengths[0]);
+        break;
+      }
+      focal_length = strtok(NULL, ",");
+    }
+  } else if (strcmp(characteristic, kLensInfoMinimumFocusDistance) == 0) {
+    AddFloatValue(value, kLensInfoMinimumFocusDistance,
+                  &(*device_infos)[camera_id].lens_info_minimum_focus_distance);
+  } else if (strcmp(characteristic, kLensInfoOptimalFocusDistance) == 0) {
+    AddFloatValue(value, kLensInfoOptimalFocusDistance,
+                  &(*device_infos)[camera_id].lens_info_optimal_focus_distance);
+  } else if (strcmp(characteristic, kVerticalViewAngle_16_9) == 0) {
+    AddFloatValue(value, kVerticalViewAngle_16_9,
+                  &(*device_infos)[camera_id].vertical_view_angle_16_9);
+  } else if (strcmp(characteristic, kVerticalViewAngle_4_3) == 0) {
+    AddFloatValue(value, kVerticalViewAngle_4_3,
+                  &(*device_infos)[camera_id].vertical_view_angle_4_3);
+  } else if (strcmp(characteristic, kLensInfoAvailableApertures) == 0) {
+    /* Do nothing. This is for hal v3 */
+  } else if (strcmp(characteristic, kSensorInfoPhysicalSize) == 0) {
+    /* Do nothing. This is for hal v3 */
+  } else if (strcmp(characteristic, kSensorInfoPixelArraySize) == 0) {
+    /* Do nothing. This is for hal v3 */
+  } else {
+    LOG(ERROR) << __func__ << ": Unknown characteristic: " << characteristic
+               << " value: " << value;
+  }
+}
+
+void CameraCharacteristics::AddFloatValue(const char* value,
+                                          const char* characteristic_name,
+                                          float* characteristic) {
+  float tmp_value = strtof(value, NULL);
+  if (tmp_value != 0.0) {
+    VLOG(1) << __func__ << ": " << characteristic_name << ": " << value;
+    *characteristic = tmp_value;
+  } else {
+    LOG(ERROR) << __func__ << ": Invalid " << characteristic_name
+               << ": " << value;
+  }
+}
diff --git a/client/site_tests/camera_V4L2/src/camera_characteristics.h b/client/site_tests/camera_V4L2/src/camera_characteristics.h
new file mode 100644
index 0000000..3d2310b
--- /dev/null
+++ b/client/site_tests/camera_V4L2/src/camera_characteristics.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef CAMERA_CHARACTERISTICS_H_
+#define CAMERA_CHARACTERISTICS_H_
+
+#include "common_types.h"
+
+#include <unordered_map>
+
+#include <base/macros.h>
+
+// CameraCharacteristics reads the file /etc/camera/camera_characteristics.conf.
+// There are several assumptions of the config file:
+//  1. Each line should be at most 256 characters long.
+//  2. camera id should be in ascending order (i.e., 0, 1, 2, ...).
+//  3. usb_vid_pid should be the first subkey.
+//  4. All configs of a module should be put together.
+//
+// Example of the config file:
+//  camera0.lens_facing=0
+//  camera0.sensor_orientation=0
+//  camera0.module0.usb_vid_pid=0123:4567
+//  camera0.module0.horizontal_view_angle=68.4
+//  camera0.module0.lens_info_available_focal_lengths=1.64
+//  camera0.module0.lens_info_minimum_focus_distance=0.22
+//  camera0.module0.lens_info_optimal_focus_distance=0.5
+//  camera0.module0.vertical_view_angle=41.6
+//  camera0.module1.usb_vid_pid=89ab:cdef
+//  camera0.module1.lens_info_available_focal_lengths=1.69,2
+//  camera1.lens_facing=1
+//  camera1.sensor_orientation=180
+class CameraCharacteristics {
+ public:
+  CameraCharacteristics();
+  ~CameraCharacteristics();
+
+  // Parses /etc/camera/camera_characteristics.conf.
+  // Returns DeviceInfos with default characteristics if the config file doesn't
+  // exist.
+  const DeviceInfos GetCharacteristicsFromFile(
+      const std::unordered_map<std::string, std::string>& devices);
+
+ private:
+  void AddPerCameraCharacteristic(
+      uint32_t camera_id, const char* characteristic, const char* value,
+      DeviceInfos* device_infos);
+  void AddPerModuleCharacteristic(
+      uint32_t camera_id, const char* characteristic, const char* value,
+      DeviceInfos* device_infos);
+  void AddFloatValue(const char* value, const char* characteristic_name,
+                     float* characteristic);
+
+  DISALLOW_COPY_AND_ASSIGN(CameraCharacteristics);
+};
+
+#endif  // CAMERA_CHARACTERISTICS_H_
diff --git a/client/site_tests/camera_V4L2/src/common_types.h b/client/site_tests/camera_V4L2/src/common_types.h
new file mode 100644
index 0000000..f16d143
--- /dev/null
+++ b/client/site_tests/camera_V4L2/src/common_types.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef COMMON_TYPES_H_
+#define COMMON_TYPES_H_
+
+#include <string>
+#include <vector>
+
+// The types in this file should match Android camera HAL.
+
+struct DeviceInfo {
+  std::string device_path;
+  std::string usb_vid; // USB vender id
+  std::string usb_pid; // USB product id
+  uint32_t lens_facing; // Direction the camera faces relative to device screen.
+  // Clockwise angle through which the output image needs to be rotated to be
+  // upright on the device screen in its native orientation.
+  int32_t sensor_orientation;
+  uint32_t frames_to_skip_after_streamon;
+  float horizontal_view_angle_16_9;
+  float horizontal_view_angle_4_3;
+  std::vector<float> lens_info_available_focal_lengths;
+  float lens_info_minimum_focus_distance;
+  float lens_info_optimal_focus_distance;
+  float vertical_view_angle_16_9;
+  float vertical_view_angle_4_3;
+  // The camera doesn't support 1280x960 resolution when the maximum resolution
+  // of the camear is larger than 1080p.
+  bool resolution_1280x960_unsupported;
+  // The camera doesn't support constant frame rate. That means HAL cannot set
+  // V4L2_CID_EXPOSURE_AUTO_PRIORITY to 0 to have constant frame rate in low
+  // light environment.
+  bool constant_framerate_unsupported;
+};
+
+typedef std::vector<DeviceInfo> DeviceInfos;
+
+struct SupportedFormat {
+  SupportedFormat() {}
+  SupportedFormat(uint32_t w, uint32_t h, uint32_t fmt, uint32_t fps)
+      : width(w), height(h), fourcc(fmt) {
+    frame_rates.push_back(fps);
+  }
+  uint32_t width;
+  uint32_t height;
+  uint32_t fourcc;
+  // All the supported frame rates in fps with given width, height, and
+  // 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;
+};
+
+typedef std::vector<SupportedFormat> SupportedFormats;
+
+#endif