| /* |
| * Copyright 2019 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 "hal_adapter/zsl_helper.h" |
| |
| #include <algorithm> |
| #include <cstdlib> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/bind.h> |
| #include <camera/camera_metadata.h> |
| #include <sync/sync.h> |
| #include <system/camera_metadata.h> |
| |
| #include "cros-camera/common.h" |
| |
| namespace cros { |
| |
| ZslBuffer::ZslBuffer() |
| : metadata_ready(false), buffer_ready(false), selected(false) {} |
| ZslBuffer::ZslBuffer(uint32_t frame_number, |
| const camera3_stream_buffer_t& buffer) |
| : frame_number(frame_number), |
| buffer(buffer), |
| metadata_ready(false), |
| buffer_ready(false), |
| selected(false) {} |
| |
| ZslBufferManager::ZslBufferManager() |
| : initialized_(false), |
| buffer_manager_(CameraBufferManager::GetInstance()) {} |
| |
| ZslBufferManager::~ZslBufferManager() { |
| if (free_buffers_.size() != buffer_pool_.size()) { |
| LOGF(WARNING) << "Not all ZSL buffers have been released"; |
| } |
| for (auto& buffer : buffer_pool_) { |
| buffer_manager_->Free(buffer); |
| } |
| } |
| |
| bool ZslBufferManager::Initialize(size_t pool_size, |
| camera3_stream_t* output_stream) { |
| output_stream_ = output_stream; |
| |
| buffer_pool_.reserve(pool_size); |
| base::AutoLock buffer_pool_lock(buffer_pool_lock_); |
| 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, |
| cros::GRALLOC, &buffer, &stride) != 0) { |
| LOGF(ERROR) << "Failed to allocate buffer"; |
| // Free previously-allocated buffers. |
| for (auto& buffer : buffer_pool_) { |
| buffer_manager_->Free(buffer); |
| } |
| return false; |
| } |
| buffer_pool_.push_back(buffer); |
| free_buffers_.push(&buffer_pool_.back()); |
| buffer_to_buffer_pointer_map_[buffer] = &buffer_pool_.back(); |
| } |
| |
| 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; |
| } |
| |
| ZslHelper::ZslHelper(const camera_metadata_t* static_info, |
| FrameNumberMapper* mapper) |
| : initialized_(false), |
| enabled_(false), |
| fence_sync_thread_("FenceSyncThread"), |
| frame_number_mapper_(mapper) { |
| VLOGF_ENTER(); |
| if (IsCapabilitySupported(static_info, kZslCapability)) { |
| uint32_t bi_width, bi_height; |
| if (SelectZslStreamSize(static_info, &bi_width, &bi_height)) { |
| LOGF(INFO) << "Selected ZSL stream size: " << bi_width << "x" |
| << bi_height; |
| // Create ZSL streams |
| bi_stream_ = std::make_unique<camera3_stream_t>(); |
| memset(bi_stream_.get(), 0, sizeof(*bi_stream_.get())); |
| bi_stream_->stream_type = CAMERA3_STREAM_BIDIRECTIONAL; |
| bi_stream_->width = bi_width; |
| bi_stream_->height = bi_height; |
| bi_stream_->format = kZslPixelFormat; |
| |
| // Initialize ZSL buffer manager |
| uint8_t max_pipeline_depth = [&]() { |
| camera_metadata_ro_entry entry; |
| if (find_camera_metadata_ro_entry( |
| static_info, ANDROID_REQUEST_PIPELINE_MAX_DEPTH, &entry) != 0) { |
| LOGF(ERROR) << "ANDROID_REQUEST_PIPELINE_MAX_DEPTH is missing from " |
| "static metadata!"; |
| // This shouldn't happen, but assigning this a value just in case. |
| return static_cast<uint8_t>(20); |
| } |
| return entry.data.u8[0]; |
| }(); |
| if (zsl_buffer_manager_.Initialize(kZslBufferSize + max_pipeline_depth, |
| bi_stream_.get())) { |
| initialized_ = true; |
| } else { |
| LOGF(ERROR) << "Failed to initialize ZSL buffer manager"; |
| } |
| } else { |
| LOGF(ERROR) << "Failed to select stream sizes for ZSL."; |
| } |
| } else { |
| LOGF(INFO) << "Device doesn't support ZSL. ZSL won't be enabled."; |
| } |
| if (!fence_sync_thread_.Start()) { |
| LOGF(ERROR) << "Fence sync thread failed to start"; |
| initialized_ = false; |
| } |
| partial_result_count_ = [&]() { |
| camera_metadata_ro_entry entry; |
| if (find_camera_metadata_ro_entry( |
| static_info, ANDROID_REQUEST_PARTIAL_RESULT_COUNT, &entry) != 0) { |
| return 1; |
| } |
| return entry.data.i32[0]; |
| }(); |
| 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]); |
| }(); |
| } |
| |
| ZslHelper::~ZslHelper() { |
| fence_sync_thread_.Stop(); |
| } |
| |
| bool ZslHelper::IsZslEnabled() { |
| base::AutoLock enabled_lock(enabled_lock_); |
| return enabled_; |
| } |
| |
| void ZslHelper::SetZslEnabled(bool enabled) { |
| base::AutoLock enabled_lock(enabled_lock_); |
| if (enabled != enabled_) { |
| LOGF(INFO) << (enabled ? "Enabling" : "Disabling") << " ZSL"; |
| enabled_ = enabled; |
| } |
| } |
| |
| bool ZslHelper::CanEnableZsl(const internal::ScopedStreams& streams) { |
| size_t num_input_streams = 0; |
| bool has_zsl_output_stream = false; |
| bool has_blob_output_stream = false; |
| for (auto it = streams.begin(); it != streams.end(); it++) { |
| camera3_stream_t* stream = it->second.get(); |
| if (stream->stream_type == CAMERA3_STREAM_INPUT || |
| stream->stream_type == CAMERA3_STREAM_BIDIRECTIONAL) { |
| num_input_streams++; |
| } |
| if (stream->stream_type == CAMERA3_STREAM_OUTPUT || |
| stream->stream_type == CAMERA3_STREAM_BIDIRECTIONAL) { |
| if (stream->format == HAL_PIXEL_FORMAT_BLOB || |
| (stream->usage & GRALLOC_USAGE_STILL_CAPTURE)) { |
| has_blob_output_stream = true; |
| } |
| if ((stream->usage & GRALLOC_USAGE_HW_CAMERA_ZSL) == |
| GRALLOC_USAGE_HW_CAMERA_ZSL) { |
| has_zsl_output_stream = true; |
| } |
| } |
| } |
| return initialized_ // Initialized means we have an allocated buffer pool. |
| && has_blob_output_stream // Has a stream for still capture. |
| && num_input_streams < max_num_input_streams_ // Has room for an extra |
| // input stream for ZSL. |
| && !has_zsl_output_stream; // HAL doesn't support multiple raw output |
| // streams. |
| } |
| |
| void ZslHelper::AttachZslStream(camera3_stream_configuration_t* stream_list, |
| std::vector<camera3_stream_t*>* streams) { |
| stream_list->num_streams++; |
| streams->push_back(bi_stream_.get()); |
| // There could be memory reallocation happening after the push_back call. |
| stream_list->streams = streams->data(); |
| |
| for (size_t i = 0; i < stream_list->num_streams; ++i) { |
| // GRALLOC_USAGE_STILL_CAPTURE is a private usage from VCD. |
| // We set the usage flag here to let VCD know ZSL is enabled. |
| if ((*streams)[i]->usage & GRALLOC_USAGE_STILL_CAPTURE) { |
| (*streams)[i]->usage |= GRALLOC_USAGE_ZSL_ENABLED; |
| } |
| } |
| |
| VLOGF(1) << "Attached ZSL streams. The list of streams after attaching:"; |
| for (size_t i = 0; i < stream_list->num_streams; ++i) { |
| VLOGF(1) << "i = " << i |
| << ", type = " << stream_list->streams[i]->stream_type |
| << ", size = " << stream_list->streams[i]->width << "x" |
| << stream_list->streams[i]->height |
| << ", format = " << stream_list->streams[i]->format; |
| } |
| } |
| |
| bool ZslHelper::IsZslRequested(camera_metadata_t* settings) { |
| bool enable_zsl = [&]() { |
| camera_metadata_ro_entry_t entry; |
| if (find_camera_metadata_ro_entry(settings, ANDROID_CONTROL_ENABLE_ZSL, |
| &entry) == 0) { |
| return static_cast<bool>(entry.data.u8[0]); |
| } |
| return false; |
| }(); |
| if (!enable_zsl) { |
| return false; |
| } |
| // We can only enable ZSL when capture intent is also still capture. |
| camera_metadata_ro_entry_t entry; |
| if (find_camera_metadata_ro_entry(settings, ANDROID_CONTROL_CAPTURE_INTENT, |
| &entry) == 0) { |
| return entry.data.u8[0] == ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE || |
| entry.data.u8[0] == ANDROID_CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG; |
| } |
| return false; |
| } |
| |
| bool ZslHelper::IsAttachedZslFrame(uint32_t frame_number) { |
| base::AutoLock ring_buffer_lock(ring_buffer_lock_); |
| return buffer_index_map_.find(frame_number) != buffer_index_map_.end(); |
| } |
| |
| bool ZslHelper::IsAttachedZslBuffer(const camera3_stream_buffer_t* buffer) { |
| return buffer && buffer->stream == bi_stream_.get(); |
| } |
| |
| bool ZslHelper::IsTransformedZslBuffer(const camera3_stream_buffer_t* buffer) { |
| return buffer && buffer->stream == bi_stream_.get(); |
| } |
| |
| void ZslHelper::ProcessZslCaptureRequest( |
| uint32_t framework_frame_number, |
| camera3_capture_request_t* request, // maybe just use *input_buffer? |
| std::vector<camera3_stream_buffer_t>* output_buffers, |
| internal::ScopedCameraMetadata* settings, |
| camera3_capture_request_t* still_request, |
| std::vector<camera3_stream_buffer_t>* still_output_buffers, |
| SelectionStrategy strategy) { |
| if (request->input_buffer != nullptr) { |
| return; |
| } |
| if (IsZslRequested(settings->get())) { |
| for (auto it = output_buffers->begin(); it != output_buffers->end();) { |
| if (it->stream->format == HAL_PIXEL_FORMAT_BLOB || |
| (it->stream->usage & GRALLOC_USAGE_STILL_CAPTURE)) { |
| still_output_buffers->push_back(std::move(*it)); |
| it = output_buffers->erase(it); |
| } else { |
| it++; |
| } |
| } |
| if (still_output_buffers->empty()) { |
| LOGF(ERROR) << "ZSL is requested, but we couldn't find any still " |
| "capture output buffers."; |
| } else { |
| camera_metadata_t* zsl_settings; |
| bool transformed = |
| TransformRequest(still_request, &zsl_settings, strategy); |
| if (transformed) { |
| still_request->frame_number = |
| frame_number_mapper_->GetHalFrameNumber(framework_frame_number); |
| still_request->settings = zsl_settings; |
| } else { |
| // TODO(lnishan): Implement 3A stabilization mechanism so that we |
| // would attempt to get a buffer in another time. |
| // Merging the buffers back for now. |
| LOGF(ERROR) << "Not splitting this request because we cannot find a " |
| "suitable ZSL buffer"; |
| for (size_t i = 0; i < still_output_buffers->size(); ++i) { |
| output_buffers->push_back(std::move((*still_output_buffers)[i])); |
| } |
| still_output_buffers->clear(); |
| } |
| } |
| still_request->num_output_buffers = still_output_buffers->size(); |
| still_request->output_buffers = const_cast<const camera3_stream_buffer_t*>( |
| still_output_buffers->data()); |
| } |
| |
| // We might end up moving all output buffers to the added request. So here we |
| // unconditionally add a ZSL output buffer. We also need a placeholder request |
| // so that we can defer a request if a suitable ZSL buffer is not found. |
| AttachRequest(request, output_buffers); |
| } |
| |
| void ZslHelper::AttachRequest( |
| camera3_capture_request_t* request, |
| std::vector<camera3_stream_buffer_t>* output_buffers) { |
| VLOGF_ENTER(); |
| if (!enabled_) { |
| LOGF(WARNING) << "Trying to attach a request when ZSL is disabled"; |
| return; |
| } |
| // Check if the oldest ZSL buffer is filled and free it if it's filled and |
| // not selected for any transformed ZSL requests. |
| base::AutoLock l(ring_buffer_lock_); |
| if (ring_buffer_.IsFilledIndex(0)) { |
| const ZslBuffer& buf = ring_buffer_.ReadBuffer(0); |
| if (!buf.selected) { |
| // We can free the buffer if it's not selected. |
| if (!zsl_buffer_manager_.ReleaseBuffer(*buf.buffer.buffer)) { |
| LOGF(ERROR) << "Unable to release the oldest buffer"; |
| } |
| } |
| // No need to remember frame-to-index mapping when this buffer is popped. |
| buffer_index_map_.erase(buf.frame_number); |
| } |
| |
| // Attach our ZSL output buffer. |
| camera3_stream_buffer_t stream_buffer; |
| stream_buffer.buffer = zsl_buffer_manager_.GetBuffer(); |
| stream_buffer.stream = bi_stream_.get(); |
| stream_buffer.acquire_fence = stream_buffer.release_fence = -1; |
| |
| buffer_index_map_[request->frame_number] = ring_buffer_.CurrentIndex(); |
| ZslBuffer buffer(request->frame_number, stream_buffer); |
| ring_buffer_.SaveToBuffer(std::move(buffer)); |
| |
| output_buffers->push_back(std::move(stream_buffer)); |
| request->num_output_buffers++; |
| } |
| |
| bool ZslHelper::TransformRequest(camera3_capture_request_t* request, |
| camera_metadata_t** settings, |
| SelectionStrategy strategy) { |
| VLOGF_ENTER(); |
| if (!enabled_) { |
| LOGF(WARNING) << "Trying to transform a request when ZSL is disabled"; |
| return false; |
| } |
| |
| // Select the best buffer. |
| ZslBuffer* selected_buffer = SelectZslBuffer(strategy); |
| if (!selected_buffer) { |
| LOGF(WARNING) << "Unable to find a suitable ZSL buffer. Request will not " |
| "be transformed."; |
| return false; |
| } |
| |
| LOGF(INFO) << "Transforming request into ZSL reprocessing request"; |
| base::AutoLock ring_buffer_lock(ring_buffer_lock_); |
| request->input_buffer = &selected_buffer->buffer; |
| request->input_buffer->stream = bi_stream_.get(); |
| request->input_buffer->acquire_fence = -1; |
| request->input_buffer->release_fence = -1; |
| |
| // Note that camera device adapter would take ownership of this pointer. |
| *settings = selected_buffer->metadata.release(); |
| return true; |
| } |
| |
| void ZslHelper::ProcessZslCaptureResult( |
| const camera3_capture_result_t* result, |
| const camera3_stream_buffer_t** attached_output, |
| const camera3_stream_buffer_t** transformed_input) { |
| VLOGF_ENTER(); |
| for (size_t i = 0; i < result->num_output_buffers; ++i) { |
| if (IsAttachedZslBuffer(&result->output_buffers[i])) { |
| *attached_output = &result->output_buffers[i]; |
| break; |
| } |
| } |
| if (result->input_buffer && IsTransformedZslBuffer(result->input_buffer)) { |
| *transformed_input = result->input_buffer; |
| ReleaseStreamBuffer(*result->input_buffer); |
| } |
| if (IsAttachedZslFrame(result->frame_number)) { |
| base::AutoLock ring_buffer_lock(ring_buffer_lock_); |
| auto it = buffer_index_map_.find(result->frame_number); |
| if (it != buffer_index_map_.end()) { |
| ZslBuffer* buffer = MutableReadBufferByBufferIndex(it->second); |
| |
| for (size_t i = 0; i < result->num_output_buffers; ++i) { |
| if (result->output_buffers[i].stream == bi_stream_.get()) { |
| WaitAttachedFrame(result->frame_number, |
| result->output_buffers[i].release_fence); |
| break; |
| } |
| } |
| |
| if (result->partial_result != 0) { // Result has metadata |
| // Merge the result metadata. |
| if (buffer) { |
| buffer->metadata.append(result->result); |
| if (result->partial_result == partial_result_count_) { |
| buffer->metadata_ready = true; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void ZslHelper::WaitAttachedFrame(uint32_t frame_number, int release_fence) { |
| fence_sync_thread_.task_runner()->PostTask( |
| FROM_HERE, |
| base::Bind(&ZslHelper::WaitAttachedFrameOnFenceSyncThread, |
| base::Unretained(this), frame_number, release_fence)); |
| } |
| |
| void ZslHelper::WaitAttachedFrameOnFenceSyncThread(uint32_t frame_number, |
| int release_fence) { |
| if (release_fence != -1 && |
| sync_wait(release_fence, 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 = buffer_index_map_.find(frame_number); |
| if (it != buffer_index_map_.end()) { |
| ZslBuffer* buffer = MutableReadBufferByBufferIndex(it->second); |
| if (buffer) { |
| buffer->buffer_ready = true; |
| } |
| } |
| return; |
| } |
| fence_sync_thread_.task_runner()->PostTask( |
| FROM_HERE, |
| base::Bind(&ZslHelper::WaitAttachedFrameOnFenceSyncThread, |
| base::Unretained(this), frame_number, release_fence)); |
| } |
| |
| void ZslHelper::ReleaseStreamBuffer(camera3_stream_buffer_t buffer) { |
| fence_sync_thread_.task_runner()->PostTask( |
| FROM_HERE, base::Bind(&ZslHelper::ReleaseStreamBufferOnFenceSyncThread, |
| base::Unretained(this), base::Passed(&buffer))); |
| } |
| |
| void ZslHelper::ReleaseStreamBufferOnFenceSyncThread( |
| camera3_stream_buffer_t buffer) { |
| if (buffer.release_fence != -1 && |
| sync_wait(buffer.release_fence, 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::Bind(&ZslHelper::ReleaseStreamBufferOnFenceSyncThread, |
| base::Unretained(this), base::Passed(&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) { |
| VLOGF_ENTER(); |
| 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; |
| } |
| *bi_width = 0; |
| *bi_height = 0; |
| |
| 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; |
| } |
| } |
| } |
| return *bi_width > 0 && *bi_height > 0; |
| } |
| |
| ZslBuffer* ZslHelper::SelectZslBuffer(SelectionStrategy strategy) { |
| // Select the best ZSL buffer based on time and statistics. |
| auto 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); |
| }; |
| base::AutoLock ring_buffer_lock(ring_buffer_lock_); |
| if (strategy == LAST_SUBMITTED) { |
| for (int i = kZslBufferSize - 1; i >= 0; --i) { |
| if (!ring_buffer_.IsFilledIndex(i)) { |
| continue; |
| } |
| ZslBuffer* buffer = ring_buffer_.MutableReadBuffer(i); |
| if (buffer->metadata_ready && buffer->buffer_ready && !buffer->selected) { |
| buffer->selected = true; |
| return buffer; |
| } |
| } |
| LOGF(WARNING) << "Failed to find a unselected submitted ZSL buffer"; |
| return nullptr; |
| } |
| |
| // For CLOSEST or CLOSEST_3A strategies. |
| int64_t cur_timestamp = GetCurrentTimestamp(); |
| LOGF(INFO) << "Current timestamp = " << cur_timestamp; |
| ZslBuffer* selected_buffer = nullptr; |
| int64_t min_diff = kZslLookbackNs; |
| int64_t ideal_timestamp = cur_timestamp - kZslLookbackNs; |
| for (int i = kZslBufferSize - 1; i >= 0; --i) { |
| if (!ring_buffer_.IsFilledIndex(i)) { |
| continue; |
| } |
| ZslBuffer* buffer = ring_buffer_.MutableReadBuffer(i); |
| if (!buffer->metadata_ready || !buffer->buffer_ready || buffer->selected) { |
| continue; |
| } |
| int64_t timestamp = GetTimestamp(buffer->metadata); |
| bool satisfy_3a = strategy == CLOSEST || (strategy == CLOSEST_3A && |
| Is3AConverged(buffer->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 = buffer; |
| } else { |
| // Not possible to find a better buffer |
| break; |
| } |
| } |
| } |
| if (selected_buffer == nullptr) { |
| LOGF(WARNING) |
| << "Failed to a find suitable ZSL buffer with the given strategy"; |
| return nullptr; |
| } |
| LOGF(INFO) << "Timestamp of the selected buffer = " |
| << GetTimestamp(selected_buffer->metadata); |
| selected_buffer->selected = true; |
| return selected_buffer; |
| } |
| |
| int64_t ZslHelper::GetCurrentTimestamp() { |
| 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; |
| } |
| |
| ZslBuffer* ZslHelper::MutableReadBufferByBufferIndex(size_t buffer_index) { |
| size_t current_index = ring_buffer_.CurrentIndex(); |
| DCHECK(current_index > buffer_index); |
| // The DCHECK fails if the caller is attempting to update a buffer that's no |
| // longer in the ring buffer. This means a capture result is returned after |
| // |kZslBufferSize| frames, which shouldn't happen and likely indicates |
| // something is wrong. |
| DCHECK(current_index - buffer_index <= ZslHelper::kZslBufferSize); |
| if (current_index - buffer_index <= ZslHelper::kZslBufferSize) { |
| size_t rel_index = |
| ZslHelper::kZslBufferSize - (current_index - buffer_index); |
| return ring_buffer_.MutableReadBuffer(rel_index); |
| } |
| return nullptr; |
| } |
| |
| } // namespace cros |