Allow to specify list of supported input configurations.
... and populate corresponding metadata entries / perform
validation based on these.
Bug: 301023410
Test: atest virtual_camera_tests
Change-Id: I66f3cf2b013d5845b6fa7429294a1ed2157318f8
diff --git a/services/camera/virtualcamera/tests/VirtualCameraDeviceTest.cc b/services/camera/virtualcamera/tests/VirtualCameraDeviceTest.cc
new file mode 100644
index 0000000..140ae65
--- /dev/null
+++ b/services/camera/virtualcamera/tests/VirtualCameraDeviceTest.cc
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+
+#include "VirtualCameraDevice.h"
+#include "aidl/android/companion/virtualcamera/Format.h"
+#include "aidl/android/companion/virtualcamera/SupportedStreamConfiguration.h"
+#include "aidl/android/hardware/camera/device/CameraMetadata.h"
+#include "aidl/android/hardware/camera/device/StreamConfiguration.h"
+#include "android/binder_interface_utils.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "log/log_main.h"
+#include "system/camera_metadata.h"
+#include "utils/Errors.h"
+
+namespace android {
+namespace companion {
+namespace virtualcamera {
+namespace {
+
+using ::aidl::android::companion::virtualcamera::Format;
+using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
+using ::aidl::android::hardware::camera::device::CameraMetadata;
+using ::aidl::android::hardware::camera::device::Stream;
+using ::aidl::android::hardware::camera::device::StreamConfiguration;
+using ::aidl::android::hardware::camera::device::StreamType;
+using ::aidl::android::hardware::graphics::common::PixelFormat;
+using ::testing::UnorderedElementsAreArray;
+using metadata_stream_t =
+ camera_metadata_enum_android_scaler_available_stream_configurations_t;
+
+constexpr int kCameraId = 42;
+constexpr int kVgaWidth = 640;
+constexpr int kVgaHeight = 480;
+constexpr int kHdWidth = 1280;
+constexpr int kHdHeight = 720;
+
+struct AvailableStreamConfiguration {
+ const int width;
+ const int height;
+ const int pixelFormat;
+ const metadata_stream_t streamConfiguration;
+};
+
+bool operator==(const AvailableStreamConfiguration& a,
+ const AvailableStreamConfiguration& b) {
+ return a.width == b.width && a.height == b.height &&
+ a.pixelFormat == b.pixelFormat &&
+ a.streamConfiguration == b.streamConfiguration;
+}
+
+std::ostream& operator<<(std::ostream& os,
+ const AvailableStreamConfiguration& config) {
+ os << config.width << "x" << config.height << " (pixfmt "
+ << config.pixelFormat << ", streamConfiguration "
+ << config.streamConfiguration << ")";
+ return os;
+}
+
+std::vector<AvailableStreamConfiguration> getAvailableStreamConfigurations(
+ const CameraMetadata& metadata) {
+ const camera_metadata_t* const raw =
+ reinterpret_cast<const camera_metadata_t*>(metadata.metadata.data());
+ camera_metadata_ro_entry_t entry;
+ if (find_camera_metadata_ro_entry(
+ raw, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry) !=
+ NO_ERROR) {
+ return {};
+ }
+
+ std::vector<AvailableStreamConfiguration> res;
+ for (int i = 0; i < entry.count; i += 4) {
+ res.push_back(AvailableStreamConfiguration{
+ .width = entry.data.i32[i + 1],
+ .height = entry.data.i32[i + 2],
+ .pixelFormat = entry.data.i32[i],
+ .streamConfiguration =
+ static_cast<metadata_stream_t>(entry.data.i32[i + 3])});
+ }
+ return res;
+}
+
+struct VirtualCameraConfigTestParam {
+ std::vector<SupportedStreamConfiguration> inputConfig;
+ std::vector<AvailableStreamConfiguration> expectedAvailableStreamConfigs;
+};
+
+class VirtualCameraDeviceTest
+ : public testing::TestWithParam<VirtualCameraConfigTestParam> {};
+
+TEST_P(VirtualCameraDeviceTest, cameraCharacteristicsForInputFormat) {
+ const VirtualCameraConfigTestParam& param = GetParam();
+ std::shared_ptr<VirtualCameraDevice> camera =
+ ndk::SharedRefBase::make<VirtualCameraDevice>(
+ kCameraId, param.inputConfig, /*virtualCameraClientCallback=*/nullptr);
+
+ CameraMetadata metadata;
+ ASSERT_TRUE(camera->getCameraCharacteristics(&metadata).isOk());
+ EXPECT_THAT(getAvailableStreamConfigurations(metadata),
+ UnorderedElementsAreArray(param.expectedAvailableStreamConfigs));
+
+ // Configuration needs to succeed for every available stream configuration
+ for (const AvailableStreamConfiguration& config :
+ param.expectedAvailableStreamConfigs) {
+ StreamConfiguration configuration{
+ .streams = std::vector<Stream>{Stream{
+ .streamType = StreamType::OUTPUT,
+ .width = config.width,
+ .height = config.height,
+ .format = static_cast<PixelFormat>(config.pixelFormat),
+ }}};
+ bool aidl_ret;
+ ASSERT_TRUE(
+ camera->isStreamCombinationSupported(configuration, &aidl_ret).isOk());
+ EXPECT_TRUE(aidl_ret);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ cameraCharacteristicsForInputFormat, VirtualCameraDeviceTest,
+ testing::Values(
+ VirtualCameraConfigTestParam{
+ .inputConfig = {SupportedStreamConfiguration{
+ .width = kVgaWidth,
+ .height = kVgaHeight,
+ .pixelFormat = Format::YUV_420_888}},
+ .expectedAvailableStreamConfigs =
+ {AvailableStreamConfiguration{
+ .width = kVgaWidth,
+ .height = kVgaHeight,
+ .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888,
+ .streamConfiguration =
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT},
+ AvailableStreamConfiguration{
+ .width = kVgaWidth,
+ .height = kVgaHeight,
+ .pixelFormat =
+ ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED,
+ .streamConfiguration =
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT},
+ AvailableStreamConfiguration{
+ .width = kVgaWidth,
+ .height = kVgaHeight,
+ .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB,
+ .streamConfiguration =
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}}},
+ VirtualCameraConfigTestParam{
+ .inputConfig = {SupportedStreamConfiguration{
+ .width = kVgaWidth,
+ .height = kVgaHeight,
+ .pixelFormat = Format::YUV_420_888},
+ SupportedStreamConfiguration{
+ .width = kHdWidth,
+ .height = kHdHeight,
+ .pixelFormat = Format::YUV_420_888}},
+ .expectedAvailableStreamConfigs = {
+ AvailableStreamConfiguration{
+ .width = kVgaWidth,
+ .height = kVgaHeight,
+ .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888,
+ .streamConfiguration =
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT},
+ AvailableStreamConfiguration{
+ .width = kVgaWidth,
+ .height = kVgaHeight,
+ .pixelFormat =
+ ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED,
+ .streamConfiguration =
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT},
+ AvailableStreamConfiguration{
+ .width = kVgaWidth,
+ .height = kVgaHeight,
+ .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB,
+ .streamConfiguration =
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT},
+ AvailableStreamConfiguration{
+ .width = kHdWidth,
+ .height = kHdHeight,
+ .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888,
+ .streamConfiguration =
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT},
+ AvailableStreamConfiguration{
+ .width = kHdWidth,
+ .height = kHdHeight,
+ .pixelFormat =
+ ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED,
+ .streamConfiguration =
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT},
+ AvailableStreamConfiguration{
+ .width = kHdWidth,
+ .height = kHdHeight,
+ .pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB,
+ .streamConfiguration =
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}}}));
+
+} // namespace
+} // namespace virtualcamera
+} // namespace companion
+} // namespace android