blob: 3fe7c118432bb56d0e563eb2ddf53e6776888137 [file] [log] [blame]
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +01001/*
Biswarup Pal6152a302023-12-19 12:44:09 +00002 * Copyright 2023 The Android Open Source Project
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +01003 *
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
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +010017#include <algorithm>
18#include <iterator>
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010019#include <memory>
20
21#include "VirtualCameraDevice.h"
22#include "aidl/android/companion/virtualcamera/Format.h"
23#include "aidl/android/companion/virtualcamera/SupportedStreamConfiguration.h"
Biswarup Pal6152a302023-12-19 12:44:09 +000024#include "aidl/android/companion/virtualcamera/VirtualCameraConfiguration.h"
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010025#include "aidl/android/hardware/camera/device/CameraMetadata.h"
26#include "aidl/android/hardware/camera/device/StreamConfiguration.h"
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +010027#include "aidl/android/hardware/graphics/common/PixelFormat.h"
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010028#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"
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +010033#include "util/MetadataUtil.h"
34#include "util/Util.h"
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010035#include "utils/Errors.h"
36
37namespace android {
38namespace companion {
39namespace virtualcamera {
40namespace {
41
42using ::aidl::android::companion::virtualcamera::Format;
Biswarup Pal112458f2023-12-28 19:50:17 +000043using ::aidl::android::companion::virtualcamera::LensFacing;
Biswarup Pal6152a302023-12-19 12:44:09 +000044using ::aidl::android::companion::virtualcamera::SensorOrientation;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010045using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
Biswarup Pal6152a302023-12-19 12:44:09 +000046using ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010047using ::aidl::android::hardware::camera::device::CameraMetadata;
48using ::aidl::android::hardware::camera::device::Stream;
49using ::aidl::android::hardware::camera::device::StreamConfiguration;
50using ::aidl::android::hardware::camera::device::StreamType;
51using ::aidl::android::hardware::graphics::common::PixelFormat;
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +010052using ::testing::ElementsAre;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010053using ::testing::UnorderedElementsAreArray;
54using metadata_stream_t =
55 camera_metadata_enum_android_scaler_available_stream_configurations_t;
56
57constexpr int kCameraId = 42;
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +010058constexpr int kQvgaWidth = 320;
59constexpr int kQvgaHeight = 240;
60constexpr int k360pWidth = 640;
61constexpr int k360pHeight = 360;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010062constexpr int kVgaWidth = 640;
63constexpr int kVgaHeight = 480;
64constexpr int kHdWidth = 1280;
65constexpr int kHdHeight = 720;
Biswarup Pal6152a302023-12-19 12:44:09 +000066constexpr int kMaxFps = 30;
Biswarup Pal37a75182024-01-16 15:53:35 +000067constexpr int kDefaultDeviceId = 0;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010068
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +010069const Stream kVgaYUV420Stream = Stream{
70 .streamType = StreamType::OUTPUT,
71 .width = kVgaWidth,
72 .height = kVgaHeight,
73 .format = PixelFormat::YCBCR_420_888,
74};
75
76const Stream kVgaJpegStream = Stream{
77 .streamType = StreamType::OUTPUT,
78 .width = kVgaWidth,
79 .height = kVgaHeight,
80 .format = PixelFormat::BLOB,
81};
82
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010083struct AvailableStreamConfiguration {
84 const int width;
85 const int height;
86 const int pixelFormat;
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +010087 const metadata_stream_t streamConfiguration =
88 ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010089};
90
91bool operator==(const AvailableStreamConfiguration& a,
92 const AvailableStreamConfiguration& b) {
93 return a.width == b.width && a.height == b.height &&
94 a.pixelFormat == b.pixelFormat &&
95 a.streamConfiguration == b.streamConfiguration;
96}
97
98std::ostream& operator<<(std::ostream& os,
99 const AvailableStreamConfiguration& config) {
100 os << config.width << "x" << config.height << " (pixfmt "
101 << config.pixelFormat << ", streamConfiguration "
102 << config.streamConfiguration << ")";
103 return os;
104}
105
106std::vector<AvailableStreamConfiguration> getAvailableStreamConfigurations(
107 const CameraMetadata& metadata) {
108 const camera_metadata_t* const raw =
109 reinterpret_cast<const camera_metadata_t*>(metadata.metadata.data());
110 camera_metadata_ro_entry_t entry;
111 if (find_camera_metadata_ro_entry(
112 raw, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry) !=
113 NO_ERROR) {
114 return {};
115 }
116
117 std::vector<AvailableStreamConfiguration> res;
118 for (int i = 0; i < entry.count; i += 4) {
119 res.push_back(AvailableStreamConfiguration{
120 .width = entry.data.i32[i + 1],
121 .height = entry.data.i32[i + 2],
122 .pixelFormat = entry.data.i32[i],
123 .streamConfiguration =
124 static_cast<metadata_stream_t>(entry.data.i32[i + 3])});
125 }
126 return res;
127}
128
129struct VirtualCameraConfigTestParam {
Biswarup Pal6152a302023-12-19 12:44:09 +0000130 VirtualCameraConfiguration inputConfig;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100131 std::vector<AvailableStreamConfiguration> expectedAvailableStreamConfigs;
132};
133
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100134class VirtualCameraDeviceCharacterisicsTest
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100135 : public testing::TestWithParam<VirtualCameraConfigTestParam> {};
136
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100137TEST_P(VirtualCameraDeviceCharacterisicsTest,
138 cameraCharacteristicsForInputFormat) {
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100139 const VirtualCameraConfigTestParam& param = GetParam();
140 std::shared_ptr<VirtualCameraDevice> camera =
Biswarup Pal37a75182024-01-16 15:53:35 +0000141 ndk::SharedRefBase::make<VirtualCameraDevice>(
142 kCameraId, param.inputConfig, kDefaultDeviceId);
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100143
144 CameraMetadata metadata;
145 ASSERT_TRUE(camera->getCameraCharacteristics(&metadata).isOk());
146 EXPECT_THAT(getAvailableStreamConfigurations(metadata),
147 UnorderedElementsAreArray(param.expectedAvailableStreamConfigs));
148
149 // Configuration needs to succeed for every available stream configuration
150 for (const AvailableStreamConfiguration& config :
151 param.expectedAvailableStreamConfigs) {
152 StreamConfiguration configuration{
153 .streams = std::vector<Stream>{Stream{
154 .streamType = StreamType::OUTPUT,
155 .width = config.width,
156 .height = config.height,
157 .format = static_cast<PixelFormat>(config.pixelFormat),
158 }}};
159 bool aidl_ret;
160 ASSERT_TRUE(
161 camera->isStreamCombinationSupported(configuration, &aidl_ret).isOk());
162 EXPECT_TRUE(aidl_ret);
163 }
164}
165
166INSTANTIATE_TEST_SUITE_P(
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100167 cameraCharacteristicsForInputFormat, VirtualCameraDeviceCharacterisicsTest,
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100168 testing::Values(
169 VirtualCameraConfigTestParam{
Biswarup Pal6152a302023-12-19 12:44:09 +0000170 .inputConfig =
171 VirtualCameraConfiguration{
172 .supportedStreamConfigs = {SupportedStreamConfiguration{
173 .width = kVgaWidth,
174 .height = kVgaHeight,
175 .pixelFormat = Format::YUV_420_888,
176 .maxFps = kMaxFps}},
177 .virtualCameraCallback = nullptr,
Biswarup Pal112458f2023-12-28 19:50:17 +0000178 .sensorOrientation = SensorOrientation::ORIENTATION_0,
179 .lensFacing = LensFacing::FRONT},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100180 .expectedAvailableStreamConfigs =
181 {AvailableStreamConfiguration{
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100182 .width = kQvgaWidth,
183 .height = kQvgaHeight,
184 .pixelFormat =
185 ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
186 AvailableStreamConfiguration{
187 .width = kQvgaWidth,
188 .height = kQvgaHeight,
189 .pixelFormat =
190 ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
191 AvailableStreamConfiguration{
192 .width = kQvgaWidth,
193 .height = kQvgaHeight,
194 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100195 AvailableStreamConfiguration{
196 .width = kVgaWidth,
197 .height = kVgaHeight,
198 .pixelFormat =
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100199 ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100200 AvailableStreamConfiguration{
201 .width = kVgaWidth,
202 .height = kVgaHeight,
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100203 .pixelFormat =
204 ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
205 AvailableStreamConfiguration{
206 .width = kVgaWidth,
207 .height = kVgaHeight,
208 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB}}},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100209 VirtualCameraConfigTestParam{
Biswarup Pal6152a302023-12-19 12:44:09 +0000210 .inputConfig =
211 VirtualCameraConfiguration{
212 .supportedStreamConfigs =
213 {SupportedStreamConfiguration{
214 .width = kVgaWidth,
215 .height = kVgaHeight,
216 .pixelFormat = Format::YUV_420_888,
217 .maxFps = kMaxFps},
218 SupportedStreamConfiguration{
219 .width = kHdWidth,
220 .height = kHdHeight,
221 .pixelFormat = Format::YUV_420_888,
222 .maxFps = kMaxFps}},
223 .virtualCameraCallback = nullptr,
Biswarup Pal112458f2023-12-28 19:50:17 +0000224 .sensorOrientation = SensorOrientation::ORIENTATION_0,
225 .lensFacing = LensFacing::BACK},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100226 .expectedAvailableStreamConfigs = {
227 AvailableStreamConfiguration{
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100228 .width = kQvgaWidth,
229 .height = kQvgaHeight,
230 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
231 AvailableStreamConfiguration{
232 .width = kQvgaWidth,
233 .height = kQvgaHeight,
234 .pixelFormat =
235 ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
236 AvailableStreamConfiguration{
237 .width = kQvgaWidth,
238 .height = kQvgaHeight,
239 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB},
240 AvailableStreamConfiguration{
241 .width = 640,
242 .height = 360,
243 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
244 AvailableStreamConfiguration{
245 .width = 640,
246 .height = 360,
247 .pixelFormat =
248 ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
249 AvailableStreamConfiguration{
250 .width = 640,
251 .height = 360,
252 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB},
253 AvailableStreamConfiguration{
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100254 .width = kVgaWidth,
255 .height = kVgaHeight,
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100256 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100257 AvailableStreamConfiguration{
258 .width = kVgaWidth,
259 .height = kVgaHeight,
260 .pixelFormat =
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100261 ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100262 AvailableStreamConfiguration{
263 .width = kVgaWidth,
264 .height = kVgaHeight,
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100265 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB},
266 AvailableStreamConfiguration{
267 .width = 1024,
268 .height = 576,
269 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
270 AvailableStreamConfiguration{
271 .width = 1024,
272 .height = 576,
273 .pixelFormat =
274 ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
275 AvailableStreamConfiguration{
276 .width = 1024,
277 .height = 576,
278 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100279 AvailableStreamConfiguration{
280 .width = kHdWidth,
281 .height = kHdHeight,
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100282 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100283 AvailableStreamConfiguration{
284 .width = kHdWidth,
285 .height = kHdHeight,
286 .pixelFormat =
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100287 ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100288 AvailableStreamConfiguration{
289 .width = kHdWidth,
290 .height = kHdHeight,
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100291 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB}}}));
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100292
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100293class VirtualCameraDeviceTest : public ::testing::Test {
294 public:
295 void SetUp() override {
296 mCamera = ndk::SharedRefBase::make<VirtualCameraDevice>(
Biswarup Pal37a75182024-01-16 15:53:35 +0000297 kCameraId,
298 VirtualCameraConfiguration{
299 .supportedStreamConfigs = {SupportedStreamConfiguration{
300 .width = kVgaWidth,
301 .height = kVgaHeight,
302 .pixelFormat = Format::YUV_420_888,
303 .maxFps = kMaxFps}},
304 .virtualCameraCallback = nullptr,
305 .sensorOrientation = SensorOrientation::ORIENTATION_0,
306 .lensFacing = LensFacing::FRONT},
307 kDefaultDeviceId);
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100308 }
309
310 protected:
311 std::shared_ptr<VirtualCameraDevice> mCamera;
312};
313
314TEST_F(VirtualCameraDeviceTest, configureMaximalNumberOfNonStallStreamsSuceeds) {
315 StreamConfiguration config;
316 std::fill_n(std::back_insert_iterator(config.streams),
317 VirtualCameraDevice::kMaxNumberOfProcessedStreams,
318 kVgaYUV420Stream);
319
320 bool aidl_ret;
321 ASSERT_TRUE(mCamera->isStreamCombinationSupported(config, &aidl_ret).isOk());
322 EXPECT_TRUE(aidl_ret);
323}
324
325TEST_F(VirtualCameraDeviceTest, configureTooManyNonStallStreamsFails) {
326 StreamConfiguration config;
327 std::fill_n(std::back_insert_iterator(config.streams),
328 VirtualCameraDevice::kMaxNumberOfProcessedStreams + 1,
329 kVgaYUV420Stream);
330
331 bool aidl_ret;
332 ASSERT_TRUE(mCamera->isStreamCombinationSupported(config, &aidl_ret).isOk());
333 EXPECT_FALSE(aidl_ret);
334}
335
336TEST_F(VirtualCameraDeviceTest, configureMaximalNumberOfStallStreamsSuceeds) {
337 StreamConfiguration config;
338 std::fill_n(std::back_insert_iterator(config.streams),
339 VirtualCameraDevice::kMaxNumberOfStallStreams, kVgaJpegStream);
340
341 bool aidl_ret;
342 ASSERT_TRUE(mCamera->isStreamCombinationSupported(config, &aidl_ret).isOk());
343 EXPECT_TRUE(aidl_ret);
344}
345
346TEST_F(VirtualCameraDeviceTest, configureTooManyStallStreamsFails) {
347 StreamConfiguration config;
348 std::fill_n(std::back_insert_iterator(config.streams),
349 VirtualCameraDevice::kMaxNumberOfStallStreams + 1, kVgaJpegStream);
350
351 bool aidl_ret;
352 ASSERT_TRUE(mCamera->isStreamCombinationSupported(config, &aidl_ret).isOk());
353 EXPECT_FALSE(aidl_ret);
354}
355
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100356TEST_F(VirtualCameraDeviceTest, thumbnailSizeWithCompatibleAspectRatio) {
357 CameraMetadata metadata;
358 ASSERT_TRUE(mCamera->getCameraCharacteristics(&metadata).isOk());
359
360 // Camera is configured with VGA input, we expect 240 x 180 thumbnail size in
361 // characteristics, since it has same aspect ratio.
362 EXPECT_THAT(getJpegAvailableThumbnailSizes(metadata),
363 ElementsAre(Resolution(0, 0), Resolution(240, 180)));
364}
365
Vadim Caen918e6dc2024-04-08 11:14:50 +0200366TEST_F(VirtualCameraDeviceTest, dump) {
367 std::string expected = R"( virtual_camera 42 belongs to virtual device 0
368 SupportedStreamConfiguration:
369 SupportedStreamConfiguration{width: 640, height: 480, pixelFormat: YUV_420_888, maxFps: 30})";
370 int expectedSize = expected.size() * sizeof(char);
371 char buffer[expectedSize];
372
373 // Create an in memory fd
374 int fd = memfd_create("tmpFile", 0);
375 mCamera->dump(fd, {}, 0);
376
377 // Check that we wrote the expected size
378 int dumpSize = lseek(fd, 0, SEEK_END);
379
380 // Rewind and read from the fd
381 lseek(fd, 0, SEEK_SET);
382 read(fd, buffer, expectedSize);
383 close(fd);
384
385 // Check the content of the dump
386 std::string name = std::string(buffer, expectedSize);
387 ASSERT_EQ(expected, name);
388 // Check the size after the content to display the string mismatch when a
389 // failure occurs
390 ASSERT_EQ(expectedSize, dumpSize);
391}
392
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100393} // namespace
394} // namespace virtualcamera
395} // namespace companion
396} // namespace android