| // Copyright 2016 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "camera3_test/camera3_module_fixture.h" |
| |
| #include <algorithm> |
| #include <string> |
| |
| #include <base/at_exit.h> |
| #include <base/bind.h> |
| #include <base/command_line.h> |
| #include <base/files/file_path.h> |
| #include <base/logging.h> |
| #include <base/macros.h> |
| #include <base/no_destructor.h> |
| #include <base/stl_util.h> |
| #include <base/strings/string_split.h> |
| #include <base/strings/stringprintf.h> |
| #include <base/sys_info.h> |
| |
| #include "camera3_test/camera3_perf_log.h" |
| #include "camera3_test/camera3_test_data_forwarder.h" |
| #include "common/utils/camera_hal_enumerator.h" |
| |
| namespace camera3_test { |
| |
| static camera_module_t* g_cam_module = NULL; |
| |
| // TODO(shik): Objects with static storage duration are forbidden unless they |
| // are trivially destructible. CameraThread is trivially not trivially |
| // destructible. |
| static cros::CameraThread g_module_thread("Camera3 Test Module Thread"); |
| |
| std::vector<std::tuple<int, int32_t, int32_t, float>> ParseRecordingParams() { |
| // This parameter would be generated and passed by the camera_HAL3 autotest. |
| if (!base::CommandLine::ForCurrentProcess()->HasSwitch("recording_params")) { |
| LOGF(ERROR) << "Missing recording parameters in the test command"; |
| // Return invalid parameters to fail the test |
| return {{-1, 0, 0, 0.0}}; |
| } |
| std::vector<std::tuple<int, int32_t, int32_t, float>> params; |
| std::string params_str = |
| base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| "recording_params"); |
| // Expected video recording parameters in the format |
| // "camera_id:width:height:frame_rate". For example: |
| // "0:1280:720:30,0:1920:1080:30,1:1280:720:30" means camcorder profiles |
| // contains 1280x720 and 1920x1080 for camera 0 and just 1280x720 for camera |
| // 1. |
| const size_t kNumParamsInProfile = 4; |
| enum { CAMERA_ID_IDX, WIDTH_IDX, HEIGHT_IDX, FRAME_RATE_IDX }; |
| for (const auto& it : base::SplitString( |
| params_str, ",", base::WhitespaceHandling::TRIM_WHITESPACE, |
| base::SplitResult::SPLIT_WANT_ALL)) { |
| auto profile = |
| base::SplitString(it, ":", base::WhitespaceHandling::TRIM_WHITESPACE, |
| base::SplitResult::SPLIT_WANT_ALL); |
| if (profile.size() != kNumParamsInProfile) { |
| ADD_FAILURE() << "Failed to parse video recording parameters (" << it |
| << ")"; |
| continue; |
| } |
| params.emplace_back( |
| std::stoi(profile[CAMERA_ID_IDX]), std::stoi(profile[WIDTH_IDX]), |
| std::stoi(profile[HEIGHT_IDX]), std::stof(profile[FRAME_RATE_IDX])); |
| } |
| |
| std::set<int> param_ids; |
| for (const auto& param : params) { |
| param_ids.insert(std::get<CAMERA_ID_IDX>(param)); |
| } |
| |
| // We are going to enable usb camera hal on all boards, so there will be more |
| // than one hals on many platforms just like today's nautilus. The |
| // recording_params is now generated from media_profiles.xml, where the camera |
| // ids are already translated by SuperHAL. But cros_camera_test is used to |
| // test only one camera hal directly without going through the hal_adapter, |
| // therefore we have to remap the ids here. |
| // |
| // TODO(shik): This is a temporary workaround for SuperHAL camera ids mapping |
| // until we have better ground truth config file. Here we exploit the fact |
| // that there are at most one back and at most one front internal cameras for |
| // now, and all cameras are sorted by facing in SuperHAL. I feel bad when |
| // implementing the following hack (sigh). |
| std::vector<std::tuple<int, int32_t, int32_t, float>> result; |
| Camera3Module module; |
| if (module.GetCameraIds().size() < param_ids.size()) { |
| // SuperHAL case |
| for (const auto& cam_id : module.GetTestCameraIds()) { |
| camera_info info; |
| EXPECT_EQ(0, Camera3Module().GetCameraInfo(cam_id, &info)); |
| bool found_matching_param = false; |
| for (auto param : params) { |
| if (std::get<CAMERA_ID_IDX>(param) == info.facing) { |
| found_matching_param = true; |
| std::get<CAMERA_ID_IDX>(param) = cam_id; |
| result.emplace_back(param); |
| } |
| } |
| EXPECT_TRUE(found_matching_param); |
| } |
| } else { |
| // Single HAL case |
| for (const auto& cam_id : module.GetTestCameraIds()) { |
| if (std::find_if( |
| params.begin(), params.end(), |
| [&](const std::tuple<int, int32_t, int32_t, float>& item) { |
| return std::get<CAMERA_ID_IDX>(item) == cam_id; |
| }) == params.end()) { |
| ADD_FAILURE() << "Missing video recording parameters for camera " |
| << cam_id; |
| } |
| } |
| result = std::move(params); |
| } |
| |
| LOGF(INFO) << "The parameters will be used for recording test:"; |
| for (const auto& param : result) { |
| LOGF(INFO) << base::StringPrintf( |
| "camera id = %d, size = %dx%d, fps = %g", |
| std::get<CAMERA_ID_IDX>(param), std::get<WIDTH_IDX>(param), |
| std::get<HEIGHT_IDX>(param), std::get<FRAME_RATE_IDX>(param)); |
| } |
| |
| return result; |
| } |
| |
| // static |
| void CameraModuleCallbacksHandler::camera_device_status_change( |
| const camera_module_callbacks_t* callbacks, int camera_id, int new_status) { |
| auto* aux = static_cast<const CameraModuleCallbacksAux*>(callbacks); |
| aux->handler->CameraDeviceStatusChange( |
| camera_id, static_cast<camera_device_status_t>(new_status)); |
| } |
| |
| // static |
| void CameraModuleCallbacksHandler::torch_mode_status_change( |
| const camera_module_callbacks_t* callbacks, |
| const char* camera_id, |
| int new_status) { |
| auto* aux = static_cast<const CameraModuleCallbacksAux*>(callbacks); |
| aux->handler->TorchModeStatusChange( |
| atoi(camera_id), static_cast<torch_mode_status_t>(new_status)); |
| } |
| |
| // static |
| CameraModuleCallbacksHandler* CameraModuleCallbacksHandler::GetInstance() { |
| static auto* instance = new CameraModuleCallbacksHandler(); |
| return instance; |
| } |
| |
| bool CameraModuleCallbacksHandler::IsExternalCameraPresent(int camera_id) { |
| base::AutoLock l(lock_); |
| auto it = device_status_.find(camera_id); |
| return it != device_status_.end() && |
| it->second == CAMERA_DEVICE_STATUS_PRESENT; |
| } |
| |
| // TODO(shik): Run tests on external cameras as well if detected. We need to |
| // relax the requirements for them just like what CTS did. |
| void CameraModuleCallbacksHandler::CameraDeviceStatusChange( |
| int camera_id, camera_device_status_t new_status) { |
| base::AutoLock l(lock_); |
| LOGF(INFO) << "camera_id = " << camera_id << ", new status = " << new_status; |
| device_status_[camera_id] = new_status; |
| } |
| |
| void CameraModuleCallbacksHandler::TorchModeStatusChange( |
| int camera_id, torch_mode_status_t new_status) { |
| LOGF(INFO) << "camera_id = " << camera_id << ", new status = " << new_status; |
| } |
| |
| int32_t ResolutionInfo::Width() const { |
| return width_; |
| } |
| |
| int32_t ResolutionInfo::Height() const { |
| return height_; |
| } |
| |
| int32_t ResolutionInfo::Area() const { |
| return width_ * height_; |
| } |
| |
| bool ResolutionInfo::operator==(const ResolutionInfo& resolution) const { |
| return (width_ == resolution.Width()) && (height_ == resolution.Height()); |
| } |
| |
| bool ResolutionInfo::operator<(const ResolutionInfo& resolution) const { |
| // Compare by area it covers, if the areas are same, then compare the widths. |
| return (Area() < resolution.Area()) || |
| (Area() == resolution.Area() && width_ < resolution.Width()); |
| } |
| |
| std::ostream& operator<<(std::ostream& out, const ResolutionInfo& info) { |
| out << info.width_ << 'x' << info.height_; |
| return out; |
| } |
| |
| static std::vector<int> GetCmdLineTestCameraIds() { |
| auto id_str = |
| base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII("camera_ids"); |
| std::vector<int> ids; |
| if (!id_str.empty()) { |
| auto id_strs = base::SplitString(id_str, ",", |
| base::WhitespaceHandling::TRIM_WHITESPACE, |
| base::SplitResult::SPLIT_WANT_ALL); |
| for (const auto& id : id_strs) { |
| ids.push_back(stoi(id)); |
| } |
| } |
| return ids; |
| } |
| |
| static void InitCameraModuleOnThread(camera_module_t* cam_module) { |
| static CameraModuleCallbacksAux* callbacks = []() { |
| auto* aux = new CameraModuleCallbacksAux(); |
| aux->camera_device_status_change = |
| &CameraModuleCallbacksHandler::camera_device_status_change; |
| aux->torch_mode_status_change = |
| &CameraModuleCallbacksHandler::torch_mode_status_change; |
| aux->handler = CameraModuleCallbacksHandler::GetInstance(); |
| return aux; |
| }(); |
| |
| if (cam_module->init) { |
| ASSERT_EQ(0, cam_module->init()); |
| } |
| int num_builtin_cameras = cam_module->get_number_of_cameras(); |
| VLOGF(1) << "num_builtin_cameras = " << num_builtin_cameras; |
| ASSERT_EQ(0, cam_module->set_callbacks(callbacks)); |
| } |
| |
| // On successfully Initialized, |cam_module_| will pointed to valid |
| // camera_module_t. We cannot dlclose |cam_hal_handle| until the lifetime |
| // conflict in b/119926433 is resolved. |
| static void InitCameraModule(const base::FilePath& camera_hal_path, |
| void** cam_hal_handle, |
| camera_module_t** cam_module) { |
| *cam_hal_handle = dlopen(camera_hal_path.value().c_str(), RTLD_NOW); |
| ASSERT_NE(nullptr, *cam_hal_handle) << "Failed to dlopen: " << dlerror(); |
| |
| camera_module_t* module = static_cast<camera_module_t*>( |
| dlsym(*cam_hal_handle, HAL_MODULE_INFO_SYM_AS_STR)); |
| ASSERT_NE(nullptr, module) << "Camera module is invalid"; |
| ASSERT_NE(nullptr, module->get_number_of_cameras) |
| << "get_number_of_cameras is not implemented"; |
| ASSERT_NE(nullptr, module->get_camera_info) |
| << "get_camera_info is not implemented"; |
| ASSERT_NE(nullptr, module->common.methods->open) << "open() is unimplemented"; |
| for (int id : GetCmdLineTestCameraIds()) { |
| ASSERT_GT(module->get_number_of_cameras(), id) |
| << "No such test camera id in HAL"; |
| } |
| ASSERT_EQ(0, g_module_thread.PostTaskSync( |
| FROM_HERE, base::Bind(&InitCameraModuleOnThread, module))); |
| *cam_module = module; |
| } |
| |
| static void InitCameraModuleByHalPath(const base::FilePath& camera_hal_path, |
| void** cam_hal_handle) { |
| InitCameraModule(camera_hal_path, cam_hal_handle, &g_cam_module); |
| } |
| |
| static void InitCameraModuleByFacing(int facing, void** cam_hal_handle) { |
| // Do cleanup when exit from ASSERT_XX |
| struct CleanupModule { |
| void operator()(void** cam_hal_handle) { |
| if (*cam_hal_handle) { |
| g_cam_module = NULL; |
| *cam_hal_handle = NULL; |
| } |
| } |
| }; |
| for (const auto& hal_path : cros::GetCameraHalPaths()) { |
| InitCameraModule(hal_path, cam_hal_handle, &g_cam_module); |
| std::unique_ptr<void*, CleanupModule> cleanup_ptr(cam_hal_handle); |
| if (g_cam_module != NULL) { |
| Camera3Module camera_module; |
| for (int i = 0; i < camera_module.GetNumberOfCameras(); i++) { |
| camera_info info; |
| ASSERT_EQ(0, camera_module.GetCameraInfo(i, &info)); |
| if (info.facing == facing) { |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| "camera_ids", std::to_string(i)); |
| cleanup_ptr.release(); |
| return; |
| } |
| } |
| } |
| } |
| FAIL() << "Cannot find camera with facing=" << facing; |
| } |
| |
| static void InitPerfLog() { |
| // GetNumberOfCameras() returns the number of internal cameras, so here we |
| // should not see any external cameras (facing = 2). |
| const std::string facing_names[] = {"back", "front"}; |
| camera3_test::Camera3Module camera_module; |
| int num_cameras = camera_module.GetNumberOfCameras(); |
| std::map<int, std::string> name_map; |
| for (int i = 0; i < num_cameras; i++) { |
| camera_info info; |
| ASSERT_EQ(0, camera_module.GetCameraInfo(i, &info)); |
| ASSERT_LE(0, info.facing); |
| ASSERT_LT(info.facing, arraysize(facing_names)); |
| name_map[i] = facing_names[info.facing]; |
| } |
| camera3_test::Camera3PerfLog::GetInstance()->SetCameraNameMap(name_map); |
| } |
| |
| static camera_module_t* GetCameraModule() { |
| return g_cam_module; |
| } |
| |
| // Camera module |
| |
| Camera3Module::Camera3Module() |
| : cam_module_(GetCameraModule()), |
| test_camera_ids_(GetCmdLineTestCameraIds()), |
| hal_thread_(&g_module_thread), |
| dev_thread_("Camera3 Test Device Thread") { |
| dev_thread_.Start(); |
| } |
| |
| int Camera3Module::Initialize() { |
| return cam_module_ ? 0 : -ENODEV; |
| } |
| |
| int Camera3Module::GetNumberOfCameras() { |
| if (!cam_module_) { |
| return -ENODEV; |
| } |
| int result = -EINVAL; |
| hal_thread_->PostTaskSync( |
| FROM_HERE, base::Bind(&Camera3Module::GetNumberOfCamerasOnHalThread, |
| base::Unretained(this), &result)); |
| return result; |
| } |
| |
| std::vector<int> Camera3Module::GetCameraIds() { |
| if (!cam_module_) { |
| return std::vector<int>(); |
| } |
| |
| int num_cams = GetNumberOfCameras(); |
| std::vector<int> ids(num_cams); |
| for (int i = 0; i < num_cams; i++) { |
| ids[i] = i; |
| } |
| |
| return ids; |
| } |
| |
| std::vector<int> Camera3Module::GetTestCameraIds() { |
| return test_camera_ids_.empty() ? GetCameraIds() : test_camera_ids_; |
| } |
| |
| void Camera3Module::GetStreamConfigEntry(int cam_id, |
| int32_t key, |
| camera_metadata_ro_entry_t* entry) { |
| entry->count = 0; |
| |
| camera_info info; |
| ASSERT_EQ(0, GetCameraInfo(cam_id, &info)) |
| << "Can't get camera info for" << cam_id; |
| |
| camera_metadata_ro_entry_t local_entry = {}; |
| ASSERT_EQ( |
| 0, find_camera_metadata_ro_entry( |
| const_cast<camera_metadata_t*>(info.static_camera_characteristics), |
| key, &local_entry)) |
| << "Fail to find metadata key " << get_camera_metadata_tag_name(key); |
| ASSERT_NE(0u, local_entry.count) << "Camera stream configuration is empty"; |
| ASSERT_EQ(0u, local_entry.count % kNumOfElementsInStreamConfigEntry) |
| << "Camera stream configuration parsing error"; |
| *entry = local_entry; |
| } |
| |
| bool Camera3Module::IsFormatAvailable(int cam_id, int format) { |
| if (!cam_module_) { |
| return false; |
| } |
| |
| camera_metadata_ro_entry_t available_config = {}; |
| GetStreamConfigEntry(cam_id, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, |
| &available_config); |
| |
| for (uint32_t i = 0; i < available_config.count; |
| i += kNumOfElementsInStreamConfigEntry) { |
| if (available_config.data.i32[i + STREAM_CONFIG_FORMAT_INDEX] == format) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| camera3_device* Camera3Module::OpenDevice(int cam_id) { |
| if (!cam_module_) { |
| return NULL; |
| } |
| camera3_device_t* cam_device = nullptr; |
| hal_thread_->PostTaskSync( |
| FROM_HERE, base::Bind(&Camera3Module::OpenDeviceOnHalThread, |
| base::Unretained(this), cam_id, &cam_device)); |
| return cam_device; |
| } |
| |
| int Camera3Module::CloseDevice(camera3_device* cam_device) { |
| VLOGF_ENTER(); |
| if (!cam_module_) { |
| return -ENODEV; |
| } |
| int result = -ENODEV; |
| dev_thread_.PostTaskSync( |
| FROM_HERE, base::Bind(&Camera3Module::CloseDeviceOnDevThread, |
| base::Unretained(this), cam_device, &result)); |
| return result; |
| } |
| |
| int Camera3Module::GetCameraInfo(int cam_id, camera_info* info) { |
| if (!cam_module_) { |
| return -ENODEV; |
| } |
| int result = -ENODEV; |
| hal_thread_->PostTaskSync( |
| FROM_HERE, base::Bind(&Camera3Module::GetCameraInfoOnHalThread, |
| base::Unretained(this), cam_id, info, &result)); |
| return result; |
| } |
| |
| std::vector<int32_t> Camera3Module::GetOutputFormats(int cam_id) { |
| if (!cam_module_) { |
| return std::vector<int32_t>(); |
| } |
| |
| camera_metadata_ro_entry_t available_config = {}; |
| GetStreamConfigEntry(cam_id, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, |
| &available_config); |
| |
| std::set<int32_t> available_formats; |
| for (uint32_t i = 0; i < available_config.count; |
| i += kNumOfElementsInStreamConfigEntry) { |
| if (available_config.data.i32[i + STREAM_CONFIG_DIRECTION_INDEX] == |
| ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) { |
| available_formats.insert( |
| available_config.data.i32[i + STREAM_CONFIG_FORMAT_INDEX]); |
| } |
| } |
| |
| return std::vector<int32_t>(available_formats.begin(), |
| available_formats.end()); |
| } |
| |
| std::vector<ResolutionInfo> Camera3Module::GetSortedOutputResolutions( |
| int cam_id, int32_t format) { |
| if (!cam_module_) { |
| return std::vector<ResolutionInfo>(); |
| } |
| |
| camera_metadata_ro_entry_t available_config = {}; |
| GetStreamConfigEntry(cam_id, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, |
| &available_config); |
| |
| std::vector<ResolutionInfo> available_resolutions; |
| for (uint32_t i = 0; i < available_config.count; |
| i += kNumOfElementsInStreamConfigEntry) { |
| int32_t fmt = available_config.data.i32[i + STREAM_CONFIG_FORMAT_INDEX]; |
| int32_t width = available_config.data.i32[i + STREAM_CONFIG_WIDTH_INDEX]; |
| int32_t height = available_config.data.i32[i + STREAM_CONFIG_HEIGHT_INDEX]; |
| int32_t in_or_out = |
| available_config.data.i32[i + STREAM_CONFIG_DIRECTION_INDEX]; |
| if ((fmt == format) && |
| (in_or_out == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT)) { |
| available_resolutions.emplace_back(width, height); |
| } |
| } |
| std::sort(available_resolutions.begin(), available_resolutions.end()); |
| return available_resolutions; |
| } |
| |
| int64_t Camera3Module::GetOutputKeyParameterI64( |
| int cam_id, |
| int32_t format, |
| const ResolutionInfo& resolution, |
| int32_t key, |
| int32_t index) { |
| if (!cam_module_) { |
| return -EINVAL; |
| } |
| |
| camera_metadata_ro_entry_t available_config = {}; |
| GetStreamConfigEntry(cam_id, key, &available_config); |
| |
| for (uint32_t i = 0; i < available_config.count; |
| i += kNumOfElementsInStreamConfigEntry) { |
| int64_t fmt = available_config.data.i64[i + STREAM_CONFIG_FORMAT_INDEX]; |
| int64_t width = available_config.data.i64[i + STREAM_CONFIG_WIDTH_INDEX]; |
| int64_t height = available_config.data.i64[i + STREAM_CONFIG_HEIGHT_INDEX]; |
| if (fmt == format && width == resolution.Width() && |
| height == resolution.Height()) { |
| return available_config.data.i64[i + index]; |
| } |
| } |
| |
| return -ENODATA; |
| } |
| |
| int64_t Camera3Module::GetOutputStallDuration( |
| int cam_id, int32_t format, const ResolutionInfo& resolution) { |
| int64_t value = GetOutputKeyParameterI64( |
| cam_id, format, resolution, ANDROID_SCALER_AVAILABLE_STALL_DURATIONS, |
| STREAM_CONFIG_STALL_DURATION_INDEX); |
| return (value != -ENODATA) |
| ? value |
| : 0; // Default duration is '0' (unsupported/no extra stall) |
| } |
| |
| int64_t Camera3Module::GetOutputMinFrameDuration( |
| int cam_id, int32_t format, const ResolutionInfo& resolution) { |
| return GetOutputKeyParameterI64(cam_id, format, resolution, |
| ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, |
| STREAM_CONFIG_MIN_DURATION_INDEX); |
| } |
| |
| void Camera3Module::GetNumberOfCamerasOnHalThread(int* result) { |
| *result = cam_module_->get_number_of_cameras(); |
| } |
| |
| void Camera3Module::GetCameraInfoOnHalThread(int cam_id, |
| camera_info* info, |
| int* result) { |
| *result = cam_module_->get_camera_info(cam_id, info); |
| } |
| |
| void Camera3Module::OpenDeviceOnHalThread(int cam_id, |
| camera3_device_t** cam_device) { |
| *cam_device = nullptr; |
| hw_device_t* device = nullptr; |
| char cam_id_name[3]; |
| snprintf(cam_id_name, sizeof(cam_id_name), "%d", cam_id); |
| if (cam_module_->common.methods->open((const hw_module_t*)cam_module_, |
| cam_id_name, &device) == 0) { |
| *cam_device = reinterpret_cast<camera3_device_t*>(device); |
| } |
| } |
| |
| void Camera3Module::CloseDeviceOnDevThread(camera3_device_t* cam_device, |
| int* result) { |
| VLOGF_ENTER(); |
| ASSERT_NE(nullptr, cam_device->common.close) |
| << "Camera close() is not implemented"; |
| *result = cam_device->common.close(&cam_device->common); |
| } |
| |
| // Test fixture |
| |
| void Camera3ModuleFixture::SetUp() { |
| ASSERT_EQ(0, cam_module_.Initialize()) |
| << "Camera module initialization fails"; |
| } |
| |
| // Test cases |
| |
| TEST_F(Camera3ModuleFixture, NumberOfCameras) { |
| ASSERT_GT(cam_module_.GetNumberOfCameras(), 0) << "No cameras found"; |
| ASSERT_LE(cam_module_.GetNumberOfCameras(), kMaxNumCameras) |
| << "Too many cameras found"; |
| } |
| |
| TEST_F(Camera3ModuleFixture, OpenDeviceOfBadIndices) { |
| auto* callbacks_handler = CameraModuleCallbacksHandler::GetInstance(); |
| std::vector<int> bad_ids = {-1}; |
| for (int id = cam_module_.GetNumberOfCameras(); bad_ids.size() < 3; id++) { |
| if (callbacks_handler->IsExternalCameraPresent(id)) { |
| LOG(INFO) << "Camera " << id << " is an external camera, skip it"; |
| continue; |
| } |
| bad_ids.push_back(id); |
| } |
| // Possible TOCTOU race here if the external camera is plugged after |
| // |IsExternalCameraPresent()|, but before |OpenDevice()|. |
| for (int id : bad_ids) { |
| ASSERT_EQ(nullptr, cam_module_.OpenDevice(id)) |
| << "Open camera device of bad id " << id; |
| } |
| } |
| |
| TEST_F(Camera3ModuleFixture, IsActiveArraySizeSubsetOfPixelArraySize) { |
| for (int cam_id = 0; cam_id < cam_module_.GetNumberOfCameras(); cam_id++) { |
| camera_info info; |
| ASSERT_EQ(0, cam_module_.GetCameraInfo(cam_id, &info)) |
| << "Can't get camera info for " << cam_id; |
| |
| camera_metadata_ro_entry_t entry; |
| ASSERT_EQ( |
| 0, |
| find_camera_metadata_ro_entry( |
| const_cast<camera_metadata_t*>(info.static_camera_characteristics), |
| ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, &entry)) |
| << "Can't find the sensor pixel array size."; |
| int pixel_array_w = entry.data.i32[0]; |
| int pixel_array_h = entry.data.i32[1]; |
| |
| ASSERT_EQ( |
| 0, |
| find_camera_metadata_ro_entry( |
| const_cast<camera_metadata_t*>(info.static_camera_characteristics), |
| ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, &entry)) |
| << "Can't find the sensor active array size."; |
| int active_array_w = entry.data.i32[0]; |
| int active_array_h = entry.data.i32[1]; |
| |
| ASSERT_LE(active_array_h, pixel_array_h); |
| ASSERT_LE(active_array_w, pixel_array_w); |
| } |
| } |
| |
| TEST_F(Camera3ModuleFixture, OpenDevice) { |
| for (int cam_id = 0; cam_id < cam_module_.GetNumberOfCameras(); cam_id++) { |
| camera3_device* cam_dev = cam_module_.OpenDevice(cam_id); |
| ASSERT_NE(nullptr, cam_dev) << "Camera open() returned a NULL device"; |
| cam_module_.CloseDevice(cam_dev); |
| } |
| } |
| |
| TEST_F(Camera3ModuleFixture, OpenDeviceTwice) { |
| for (int cam_id = 0; cam_id < cam_module_.GetNumberOfCameras(); cam_id++) { |
| camera3_device* cam_dev = cam_module_.OpenDevice(cam_id); |
| ASSERT_NE(nullptr, cam_dev) << "Camera open() returned a NULL device"; |
| // Open the device again |
| camera3_device* cam_bad_dev = cam_module_.OpenDevice(cam_id); |
| ASSERT_EQ(nullptr, cam_bad_dev) |
| << "Opening camera device " << cam_id << " should have failed"; |
| cam_module_.CloseDevice(cam_dev); |
| } |
| } |
| |
| TEST_F(Camera3ModuleFixture, RequiredFormats) { |
| auto IsResolutionSupported = |
| [](const std::vector<ResolutionInfo>& resolution_list, |
| const ResolutionInfo& resolution) { |
| return std::find(resolution_list.begin(), resolution_list.end(), |
| resolution) != resolution_list.end(); |
| }; |
| auto RemoveResolution = [](std::vector<ResolutionInfo>& resolution_list, |
| const ResolutionInfo& resolution) { |
| auto it = |
| std::find(resolution_list.begin(), resolution_list.end(), resolution); |
| if (it != resolution_list.end()) { |
| resolution_list.erase(it); |
| } |
| }; |
| auto GetMaxVideoResolution = [](int cam_id) { |
| auto recording_params = ParseRecordingParams(); |
| int32_t width = 0; |
| int32_t height = 0; |
| for (const auto& it : recording_params) { |
| int32_t area = std::get<1>(it) * std::get<2>(it); |
| if (std::get<0>(it) == cam_id && |
| (width * height < area || |
| (width * height == area && width < std::get<1>(it)))) { |
| width = std::get<1>(it); |
| height = std::get<2>(it); |
| } |
| } |
| return ResolutionInfo(width, height); |
| }; |
| |
| for (int cam_id = 0; cam_id < cam_module_.GetNumberOfCameras(); cam_id++) { |
| ASSERT_TRUE(cam_module_.IsFormatAvailable(cam_id, HAL_PIXEL_FORMAT_BLOB)) |
| << "Camera stream configuration does not support JPEG"; |
| ASSERT_TRUE( |
| cam_module_.IsFormatAvailable(cam_id, HAL_PIXEL_FORMAT_YCbCr_420_888)) |
| << "Camera stream configuration does not support flexible YUV"; |
| |
| // Reference: |
| // camera2/cts/ExtendedCameraCharacteristicsTest.java#testAvailableStreamConfigs |
| camera_info info; |
| ASSERT_EQ(0, cam_module_.GetCameraInfo(cam_id, &info)) |
| << "Can't get camera info for " << cam_id; |
| |
| std::vector<ResolutionInfo> jpeg_resolutions = |
| cam_module_.GetSortedOutputResolutions(cam_id, HAL_PIXEL_FORMAT_BLOB); |
| std::vector<ResolutionInfo> yuv_resolutions = |
| cam_module_.GetSortedOutputResolutions(cam_id, |
| HAL_PIXEL_FORMAT_YCbCr_420_888); |
| std::vector<ResolutionInfo> private_resolutions = |
| cam_module_.GetSortedOutputResolutions( |
| cam_id, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED); |
| |
| const ResolutionInfo full_hd(1920, 1080), full_hd_alt(1920, 1088), |
| hd(1280, 720), vga(640, 480), qvga(320, 240); |
| |
| camera_metadata_ro_entry_t entry; |
| ASSERT_EQ( |
| 0, |
| find_camera_metadata_ro_entry( |
| const_cast<camera_metadata_t*>(info.static_camera_characteristics), |
| ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, &entry)) |
| << "Can't find the sensor active array size."; |
| ResolutionInfo active_array(entry.data.i32[0], entry.data.i32[1]); |
| if ((active_array.Width() >= full_hd.Width()) && |
| (active_array.Height() >= full_hd.Height())) { |
| EXPECT_TRUE(IsResolutionSupported(jpeg_resolutions, full_hd) || |
| IsResolutionSupported(jpeg_resolutions, full_hd_alt)) |
| << "Required FULLHD size not found for JPEG for camera " << cam_id; |
| } |
| if ((active_array.Width() >= hd.Width()) && |
| (active_array.Height() >= hd.Height())) { |
| EXPECT_TRUE(IsResolutionSupported(jpeg_resolutions, hd)) |
| << "Required HD size not found for JPEG for camera " << cam_id; |
| } |
| if ((active_array.Width() >= vga.Width()) && |
| (active_array.Height() >= vga.Height())) { |
| EXPECT_TRUE(IsResolutionSupported(jpeg_resolutions, vga)) |
| << "Required VGA size not found for JPEG for camera " << cam_id; |
| } |
| if ((active_array.Width() >= qvga.Width()) && |
| (active_array.Height() >= qvga.Height())) { |
| EXPECT_TRUE(IsResolutionSupported(jpeg_resolutions, qvga)) |
| << "Required QVGA size not found for JPEG for camera " << cam_id; |
| } |
| |
| ASSERT_EQ( |
| 0, |
| find_camera_metadata_ro_entry( |
| const_cast<camera_metadata_t*>(info.static_camera_characteristics), |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, &entry)) |
| << "Cannot find the metadata ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL"; |
| int32_t hw_level = entry.data.i32[0]; |
| |
| // Handle FullHD special case first |
| if (IsResolutionSupported(jpeg_resolutions, full_hd)) { |
| if (hw_level == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL) { |
| EXPECT_TRUE(IsResolutionSupported(yuv_resolutions, full_hd) || |
| IsResolutionSupported(yuv_resolutions, full_hd_alt)) |
| << "FullHD YUV size not found in Full device "; |
| EXPECT_TRUE(IsResolutionSupported(private_resolutions, full_hd) || |
| IsResolutionSupported(private_resolutions, full_hd_alt)) |
| << "FullHD private size not found in Full device "; |
| } |
| // Remove all FullHD or FullHD_Alt sizes for the remaining of the test |
| RemoveResolution(jpeg_resolutions, full_hd); |
| RemoveResolution(jpeg_resolutions, full_hd_alt); |
| } |
| |
| // Check all sizes other than FullHD |
| if (hw_level == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED) { |
| // Remove all jpeg sizes larger than max video size |
| auto max_video_resolution = GetMaxVideoResolution(cam_id); |
| for (auto it = jpeg_resolutions.begin(); it != jpeg_resolutions.end();) { |
| if (it->Width() >= max_video_resolution.Width() && |
| it->Height() >= max_video_resolution.Height()) { |
| it = jpeg_resolutions.erase(it); |
| } else { |
| it++; |
| } |
| } |
| } |
| |
| std::stringstream ss; |
| auto PrintResolutions = |
| [&](const std::vector<ResolutionInfo>& resolutions) { |
| ss.str(""); |
| for (const auto& it : resolutions) { |
| ss << (ss.str().empty() ? "" : ", ") << it.Width() << "x" |
| << it.Height(); |
| } |
| return ss.str().c_str(); |
| }; |
| if (hw_level == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL || |
| hw_level == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED) { |
| std::vector<ResolutionInfo> diff; |
| std::set_difference(jpeg_resolutions.begin(), jpeg_resolutions.end(), |
| yuv_resolutions.begin(), yuv_resolutions.end(), |
| std::inserter(diff, diff.begin())); |
| EXPECT_TRUE(diff.empty()) |
| << "Sizes " << PrintResolutions(diff) << " not found in YUV format"; |
| } |
| |
| std::vector<ResolutionInfo> diff; |
| std::set_difference(jpeg_resolutions.begin(), jpeg_resolutions.end(), |
| private_resolutions.begin(), private_resolutions.end(), |
| std::inserter(diff, diff.begin())); |
| EXPECT_TRUE(diff.empty()) |
| << "Sizes " << PrintResolutions(diff) << " not found in private format"; |
| } |
| } |
| |
| // TODO(hywu): test keys used by RAW, burst and reprocessing capabilities when |
| // full mode is supported |
| |
| static bool AreAllCapabilitiesSupported( |
| camera_metadata_t* characteristics, |
| const std::vector<int32_t>& capabilities) { |
| std::set<int32_t> supported_capabilities; |
| camera_metadata_ro_entry_t entry; |
| if (find_camera_metadata_ro_entry(characteristics, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES, |
| &entry) == 0) { |
| for (size_t i = 0; i < entry.count; i++) { |
| if ((entry.data.i32[i] >= |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) && |
| (entry.data.i32[i] <= |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO)) { // NOLINT(whitespace/line_length) |
| supported_capabilities.insert(entry.data.i32[i]); |
| } |
| } |
| } |
| |
| for (const auto& it : capabilities) { |
| if (supported_capabilities.find(it) == supported_capabilities.end()) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| static void ExpectKeyAvailable(camera_metadata_t* characteristics, |
| int32_t key, |
| int32_t hw_level, |
| const std::vector<int32_t>& capabilities) { |
| camera_metadata_ro_entry_t entry; |
| ASSERT_EQ(0, |
| find_camera_metadata_ro_entry( |
| characteristics, ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, &entry)) |
| << "Cannot find the metadata ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL"; |
| int32_t actual_hw_level = entry.data.i32[0]; |
| |
| // For LIMITED-level targeted keys, rely on capability check, not level |
| if (actual_hw_level >= hw_level && |
| hw_level != ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED) { |
| ASSERT_EQ(0, find_camera_metadata_ro_entry(characteristics, key, &entry)) |
| << "Key " << get_camera_metadata_tag_name(key) |
| << " must be in characteristics for this hardware level "; |
| } else if (AreAllCapabilitiesSupported(characteristics, capabilities)) { |
| if (!(hw_level == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED && |
| actual_hw_level < hw_level)) { |
| // Don't enforce LIMITED-starting keys on LEGACY level, even if cap is |
| // defined |
| std::stringstream ss; |
| auto PrintCapabilities = [&]() { |
| for (const auto& it : capabilities) { |
| ss << (ss.str().empty() ? "" : ", "); |
| switch (it) { |
| case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE: |
| ss << "BACKWARD_COMPATIBLE"; |
| break; |
| case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR: |
| ss << "MANUAL_SENSOR"; |
| break; |
| case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING: |
| ss << "MANUAL_POST_PROCESSING"; |
| break; |
| case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW: |
| ss << "RAW"; |
| break; |
| case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING: |
| ss << "PRIVATE_PROCESSING"; |
| break; |
| case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS: |
| ss << "READ_SENSOR_SETTINGS"; |
| break; |
| case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE: |
| ss << "BURST_CAPTURE"; |
| break; |
| case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING: |
| ss << "YUV_REPROCESSING"; |
| break; |
| case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT: |
| ss << "DEPTH_OUTPUT"; |
| break; |
| case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO: // NOLINT(whitespace/line_length) |
| ss << "HIGHT_SPEED_VIDEO"; |
| break; |
| default: |
| ss << "unknown(" << it << ")"; |
| } |
| } |
| return ss.str().c_str(); |
| }; |
| ASSERT_EQ(0, find_camera_metadata_ro_entry(characteristics, key, &entry)) |
| << "Key " << get_camera_metadata_tag_name(key) |
| << " must be in characteristics for capabilities " |
| << PrintCapabilities(); |
| } |
| } |
| } |
| |
| static void ExpectKeyAvailable(camera_metadata_t* c, |
| int32_t key, |
| int32_t hw_level, |
| int32_t capability) { |
| return ExpectKeyAvailable(c, key, hw_level, std::vector<int>({capability})); |
| } |
| |
| TEST_F(Camera3ModuleFixture, StaticKeysTest) { |
| // Reference: |
| // camera2/cts/ExtendedCameraCharacteristicsTest.java#testKeys |
| #define IGNORE_HARDWARE_LEVEL INT32_MAX |
| #define IGNORE_CAPABILITY -1 |
| for (int cam_id = 0; cam_id < cam_module_.GetNumberOfCameras(); cam_id++) { |
| camera_info info; |
| ASSERT_EQ(0, cam_module_.GetCameraInfo(cam_id, &info)) |
| << "Can't get camera info for " << cam_id; |
| auto c = const_cast<camera_metadata_t*>(info.static_camera_characteristics); |
| |
| // The BACKWARD_COMPATIBLE capability must always be defined for HALv3: |
| // https://source.android.com/devices/camera/versioning#camera_api2 |
| EXPECT_TRUE(AreAllCapabilitiesSupported( |
| c, {ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE})); |
| |
| ExpectKeyAvailable( |
| c, ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_CONTROL_AVAILABLE_MODES, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_CONTROL_AE_AVAILABLE_MODES, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_CONTROL_AE_COMPENSATION_RANGE, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_CONTROL_AE_COMPENSATION_STEP, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_CONTROL_AE_LOCK_AVAILABLE, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_CONTROL_AF_AVAILABLE_MODES, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_CONTROL_AVAILABLE_EFFECTS, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_CONTROL_AVAILABLE_SCENE_MODES, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_CONTROL_AWB_AVAILABLE_MODES, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_CONTROL_AWB_LOCK_AVAILABLE, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| // TODO(hywu): ANDROID_CONTROL_MAX_REGIONS_AE, |
| // ANDROID_CONTROL_MAX_REGIONS_AF, |
| // ANDROID_CONTROL_MAX_REGIONS_AWB |
| ExpectKeyAvailable(c, ANDROID_EDGE_AVAILABLE_EDGE_MODES, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| IGNORE_CAPABILITY); |
| ExpectKeyAvailable( |
| c, ANDROID_FLASH_INFO_AVAILABLE, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable(c, ANDROID_HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW); |
| ExpectKeyAvailable( |
| c, ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_LENS_FACING, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable(c, ANDROID_LENS_INFO_AVAILABLE_APERTURES, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR); |
| ExpectKeyAvailable(c, ANDROID_LENS_INFO_AVAILABLE_FILTER_DENSITIES, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR); |
| ExpectKeyAvailable( |
| c, ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable(c, ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR); |
| ExpectKeyAvailable( |
| c, ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_REQUEST_AVAILABLE_CAPABILITIES, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS, IGNORE_HARDWARE_LEVEL, |
| std::vector<int32_t>( |
| {ANDROID_REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING})); |
| ExpectKeyAvailable( |
| c, ANDROID_REQUEST_PARTIAL_RESULT_COUNT, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_REQUEST_PIPELINE_MAX_DEPTH, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_SCALER_CROPPING_TYPE, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_SENSOR_BLACK_LEVEL_PATTERN, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| std::vector<int32_t>( |
| {ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW})); |
| ExpectKeyAvailable(c, ANDROID_SENSOR_CALIBRATION_TRANSFORM1, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW); |
| ExpectKeyAvailable(c, ANDROID_SENSOR_COLOR_TRANSFORM1, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW); |
| ExpectKeyAvailable(c, ANDROID_SENSOR_FORWARD_MATRIX1, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW); |
| ExpectKeyAvailable( |
| c, ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, IGNORE_HARDWARE_LEVEL, |
| std::vector<int32_t>( |
| {ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW})); |
| ExpectKeyAvailable(c, ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW); |
| ExpectKeyAvailable(c, ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR); |
| ExpectKeyAvailable(c, ANDROID_SENSOR_INFO_MAX_FRAME_DURATION, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR); |
| ExpectKeyAvailable( |
| c, ANDROID_SENSOR_INFO_PHYSICAL_SIZE, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable(c, ANDROID_SENSOR_INFO_SENSITIVITY_RANGE, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR); |
| ExpectKeyAvailable(c, ANDROID_SENSOR_INFO_WHITE_LEVEL, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW); |
| ExpectKeyAvailable( |
| c, ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable(c, ANDROID_SENSOR_MAX_ANALOG_SENSITIVITY, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR); |
| ExpectKeyAvailable( |
| c, ANDROID_SENSOR_ORIENTATION, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable(c, ANDROID_SENSOR_REFERENCE_ILLUMINANT1, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW); |
| ExpectKeyAvailable( |
| c, ANDROID_SHADING_AVAILABLE_MODES, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| std::vector<int32_t>( |
| {ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW})); |
| ExpectKeyAvailable( |
| c, ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable(c, ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW); |
| ExpectKeyAvailable(c, |
| ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW); |
| ExpectKeyAvailable( |
| c, ANDROID_STATISTICS_INFO_MAX_FACE_COUNT, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_SYNC_MAX_LATENCY, IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); |
| ExpectKeyAvailable( |
| c, ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING); |
| ExpectKeyAvailable( |
| c, ANDROID_TONEMAP_MAX_CURVE_POINTS, |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING); |
| camera_metadata_ro_entry_t entry; |
| if (find_camera_metadata_ro_entry(c, ANDROID_SENSOR_REFERENCE_ILLUMINANT2, |
| &entry) == 0) { |
| ExpectKeyAvailable(c, ANDROID_SENSOR_REFERENCE_ILLUMINANT2, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW); |
| ExpectKeyAvailable(c, ANDROID_SENSOR_COLOR_TRANSFORM2, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW); |
| ExpectKeyAvailable(c, ANDROID_SENSOR_CALIBRATION_TRANSFORM2, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW); |
| ExpectKeyAvailable(c, ANDROID_SENSOR_FORWARD_MATRIX2, |
| IGNORE_HARDWARE_LEVEL, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW); |
| } |
| } |
| } |
| |
| TEST_F(Camera3ModuleFixture, StreamConfigurationMapTest) { |
| // Reference: |
| // camera2/cts/ExtendedCameraCharacteristicsTest.java#testStreamConfigurationMap |
| const int64_t kToleranceFactor = 2; |
| for (int cam_id = 0; cam_id < cam_module_.GetNumberOfCameras(); cam_id++) { |
| camera_info info; |
| ASSERT_EQ(0, cam_module_.GetCameraInfo(cam_id, &info)) |
| << "Can't get camera info for " << cam_id; |
| |
| std::vector<int32_t> available_formats = |
| cam_module_.GetOutputFormats(cam_id); |
| for (const auto& format : available_formats) { |
| std::vector<ResolutionInfo> available_resolutions = |
| cam_module_.GetSortedOutputResolutions(cam_id, format); |
| size_t resolution_count = available_resolutions.size(); |
| for (size_t i = 0; i < resolution_count; i++) { |
| int64_t stall_duration = cam_module_.GetOutputStallDuration( |
| cam_id, format, available_resolutions[i]); |
| if (stall_duration >= 0) { |
| if (format == HAL_PIXEL_FORMAT_YCbCr_420_888) { |
| EXPECT_EQ(0, stall_duration) |
| << "YUV_420_888 may not have a non-zero stall duration"; |
| } else if (format == HAL_PIXEL_FORMAT_BLOB) { |
| // Stall duration should be in a reasonable range: larger size |
| // should normally have larger stall duration |
| if (i > 0) { |
| int64_t prev_duration = cam_module_.GetOutputStallDuration( |
| cam_id, format, available_resolutions[i - 1]); |
| EXPECT_LE(prev_duration / kToleranceFactor, stall_duration) |
| << "Stall duration (format " << format << " and size " |
| << available_resolutions[i].Width() << "x" |
| << available_resolutions[i].Height() |
| << ") is not in the right range"; |
| } |
| } |
| } else { |
| ADD_FAILURE() << "Negative stall duration for format " << format; |
| } |
| |
| int64_t min_duration = cam_module_.GetOutputMinFrameDuration( |
| cam_id, format, available_resolutions[i]); |
| if (AreAllCapabilitiesSupported( |
| const_cast<camera_metadata_t*>( |
| info.static_camera_characteristics), |
| std::vector<int32_t>( |
| {ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR}))) { |
| EXPECT_LT(0, min_duration) |
| << "MANUAL_SENSOR capability, need positive min frame duration " |
| "for format " |
| << format << " and size " << available_resolutions[i].Width() |
| << "x" << available_resolutions[i].Height(); |
| } else { |
| EXPECT_LE(0, min_duration) |
| << "Need non-negative min frame duration for format " << format |
| << " and size " << available_resolutions[i].Width() << "x" |
| << available_resolutions[i].Height(); |
| } |
| } |
| } |
| } |
| } |
| |
| TEST_F(Camera3ModuleFixture, ChromeOSRequiredResolution) { |
| const int required_formats[] = {HAL_PIXEL_FORMAT_BLOB, |
| HAL_PIXEL_FORMAT_YCbCr_420_888, |
| HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED}; |
| const ResolutionInfo required_resolutions[] = {ResolutionInfo(1600, 1200), |
| ResolutionInfo(1280, 960)}; |
| for (const auto& cam_id : cam_module_.GetCameraIds()) { |
| camera_info info; |
| ASSERT_EQ(0, cam_module_.GetCameraInfo(cam_id, &info)) |
| << "Can't get camera info for " << cam_id; |
| camera_metadata_ro_entry_t entry; |
| ASSERT_EQ( |
| 0, |
| find_camera_metadata_ro_entry( |
| const_cast<camera_metadata_t*>(info.static_camera_characteristics), |
| ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, &entry)) |
| << "Can't find the sensor active array size."; |
| ASSERT_GE(entry.count, 2); |
| ResolutionInfo active_array(entry.data.i32[0], entry.data.i32[1]); |
| for (const auto& resolution : required_resolutions) { |
| if ((active_array.Width() >= resolution.Width()) && |
| (active_array.Height() >= resolution.Height())) { |
| for (const auto& format : required_formats) { |
| auto resolutions = |
| cam_module_.GetSortedOutputResolutions(cam_id, format); |
| EXPECT_NE(resolutions.end(), std::find(resolutions.begin(), |
| resolutions.end(), resolution)) |
| << "Required size " << resolution.Width() << "x" |
| << resolution.Height() << " not found for format " << format |
| << " for camera " << cam_id; |
| } |
| } |
| } |
| } |
| } |
| |
| } // namespace camera3_test |
| |
| static void AddGtestFilterNegativePattern(std::string negative) { |
| using ::testing::GTEST_FLAG(filter); |
| |
| GTEST_FLAG(filter) |
| .append((GTEST_FLAG(filter).find('-') == std::string::npos) ? "-" : ":") |
| .append(negative); |
| } |
| |
| // Return -ENOENT for no facing specified, -EINVAL for invalid facing name. |
| static int GetCmdLineTestCameraFacing(const base::CommandLine& cmd_line) { |
| const std::string facing_names[] = {"back", "front"}; |
| const auto& facing_name = cmd_line.GetSwitchValueASCII("camera_facing"); |
| if (facing_name.empty()) |
| return -ENOENT; |
| int idx = std::distance( |
| facing_names, |
| std::find(facing_names, facing_names + arraysize(facing_names), |
| facing_name)); |
| if (idx == arraysize(facing_names)) { |
| ADD_FAILURE() << "Invalid facing name: " << facing_name; |
| return -EINVAL; |
| } |
| return idx; |
| } |
| |
| bool InitializeTest(int* argc, char*** argv, void** cam_hal_handle) { |
| // Set up logging so we can enable VLOGs with -v / --vmodule. |
| base::CommandLine::Init(*argc, *argv); |
| logging::LoggingSettings settings; |
| settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; |
| LOG_ASSERT(logging::InitLogging(settings)); |
| |
| if (geteuid() == 0) { |
| LOGF(WARNING) |
| << "Running tests as root might leak some root owned resources, which " |
| "cannot be accessed by the user arc-camera."; |
| } |
| |
| base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); |
| base::FilePath camera_hal_path = |
| cmd_line->GetSwitchValuePath("camera_hal_path"); |
| int facing = GetCmdLineTestCameraFacing(*cmd_line); |
| |
| if (facing != -ENOENT) { |
| if (facing == -EINVAL) { |
| LOG(ERROR) << "Invalid camera facing name."; |
| return false; |
| } else if (!camera_hal_path.empty() || |
| !camera3_test::GetCmdLineTestCameraIds().empty()) { |
| LOGF(ERROR) << "Cannot specify both --camera_hal_path/--camera_ids and " |
| "--camera_facing."; |
| return false; |
| } |
| } else if (camera_hal_path.empty()) { |
| std::vector<base::FilePath> camera_hal_paths = cros::GetCameraHalPaths(); |
| |
| if (camera_hal_paths.size() == 1) { |
| // TODO(shik): Ignore usb.so if there is no built-in USB camera, so we |
| // have a better heuristic guess. |
| |
| camera_hal_path = camera_hal_paths[0]; |
| |
| LOG(INFO) << "camera_hal_path unspecified, using " |
| << camera_hal_path.value() << " as default. " |
| << "You can override this behavior by the command line " |
| << "argument `--camera_hal_path=`"; |
| } else { |
| LOGF(ERROR) << "camera_hal_path unspecified. " |
| << "Since we cannot determine the suitable one, please add " |
| << "`--camera_hal_path=` into command line argument."; |
| |
| if (!camera_hal_paths.empty()) { |
| LOGF(ERROR) << "List of possible paths:"; |
| for (const auto& path : camera_hal_paths) { |
| LOGF(ERROR) << path.value(); |
| } |
| } |
| |
| return false; |
| } |
| } |
| |
| // Open camera HAL and get module |
| camera3_test::g_module_thread.Start(); |
| if (facing != -ENOENT) { |
| camera3_test::InitCameraModuleByFacing(facing, cam_hal_handle); |
| } else { |
| camera3_test::InitCameraModuleByHalPath(camera_hal_path, cam_hal_handle); |
| } |
| |
| camera3_test::InitPerfLog(); |
| |
| // Initialize gtest |
| ::testing::InitGoogleTest(argc, *argv); |
| if (testing::Test::HasFailure()) { |
| camera3_test::g_module_thread.Stop(); |
| return false; |
| } |
| |
| if (camera_hal_path.value().find("usb") != std::string::npos) { |
| // Skip 3A algorithm sandbox IPC tests for USB HAL |
| AddGtestFilterNegativePattern("*Camera3AlgoSandboxIPCErrorTest*"); |
| } |
| |
| const std::vector<std::string> kIgnoreSensorOrientationTestBoards = { |
| "nocturne", |
| "scarlet", |
| }; |
| std::string board = base::SysInfo::GetLsbReleaseBoard(); |
| if (base::ContainsValue(kIgnoreSensorOrientationTestBoards, board)) { |
| VLOG(1) << "Ignore SensorOrientationTest on " << board; |
| AddGtestFilterNegativePattern("*SensorOrientationTest/*"); |
| } |
| |
| return true; |
| } |
| |
| #ifdef FUZZER |
| |
| extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { |
| void* cam_hal_handle = NULL; |
| if (!InitializeTest(argc, argv, &cam_hal_handle)) { |
| exit(EXIT_FAILURE); |
| } |
| ::testing::TestEventListeners& listeners = |
| ::testing::UnitTest::GetInstance()->listeners(); |
| delete listeners.Release(listeners.default_result_printer()); |
| return 0; |
| } |
| |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { |
| camera3_test::Camera3TestDataForwarder::GetInstance()->SetData(Data, Size); |
| ignore_result(RUN_ALL_TESTS()); |
| return 0; |
| } |
| |
| #else |
| int main(int argc, char** argv) { |
| // We have to make it leaky until the lifetime conflict in b/119926433 is |
| // resolved. |
| base::NoDestructor<base::AtExitManager> leaky_at_exit_manager; |
| int result = EXIT_FAILURE; |
| void* cam_hal_handle = NULL; |
| if (!InitializeTest(&argc, &argv, &cam_hal_handle)) { |
| return result; |
| } else { |
| result = RUN_ALL_TESTS(); |
| } |
| camera3_test::g_module_thread.Stop(); |
| |
| return result; |
| } |
| #endif |