blob: ad9d83b0a8cee215f0b0dffcc54879112cbf2a0a [file] [log] [blame]
/*
* Copyright 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 <algorithm>
#include <iterator>
#include <memory>
#include "VirtualCameraDevice.h"
#include "aidl/android/companion/virtualcamera/Format.h"
#include "aidl/android/companion/virtualcamera/SupportedStreamConfiguration.h"
#include "aidl/android/companion/virtualcamera/VirtualCameraConfiguration.h"
#include "aidl/android/hardware/camera/device/CameraMetadata.h"
#include "aidl/android/hardware/camera/device/StreamConfiguration.h"
#include "aidl/android/hardware/graphics/common/PixelFormat.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 "util/MetadataUtil.h"
#include "util/Util.h"
#include "utils/Errors.h"
namespace android {
namespace companion {
namespace virtualcamera {
namespace {
using ::aidl::android::companion::virtualcamera::Format;
using ::aidl::android::companion::virtualcamera::LensFacing;
using ::aidl::android::companion::virtualcamera::SensorOrientation;
using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
using ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration;
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::ElementsAre;
using ::testing::UnorderedElementsAreArray;
using metadata_stream_t =
camera_metadata_enum_android_scaler_available_stream_configurations_t;
constexpr int kCameraId = 42;
constexpr int kQvgaWidth = 320;
constexpr int kQvgaHeight = 240;
constexpr int k360pWidth = 640;
constexpr int k360pHeight = 360;
constexpr int kVgaWidth = 640;
constexpr int kVgaHeight = 480;
constexpr int kHdWidth = 1280;
constexpr int kHdHeight = 720;
constexpr int kMaxFps = 30;
const Stream kVgaYUV420Stream = Stream{
.streamType = StreamType::OUTPUT,
.width = kVgaWidth,
.height = kVgaHeight,
.format = PixelFormat::YCBCR_420_888,
};
const Stream kVgaJpegStream = Stream{
.streamType = StreamType::OUTPUT,
.width = kVgaWidth,
.height = kVgaHeight,
.format = PixelFormat::BLOB,
};
struct AvailableStreamConfiguration {
const int width;
const int height;
const int pixelFormat;
const metadata_stream_t streamConfiguration =
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT;
};
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 {
VirtualCameraConfiguration inputConfig;
std::vector<AvailableStreamConfiguration> expectedAvailableStreamConfigs;
};
class VirtualCameraDeviceCharacterisicsTest
: public testing::TestWithParam<VirtualCameraConfigTestParam> {};
TEST_P(VirtualCameraDeviceCharacterisicsTest,
cameraCharacteristicsForInputFormat) {
const VirtualCameraConfigTestParam& param = GetParam();
std::shared_ptr<VirtualCameraDevice> camera =
ndk::SharedRefBase::make<VirtualCameraDevice>(kCameraId,
param.inputConfig);
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, VirtualCameraDeviceCharacterisicsTest,
testing::Values(
VirtualCameraConfigTestParam{
.inputConfig =
VirtualCameraConfiguration{
.supportedStreamConfigs = {SupportedStreamConfiguration{
.width = kVgaWidth,
.height = kVgaHeight,
.pixelFormat = Format::YUV_420_888,
.maxFps = kMaxFps}},
.virtualCameraCallback = nullptr,
.sensorOrientation = SensorOrientation::ORIENTATION_0,
.lensFacing = LensFacing::FRONT},
.expectedAvailableStreamConfigs =
{AvailableStreamConfiguration{
.width = kQvgaWidth,
.height = kQvgaHeight,
.pixelFormat =
ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
AvailableStreamConfiguration{
.width = kQvgaWidth,
.height = kQvgaHeight,
.pixelFormat =
ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
AvailableStreamConfiguration{
.width = kQvgaWidth,
.height = kQvgaHeight,
.pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB},
AvailableStreamConfiguration{
.width = kVgaWidth,
.height = kVgaHeight,
.pixelFormat =
ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
AvailableStreamConfiguration{
.width = kVgaWidth,
.height = kVgaHeight,
.pixelFormat =
ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
AvailableStreamConfiguration{
.width = kVgaWidth,
.height = kVgaHeight,
.pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB}}},
VirtualCameraConfigTestParam{
.inputConfig =
VirtualCameraConfiguration{
.supportedStreamConfigs =
{SupportedStreamConfiguration{
.width = kVgaWidth,
.height = kVgaHeight,
.pixelFormat = Format::YUV_420_888,
.maxFps = kMaxFps},
SupportedStreamConfiguration{
.width = kHdWidth,
.height = kHdHeight,
.pixelFormat = Format::YUV_420_888,
.maxFps = kMaxFps}},
.virtualCameraCallback = nullptr,
.sensorOrientation = SensorOrientation::ORIENTATION_0,
.lensFacing = LensFacing::BACK},
.expectedAvailableStreamConfigs = {
AvailableStreamConfiguration{
.width = kQvgaWidth,
.height = kQvgaHeight,
.pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
AvailableStreamConfiguration{
.width = kQvgaWidth,
.height = kQvgaHeight,
.pixelFormat =
ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
AvailableStreamConfiguration{
.width = kQvgaWidth,
.height = kQvgaHeight,
.pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB},
AvailableStreamConfiguration{
.width = 640,
.height = 360,
.pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
AvailableStreamConfiguration{
.width = 640,
.height = 360,
.pixelFormat =
ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
AvailableStreamConfiguration{
.width = 640,
.height = 360,
.pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB},
AvailableStreamConfiguration{
.width = kVgaWidth,
.height = kVgaHeight,
.pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
AvailableStreamConfiguration{
.width = kVgaWidth,
.height = kVgaHeight,
.pixelFormat =
ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
AvailableStreamConfiguration{
.width = kVgaWidth,
.height = kVgaHeight,
.pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB},
AvailableStreamConfiguration{
.width = 1024,
.height = 576,
.pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
AvailableStreamConfiguration{
.width = 1024,
.height = 576,
.pixelFormat =
ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
AvailableStreamConfiguration{
.width = 1024,
.height = 576,
.pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB},
AvailableStreamConfiguration{
.width = kHdWidth,
.height = kHdHeight,
.pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888},
AvailableStreamConfiguration{
.width = kHdWidth,
.height = kHdHeight,
.pixelFormat =
ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED},
AvailableStreamConfiguration{
.width = kHdWidth,
.height = kHdHeight,
.pixelFormat = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB}}}));
class VirtualCameraDeviceTest : public ::testing::Test {
public:
void SetUp() override {
mCamera = ndk::SharedRefBase::make<VirtualCameraDevice>(
kCameraId, VirtualCameraConfiguration{
.supportedStreamConfigs = {SupportedStreamConfiguration{
.width = kVgaWidth,
.height = kVgaHeight,
.pixelFormat = Format::YUV_420_888,
.maxFps = kMaxFps}},
.virtualCameraCallback = nullptr,
.sensorOrientation = SensorOrientation::ORIENTATION_0,
.lensFacing = LensFacing::FRONT});
}
protected:
std::shared_ptr<VirtualCameraDevice> mCamera;
};
TEST_F(VirtualCameraDeviceTest, configureMaximalNumberOfNonStallStreamsSuceeds) {
StreamConfiguration config;
std::fill_n(std::back_insert_iterator(config.streams),
VirtualCameraDevice::kMaxNumberOfProcessedStreams,
kVgaYUV420Stream);
bool aidl_ret;
ASSERT_TRUE(mCamera->isStreamCombinationSupported(config, &aidl_ret).isOk());
EXPECT_TRUE(aidl_ret);
}
TEST_F(VirtualCameraDeviceTest, configureTooManyNonStallStreamsFails) {
StreamConfiguration config;
std::fill_n(std::back_insert_iterator(config.streams),
VirtualCameraDevice::kMaxNumberOfProcessedStreams + 1,
kVgaYUV420Stream);
bool aidl_ret;
ASSERT_TRUE(mCamera->isStreamCombinationSupported(config, &aidl_ret).isOk());
EXPECT_FALSE(aidl_ret);
}
TEST_F(VirtualCameraDeviceTest, configureMaximalNumberOfStallStreamsSuceeds) {
StreamConfiguration config;
std::fill_n(std::back_insert_iterator(config.streams),
VirtualCameraDevice::kMaxNumberOfStallStreams, kVgaJpegStream);
bool aidl_ret;
ASSERT_TRUE(mCamera->isStreamCombinationSupported(config, &aidl_ret).isOk());
EXPECT_TRUE(aidl_ret);
}
TEST_F(VirtualCameraDeviceTest, configureTooManyStallStreamsFails) {
StreamConfiguration config;
std::fill_n(std::back_insert_iterator(config.streams),
VirtualCameraDevice::kMaxNumberOfStallStreams + 1, kVgaJpegStream);
bool aidl_ret;
ASSERT_TRUE(mCamera->isStreamCombinationSupported(config, &aidl_ret).isOk());
EXPECT_FALSE(aidl_ret);
}
TEST_F(VirtualCameraDeviceTest, thumbnailSizeWithCompatibleAspectRatio) {
CameraMetadata metadata;
ASSERT_TRUE(mCamera->getCameraCharacteristics(&metadata).isOk());
// Camera is configured with VGA input, we expect 240 x 180 thumbnail size in
// characteristics, since it has same aspect ratio.
EXPECT_THAT(getJpegAvailableThumbnailSizes(metadata),
ElementsAre(Resolution(0, 0), Resolution(240, 180)));
}
} // namespace
} // namespace virtualcamera
} // namespace companion
} // namespace android