1 /*
2  * Copyright 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 <iterator>
19 #include <memory>
20 
21 #include "VirtualCameraDevice.h"
22 #include "aidl/android/companion/virtualcamera/Format.h"
23 #include "aidl/android/companion/virtualcamera/SupportedStreamConfiguration.h"
24 #include "aidl/android/companion/virtualcamera/VirtualCameraConfiguration.h"
25 #include "aidl/android/hardware/camera/device/CameraMetadata.h"
26 #include "aidl/android/hardware/camera/device/StreamConfiguration.h"
27 #include "aidl/android/hardware/graphics/common/PixelFormat.h"
28 #include "android/binder_interface_utils.h"
29 #include "gmock/gmock.h"
30 #include "gtest/gtest.h"
31 #include "log/log_main.h"
32 #include "system/camera_metadata.h"
33 #include "util/MetadataUtil.h"
34 #include "util/Util.h"
35 #include "utils/Errors.h"
36 
37 namespace android {
38 namespace companion {
39 namespace virtualcamera {
40 namespace {
41 
42 using ::aidl::android::companion::virtualcamera::Format;
43 using ::aidl::android::companion::virtualcamera::LensFacing;
44 using ::aidl::android::companion::virtualcamera::SensorOrientation;
45 using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
46 using ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration;
47 using ::aidl::android::hardware::camera::device::CameraMetadata;
48 using ::aidl::android::hardware::camera::device::Stream;
49 using ::aidl::android::hardware::camera::device::StreamConfiguration;
50 using ::aidl::android::hardware::camera::device::StreamType;
51 using ::aidl::android::hardware::graphics::common::PixelFormat;
52 using ::testing::ElementsAre;
53 using ::testing::UnorderedElementsAreArray;
54 using metadata_stream_t =
55     camera_metadata_enum_android_scaler_available_stream_configurations_t;
56 
57 constexpr char kCameraId[] = "42";
58 constexpr int kQvgaWidth = 320;
59 constexpr int kQvgaHeight = 240;
60 constexpr int kVgaWidth = 640;
61 constexpr int kVgaHeight = 480;
62 constexpr int kHdWidth = 1280;
63 constexpr int kHdHeight = 720;
64 constexpr int kMaxFps = 30;
65 constexpr int kDefaultDeviceId = 0;
66 
67 const Stream kVgaYUV420Stream = Stream{
68     .streamType = StreamType::OUTPUT,
69     .width = kVgaWidth,
70     .height = kVgaHeight,
71     .format = PixelFormat::YCBCR_420_888,
72 };
73 
74 const Stream kVgaJpegStream = Stream{
75     .streamType = StreamType::OUTPUT,
76     .width = kVgaWidth,
77     .height = kVgaHeight,
78     .format = PixelFormat::BLOB,
79 };
80 
81 struct AvailableStreamConfiguration {
82   const int width;
83   const int height;
84   const int pixelFormat;
85   const metadata_stream_t streamConfiguration =
86       ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT;
87 };
88 
operator ==(const AvailableStreamConfiguration & a,const AvailableStreamConfiguration & b)89 bool operator==(const AvailableStreamConfiguration& a,
90                 const AvailableStreamConfiguration& b) {
91   return a.width == b.width && a.height == b.height &&
92          a.pixelFormat == b.pixelFormat &&
93          a.streamConfiguration == b.streamConfiguration;
94 }
95 
operator <<(std::ostream & os,const AvailableStreamConfiguration & config)96 std::ostream& operator<<(std::ostream& os,
97                          const AvailableStreamConfiguration& config) {
98   os << config.width << "x" << config.height << " (pixfmt "
99      << config.pixelFormat << ", streamConfiguration "
100      << config.streamConfiguration << ")";
101   return os;
102 }
103 
getAvailableStreamConfigurations(const CameraMetadata & metadata)104 std::vector<AvailableStreamConfiguration> getAvailableStreamConfigurations(
105     const CameraMetadata& metadata) {
106   const camera_metadata_t* const raw =
107       reinterpret_cast<const camera_metadata_t*>(metadata.metadata.data());
108   camera_metadata_ro_entry_t entry;
109   if (find_camera_metadata_ro_entry(
110           raw, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry) !=
111       NO_ERROR) {
112     return {};
113   }
114 
115   std::vector<AvailableStreamConfiguration> res;
116   for (int i = 0; i < entry.count; i += 4) {
117     res.push_back(AvailableStreamConfiguration{
118         .width = entry.data.i32[i + 1],
119         .height = entry.data.i32[i + 2],
120         .pixelFormat = entry.data.i32[i],
121         .streamConfiguration =
122             static_cast<metadata_stream_t>(entry.data.i32[i + 3])});
123   }
124   return res;
125 }
126 
127 struct VirtualCameraConfigTestParam {
128   VirtualCameraConfiguration inputConfig;
129   std::vector<AvailableStreamConfiguration> expectedAvailableStreamConfigs;
130 };
131 
132 class VirtualCameraDeviceCharacterisicsTest
133     : public testing::TestWithParam<VirtualCameraConfigTestParam> {};
134 
TEST_P(VirtualCameraDeviceCharacterisicsTest,cameraCharacteristicsForInputFormat)135 TEST_P(VirtualCameraDeviceCharacterisicsTest,
136        cameraCharacteristicsForInputFormat) {
137   const VirtualCameraConfigTestParam& param = GetParam();
138   std::shared_ptr<VirtualCameraDevice> camera =
139       ndk::SharedRefBase::make<VirtualCameraDevice>(
140           kCameraId, param.inputConfig, kDefaultDeviceId);
141 
142   CameraMetadata metadata;
143   ASSERT_TRUE(camera->getCameraCharacteristics(&metadata).isOk());
144   EXPECT_THAT(getAvailableStreamConfigurations(metadata),
145               UnorderedElementsAreArray(param.expectedAvailableStreamConfigs));
146 
147   // Configuration needs to succeed for every available stream configuration
148   for (const AvailableStreamConfiguration& config :
149        param.expectedAvailableStreamConfigs) {
150     StreamConfiguration configuration{
151         .streams = std::vector<Stream>{Stream{
152             .streamType = StreamType::OUTPUT,
153             .width = config.width,
154             .height = config.height,
155             .format = static_cast<PixelFormat>(config.pixelFormat),
156         }}};
157     bool aidl_ret;
158     ASSERT_TRUE(
159         camera->isStreamCombinationSupported(configuration, &aidl_ret).isOk());
160     EXPECT_TRUE(aidl_ret);
161   }
162 }
163 
164 INSTANTIATE_TEST_SUITE_P(
165     cameraCharacteristicsForInputFormat, VirtualCameraDeviceCharacterisicsTest,
166     testing::Values(
167         VirtualCameraConfigTestParam{
168             .inputConfig =
169                 VirtualCameraConfiguration{
170                     .supportedStreamConfigs = {SupportedStreamConfiguration{
171                         .width = kVgaWidth,
172                         .height = kVgaHeight,
173                         .pixelFormat = Format::YUV_420_888,
174                         .maxFps = kMaxFps}},
175                     .virtualCameraCallback = nullptr,
176                     .sensorOrientation = SensorOrientation::ORIENTATION_0,
177                     .lensFacing = LensFacing::FRONT},
178             .expectedAvailableStreamConfigs =
179                 {AvailableStreamConfiguration{
180                      .width = kQvgaWidth,
181                      .height = kQvgaHeight,
182                      .pixelFormat =
183                          ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
184                  AvailableStreamConfiguration{
185                      .width = kQvgaWidth,
186                      .height = kQvgaHeight,
187                      .pixelFormat =
188                          ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
189                  AvailableStreamConfiguration{
190                      .width = kQvgaWidth,
191                      .height = kQvgaHeight,
192                      .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB},
193                  AvailableStreamConfiguration{
194                      .width = kVgaWidth,
195                      .height = kVgaHeight,
196                      .pixelFormat =
197                          ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
198                  AvailableStreamConfiguration{
199                      .width = kVgaWidth,
200                      .height = kVgaHeight,
201                      .pixelFormat =
202                          ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
203                  AvailableStreamConfiguration{
204                      .width = kVgaWidth,
205                      .height = kVgaHeight,
206                      .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB}}},
207         VirtualCameraConfigTestParam{
208             .inputConfig =
209                 VirtualCameraConfiguration{
210                     .supportedStreamConfigs =
211                         {SupportedStreamConfiguration{
212                              .width = kVgaWidth,
213                              .height = kVgaHeight,
214                              .pixelFormat = Format::YUV_420_888,
215                              .maxFps = kMaxFps},
216                          SupportedStreamConfiguration{
217                              .width = kHdWidth,
218                              .height = kHdHeight,
219                              .pixelFormat = Format::YUV_420_888,
220                              .maxFps = kMaxFps}},
221                     .virtualCameraCallback = nullptr,
222                     .sensorOrientation = SensorOrientation::ORIENTATION_0,
223                     .lensFacing = LensFacing::BACK},
224             .expectedAvailableStreamConfigs = {
225                 AvailableStreamConfiguration{
226                     .width = kQvgaWidth,
227                     .height = kQvgaHeight,
228                     .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
229                 AvailableStreamConfiguration{
230                     .width = kQvgaWidth,
231                     .height = kQvgaHeight,
232                     .pixelFormat =
233                         ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
234                 AvailableStreamConfiguration{
235                     .width = kQvgaWidth,
236                     .height = kQvgaHeight,
237                     .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB},
238                 AvailableStreamConfiguration{
239                     .width = 640,
240                     .height = 360,
241                     .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
242                 AvailableStreamConfiguration{
243                     .width = 640,
244                     .height = 360,
245                     .pixelFormat =
246                         ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
247                 AvailableStreamConfiguration{
248                     .width = 640,
249                     .height = 360,
250                     .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB},
251                 AvailableStreamConfiguration{
252                     .width = kVgaWidth,
253                     .height = kVgaHeight,
254                     .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
255                 AvailableStreamConfiguration{
256                     .width = kVgaWidth,
257                     .height = kVgaHeight,
258                     .pixelFormat =
259                         ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
260                 AvailableStreamConfiguration{
261                     .width = kVgaWidth,
262                     .height = kVgaHeight,
263                     .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB},
264                 AvailableStreamConfiguration{
265                     .width = 1024,
266                     .height = 576,
267                     .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
268                 AvailableStreamConfiguration{
269                     .width = 1024,
270                     .height = 576,
271                     .pixelFormat =
272                         ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
273                 AvailableStreamConfiguration{
274                     .width = 1024,
275                     .height = 576,
276                     .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB},
277                 AvailableStreamConfiguration{
278                     .width = kHdWidth,
279                     .height = kHdHeight,
280                     .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
281                 AvailableStreamConfiguration{
282                     .width = kHdWidth,
283                     .height = kHdHeight,
284                     .pixelFormat =
285                         ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
286                 AvailableStreamConfiguration{
287                     .width = kHdWidth,
288                     .height = kHdHeight,
289                     .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB}}}));
290 
291 class VirtualCameraDeviceTest : public ::testing::Test {
292  public:
SetUp()293   void SetUp() override {
294     mCamera = ndk::SharedRefBase::make<VirtualCameraDevice>(
295         kCameraId,
296         VirtualCameraConfiguration{
297             .supportedStreamConfigs = {SupportedStreamConfiguration{
298                 .width = kVgaWidth,
299                 .height = kVgaHeight,
300                 .pixelFormat = Format::YUV_420_888,
301                 .maxFps = kMaxFps}},
302             .virtualCameraCallback = nullptr,
303             .sensorOrientation = SensorOrientation::ORIENTATION_0,
304             .lensFacing = LensFacing::FRONT},
305         kDefaultDeviceId);
306   }
307 
308  protected:
309   std::shared_ptr<VirtualCameraDevice> mCamera;
310 };
311 
TEST_F(VirtualCameraDeviceTest,configureMaximalNumberOfNonStallStreamsSuceeds)312 TEST_F(VirtualCameraDeviceTest, configureMaximalNumberOfNonStallStreamsSuceeds) {
313   StreamConfiguration config;
314   std::fill_n(std::back_insert_iterator(config.streams),
315               VirtualCameraDevice::kMaxNumberOfProcessedStreams,
316               kVgaYUV420Stream);
317 
318   bool aidl_ret;
319   ASSERT_TRUE(mCamera->isStreamCombinationSupported(config, &aidl_ret).isOk());
320   EXPECT_TRUE(aidl_ret);
321 }
322 
TEST_F(VirtualCameraDeviceTest,configureTooManyNonStallStreamsFails)323 TEST_F(VirtualCameraDeviceTest, configureTooManyNonStallStreamsFails) {
324   StreamConfiguration config;
325   std::fill_n(std::back_insert_iterator(config.streams),
326               VirtualCameraDevice::kMaxNumberOfProcessedStreams + 1,
327               kVgaYUV420Stream);
328 
329   bool aidl_ret;
330   ASSERT_TRUE(mCamera->isStreamCombinationSupported(config, &aidl_ret).isOk());
331   EXPECT_FALSE(aidl_ret);
332 }
333 
TEST_F(VirtualCameraDeviceTest,configureMaximalNumberOfStallStreamsSuceeds)334 TEST_F(VirtualCameraDeviceTest, configureMaximalNumberOfStallStreamsSuceeds) {
335   StreamConfiguration config;
336   std::fill_n(std::back_insert_iterator(config.streams),
337               VirtualCameraDevice::kMaxNumberOfStallStreams, kVgaJpegStream);
338 
339   bool aidl_ret;
340   ASSERT_TRUE(mCamera->isStreamCombinationSupported(config, &aidl_ret).isOk());
341   EXPECT_TRUE(aidl_ret);
342 }
343 
TEST_F(VirtualCameraDeviceTest,configureTooManyStallStreamsFails)344 TEST_F(VirtualCameraDeviceTest, configureTooManyStallStreamsFails) {
345   StreamConfiguration config;
346   std::fill_n(std::back_insert_iterator(config.streams),
347               VirtualCameraDevice::kMaxNumberOfStallStreams + 1, kVgaJpegStream);
348 
349   bool aidl_ret;
350   ASSERT_TRUE(mCamera->isStreamCombinationSupported(config, &aidl_ret).isOk());
351   EXPECT_FALSE(aidl_ret);
352 }
353 
TEST_F(VirtualCameraDeviceTest,thumbnailSizeWithCompatibleAspectRatio)354 TEST_F(VirtualCameraDeviceTest, thumbnailSizeWithCompatibleAspectRatio) {
355   CameraMetadata metadata;
356   ASSERT_TRUE(mCamera->getCameraCharacteristics(&metadata).isOk());
357 
358   // Camera is configured with VGA input, we expect 240 x 180 thumbnail size in
359   // characteristics, since it has same aspect ratio.
360   EXPECT_THAT(getJpegAvailableThumbnailSizes(metadata),
361               ElementsAre(Resolution(0, 0), Resolution(240, 180)));
362 }
363 
TEST_F(VirtualCameraDeviceTest,dump)364 TEST_F(VirtualCameraDeviceTest, dump) {
365   std::string expected = R"(  virtual_camera 42 belongs to virtual device 0
366   SupportedStreamConfiguration:
367     SupportedStreamConfiguration{width: 640, height: 480, pixelFormat: YUV_420_888, maxFps: 30})";
368   int expectedSize = expected.size() * sizeof(char);
369   char buffer[expectedSize];
370 
371   // Create an in memory fd
372   int fd = memfd_create("tmpFile", 0);
373   mCamera->dump(fd, {}, 0);
374 
375   // Check that we wrote the expected size
376   int dumpSize = lseek(fd, 0, SEEK_END);
377 
378   // Rewind and read from the fd
379   lseek(fd, 0, SEEK_SET);
380   read(fd, buffer, expectedSize);
381   close(fd);
382 
383   // Check the content of the dump
384   std::string name = std::string(buffer, expectedSize);
385   ASSERT_EQ(expected, name);
386   // Check the size after the content to display the string mismatch when a
387   // failure occurs
388   ASSERT_EQ(expectedSize, dumpSize);
389 }
390 
391 }  // namespace
392 }  // namespace virtualcamera
393 }  // namespace companion
394 }  // namespace android
395