blob: 32cd23fb7d7be58f8936c8eaa6d3954f9cbefa83 [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
Biswarup Pal15dcd132024-06-02 14:02:03 +000057constexpr char kCameraId[] = "42";
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +010058constexpr int kQvgaWidth = 320;
59constexpr int kQvgaHeight = 240;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010060constexpr int kVgaWidth = 640;
61constexpr int kVgaHeight = 480;
62constexpr int kHdWidth = 1280;
63constexpr int kHdHeight = 720;
Biswarup Pal6152a302023-12-19 12:44:09 +000064constexpr int kMaxFps = 30;
Biswarup Pal37a75182024-01-16 15:53:35 +000065constexpr int kDefaultDeviceId = 0;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010066
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +010067const Stream kVgaYUV420Stream = Stream{
68 .streamType = StreamType::OUTPUT,
69 .width = kVgaWidth,
70 .height = kVgaHeight,
71 .format = PixelFormat::YCBCR_420_888,
72};
73
74const Stream kVgaJpegStream = Stream{
75 .streamType = StreamType::OUTPUT,
76 .width = kVgaWidth,
77 .height = kVgaHeight,
78 .format = PixelFormat::BLOB,
79};
80
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010081struct AvailableStreamConfiguration {
82 const int width;
83 const int height;
84 const int pixelFormat;
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +010085 const metadata_stream_t streamConfiguration =
86 ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010087};
88
89bool 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
96std::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
104std::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
127struct VirtualCameraConfigTestParam {
Biswarup Pal6152a302023-12-19 12:44:09 +0000128 VirtualCameraConfiguration inputConfig;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100129 std::vector<AvailableStreamConfiguration> expectedAvailableStreamConfigs;
130};
131
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100132class VirtualCameraDeviceCharacterisicsTest
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100133 : public testing::TestWithParam<VirtualCameraConfigTestParam> {};
134
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100135TEST_P(VirtualCameraDeviceCharacterisicsTest,
136 cameraCharacteristicsForInputFormat) {
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100137 const VirtualCameraConfigTestParam& param = GetParam();
138 std::shared_ptr<VirtualCameraDevice> camera =
Biswarup Pal37a75182024-01-16 15:53:35 +0000139 ndk::SharedRefBase::make<VirtualCameraDevice>(
140 kCameraId, param.inputConfig, kDefaultDeviceId);
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100141
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
164INSTANTIATE_TEST_SUITE_P(
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100165 cameraCharacteristicsForInputFormat, VirtualCameraDeviceCharacterisicsTest,
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100166 testing::Values(
167 VirtualCameraConfigTestParam{
Biswarup Pal6152a302023-12-19 12:44:09 +0000168 .inputConfig =
169 VirtualCameraConfiguration{
170 .supportedStreamConfigs = {SupportedStreamConfiguration{
171 .width = kVgaWidth,
172 .height = kVgaHeight,
173 .pixelFormat = Format::YUV_420_888,
174 .maxFps = kMaxFps}},
175 .virtualCameraCallback = nullptr,
Biswarup Pal112458f2023-12-28 19:50:17 +0000176 .sensorOrientation = SensorOrientation::ORIENTATION_0,
177 .lensFacing = LensFacing::FRONT},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100178 .expectedAvailableStreamConfigs =
179 {AvailableStreamConfiguration{
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100180 .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},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100193 AvailableStreamConfiguration{
194 .width = kVgaWidth,
195 .height = kVgaHeight,
196 .pixelFormat =
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100197 ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100198 AvailableStreamConfiguration{
199 .width = kVgaWidth,
200 .height = kVgaHeight,
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100201 .pixelFormat =
202 ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
203 AvailableStreamConfiguration{
204 .width = kVgaWidth,
205 .height = kVgaHeight,
206 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB}}},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100207 VirtualCameraConfigTestParam{
Biswarup Pal6152a302023-12-19 12:44:09 +0000208 .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,
Biswarup Pal112458f2023-12-28 19:50:17 +0000222 .sensorOrientation = SensorOrientation::ORIENTATION_0,
223 .lensFacing = LensFacing::BACK},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100224 .expectedAvailableStreamConfigs = {
225 AvailableStreamConfiguration{
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100226 .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{
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100252 .width = kVgaWidth,
253 .height = kVgaHeight,
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100254 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100255 AvailableStreamConfiguration{
256 .width = kVgaWidth,
257 .height = kVgaHeight,
258 .pixelFormat =
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100259 ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100260 AvailableStreamConfiguration{
261 .width = kVgaWidth,
262 .height = kVgaHeight,
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100263 .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},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100277 AvailableStreamConfiguration{
278 .width = kHdWidth,
279 .height = kHdHeight,
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100280 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100281 AvailableStreamConfiguration{
282 .width = kHdWidth,
283 .height = kHdHeight,
284 .pixelFormat =
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100285 ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100286 AvailableStreamConfiguration{
287 .width = kHdWidth,
288 .height = kHdHeight,
Jan Sebechlebsky4c9bb1e2024-02-28 16:32:39 +0100289 .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB}}}));
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100290
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100291class VirtualCameraDeviceTest : public ::testing::Test {
292 public:
293 void SetUp() override {
294 mCamera = ndk::SharedRefBase::make<VirtualCameraDevice>(
Biswarup Pal37a75182024-01-16 15:53:35 +0000295 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);
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100306 }
307
308 protected:
309 std::shared_ptr<VirtualCameraDevice> mCamera;
310};
311
312TEST_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
323TEST_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
334TEST_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
344TEST_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
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100354TEST_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
Vadim Caen918e6dc2024-04-08 11:14:50 +0200364TEST_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
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100391} // namespace
392} // namespace virtualcamera
393} // namespace companion
394} // namespace android