| // Copyright 2017 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_service.h" |
| |
| #include <unistd.h> |
| |
| #include <algorithm> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/bind.h> |
| #include <base/check.h> |
| #include <base/command_line.h> |
| #include <base/strings/string_number_conversions.h> |
| |
| #include "camera3_test/camera3_perf_log.h" |
| #include "cros-camera/common.h" |
| |
| namespace camera3_test { |
| |
| namespace { |
| |
| constexpr int kWaitForStopPreviewTimeoutMs = 3000; |
| constexpr int kWaitForStopRecordingTimeoutMs = 3000; |
| |
| double Get3ATimeoutMultiplier() { |
| static double multiplier = [] { |
| constexpr char kSwitch[] = "3a_timeout_multiplier"; |
| const auto* cl = base::CommandLine::ForCurrentProcess(); |
| std::string val = cl->GetSwitchValueASCII(kSwitch); |
| double out; |
| if (val.empty() || !base::StringToDouble(val, &out)) { |
| return 1.0; |
| } |
| return out; |
| }(); |
| return multiplier; |
| } |
| |
| int GetWaitForFocusDoneTimeoutMs() { |
| return static_cast<int>(6000 * Get3ATimeoutMultiplier()); |
| } |
| |
| int GetWaitForAWBConvergedTimeoutMs() { |
| return static_cast<int>(3000 * Get3ATimeoutMultiplier()); |
| } |
| |
| } // namespace |
| |
| Camera3Service::~Camera3Service() {} |
| |
| int Camera3Service::Initialize( |
| ProcessStillCaptureResultCallback still_capture_cb, |
| ProcessRecordingResultCallback recording_cb, |
| ProcessPreviewResultCallback preview_cb) { |
| base::AutoLock l(lock_); |
| if (initialized_) { |
| LOGF(ERROR) << "Camera service is already initialized"; |
| return -EINVAL; |
| } |
| for (const auto& it : cam_ids_) { |
| cam_dev_service_map_[it].reset(new Camera3Service::Camera3DeviceService( |
| it, still_capture_cb, recording_cb, preview_cb)); |
| int result = cam_dev_service_map_[it]->Initialize(); |
| if (result != 0) { |
| LOGF(ERROR) << "Camera device " << it << " service initialization fails"; |
| cam_dev_service_map_.clear(); |
| return result; |
| } |
| } |
| initialized_ = true; |
| return 0; |
| } |
| |
| void Camera3Service::Destroy() { |
| base::AutoLock l(lock_); |
| if (!initialized_) { |
| return; |
| } |
| for (auto& it : cam_dev_service_map_) { |
| it.second->Destroy(); |
| } |
| cam_dev_service_map_.clear(); |
| initialized_ = false; |
| } |
| |
| int Camera3Service::StartPreview(int cam_id, |
| const ResolutionInfo& preview_resolution, |
| const ResolutionInfo& still_capture_resolution, |
| const ResolutionInfo& recording_resolution) { |
| base::AutoLock l(lock_); |
| if (!initialized_ || |
| cam_dev_service_map_.find(cam_id) == cam_dev_service_map_.end()) { |
| return -ENODEV; |
| } |
| return cam_dev_service_map_[cam_id]->StartPreview( |
| preview_resolution, still_capture_resolution, recording_resolution); |
| } |
| |
| void Camera3Service::StopPreview(int cam_id) { |
| base::AutoLock l(lock_); |
| if (initialized_ && |
| cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) { |
| cam_dev_service_map_[cam_id]->StopPreview(); |
| } |
| } |
| |
| void Camera3Service::StartAutoFocus(int cam_id) { |
| base::AutoLock l(lock_); |
| if (initialized_ && |
| cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) { |
| cam_dev_service_map_[cam_id]->StartAutoFocus(); |
| } |
| } |
| |
| int Camera3Service::WaitForAutoFocusDone(int cam_id) { |
| base::AutoLock l(lock_); |
| if (!initialized_ || |
| cam_dev_service_map_.find(cam_id) == cam_dev_service_map_.end()) { |
| return -ENODEV; |
| } |
| return cam_dev_service_map_[cam_id]->WaitForAutoFocusDone(); |
| } |
| |
| int Camera3Service::WaitForAWBConvergedAndLock(int cam_id) { |
| base::AutoLock l(lock_); |
| if (!initialized_ || |
| cam_dev_service_map_.find(cam_id) == cam_dev_service_map_.end()) { |
| return -ENODEV; |
| } |
| return cam_dev_service_map_[cam_id]->WaitForAWBConvergedAndLock(); |
| } |
| |
| void Camera3Service::StartAEPrecapture(int cam_id) { |
| base::AutoLock l(lock_); |
| if (initialized_ && |
| cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) { |
| cam_dev_service_map_[cam_id]->StartAEPrecapture(); |
| } |
| } |
| |
| int Camera3Service::WaitForAEStable(int cam_id) { |
| base::AutoLock l(lock_); |
| if (!initialized_ || |
| cam_dev_service_map_.find(cam_id) == cam_dev_service_map_.end()) { |
| return -ENODEV; |
| } |
| return cam_dev_service_map_[cam_id]->WaitForAEStable(); |
| } |
| |
| void Camera3Service::StartFaceDetection(int cam_id) { |
| base::AutoLock l(lock_); |
| if (initialized_ && |
| cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) { |
| cam_dev_service_map_[cam_id]->StartFaceDetection(); |
| } |
| } |
| |
| void Camera3Service::StopFaceDetection(int cam_id) { |
| base::AutoLock l(lock_); |
| if (initialized_ && |
| cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) { |
| cam_dev_service_map_[cam_id]->StopFaceDetection(); |
| } |
| } |
| |
| void Camera3Service::TakeStillCapture(int cam_id, |
| const camera_metadata_t* metadata) { |
| base::AutoLock l(lock_); |
| if (initialized_ && |
| cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) { |
| cam_dev_service_map_[cam_id]->TakeStillCapture(metadata); |
| } |
| } |
| |
| int Camera3Service::StartRecording(int cam_id, |
| const camera_metadata_t* metadata) { |
| base::AutoLock l(lock_); |
| if (!initialized_ || |
| cam_dev_service_map_.find(cam_id) == cam_dev_service_map_.end()) { |
| return -ENODEV; |
| } |
| return cam_dev_service_map_[cam_id]->StartRecording(metadata); |
| } |
| |
| void Camera3Service::StopRecording(int cam_id) { |
| base::AutoLock l(lock_); |
| if (initialized_ && |
| cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) { |
| cam_dev_service_map_[cam_id]->StopRecording(); |
| } |
| } |
| |
| int Camera3Service::WaitForPreviewFrames(int cam_id, |
| uint32_t num_frames, |
| uint32_t timeout_ms) { |
| base::AutoLock l(lock_); |
| if (!initialized_ || |
| cam_dev_service_map_.find(cam_id) == cam_dev_service_map_.end()) { |
| return -ENODEV; |
| } |
| return cam_dev_service_map_[cam_id]->WaitForPreviewFrames(num_frames, |
| timeout_ms); |
| } |
| |
| const Camera3Device::StaticInfo* Camera3Service::GetStaticInfo(int cam_id) { |
| base::AutoLock l(lock_); |
| if (initialized_ && |
| cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) { |
| return cam_dev_service_map_[cam_id]->GetStaticInfo(); |
| } |
| return nullptr; |
| } |
| |
| const camera_metadata_t* Camera3Service::ConstructDefaultRequestSettings( |
| int cam_id, int type) { |
| base::AutoLock l(lock_); |
| if (initialized_ && |
| cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) { |
| return cam_dev_service_map_[cam_id]->ConstructDefaultRequestSettings(type); |
| } |
| return nullptr; |
| } |
| |
| int Camera3Service::Camera3DeviceService::Initialize() { |
| Camera3Module cam_module; |
| if (cam_device_.Initialize(&cam_module) != 0) { |
| LOGF(ERROR) << "Camera device initialization fails"; |
| return -ENODEV; |
| } |
| if (!service_thread_.Start()) { |
| LOGF(ERROR) << "Failed to start thread"; |
| return -EINVAL; |
| } |
| cam_device_.RegisterResultMetadataOutputBufferCallback(base::Bind( |
| &Camera3Service::Camera3DeviceService::ProcessResultMetadataOutputBuffers, |
| base::Unretained(this))); |
| repeating_preview_metadata_.reset(clone_camera_metadata( |
| cam_device_.ConstructDefaultRequestSettings(CAMERA3_TEMPLATE_PREVIEW))); |
| if (!repeating_preview_metadata_) { |
| LOGF(ERROR) << "Failed to create preview metadata"; |
| return -ENOMEM; |
| } |
| sem_init(&preview_frame_sem_, 0, 0); |
| return 0; |
| } |
| |
| void Camera3Service::Camera3DeviceService::Destroy() { |
| service_thread_.Stop(); |
| sem_destroy(&preview_frame_sem_); |
| cam_device_.Destroy(); |
| } |
| |
| int Camera3Service::Camera3DeviceService::StartPreview( |
| const ResolutionInfo& preview_resolution, |
| const ResolutionInfo& still_capture_resolution, |
| const ResolutionInfo& recording_resolution) { |
| VLOGF_ENTER(); |
| int result = -EIO; |
| service_thread_.PostTaskSync( |
| FROM_HERE, |
| base::Bind( |
| &Camera3Service::Camera3DeviceService::StartPreviewOnServiceThread, |
| base::Unretained(this), preview_resolution, still_capture_resolution, |
| recording_resolution, &result)); |
| return result; |
| } |
| |
| void Camera3Service::Camera3DeviceService::StopPreview() { |
| VLOGF_ENTER(); |
| auto future = cros::Future<void>::Create(nullptr); |
| service_thread_.PostTaskAsync( |
| FROM_HERE, |
| base::Bind( |
| &Camera3Service::Camera3DeviceService::StopPreviewOnServiceThread, |
| base::Unretained(this), cros::GetFutureCallback(future))); |
| if (!future->Wait(kWaitForStopPreviewTimeoutMs)) { |
| LOGF(ERROR) << "Timeout stopping preview"; |
| } |
| } |
| |
| void Camera3Service::Camera3DeviceService::StartAutoFocus() { |
| service_thread_.PostTaskAsync( |
| FROM_HERE, |
| base::Bind( |
| &Camera3Service::Camera3DeviceService::StartAutoFocusOnServiceThread, |
| base::Unretained(this))); |
| } |
| |
| int Camera3Service::Camera3DeviceService::WaitForAutoFocusDone() { |
| VLOGF_ENTER(); |
| auto future = cros::Future<void>::Create(nullptr); |
| int32_t result; |
| service_thread_.PostTaskAsync( |
| FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService:: |
| AddMetadataListenerOnServiceThread, |
| base::Unretained(this), ANDROID_CONTROL_AF_STATE, |
| std::unordered_set<int32_t>( |
| {ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED, |
| ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED}), |
| cros::GetFutureCallback(future), &result)); |
| if (!future->Wait(GetWaitForFocusDoneTimeoutMs())) { |
| service_thread_.PostTaskSync( |
| FROM_HERE, |
| base::Bind(&Camera3Service::Camera3DeviceService:: |
| DeleteMetadataListenerOnServiceThread, |
| base::Unretained(this), ANDROID_CONTROL_AF_STATE, |
| std::unordered_set<int32_t>( |
| {ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED, |
| ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED}))); |
| return -ETIMEDOUT; |
| } |
| if (result == ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) { |
| VLOGF(1) << "Auto-focus did not lock"; |
| } |
| return 0; |
| } |
| |
| int Camera3Service::Camera3DeviceService::WaitForAWBConvergedAndLock() { |
| VLOGF_ENTER(); |
| auto future = cros::Future<void>::Create(nullptr); |
| service_thread_.PostTaskAsync( |
| FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService:: |
| AddMetadataListenerOnServiceThread, |
| base::Unretained(this), ANDROID_CONTROL_AWB_STATE, |
| std::unordered_set<int32_t>( |
| {ANDROID_CONTROL_AWB_STATE_CONVERGED}), |
| cros::GetFutureCallback(future), nullptr)); |
| if (!future->Wait(GetWaitForAWBConvergedTimeoutMs())) { |
| service_thread_.PostTaskSync( |
| FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService:: |
| DeleteMetadataListenerOnServiceThread, |
| base::Unretained(this), ANDROID_CONTROL_AWB_STATE, |
| std::unordered_set<int32_t>( |
| {ANDROID_CONTROL_AWB_STATE_CONVERGED}))); |
| return -ETIMEDOUT; |
| } |
| |
| if (cam_device_.GetStaticInfo()->IsAWBLockSupported()) { |
| service_thread_.PostTaskAsync( |
| FROM_HERE, |
| base::Bind( |
| &Camera3Service::Camera3DeviceService::LockAWBOnServiceThread, |
| base::Unretained(this))); |
| } |
| return 0; |
| } |
| |
| void Camera3Service::Camera3DeviceService::StartAEPrecapture() { |
| VLOGF_ENTER(); |
| service_thread_.PostTaskAsync( |
| FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService:: |
| StartAEPrecaptureOnServiceThread, |
| base::Unretained(this))); |
| } |
| |
| int Camera3Service::Camera3DeviceService::WaitForAEStable() { |
| VLOGF_ENTER(); |
| auto future = cros::Future<void>::Create(nullptr); |
| int32_t result; |
| service_thread_.PostTaskAsync( |
| FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService:: |
| AddMetadataListenerOnServiceThread, |
| base::Unretained(this), ANDROID_CONTROL_AE_STATE, |
| std::unordered_set<int32_t>( |
| {ANDROID_CONTROL_AE_STATE_CONVERGED, |
| ANDROID_CONTROL_AE_STATE_FLASH_REQUIRED}), |
| cros::GetFutureCallback(future), &result)); |
| if (!future->Wait(GetWaitForFocusDoneTimeoutMs())) { |
| service_thread_.PostTaskSync( |
| FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService:: |
| DeleteMetadataListenerOnServiceThread, |
| base::Unretained(this), ANDROID_CONTROL_AE_STATE, |
| std::unordered_set<int32_t>( |
| {ANDROID_CONTROL_AE_STATE_CONVERGED, |
| ANDROID_CONTROL_AE_STATE_FLASH_REQUIRED}))); |
| return -ETIMEDOUT; |
| } |
| if (result == ANDROID_CONTROL_AE_STATE_FLASH_REQUIRED) { |
| VLOGF(1) << "Flash needs to be fired for good quality still capture"; |
| } |
| return 0; |
| } |
| |
| void Camera3Service::Camera3DeviceService::StartFaceDetection() { |
| VLOGF_ENTER(); |
| service_thread_.PostTaskAsync( |
| FROM_HERE, base::BindRepeating(&Camera3Service::Camera3DeviceService:: |
| StartFaceDetectionOnServiceThread, |
| base::Unretained(this))); |
| } |
| |
| void Camera3Service::Camera3DeviceService::StopFaceDetection() { |
| VLOGF_ENTER(); |
| service_thread_.PostTaskAsync( |
| FROM_HERE, base::BindRepeating(&Camera3Service::Camera3DeviceService:: |
| StopFaceDetectionOnServiceThread, |
| base::Unretained(this))); |
| } |
| |
| void Camera3Service::Camera3DeviceService::TakeStillCapture( |
| const camera_metadata_t* metadata) { |
| VLOGF_ENTER(); |
| auto future = cros::Future<void>::Create(nullptr); |
| service_thread_.PostTaskAsync( |
| FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService:: |
| TakeStillCaptureOnServiceThread, |
| base::Unretained(this), metadata, |
| cros::GetFutureCallback(future))); |
| // Wait for ProcessPreviewRequestOnServiceThread() to finish processing |
| // |metadata| |
| future->Wait(); |
| } |
| |
| int Camera3Service::Camera3DeviceService::StartRecording( |
| const camera_metadata_t* metadata) { |
| VLOGF_ENTER(); |
| auto future = cros::Future<int>::Create(nullptr); |
| service_thread_.PostTaskSync( |
| FROM_HERE, |
| base::Bind( |
| &Camera3Service::Camera3DeviceService::StartRecordingOnServiceThread, |
| base::Unretained(this), metadata, cros::GetFutureCallback(future))); |
| return future->Wait(); |
| } |
| |
| void Camera3Service::Camera3DeviceService::StopRecording() { |
| VLOGF_ENTER(); |
| auto future = cros::Future<void>::Create(nullptr); |
| service_thread_.PostTaskAsync( |
| FROM_HERE, |
| base::Bind( |
| &Camera3Service::Camera3DeviceService::StopRecordingOnServiceThread, |
| base::Unretained(this), cros::GetFutureCallback(future))); |
| if (!future->Wait(kWaitForStopRecordingTimeoutMs)) { |
| LOGF(ERROR) << "Timeout stopping preview"; |
| } |
| } |
| |
| int Camera3Service::Camera3DeviceService::WaitForPreviewFrames( |
| uint32_t num_frames, uint32_t timeout_ms) { |
| VLOGF_ENTER(); |
| while (sem_trywait(&preview_frame_sem_) == 0) { |
| } |
| for (uint32_t i = 0; i < num_frames; ++i) { |
| struct timespec timeout = {}; |
| if (clock_gettime(CLOCK_REALTIME, &timeout)) { |
| LOGF(ERROR) << "Failed to get clock time"; |
| return -errno; |
| } |
| timeout.tv_sec += timeout_ms / 1000; |
| timeout.tv_nsec += (timeout_ms % 1000) * 1000; |
| if (sem_timedwait(&preview_frame_sem_, &timeout) != 0) { |
| return -errno; |
| } |
| } |
| return 0; |
| } |
| |
| const Camera3Device::StaticInfo* |
| Camera3Service::Camera3DeviceService::GetStaticInfo() const { |
| return cam_device_.GetStaticInfo(); |
| } |
| |
| const camera_metadata_t* |
| Camera3Service::Camera3DeviceService::ConstructDefaultRequestSettings( |
| int type) { |
| return cam_device_.ConstructDefaultRequestSettings(type); |
| } |
| |
| void Camera3Service::Camera3DeviceService::ProcessResultMetadataOutputBuffers( |
| uint32_t frame_number, |
| ScopedCameraMetadata metadata, |
| std::vector<cros::ScopedBufferHandle> buffers) { |
| VLOGF_ENTER(); |
| service_thread_.PostTaskAsync( |
| FROM_HERE, |
| base::Bind(&Camera3Service::Camera3DeviceService:: |
| ProcessResultMetadataOutputBuffersOnServiceThread, |
| base::Unretained(this), frame_number, base::Passed(&metadata), |
| base::Passed(&buffers))); |
| } |
| |
| void Camera3Service::Camera3DeviceService::StartPreviewOnServiceThread( |
| const ResolutionInfo preview_resolution, |
| const ResolutionInfo still_capture_resolution, |
| const ResolutionInfo recording_resolution, |
| int* result) { |
| DCHECK(service_thread_.IsCurrentThread()); |
| VLOGF_ENTER(); |
| if (preview_state_ != PREVIEW_STOPPED) { |
| LOGF(ERROR) << "Failed to start preview because it is not stopped"; |
| *result = -EAGAIN; |
| return; |
| } |
| |
| if (still_capture_resolution.Area()) { |
| cam_device_.AddOutputStream( |
| HAL_PIXEL_FORMAT_BLOB, still_capture_resolution.Width(), |
| still_capture_resolution.Height(), CAMERA3_STREAM_ROTATION_0); |
| } |
| if (recording_resolution.Area()) { |
| cam_device_.AddOutputStream( |
| HAL_PIXEL_FORMAT_YCbCr_420_888, recording_resolution.Width(), |
| recording_resolution.Height(), CAMERA3_STREAM_ROTATION_0); |
| } |
| cam_device_.AddOutputStream( |
| HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, preview_resolution.Width(), |
| preview_resolution.Height(), CAMERA3_STREAM_ROTATION_0); |
| if (cam_device_.ConfigureStreams(&streams_) != 0) { |
| ADD_FAILURE() << "Configuring stream fails"; |
| *result = -EINVAL; |
| return; |
| } |
| auto GetStream = [this](int format) { |
| auto it = std::find_if(streams_.begin(), streams_.end(), |
| [&](const camera3_stream_t* stream) { |
| return (stream->format == format); |
| }); |
| return (it == streams_.end()) ? nullptr : *it; |
| }; |
| const camera3_stream_t* preview_stream = |
| GetStream(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED); |
| const camera3_stream_t* record_stream = |
| GetStream(HAL_PIXEL_FORMAT_YCbCr_420_888); |
| if (!preview_stream || (recording_resolution.Area() && !record_stream)) { |
| ADD_FAILURE() << "Failed to find configured stream"; |
| *result = -EINVAL; |
| return; |
| } |
| |
| number_of_capture_requests_ = preview_stream->max_buffers; |
| if (recording_resolution.Area()) { |
| number_of_capture_requests_ = |
| std::min(number_of_capture_requests_, record_stream->max_buffers); |
| } |
| capture_requests_.resize(number_of_capture_requests_); |
| output_stream_buffers_ = std::vector<std::vector<camera3_stream_buffer_t>>( |
| number_of_capture_requests_, |
| std::vector<camera3_stream_buffer_t>(kNumberOfOutputStreamBuffers)); |
| // Submit initial preview capture requests to fill the HAL pipeline first. |
| // Then when a result callback is processed, the corresponding capture |
| // request (and output buffer) is recycled and submitted again. |
| for (uint32_t i = 0; i < number_of_capture_requests_; i++) { |
| std::vector<const camera3_stream_t*> streams(1, preview_stream); |
| std::vector<camera3_stream_buffer_t> output_buffers; |
| if (cam_device_.AllocateOutputBuffersByStreams(streams, &output_buffers) != |
| 0) { |
| ADD_FAILURE() << "Failed to allocate output buffer"; |
| *result = -EINVAL; |
| return; |
| } |
| output_stream_buffers_[i][kPreviewOutputStreamIdx] = output_buffers.front(); |
| capture_requests_[i] = std::make_pair( |
| camera3_capture_request_t{ |
| .frame_number = |
| UINT32_MAX, // Will be overwritten with correct value |
| .settings = repeating_preview_metadata_.get(), |
| .input_buffer = NULL, |
| .num_output_buffers = 1, |
| .output_buffers = output_stream_buffers_[i].data(), |
| .num_physcam_settings = 0}, |
| false); |
| ProcessPreviewRequestOnServiceThread(); |
| } |
| preview_state_ = PREVIEW_STARTING; |
| *result = 0; |
| } |
| |
| void Camera3Service::Camera3DeviceService::StopPreviewOnServiceThread( |
| base::Callback<void()> cb) { |
| DCHECK(service_thread_.IsCurrentThread()); |
| VLOGF_ENTER(); |
| if (preview_state_ != PREVIEW_STARTED && preview_state_ != PREVIEW_STARTING) { |
| return; |
| } |
| preview_state_ = PREVIEW_STOPPING; |
| stop_preview_cb_ = cb; |
| } |
| |
| void Camera3Service::Camera3DeviceService::StartAutoFocusOnServiceThread() { |
| DCHECK(service_thread_.IsCurrentThread()); |
| uint8_t af_mode = ANDROID_CONTROL_AF_MODE_AUTO; |
| EXPECT_EQ(0, UpdateMetadata(ANDROID_CONTROL_AF_MODE, &af_mode, 1, |
| &repeating_preview_metadata_)); |
| if (!oneshot_preview_metadata_.get()) { |
| oneshot_preview_metadata_.reset( |
| clone_camera_metadata(repeating_preview_metadata_.get())); |
| } |
| uint8_t af_trigger = ANDROID_CONTROL_AF_TRIGGER_START; |
| EXPECT_EQ(0, UpdateMetadata(ANDROID_CONTROL_AF_TRIGGER, &af_trigger, 1, |
| &oneshot_preview_metadata_)); |
| } |
| |
| void Camera3Service::Camera3DeviceService::AddMetadataListenerOnServiceThread( |
| int32_t key, |
| std::unordered_set<int32_t> values, |
| base::Callback<void()> cb, |
| int32_t* result) { |
| DCHECK(service_thread_.IsCurrentThread()); |
| metadata_listener_list_.emplace_back(key, values, cb, result); |
| } |
| |
| void Camera3Service::Camera3DeviceService:: |
| DeleteMetadataListenerOnServiceThread(int32_t key, |
| std::unordered_set<int32_t> values) { |
| DCHECK(service_thread_.IsCurrentThread()); |
| for (auto it = metadata_listener_list_.begin(); |
| it != metadata_listener_list_.end(); it++) { |
| if (it->key == key && it->values == values) { |
| it = metadata_listener_list_.erase(it); |
| break; |
| } |
| } |
| } |
| |
| void Camera3Service::Camera3DeviceService::LockAWBOnServiceThread() { |
| DCHECK(service_thread_.IsCurrentThread()); |
| uint8_t awb_lock = ANDROID_CONTROL_AWB_LOCK_ON; |
| EXPECT_EQ(0, UpdateMetadata(ANDROID_CONTROL_AWB_LOCK, &awb_lock, 1, |
| &repeating_preview_metadata_)); |
| } |
| |
| void Camera3Service::Camera3DeviceService::StartAEPrecaptureOnServiceThread() { |
| DCHECK(service_thread_.IsCurrentThread()); |
| if (!oneshot_preview_metadata_.get()) { |
| oneshot_preview_metadata_.reset( |
| clone_camera_metadata(repeating_preview_metadata_.get())); |
| } |
| uint8_t ae_trigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_START; |
| EXPECT_EQ(0, UpdateMetadata(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, |
| &ae_trigger, 1, &oneshot_preview_metadata_)); |
| } |
| |
| void Camera3Service::Camera3DeviceService::StartFaceDetectionOnServiceThread() { |
| DCHECK(service_thread_.IsCurrentThread()); |
| uint8_t fd_mode = ANDROID_STATISTICS_FACE_DETECT_MODE_SIMPLE; |
| EXPECT_EQ(0, UpdateMetadata(ANDROID_STATISTICS_FACE_DETECT_MODE, &fd_mode, 1, |
| &repeating_preview_metadata_)); |
| } |
| |
| void Camera3Service::Camera3DeviceService::StopFaceDetectionOnServiceThread() { |
| DCHECK(service_thread_.IsCurrentThread()); |
| uint8_t fd_mode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF; |
| EXPECT_EQ(0, UpdateMetadata(ANDROID_STATISTICS_FACE_DETECT_MODE, &fd_mode, 1, |
| &repeating_preview_metadata_)); |
| } |
| |
| void Camera3Service::Camera3DeviceService::TakeStillCaptureOnServiceThread( |
| const camera_metadata_t* metadata, base::Callback<void()> cb) { |
| DCHECK(service_thread_.IsCurrentThread()); |
| still_capture_metadata_ = metadata; |
| still_capture_cb_ = cb; |
| } |
| |
| void Camera3Service::Camera3DeviceService::StartRecordingOnServiceThread( |
| const camera_metadata_t* metadata, base::Callback<void(int)> cb) { |
| DCHECK(service_thread_.IsCurrentThread()); |
| VLOGF_ENTER(); |
| if (!metadata) { |
| LOGF(ERROR) << "Invalid metadata settings"; |
| cb.Run(-EINVAL); |
| return; |
| } |
| if (preview_state_ != PREVIEW_STARTED) { |
| VLOGF(2) << "Preview is not started yet. Retrying..."; |
| usleep(500); |
| service_thread_.PostTaskAsync( |
| FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService:: |
| StartRecordingOnServiceThread, |
| base::Unretained(this), metadata, cb)); |
| return; |
| } |
| auto it = std::find_if( |
| streams_.begin(), streams_.end(), [](const camera3_stream_t* stream) { |
| return (stream->format == HAL_PIXEL_FORMAT_YCbCr_420_888); |
| }); |
| if (it == streams_.end()) { |
| ADD_FAILURE() << "Failed to find configured recording stream"; |
| cb.Run(-EINVAL); |
| return; |
| } |
| const camera3_stream_t* record_stream = *it; |
| |
| for (uint32_t i = 0; i < number_of_capture_requests_; i++) { |
| std::vector<const camera3_stream_t*> streams(1, record_stream); |
| std::vector<camera3_stream_buffer_t> output_buffers; |
| if (cam_device_.AllocateOutputBuffersByStreams(streams, &output_buffers) != |
| 0) { |
| ADD_FAILURE() << "Failed to allocate output buffer"; |
| cb.Run(-EINVAL); |
| return; |
| } |
| output_stream_buffers_[i][kRecordingOutputStreamIdx] = |
| output_buffers.front(); |
| } |
| recording_metadata_ = metadata; |
| cb.Run(0); |
| } |
| |
| void Camera3Service::Camera3DeviceService::StopRecordingOnServiceThread( |
| base::Callback<void()> cb) { |
| VLOGF_ENTER(); |
| recording_metadata_ = nullptr; |
| stop_recording_cb_ = cb; |
| } |
| |
| void Camera3Service::Camera3DeviceService:: |
| ProcessPreviewRequestOnServiceThread() { |
| DCHECK(service_thread_.IsCurrentThread()); |
| size_t capture_request_idx = 0; |
| for (; capture_request_idx < capture_requests_.size(); |
| capture_request_idx++) { |
| if (!capture_requests_[capture_request_idx].second) { |
| break; |
| } |
| } |
| ASSERT_NE(capture_request_idx, capture_requests_.size()) |
| << "Out of captures requests"; |
| capture_requests_[capture_request_idx].second = true; |
| camera3_capture_request_t* request = |
| &capture_requests_[capture_request_idx].first; |
| // Initially there is preview stream only |
| request->num_output_buffers = 1; |
| if (recording_metadata_) { |
| request->settings = recording_metadata_; |
| ++request->num_output_buffers; |
| } |
| if (still_capture_metadata_) { |
| auto it = std::find_if(streams_.begin(), streams_.end(), |
| [](const camera3_stream_t* stream) { |
| return (stream->format == HAL_PIXEL_FORMAT_BLOB); |
| }); |
| ASSERT_NE(streams_.end(), it) |
| << "Failed to find configured still capture stream"; |
| const camera3_stream_t* still_capture_stream = *it; |
| std::vector<const camera3_stream_t*> streams(1, still_capture_stream); |
| std::vector<camera3_stream_buffer_t> output_buffers; |
| ASSERT_EQ( |
| 0, cam_device_.AllocateOutputBuffersByStreams(streams, &output_buffers)) |
| << "Failed to allocate output buffer"; |
| request->settings = still_capture_metadata_; |
| output_stream_buffers_[capture_request_idx][request->num_output_buffers] = |
| output_buffers.front(); |
| ++request->num_output_buffers; |
| } |
| if (!recording_metadata_ && !still_capture_metadata_) { |
| // Request with one-shot metadata if there is one |
| request->settings = oneshot_preview_metadata_.get() |
| ? oneshot_preview_metadata_.get() |
| : repeating_preview_metadata_.get(); |
| } |
| ASSERT_EQ(0, cam_device_.ProcessCaptureRequest(request)) |
| << "Failed to process capture request"; |
| ++number_of_in_flight_requests_; |
| VLOGF(1) << "Capture request"; |
| VLOGF(1) << " Frame " << request->frame_number; |
| VLOGF(1) << " Index " << capture_request_idx; |
| for (size_t i = 0; i < request->num_output_buffers; i++) { |
| VLOGF(1) << " Buffer " << *request->output_buffers[i].buffer |
| << " (format:" << request->output_buffers[i].stream->format << "," |
| << request->output_buffers[i].stream->width << "x" |
| << request->output_buffers[i].stream->height << ")"; |
| } |
| VLOGF(1) << " Settings " << request->settings; |
| if (still_capture_metadata_) { |
| still_capture_metadata_ = nullptr; |
| still_capture_cb_.Run(); |
| } else if (!recording_metadata_ && !still_capture_metadata_ && |
| oneshot_preview_metadata_.get()) { |
| oneshot_preview_metadata_.reset(nullptr); |
| } |
| } |
| |
| void Camera3Service::Camera3DeviceService:: |
| ProcessResultMetadataOutputBuffersOnServiceThread( |
| uint32_t frame_number, |
| ScopedCameraMetadata metadata, |
| std::vector<cros::ScopedBufferHandle> buffers) { |
| DCHECK(service_thread_.IsCurrentThread()); |
| --number_of_in_flight_requests_; |
| size_t capture_request_idx = 0; |
| for (; capture_request_idx < capture_requests_.size(); |
| capture_request_idx++) { |
| if (capture_requests_[capture_request_idx].first.frame_number == |
| frame_number) { |
| break; |
| } |
| } |
| ASSERT_NE(capture_request_idx, capture_requests_.size()) |
| << "Failed to find frame " << frame_number << " in the requests"; |
| VLOGF(1) << "Capture result"; |
| VLOGF(1) << " Frame " << frame_number; |
| VLOGF(1) << " Index " << capture_request_idx; |
| // Process result metadata according to listeners |
| for (auto it = metadata_listener_list_.begin(); |
| it != metadata_listener_list_.end();) { |
| camera_metadata_ro_entry_t entry; |
| if (find_camera_metadata_ro_entry(metadata.get(), it->key, &entry) == 0 && |
| it->values.find(entry.data.i32[0]) != it->values.end()) { |
| VLOGF(1) << "Metadata listener gets tag " |
| << get_camera_metadata_tag_name(it->key) << " value " |
| << entry.data.i32[0]; |
| if (it->result) { |
| *it->result = entry.data.i32[0]; |
| } |
| it->cb.Run(); |
| it = metadata_listener_list_.erase(it); |
| } else { |
| it++; |
| } |
| } |
| // Process output buffers and record perf logs. We record preview and video |
| // perf logs only if there's no still capture in this result since it is |
| // expected to take longer time. |
| const bool has_still_capture = std::any_of( |
| buffers.begin(), buffers.end(), |
| [](const cros::ScopedBufferHandle& buffer) { |
| return Camera3TestGralloc::GetFormat(*buffer) == HAL_PIXEL_FORMAT_BLOB; |
| }); |
| const bool stopping_preview = |
| (preview_state_ == PREVIEW_STOPPING) && !still_capture_metadata_; |
| bool have_yuv_buffer = false; |
| for (auto& it : buffers) { |
| VLOGF(1) << " Buffer " << *it |
| << " (format:" << Camera3TestGralloc::GetFormat(*it) << ")"; |
| switch (Camera3TestGralloc::GetFormat(*it)) { |
| case HAL_PIXEL_FORMAT_BLOB: |
| Camera3PerfLog::GetInstance()->UpdateFrameEvent( |
| cam_id_, frame_number, FrameEvent::STILL_CAPTURE_RESULT, |
| base::TimeTicks::Now()); |
| if (!process_still_capture_result_cb_.is_null()) { |
| process_still_capture_result_cb_.Run( |
| cam_id_, frame_number, std::move(metadata), std::move(it)); |
| } |
| break; |
| case HAL_PIXEL_FORMAT_YCbCr_420_888: |
| have_yuv_buffer = true; |
| if (!has_still_capture) { |
| Camera3PerfLog::GetInstance()->UpdateFrameEvent( |
| cam_id_, frame_number, FrameEvent::VIDEO_RECORD_RESULT, |
| base::TimeTicks::Now()); |
| } |
| if (!process_recording_result_cb_.is_null()) { |
| process_recording_result_cb_.Run(cam_id_, frame_number, |
| std::move(metadata)); |
| } |
| if (recording_metadata_ && !stopping_preview) { |
| // Register buffer back to be used by future requests |
| cam_device_.RegisterOutputBuffer( |
| *output_stream_buffers_[capture_request_idx] |
| [kRecordingOutputStreamIdx] |
| .stream, |
| std::move(it)); |
| } |
| break; |
| default: |
| if (!process_preview_result_cb_.is_null()) { |
| process_preview_result_cb_.Run(cam_id_, frame_number, |
| std::move(metadata)); |
| } |
| if (!has_still_capture) { |
| Camera3PerfLog::GetInstance()->UpdateFrameEvent( |
| cam_id_, frame_number, FrameEvent::PREVIEW_RESULT, |
| base::TimeTicks::Now()); |
| } |
| if (!stopping_preview) { |
| // Register buffer back to be used by future requests |
| cam_device_.RegisterOutputBuffer( |
| *output_stream_buffers_[capture_request_idx] |
| [kPreviewOutputStreamIdx] |
| .stream, |
| std::move(it)); |
| } |
| } |
| } |
| if (preview_state_ == PREVIEW_STARTING) { |
| preview_state_ = PREVIEW_STARTED; |
| Camera3PerfLog::GetInstance()->UpdateDeviceEvent( |
| cam_id_, DeviceEvent::PREVIEW_STARTED, base::TimeTicks::Now()); |
| } |
| sem_post(&preview_frame_sem_); |
| |
| if (!stop_recording_cb_.is_null() && !have_yuv_buffer) { |
| stop_recording_cb_.Run(); |
| stop_recording_cb_.Reset(); |
| } |
| if (stopping_preview) { |
| VLOGF(1) << "Stopping preview ... (" << number_of_in_flight_requests_ |
| << " requests in flight"; |
| if (number_of_in_flight_requests_ == 0) { |
| preview_state_ = PREVIEW_STOPPED; |
| stop_preview_cb_.Run(); |
| stop_preview_cb_.Reset(); |
| } |
| return; |
| } |
| capture_requests_[capture_request_idx].second = false; |
| ProcessPreviewRequestOnServiceThread(); |
| } |
| |
| } // namespace camera3_test |