1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <algorithm>
18 #include <charconv>
19 #include <numeric>
20 #include <string_view>
21 #include <math.h>
22 
23 #include <log/log.h>
24 
25 #include "debug.h"
26 #include "list_qemu_cameras.h"
27 #include "QemuCamera.h"
28 #include "qemu_channel.h"
29 
30 namespace android {
31 namespace hardware {
32 namespace camera {
33 namespace provider {
34 namespace implementation {
35 namespace hw {
36 namespace {
findToken(const std::string_view str,const std::string_view key,std::string_view * value)37 bool findToken(const std::string_view str, const std::string_view key, std::string_view* value) {
38     size_t pos = 0;
39     while (true) {
40         pos = str.find(key, pos);
41         if (pos == std::string_view::npos) {
42             return false;
43         } else if ((!pos || str[pos - 1] == ' ') &&
44                 (str.size() >= (pos + key.size() + 1)) &&
45                 (str[pos + key.size()] == '=')) {
46             const size_t vbegin = pos + key.size() + 1;
47             const size_t vend = str.find(' ', vbegin);
48             if (vend == std::string_view::npos) {
49                 *value = str.substr(vbegin, str.size() - vbegin);
50             } else {
51                 *value = str.substr(vbegin, vend - vbegin);
52             }
53             return true;
54         } else {
55             ++pos;
56         }
57     }
58 }
59 
parseResolutions(const std::string_view str,std::vector<Rect<uint16_t>> * supportedResolutions)60 bool parseResolutions(const std::string_view str, std::vector<Rect<uint16_t>>* supportedResolutions) {
61     const char* i = &*str.begin();
62     const char* const end = &*str.end();
63     if (i == end) {
64         return FAILURE(false);
65     }
66 
67     while (true) {
68         Rect<uint16_t> resolution;
69         std::from_chars_result r =  std::from_chars(i, end, resolution.width, 10);
70         if (r.ec != std::errc()) {
71             return FAILURE(false);
72         }
73         i = r.ptr;
74         if (i == end) {
75             return FAILURE(false);
76         }
77         if (*i != 'x') {
78             return FAILURE(false);
79         }
80         r =  std::from_chars(i + 1, end, resolution.height, 10);
81         if (r.ec != std::errc()) {
82             return FAILURE(false);
83         }
84         i = r.ptr;
85 
86         if ((resolution.width > 0) && (resolution.height > 0)) {
87             supportedResolutions->push_back(resolution);
88         }
89 
90         if (i == end) {
91             break;
92         } else {
93             if (*i == ',') {
94                 ++i;
95             } else {
96                 return FAILURE(false);
97             }
98         }
99     }
100 
101     return true;
102 }
103 
calcThumbnailResolution(const double aspectRatio,const size_t targetArea)104 Rect<uint16_t> calcThumbnailResolution(const double aspectRatio,
105                                        const size_t targetArea) {
106     // round to a multiple of 16, a tad down
107     const uint16_t height =
108         ((uint16_t(sqrt(targetArea / aspectRatio)) + 7) >> 4) << 4;
109 
110     // round width to be even
111     const uint16_t width = (uint16_t(height * aspectRatio + 1) >> 1) << 1;
112 
113     return {width, height};
114 }
115 
116 struct RectAreaComparator {
operator ()android::hardware::camera::provider::implementation::hw::__anon9a409dc00111::RectAreaComparator117     bool operator()(Rect<uint16_t> lhs, Rect<uint16_t> rhs) const {
118         const size_t lArea = lhs.area();
119         const size_t rArea = rhs.area();
120 
121         if (lArea < rArea) {
122             return true;
123         } else if (lArea > rArea) {
124             return false;
125         } else {
126             return lhs.width < rhs.width;
127         }
128     }
129 };
130 
131 } // namespace
132 
listQemuCameras(const std::function<void (HwCameraFactory)> & cameraSink)133 bool listQemuCameras(const std::function<void(HwCameraFactory)>& cameraSink) {
134     using namespace std::literals;
135     static const char kListQuery[] = "list";
136 
137     const auto fd = qemuOpenChannel();
138     if (!fd.ok()) { return false; }
139 
140     std::vector<uint8_t> data;
141     if (qemuRunQuery(fd.get(), kListQuery, sizeof(kListQuery), &data) < 0) {
142         return FAILURE(false);
143     }
144 
145     const char* i = reinterpret_cast<const char*>(&*data.begin());
146     const char* const end = reinterpret_cast<const char*>(&*data.end());
147 
148     while (i < end) {
149         const char* const lf = std::find(i, end, '\n');
150         if (lf == end) {
151             if (*i) {
152                 return FAILURE(false);
153             } else {
154                 break;
155             }
156         }
157 
158         // line='name=virtualscene channel=0 pix=876758866 dir=back framedims=640x480,352x288,320x240,176x144,1280x720,1280x960'
159         const std::string_view line(i, lf - i);
160 
161         std::string_view name;
162         if (!findToken(line, "name"sv, &name)) { return false; }
163 
164         std::string_view dir;
165         if (!findToken(line, "dir"sv, &dir)) { return FAILURE(false); }
166 
167         std::string_view framedims;
168         if (!findToken(line, "framedims"sv, &framedims)) { return FAILURE(false); }
169 
170         QemuCamera::Parameters params;
171         if (!parseResolutions(framedims, &params.supportedResolutions)) {
172             return FAILURE(false);
173         }
174 
175         if (params.supportedResolutions.empty()) {
176             return FAILURE(false);
177         } else {
178             std::sort(params.supportedResolutions.begin(),
179                       params.supportedResolutions.end(),
180                       RectAreaComparator());
181 
182             std::vector<Rect<uint16_t>> thumbnailResolutions;
183             thumbnailResolutions.push_back({0, 0});
184 
185             for (const Rect<uint16_t> res : params.supportedResolutions) {
186                 const double aspectRatio = double(res.width) / res.height;
187                 const size_t resArea4 = res.area() / 4;
188                 Rect<uint16_t> thumbnailRes;
189                 size_t thumbnailResArea;
190 
191                 do {
192                     thumbnailRes = calcThumbnailResolution(aspectRatio, 4900);
193                     thumbnailResArea = thumbnailRes.area();
194                     if ((thumbnailResArea > 0) && (thumbnailResArea < resArea4)) {
195                         thumbnailResolutions.push_back(thumbnailRes);
196                     } else {
197                         thumbnailRes = calcThumbnailResolution(aspectRatio, 1800);
198                         thumbnailResArea = thumbnailRes.area();
199                         if ((thumbnailResArea > 0) && (thumbnailRes.area() < resArea4)) {
200                             thumbnailResolutions.push_back(thumbnailRes);
201                         } else {
202                             // `res` is too small for a thumbnail
203                         }
204                         break;
205                     }
206 
207                     thumbnailRes = calcThumbnailResolution(aspectRatio, 19500);
208                     thumbnailResArea = thumbnailRes.area();
209                     if ((thumbnailResArea > 0) && (thumbnailRes.area() < resArea4)) {
210                         thumbnailResolutions.push_back(thumbnailRes);
211                     } else {
212                         break;
213                     }
214 
215                     thumbnailRes = calcThumbnailResolution(aspectRatio, 77000);
216                     thumbnailResArea = thumbnailRes.area();
217                     if ((thumbnailResArea > 0) && (thumbnailRes.area() < resArea4)) {
218                         thumbnailResolutions.push_back(thumbnailRes);
219                     }
220                 } while (false);
221             }
222 
223             std::sort(thumbnailResolutions.begin(), thumbnailResolutions.end(),
224                       RectAreaComparator());
225 
226             thumbnailResolutions.erase(std::unique(thumbnailResolutions.begin(),
227                                                    thumbnailResolutions.end()),
228                                        thumbnailResolutions.end());
229 
230             params.availableThumbnailResolutions = std::move(thumbnailResolutions);
231         }
232 
233         ALOGD("%s:%d found a '%.*s' QEMU camera, dir=%.*s framedims=%.*s",
234               __func__, __LINE__,
235               int(name.size()), name.data(),
236               int(dir.size()), dir.data(),
237               int(framedims.size()), framedims.data());
238 
239         params.name = std::string(name.begin(), name.end());
240 
241         params.sensorSize = std::accumulate(
242             std::next(params.supportedResolutions.begin()),
243             params.supportedResolutions.end(),
244             params.supportedResolutions.front(),
245             [](Rect<uint16_t> z, Rect<uint16_t> x) -> Rect<uint16_t> {
246                 return {std::max(z.width, x.width),
247                         std::max(z.height, x.height)};
248         });
249 
250         params.isBackFacing = (dir == "back"sv);
251 
252         cameraSink([params = std::move(params)]() {
253             return std::make_unique<QemuCamera>(params);
254         });
255 
256         i = lf + 1;
257     }
258 
259     return true;
260 }
261 
262 }  // namespace hw
263 }  // namespace implementation
264 }  // namespace provider
265 }  // namespace camera
266 }  // namespace hardware
267 }  // namespace android
268