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