• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 The Chromium OS Authors. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  */
6 
7 #include "camera_characteristics.h"
8 
9 #include <fstream>
10 #include <map>
11 #include <set>
12 #include <sstream>
13 #include <utility>
14 #include <vector>
15 
16 #include <base/files/file_util.h>
17 #include <base/strings/string_split.h>
18 #include <base/strings/string_util.h>
19 #include <re2/re2.h>
20 
21 // TODO(shik): Should we replace the custom format by proto/json/yaml/toml/xml?
22 
23 namespace {
24 
25 template <typename T>
ParseSize(const std::string & value,T * width,T * height)26 void ParseSize(const std::string& value, T* width, T* height) {
27   CHECK(RE2::FullMatch(value, "(.*)x(.*)", width, height));
28 }
29 
30 template <typename T>
ParseCommaSeparated(const std::string & value)31 std::vector<T> ParseCommaSeparated(const std::string& value) {
32   std::vector<std::string> values = base::SplitString(
33       value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
34   size_t n = values.size();
35   std::vector<T> res(n);
36   for (size_t i = 0; i < n; i++) {
37     CHECK(std::istringstream(values[i]) >> res[i])
38         << "Failed to parse " << values[i];
39   }
40   return res;
41 }
42 
SetEntry(const std::string & key,const std::string & value,DeviceInfo * info)43 void SetEntry(const std::string& key,
44               const std::string& value,
45               DeviceInfo* info) {
46   // Follow the same order as defined in common_types.h
47   if (key == "usb_vid_pid") {
48     CHECK(RE2::FullMatch(value, "([0-9a-f]{4}):([0-9a-f]{4})", &info->usb_vid,
49                          &info->usb_pid));
50   } else if (key == "frames_to_skip_after_streamon") {
51     info->frames_to_skip_after_streamon = stoi(value);
52   } else if (key == "constant_framerate_unsupported") {
53     std::istringstream(value) >> std::boolalpha >>
54         info->constant_framerate_unsupported;
55   } else if (key == "lens_facing") {
56     info->lens_facing = stoi(value);
57   } else if (key == "sensor_orientation") {
58     info->sensor_orientation = stoi(value);
59   } else if (key == "lens_info_available_apertures") {
60     info->lens_info_available_apertures = ParseCommaSeparated<float>(value);
61   } else if (key == "lens_info_available_focal_lengths") {
62     info->lens_info_available_focal_lengths = ParseCommaSeparated<float>(value);
63   } else if (key == "lens_info_minimum_focus_distance") {
64     info->lens_info_minimum_focus_distance = stof(value);
65   } else if (key == "lens_info_optimal_focus_distance") {
66     info->lens_info_optimal_focus_distance = stof(value);
67   } else if (key == "sensor_info_physical_size") {
68     ParseSize(value, &info->sensor_info_physical_size_width,
69               &info->sensor_info_physical_size_height);
70   } else if (key == "sensor_info_pixel_array_size") {
71     ParseSize(value, &info->sensor_info_pixel_array_size_width,
72               &info->sensor_info_pixel_array_size_height);
73   } else if (key == "horizontal_view_angle_16_9") {
74     info->horizontal_view_angle_16_9 = stof(value);
75   } else if (key == "horizontal_view_angle_4_3") {
76     info->horizontal_view_angle_4_3 = stof(value);
77   } else if (key == "vertical_view_angle_16_9") {
78     info->vertical_view_angle_16_9 = stof(value);
79   } else if (key == "vertical_view_angle_4_3") {
80     info->vertical_view_angle_4_3 = stof(value);
81   } else {
82     LOGF(WARNING) << "Unknown or deprecated key: " << key << " value: "
83                   << value;
84   }
85 }
86 
87 }  // namespace
88 
89 // static
ConfigFileExists()90 bool CameraCharacteristics::ConfigFileExists() {
91   return base::PathExists(kCameraCharacteristicsConfigFile);
92 }
93 
CameraCharacteristics()94 CameraCharacteristics::CameraCharacteristics() {
95   if (ConfigFileExists()) {
96     InitFrom(kCameraCharacteristicsConfigFile);
97   }
98 }
99 
CameraCharacteristics(const base::FilePath & config_file)100 CameraCharacteristics::CameraCharacteristics(
101     const base::FilePath& config_file) {
102   InitFrom(config_file);
103 }
104 
InitFrom(const base::FilePath & config_file)105 void CameraCharacteristics::InitFrom(const base::FilePath& config_file) {
106   CHECK(base::PathExists(config_file))
107       << config_file.value() << " does not exist";
108   std::ifstream ifs(config_file.value());
109   CHECK(ifs.good()) << "Can't open file " << config_file.value();
110 
111   // Used as per_camera_infos[camera_id].
112   DeviceInfos per_camera_infos;
113 
114   // Used as per_module_infos[camera_id][module_id].
115   std::vector<DeviceInfos> per_module_infos;
116 
117   for (std::string line; std::getline(ifs, line);) {
118     line = base::ToLowerASCII(base::TrimWhitespaceASCII(line, base::TRIM_ALL));
119 
120     // Skip empty lines and comments.
121     if (line.empty() || line[0] == '#') {
122       continue;
123     }
124 
125     size_t camera_id, module_id;
126     std::string key, value;
127 
128     if (RE2::FullMatch(line, R"(camera(\d+)\.([^.=]+)=(.+))", &camera_id, &key,
129                        &value)) {
130       // camera{x}.{key}={value}
131       if (camera_id == per_camera_infos.size()) {
132         per_camera_infos.push_back({.camera_id = static_cast<int>(camera_id)});
133         per_module_infos.push_back({});
134       }
135       CHECK(camera_id + 1 == per_camera_infos.size())
136           << "Invalid camera id " << camera_id;
137       CHECK(per_module_infos.back().empty())
138           << "Module specific characteristics should come after camera "
139              "specific ones";
140 
141       SetEntry(key, value, &per_camera_infos[camera_id]);
142     } else if (RE2::FullMatch(line, R"(camera(\d+)\.module(\d+).([^.=]+)=(.+))",
143                               &camera_id, &module_id, &key, &value)) {
144       // camera{x}.module{y}.{key}={value}
145       DeviceInfos& module_infos = per_module_infos[camera_id];
146       if (module_id == module_infos.size()) {
147         module_infos.push_back(per_camera_infos[camera_id]);
148       }
149       CHECK(module_id + 1 == module_infos.size())
150           << "Invalid module id " << module_id;
151 
152       SetEntry(key, value, &module_infos[module_id]);
153     } else {
154       LOGF(FATAL) << "Failed to parse: " << line;
155     }
156   }
157 
158   for (const auto& camera_infos : per_module_infos) {
159     for (const auto& module_info : camera_infos) {
160       auto ret = camera_module_infos_.insert(
161           {{module_info.usb_vid, module_info.usb_pid}, module_info});
162       CHECK(ret.second) << "Duplicate vid:pid in config";
163     }
164   }
165 }
166 
Find(const std::string & vid,const std::string & pid) const167 const DeviceInfo* CameraCharacteristics::Find(const std::string& vid,
168                                               const std::string& pid) const {
169   auto it = camera_module_infos_.find({vid, pid});
170   if (it != camera_module_infos_.end()) {
171     const DeviceInfo& info = it->second;
172     VLOGF(1) << "Found camera" << info.camera_id << " in characteristics"
173              << " with vid:pid = " << vid << ":" << pid;
174     return &it->second;
175   } else {
176     VLOGF(1) << "No camera with vid:pid = " << vid << ":" << pid
177              << " found in characteristics";
178     return nullptr;
179   }
180 }
181