| /* | |
| * Copyright 2019 The ChromiumOS Authors | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "features/zsl/zsl_helper.h" | |
| #include <algorithm> | |
| #include <cmath> | |
| #include <cstdlib> | |
| #include <functional> | |
| #include <utility> | |
| #include <vector> | |
| #include <base/check.h> | |
| #include <base/check_op.h> | |
| #include <base/functional/bind.h> | |
| #include <base/numerics/safe_conversions.h> | |
| #include <camera/camera_metadata.h> | |
| #include <sync/sync.h> | |
| #include <system/camera_metadata.h> | |
| #include "common/camera_hal3_helpers.h" | |
| #include "cros-camera/camera_metadata_utils.h" | |
| #include "cros-camera/common.h" | |
| #include "cros-camera/constants.h" | |
| #include "cros-camera/utils/camera_config.h" | |
| namespace cros { | |
| namespace { | |
| static constexpr int64_t kOverrideCurrentTimestampNotSet = -1; | |
| bool IsInputStream(camera3_stream_t* stream) { | |
| return stream->stream_type == CAMERA3_STREAM_INPUT || | |
| stream->stream_type == CAMERA3_STREAM_BIDIRECTIONAL; | |
| } | |
| bool IsOutputStream(camera3_stream_t* stream) { | |
| return stream->stream_type == CAMERA3_STREAM_OUTPUT || | |
| stream->stream_type == CAMERA3_STREAM_BIDIRECTIONAL; | |
| } | |
| int64_t GetTimestamp(const android::CameraMetadata& android_metadata) { | |
| camera_metadata_ro_entry_t entry; | |
| if (android_metadata.exists(ANDROID_SENSOR_TIMESTAMP)) { | |
| entry = android_metadata.find(ANDROID_SENSOR_TIMESTAMP); | |
| return entry.data.i64[0]; | |
| } | |
| LOGF(ERROR) << "Cannot find sensor timestamp in ZSL buffer"; | |
| return static_cast<int64_t>(-1); | |
| } | |
| // Checks the static metadata of the camera device to see if we can attempt to | |
| // enable our in-house ZSL solution for it. It checks whether or not the | |
| // device already supports ZSL, and checks for private processing capability | |
| // if not. | |
| bool CanEnableZsl(const camera_metadata_t* metadata) { | |
| // Determine if it's possible for us to enable our in-house ZSL solution. Note | |
| // that we may end up not enabling it in situations where we cannot allocate | |
| // sufficient private buffers or the camera HAL client's stream configuration | |
| // wouldn't allow us to set up the streams we need. | |
| base::span<const uint8_t> available_caps = GetRoMetadataAsSpan<uint8_t>( | |
| metadata, ANDROID_REQUEST_AVAILABLE_CAPABILITIES); | |
| if (available_caps.empty()) { | |
| return false; | |
| } | |
| if (std::find(available_caps.begin(), available_caps.end(), | |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING) == | |
| available_caps.end()) { | |
| return false; | |
| } | |
| // See if the camera HAL already supports ZSL. | |
| base::span<const int32_t> req_keys = GetRoMetadataAsSpan<int32_t>( | |
| metadata, ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS); | |
| if (req_keys.empty()) { | |
| return false; | |
| } | |
| if (std::find(req_keys.begin(), req_keys.end(), ANDROID_CONTROL_ENABLE_ZSL) != | |
| req_keys.end()) { | |
| LOGF(INFO) << "Device supports vendor-provided ZSL"; | |
| return false; | |
| } | |
| return true; | |
| } | |
| } // namespace | |
| ZslBuffer::ZslBuffer() | |
| : metadata_ready(false), buffer_ready(false), selected(false) {} | |
| ZslBuffer::ZslBuffer(uint32_t frame_number, camera3_stream_buffer_t buffer) | |
| : frame_number(frame_number), | |
| buffer(std::move(buffer)), | |
| metadata_ready(false), | |
| buffer_ready(false), | |
| selected(false) {} | |
| void ZslBuffer::AttachToRequest(Camera3CaptureDescriptor* capture_request) { | |
| capture_request->AppendOutputBuffer( | |
| Camera3StreamBuffer::MakeRequestOutput(buffer)); | |
| } | |
| ZslBufferManager::ZslBufferManager() | |
| : initialized_(false), buffer_manager_(nullptr) {} | |
| ZslBufferManager::~ZslBufferManager() { | |
| Reset(); | |
| } | |
| bool ZslBufferManager::Initialize(size_t pool_size, | |
| const camera3_stream_t* output_stream) { | |
| DCHECK(buffer_pool_.empty()); | |
| // |buffer_manager_| could be set by SetCameraBufferManagerForTesting(). | |
| if (!buffer_manager_) { | |
| buffer_manager_ = CameraBufferManager::GetInstance(); | |
| } | |
| bool success = true; | |
| output_stream_ = output_stream; | |
| { | |
| base::AutoLock l(buffer_pool_lock_); | |
| buffer_pool_.reserve(pool_size); | |
| for (size_t i = 0; i < pool_size; ++i) { | |
| uint32_t stride; | |
| buffer_handle_t buffer; | |
| if (buffer_manager_->Allocate( | |
| output_stream_->width, output_stream_->height, | |
| ZslHelper::kZslPixelFormat, | |
| GRALLOC_USAGE_HW_CAMERA_ZSL | GRALLOC_USAGE_SW_READ_OFTEN | | |
| GRALLOC_USAGE_SW_WRITE_OFTEN, | |
| &buffer, &stride) != 0) { | |
| LOGF(ERROR) << "Failed to allocate buffer"; | |
| success = false; | |
| break; | |
| } | |
| buffer_pool_.push_back(buffer); | |
| free_buffers_.push(&buffer_pool_.back()); | |
| buffer_to_buffer_pointer_map_[buffer] = &buffer_pool_.back(); | |
| } | |
| } | |
| if (!success) { | |
| Reset(); | |
| return false; | |
| } | |
| initialized_ = true; | |
| return true; | |
| } | |
| buffer_handle_t* ZslBufferManager::GetBuffer() { | |
| base::AutoLock buffer_pool_lock(buffer_pool_lock_); | |
| if (!initialized_) { | |
| LOGF(ERROR) << "ZSL buffer manager has not been initialized"; | |
| return nullptr; | |
| } | |
| if (free_buffers_.empty()) { | |
| LOGF(ERROR) << "No more buffer left in the pool. This shouldn't happen"; | |
| return nullptr; | |
| } | |
| buffer_handle_t* buffer = free_buffers_.front(); | |
| free_buffers_.pop(); | |
| return buffer; | |
| } | |
| bool ZslBufferManager::ReleaseBuffer(buffer_handle_t buffer_to_release) { | |
| base::AutoLock buffer_pool_lock(buffer_pool_lock_); | |
| if (!initialized_) { | |
| LOGF(ERROR) << "ZSL buffer manager has not been initialized"; | |
| return false; | |
| } | |
| auto it = buffer_to_buffer_pointer_map_.find(buffer_to_release); | |
| if (it == buffer_to_buffer_pointer_map_.end()) { | |
| LOGF(ERROR) << "The released buffer doesn't belong to ZSL buffer manager"; | |
| return false; | |
| } | |
| free_buffers_.push(it->second); | |
| return true; | |
| } | |
| void ZslBufferManager::Reset() { | |
| initialized_ = false; | |
| base::AutoLock l(buffer_pool_lock_); | |
| for (auto& buffer : buffer_pool_) { | |
| buffer_manager_->Free(buffer); | |
| } | |
| buffer_pool_.clear(); | |
| free_buffers_ = {}; | |
| buffer_to_buffer_pointer_map_.clear(); | |
| } | |
| void ZslBufferManager::SetCameraBufferManagerForTesting( | |
| CameraBufferManager* buffer_manager) { | |
| buffer_manager_ = buffer_manager; | |
| } | |
| ZslHelper::ZslHelper(const camera_metadata_t* static_info) | |
| : zsl_buffer_manager_(new ZslBufferManager), | |
| fence_sync_thread_("FenceSyncThread"), | |
| override_current_timestamp_for_testing_(kOverrideCurrentTimestampNotSet) { | |
| if (!IsCapabilitySupported( | |
| static_info, | |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING)) { | |
| LOGF(INFO) << "Private reprocessing not supported, ZSL won't be enabled"; | |
| return; | |
| } | |
| uint32_t bi_width, bi_height; | |
| if (!SelectZslStreamSize(static_info, &bi_width, &bi_height, | |
| &bi_stream_min_frame_duration_)) { | |
| LOGF(ERROR) << "Failed to select stream sizes for ZSL."; | |
| return; | |
| } | |
| LOGF(INFO) << "Selected ZSL stream size = " << bi_width << "x" << bi_height; | |
| // Create ZSL streams | |
| bi_stream_ = std::make_unique<camera3_stream_t>(); | |
| bi_stream_->stream_type = CAMERA3_STREAM_BIDIRECTIONAL; | |
| bi_stream_->width = bi_width; | |
| bi_stream_->height = bi_height; | |
| bi_stream_->format = kZslPixelFormat; | |
| if (!fence_sync_thread_.Start()) { | |
| LOGF(ERROR) << "Fence sync thread failed to start"; | |
| } | |
| partial_result_count_ = GetPartialResultCount(static_info); | |
| max_num_input_streams_ = [&]() { | |
| camera_metadata_ro_entry_t entry; | |
| if (find_camera_metadata_ro_entry( | |
| static_info, ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS, &entry) != 0) { | |
| LOGF(ERROR) << "Failed to get maximum number of input streams."; | |
| return 0; | |
| } | |
| return entry.data.i32[0]; | |
| }(); | |
| timestamp_source_ = [&]() { | |
| camera_metadata_ro_entry_t entry; | |
| if (find_camera_metadata_ro_entry( | |
| static_info, ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE, &entry) != 0) { | |
| LOGF(ERROR) << "Failed to get timestamp source. Assuming it's UNKNOWN."; | |
| return ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN; | |
| } | |
| return static_cast< | |
| camera_metadata_enum_android_sensor_info_timestamp_source_t>( | |
| entry.data.u8[0]); | |
| }(); | |
| auto camera_config = | |
| cros::CameraConfig::Create(cros::constants::kCrosCameraConfigPathString); | |
| // We're casting an int to int64_t here. Make sure the configured time doesn't | |
| // overflow (roughly 2.1s). | |
| zsl_lookback_ns_ = base::strict_cast<int64_t>(camera_config->GetInteger( | |
| cros::constants::kCrosZslLookback, | |
| base::checked_cast<int>(kZslDefaultLookbackNs))); | |
| LOGF(INFO) << "Configured ZSL lookback time = " << zsl_lookback_ns_; | |
| } | |
| ZslHelper::~ZslHelper() { | |
| fence_sync_thread_.Stop(); | |
| } | |
| bool ZslHelper::AttachZslStream(Camera3StreamConfiguration* stream_config) { | |
| if (!CanEnableZsl(stream_config->GetStreams())) { | |
| return false; | |
| } | |
| stream_config->AppendStream(bi_stream_.get()); | |
| if (VLOG_IS_ON(1)) { | |
| VLOGF(1) << "Attached ZSL streams. The list of streams after attaching:"; | |
| for (const auto* s : stream_config->GetStreams()) { | |
| VLOGF(1) << ", type = " << s->stream_type << ", size = " << s->width | |
| << "x" << s->height << ", format = " << s->format; | |
| } | |
| } | |
| return true; | |
| } | |
| bool ZslHelper::Initialize(Camera3StreamConfiguration* stream_config) { | |
| auto GetStillCaptureMaxBuffers = [&]() { | |
| uint32_t max_buffers = 0; | |
| for (auto* stream : stream_config->GetStreams()) { | |
| if (!IsOutputStream(stream)) { | |
| continue; | |
| } | |
| // If our private usage flag is specified, we know only this stream | |
| // will be used for ZSL capture. | |
| if (stream->usage & cros::GRALLOC_USAGE_STILL_CAPTURE) { | |
| return stream->max_buffers; | |
| } else if (stream->format == HAL_PIXEL_FORMAT_BLOB) { | |
| max_buffers += stream->max_buffers; | |
| } | |
| } | |
| return max_buffers; | |
| }; | |
| base::AutoLock ring_buffer_lock(ring_buffer_lock_); | |
| // First, clear all the buffers and states. | |
| ring_buffer_.clear(); | |
| zsl_buffer_manager_->Reset(); | |
| // Determine at most how many buffers would be selected for private | |
| // reprocessing simultaneously, and remove the ZSL stream we attached along | |
| // the way. | |
| bi_stream_max_buffers_ = 0; | |
| base::span<camera3_stream_t* const> streams = stream_config->GetStreams(); | |
| std::vector<camera3_stream_t*> modified_streams; | |
| for (auto* s : streams) { | |
| if (s == bi_stream_.get()) { | |
| bi_stream_max_buffers_ = s->max_buffers; | |
| } else { | |
| modified_streams.push_back(s); | |
| } | |
| } | |
| stream_config->SetStreams(modified_streams); | |
| if (bi_stream_max_buffers_ == 0) { | |
| LOGF(ERROR) << "Failed to acquire max_buffers for the private stream"; | |
| return false; | |
| } | |
| VLOGF(1) << "Max buffers for private stream = " << bi_stream_max_buffers_; | |
| // Determine at most how many still capture buffers would be in-flight. | |
| uint32_t still_max_buffers = GetStillCaptureMaxBuffers(); | |
| if (still_max_buffers == 0) { | |
| LOGF(ERROR) << "Failed to acquire max_buffers for the still capture stream"; | |
| return false; | |
| } | |
| VLOGF(1) << "Max buffers for still capture streams = " << still_max_buffers; | |
| // We look back at most | |
| // ceil(|zsl_lookback_ns_| / |bi_stream_min_frame_duration_| frames, and there | |
| // will be at most |bi_stream_max_buffers_| being processed. We also need to | |
| // have |still_max_buffers| additional buffers in the buffer pool. | |
| if (!zsl_buffer_manager_->Initialize( | |
| static_cast<size_t>(std::ceil(static_cast<double>(zsl_lookback_ns_) / | |
| bi_stream_min_frame_duration_)) + | |
| bi_stream_max_buffers_ + still_max_buffers, | |
| bi_stream_.get())) { | |
| LOGF(ERROR) << "Failed to initialize ZSL buffer manager"; | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool ZslHelper::CanEnableZsl(base::span<camera3_stream_t* const> streams) { | |
| size_t num_input_streams = 0; | |
| bool has_still_capture_output_stream = false; | |
| bool has_zsl_output_stream = false; | |
| for (auto* stream : streams) { | |
| if (IsInputStream(stream)) { | |
| num_input_streams++; | |
| } | |
| if (IsOutputStream(stream) && | |
| (stream->format == HAL_PIXEL_FORMAT_BLOB || | |
| (stream->usage & GRALLOC_USAGE_STILL_CAPTURE))) { | |
| has_still_capture_output_stream = true; | |
| } | |
| if (IsOutputStream(stream) && | |
| (stream->usage & GRALLOC_USAGE_HW_CAMERA_ZSL) == | |
| GRALLOC_USAGE_HW_CAMERA_ZSL) { | |
| has_zsl_output_stream = true; | |
| } | |
| } | |
| return num_input_streams < max_num_input_streams_ // Has room for an extra | |
| // input stream for ZSL. | |
| && has_still_capture_output_stream // Has a stream for still capture. | |
| && !has_zsl_output_stream; // HAL doesn't support multiple raw output | |
| // streams. | |
| } | |
| bool ZslHelper::IsZslRequested(const Camera3CaptureDescriptor* request) { | |
| bool enable_zsl = [&]() { | |
| base::span<const uint8_t> entry = | |
| request->GetMetadata<uint8_t>(ANDROID_CONTROL_ENABLE_ZSL); | |
| if (!entry.empty()) { | |
| return entry[0] == ANDROID_CONTROL_ENABLE_ZSL_TRUE; | |
| } | |
| return false; | |
| }(); | |
| if (!enable_zsl) { | |
| return false; | |
| } | |
| // We can only enable ZSL when capture intent is also still capture. | |
| base::span<const uint8_t> entry = | |
| request->GetMetadata<uint8_t>(ANDROID_CONTROL_CAPTURE_INTENT); | |
| if (!entry.empty()) { | |
| return entry[0] == ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE || | |
| entry[0] == ANDROID_CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG; | |
| } | |
| return false; | |
| } | |
| bool ZslHelper::IsTransformedZslBuffer(const Camera3StreamBuffer& buffer) { | |
| return buffer.stream() == bi_stream_.get(); | |
| } | |
| void ZslHelper::TryReleaseBuffer() { | |
| ring_buffer_lock_.AssertAcquired(); | |
| // Check if the oldest buffer is already too old to be selected. In which | |
| // case, we can remove it from our ring buffer. If the buffer is not selected, | |
| // we release it back to the buffer pool. If the buffer is selected, we | |
| // release it when it returns from ProcessZslCaptureResult. | |
| if (ring_buffer_.empty()) { | |
| return; | |
| } | |
| const ZslBuffer& oldest_buffer = ring_buffer_.back(); | |
| if (oldest_buffer.selected) { | |
| ring_buffer_.pop_back(); | |
| return; | |
| } | |
| if (!oldest_buffer.metadata_ready) { | |
| return; | |
| } | |
| auto timestamp = GetTimestamp(oldest_buffer.metadata); | |
| DCHECK_NE(timestamp, -1); | |
| if (GetCurrentTimestamp() - timestamp <= zsl_lookback_ns_) { | |
| // Buffer is too new that we should keep it. This will happen for the | |
| // initial buffers. | |
| return; | |
| } | |
| if (!zsl_buffer_manager_->ReleaseBuffer(*oldest_buffer.buffer.buffer)) { | |
| LOGF(ERROR) << "Unable to release the oldest buffer"; | |
| return; | |
| } | |
| ring_buffer_.pop_back(); | |
| } | |
| bool ZslHelper::ProcessZslCaptureRequest(Camera3CaptureDescriptor* request, | |
| SelectionStrategy strategy) { | |
| if (request->has_input_buffer()) { | |
| return false; | |
| } | |
| bool transformed = false; | |
| if (IsZslRequested(request)) { | |
| transformed = TransformRequest(request, strategy); | |
| if (!transformed) { | |
| LOGF(ERROR) << "Failed to find a suitable ZSL buffer"; | |
| } | |
| } else { | |
| AttachRequest(request); | |
| } | |
| return transformed; | |
| } | |
| void ZslHelper::AttachRequest(Camera3CaptureDescriptor* request) { | |
| base::AutoLock l(ring_buffer_lock_); | |
| TryReleaseBuffer(); | |
| auto* buffer = zsl_buffer_manager_->GetBuffer(); | |
| if (buffer == nullptr) { | |
| LOGF(ERROR) << "Failed to acquire a ZSL buffer"; | |
| return; | |
| } | |
| // Attach our ZSL output buffer. | |
| camera3_stream_buffer_t stream_buffer = { | |
| .stream = bi_stream_.get(), | |
| .buffer = buffer, | |
| .status = CAMERA3_BUFFER_STATUS_OK, | |
| .acquire_fence = -1, | |
| .release_fence = -1, | |
| }; | |
| ZslBuffer zsl_buffer(request->frame_number(), stream_buffer); | |
| zsl_buffer.AttachToRequest(request); | |
| ring_buffer_.push_front(std::move(zsl_buffer)); | |
| } | |
| bool ZslHelper::TransformRequest(Camera3CaptureDescriptor* request, | |
| SelectionStrategy strategy) { | |
| base::AutoLock l(ring_buffer_lock_); | |
| const int32_t jpeg_orientation = [&]() { | |
| base::span<const int32_t> entry = | |
| request->GetMetadata<int32_t>(ANDROID_JPEG_ORIENTATION); | |
| if (entry.empty()) { | |
| return 0; | |
| } | |
| return entry[0]; | |
| }(); | |
| const std::vector<int32_t> jpeg_thumbnail_size = [&]() { | |
| base::span<const int32_t> entry = | |
| request->GetMetadata<int32_t>(ANDROID_JPEG_THUMBNAIL_SIZE); | |
| if (entry.empty()) { | |
| LOGF(ERROR) << "Failed to find JPEG thumbnail size, defaulting to [0, 0]"; | |
| return std::vector<int32_t>{0, 0}; | |
| } | |
| return std::vector<int32_t>{entry[0], entry[1]}; | |
| }(); | |
| // Select the best buffer. | |
| ZslBufferIterator selected_buffer_it = SelectZslBuffer(strategy); | |
| if (selected_buffer_it == ring_buffer_.end()) { | |
| LOGF(WARNING) << "Unable to find a suitable ZSL buffer. Request will not " | |
| "be transformed."; | |
| return false; | |
| } | |
| LOGF(INFO) << "Transforming request into ZSL reprocessing request"; | |
| selected_buffer_it->buffer.stream = bi_stream_.get(); | |
| selected_buffer_it->buffer.acquire_fence = -1; | |
| selected_buffer_it->buffer.acquire_fence = -1; | |
| request->SetInputBuffer( | |
| Camera3StreamBuffer::MakeRequestInput(selected_buffer_it->buffer)); | |
| // The result metadata for the RAW buffers come from the preview frames. We | |
| // need to add JPEG orientation back so that the resulting JPEG is of the | |
| // correct orientation. | |
| if (selected_buffer_it->metadata.update(ANDROID_JPEG_ORIENTATION, | |
| &jpeg_orientation, 1) != 0) { | |
| LOGF(ERROR) << "Failed to update JPEG_ORIENTATION"; | |
| } | |
| if (selected_buffer_it->metadata.update(ANDROID_JPEG_THUMBNAIL_SIZE, | |
| jpeg_thumbnail_size.data(), | |
| jpeg_thumbnail_size.size()) != 0) { | |
| LOGF(ERROR) << "Failed to update JPEG_THUMBNAIL_SIZE"; | |
| } | |
| request->SetMetadata(selected_buffer_it->metadata.getAndLock()); | |
| return true; | |
| } | |
| void ZslHelper::ProcessZslCaptureResult(Camera3CaptureDescriptor* result, | |
| bool* is_input_transformed) { | |
| for (auto& buffer : result->AcquireOutputBuffers()) { | |
| if (buffer.stream() == bi_stream_.get()) { | |
| WaitAttachedFrame(result->frame_number(), | |
| base::ScopedFD(buffer.take_release_fence())); | |
| } else { | |
| result->AppendOutputBuffer(std::move(buffer)); | |
| } | |
| } | |
| const Camera3StreamBuffer* input_buffer = result->GetInputBuffer(); | |
| if (input_buffer && IsTransformedZslBuffer(*input_buffer)) { | |
| *is_input_transformed = true; | |
| ReleaseStreamBuffer(result->AcquireInputBuffer()); | |
| } else { | |
| *is_input_transformed = false; | |
| } | |
| base::AutoLock ring_buffer_lock(ring_buffer_lock_); | |
| auto it = std::find_if(ring_buffer_.begin(), ring_buffer_.end(), | |
| [&](const ZslBuffer& buffer) { | |
| return buffer.frame_number == result->frame_number(); | |
| }); | |
| if (it == ring_buffer_.end()) { | |
| return; | |
| } | |
| if (result->partial_result() != 0) { // Result has metadata. Merge it. | |
| const camera3_capture_result_t* locked_result = result->LockForResult(); | |
| if (locked_result->result) { | |
| it->metadata.append(locked_result->result); | |
| } else { | |
| LOGF(ERROR) << "No result metadata although partial_result = " | |
| << result->partial_result(); | |
| } | |
| result->Unlock(); | |
| if (result->partial_result() == partial_result_count_) { | |
| it->metadata_ready = true; | |
| } | |
| } | |
| } | |
| void ZslHelper::OnNotifyError(const camera3_error_msg_t& error_msg) { | |
| if (error_msg.error_stream == bi_stream_.get()) { | |
| LOGFID(ERROR, error_msg.frame_number) | |
| << "Received error message: " << error_msg.error_code; | |
| } | |
| } | |
| void ZslHelper::WaitAttachedFrame(uint32_t frame_number, | |
| base::ScopedFD release_fence) { | |
| fence_sync_thread_.task_runner()->PostTask( | |
| FROM_HERE, base::BindOnce(&ZslHelper::WaitAttachedFrameOnFenceSyncThread, | |
| base::Unretained(this), frame_number, | |
| std::move(release_fence))); | |
| } | |
| void ZslHelper::WaitAttachedFrameOnFenceSyncThread( | |
| uint32_t frame_number, base::ScopedFD release_fence) { | |
| if (release_fence.is_valid() && | |
| sync_wait(release_fence.get(), ZslHelper::kZslSyncWaitTimeoutMs)) { | |
| LOGF(WARNING) << "Failed to wait for release fence on attached ZSL buffer"; | |
| } else { | |
| base::AutoLock ring_buffer_lock(ring_buffer_lock_); | |
| auto it = std::find_if(ring_buffer_.begin(), ring_buffer_.end(), | |
| [&](const ZslBuffer& buffer) { | |
| return buffer.frame_number == frame_number; | |
| }); | |
| if (it != ring_buffer_.end()) { | |
| it->buffer_ready = true; | |
| } | |
| return; | |
| } | |
| fence_sync_thread_.task_runner()->PostTask( | |
| FROM_HERE, base::BindOnce(&ZslHelper::WaitAttachedFrameOnFenceSyncThread, | |
| base::Unretained(this), frame_number, | |
| std::move(release_fence))); | |
| } | |
| void ZslHelper::ReleaseStreamBuffer(std::optional<Camera3StreamBuffer> buffer) { | |
| if (!buffer) { | |
| return; | |
| } | |
| fence_sync_thread_.task_runner()->PostTask( | |
| FROM_HERE, | |
| base::BindOnce(&ZslHelper::ReleaseStreamBufferOnFenceSyncThread, | |
| base::Unretained(this), std::move(buffer.value()))); | |
| } | |
| void ZslHelper::ReleaseStreamBufferOnFenceSyncThread( | |
| Camera3StreamBuffer buffer) { | |
| if (!buffer.WaitOnAndClearReleaseFence(ZslHelper::kZslSyncWaitTimeoutMs)) { | |
| LOGF(WARNING) << "Failed to wait for release fence on ZSL input buffer"; | |
| } else { | |
| if (!zsl_buffer_manager_->ReleaseBuffer(*buffer.buffer())) { | |
| LOGF(ERROR) << "Failed to release this stream buffer"; | |
| } | |
| // The above error should only happen when the mapping in buffer manager | |
| // becomes invalid somwhow. It's not recoverable, so we don't retry here. | |
| return; | |
| } | |
| fence_sync_thread_.task_runner()->PostTask( | |
| FROM_HERE, | |
| base::BindOnce(&ZslHelper::ReleaseStreamBufferOnFenceSyncThread, | |
| base::Unretained(this), std::move(buffer))); | |
| } | |
| bool ZslHelper::IsCapabilitySupported(const camera_metadata_t* static_info, | |
| uint8_t capability) { | |
| camera_metadata_ro_entry_t entry; | |
| if (find_camera_metadata_ro_entry( | |
| static_info, ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry) == 0) { | |
| return std::find(entry.data.u8, entry.data.u8 + entry.count, capability) != | |
| entry.data.u8 + entry.count; | |
| } | |
| return false; | |
| } | |
| bool ZslHelper::SelectZslStreamSize(const camera_metadata_t* static_info, | |
| uint32_t* bi_width, | |
| uint32_t* bi_height, | |
| int64_t* min_frame_duration) { | |
| *bi_width = 0; | |
| *bi_height = 0; | |
| camera_metadata_ro_entry entry; | |
| if (find_camera_metadata_ro_entry( | |
| static_info, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, | |
| &entry) != 0) { | |
| LOGF(ERROR) << "Failed to find stream configurations map"; | |
| return false; | |
| } | |
| VLOGF(1) << "Iterating stream configuration map for ZSL streams"; | |
| for (size_t i = 0; i < entry.count; i += 4) { | |
| const int32_t& format = entry.data.i32[i + STREAM_CONFIG_FORMAT_INDEX]; | |
| if (format != kZslPixelFormat) | |
| continue; | |
| const int32_t& width = entry.data.i32[i + STREAM_CONFIG_WIDTH_INDEX]; | |
| const int32_t& height = entry.data.i32[i + STREAM_CONFIG_HEIGHT_INDEX]; | |
| const int32_t& direction = | |
| entry.data.i32[i + STREAM_CONFIG_DIRECTION_INDEX]; | |
| VLOGF(1) << "format = " << format << ", " << "width = " << width << ", " | |
| << "height = " << height << ", " << "direction = " << direction; | |
| if (direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) { | |
| if (width * height > (*bi_width) * (*bi_height)) { | |
| *bi_width = width; | |
| *bi_height = height; | |
| } | |
| } | |
| } | |
| if (*bi_width == 0 || *bi_height == 0) { | |
| LOGF(ERROR) << "Failed to select ZSL stream size"; | |
| return false; | |
| } | |
| *min_frame_duration = 0; | |
| if (find_camera_metadata_ro_entry( | |
| static_info, ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, &entry) != | |
| 0) { | |
| LOGF(ERROR) << "Failed to find the minimum frame durations"; | |
| return false; | |
| } | |
| for (size_t i = 0; i < entry.count; i += 4) { | |
| const int64_t& format = entry.data.i64[i + FRAME_DURATION_FOMRAT_INDEX]; | |
| const int64_t& width = entry.data.i64[i + FRAME_DURATION_WIDTH_INDEX]; | |
| const int64_t& height = entry.data.i64[i + FRAME_DURATION_HEIGHT_INDEX]; | |
| const int64_t& duration = entry.data.i64[i + FRAME_DURATION_DURATION_INDEX]; | |
| if (format == kZslPixelFormat && width == *bi_width && | |
| height == *bi_height) { | |
| *min_frame_duration = duration; | |
| break; | |
| } | |
| } | |
| if (*min_frame_duration == 0) { | |
| LOGF(ERROR) << "Failed to find the minimum frame duration for the selected " | |
| "ZSL stream"; | |
| return false; | |
| } | |
| return true; | |
| } | |
| ZslHelper::ZslBufferIterator ZslHelper::SelectZslBuffer( | |
| SelectionStrategy strategy) { | |
| ring_buffer_lock_.AssertAcquired(); | |
| if (strategy == LAST_SUBMITTED) { | |
| for (auto it = ring_buffer_.begin(); it != ring_buffer_.end(); it++) { | |
| if (it->metadata_ready && it->buffer_ready && !it->selected) { | |
| it->selected = true; | |
| return it; | |
| } | |
| } | |
| LOGF(WARNING) << "Failed to find a unselected submitted ZSL buffer"; | |
| return ring_buffer_.end(); | |
| } | |
| // For CLOSEST or CLOSEST_3A strategies. | |
| int64_t cur_timestamp = GetCurrentTimestamp(); | |
| LOGF(INFO) << "Current timestamp = " << cur_timestamp; | |
| ZslBufferIterator selected_buffer_it = ring_buffer_.end(); | |
| int64_t min_diff = zsl_lookback_ns_; | |
| int64_t ideal_timestamp = cur_timestamp - zsl_lookback_ns_; | |
| for (auto it = ring_buffer_.begin(); it != ring_buffer_.end(); it++) { | |
| if (!it->metadata_ready || !it->buffer_ready || it->selected) { | |
| continue; | |
| } | |
| int64_t timestamp = GetTimestamp(it->metadata); | |
| bool satisfy_3a = strategy == CLOSEST || | |
| (strategy == CLOSEST_3A && Is3AConverged(it->metadata)); | |
| int64_t diff = timestamp - ideal_timestamp; | |
| VLOGF(1) << "Candidate timestamp = " << timestamp | |
| << " (Satisfy 3A = " << satisfy_3a << ", " | |
| << "Difference from desired timestamp = " << diff << ")"; | |
| if (diff > kZslLookbackLengthNs) { | |
| continue; | |
| } else if (diff < 0) { | |
| // We don't select buffers that are older than what is displayed. | |
| break; | |
| } | |
| if (satisfy_3a) { | |
| if (diff < min_diff) { | |
| min_diff = diff; | |
| selected_buffer_it = it; | |
| } else { | |
| // Not possible to find a better buffer | |
| break; | |
| } | |
| } | |
| } | |
| if (selected_buffer_it == ring_buffer_.end()) { | |
| LOGF(WARNING) | |
| << "Failed to a find suitable ZSL buffer with the given strategy"; | |
| return selected_buffer_it; | |
| } | |
| LOGF(INFO) << "Timestamp of the selected buffer = " | |
| << GetTimestamp(selected_buffer_it->metadata); | |
| selected_buffer_it->selected = true; | |
| return selected_buffer_it; | |
| } | |
| int64_t ZslHelper::GetCurrentTimestamp() { | |
| if (override_current_timestamp_for_testing_ != | |
| kOverrideCurrentTimestampNotSet) { | |
| return override_current_timestamp_for_testing_; | |
| } | |
| struct timespec t = {}; | |
| clock_gettime( | |
| timestamp_source_ == ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN | |
| ? CLOCK_MONOTONIC | |
| : CLOCK_BOOTTIME /* ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME */, | |
| &t); | |
| return static_cast<int64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec; | |
| } | |
| bool ZslHelper::Is3AConverged(const android::CameraMetadata& android_metadata) { | |
| auto GetState = [&](size_t tag) { | |
| camera_metadata_ro_entry_t entry; | |
| if (android_metadata.exists(tag)) { | |
| entry = android_metadata.find(tag); | |
| return entry.data.u8[0]; | |
| } | |
| LOGF(ERROR) << "Cannot find the metadata for " | |
| << get_camera_metadata_tag_name(tag); | |
| return static_cast<uint8_t>(0); | |
| }; | |
| uint8_t ae_mode = GetState(ANDROID_CONTROL_AE_MODE); | |
| uint8_t ae_state = GetState(ANDROID_CONTROL_AE_STATE); | |
| bool ae_converged = [&]() { | |
| if (ae_mode != ANDROID_CONTROL_AE_MODE_OFF) { | |
| if (ae_state != ANDROID_CONTROL_AE_STATE_CONVERGED && | |
| ae_state != ANDROID_CONTROL_AE_STATE_FLASH_REQUIRED && | |
| ae_state != ANDROID_CONTROL_AE_STATE_LOCKED) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| }(); | |
| if (!ae_converged) { | |
| return false; | |
| } | |
| uint8_t af_mode = GetState(ANDROID_CONTROL_AF_MODE); | |
| uint8_t af_state = GetState(ANDROID_CONTROL_AF_STATE); | |
| bool af_converged = [&]() { | |
| if (af_mode != ANDROID_CONTROL_AF_MODE_OFF) { | |
| if (af_state != ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED && | |
| af_state != ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| }(); | |
| if (!af_converged) { | |
| return false; | |
| } | |
| uint8_t awb_mode = GetState(ANDROID_CONTROL_AWB_MODE); | |
| uint8_t awb_state = GetState(ANDROID_CONTROL_AWB_STATE); | |
| bool awb_converged = [&]() { | |
| if (awb_mode != ANDROID_CONTROL_AWB_MODE_OFF) { | |
| if (awb_state != ANDROID_CONTROL_AWB_STATE_CONVERGED && | |
| awb_state != ANDROID_CONTROL_AWB_STATE_LOCKED) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| }(); | |
| // We won't reach here if neither AE nor AF is converged. | |
| return awb_converged; | |
| } | |
| void ZslHelper::SetZslBufferManagerForTesting( | |
| std::unique_ptr<ZslBufferManager> zsl_buffer_manager) { | |
| zsl_buffer_manager_ = std::move(zsl_buffer_manager); | |
| } | |
| void ZslHelper::OverrideCurrentTimestampForTesting(int64_t timestamp) { | |
| override_current_timestamp_for_testing_ = timestamp; | |
| } | |
| bool AddVendorTags(VendorTagManager& vendor_tag_manager) { | |
| if (!vendor_tag_manager.Add(kCrosZslVendorTagCanAttempt, | |
| kCrosZslVendorTagSectionName, | |
| kCrosZslVendorTagCanAttemptName, TYPE_BYTE)) { | |
| LOGF(ERROR) | |
| << "Failed to add the vendor tag for CrOS ZSL attemptable indicator"; | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool TryAddEnableZslKey(android::CameraMetadata* metadata) { | |
| const camera_metadata_t* locked_metadata = metadata->getAndLock(); | |
| if (!CanEnableZsl(locked_metadata)) { | |
| metadata->unlock(locked_metadata); | |
| return false; | |
| } | |
| metadata->unlock(locked_metadata); | |
| auto entry = metadata->find(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS); | |
| std::vector<int32_t> new_request_keys(entry.data.i32, | |
| entry.data.i32 + entry.count); | |
| new_request_keys.push_back(ANDROID_CONTROL_ENABLE_ZSL); | |
| if (metadata->update(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, | |
| new_request_keys.data(), new_request_keys.size()) != 0) { | |
| LOGF(ERROR) << "Failed to add ANDROID_CONTROL_ENABLE_ZSL to metadata"; | |
| return false; | |
| } | |
| LOGF(INFO) << "Added ANDROID_CONTROL_ENABLE_ZSL to static metadata"; | |
| return true; | |
| } | |
| } // namespace cros |