| // Copyright 2019 The Fuchsia 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 "src/camera/drivers/controller/input_node.h" |
| |
| #include <fuchsia/hardware/isp/c/banjo.h> |
| #include <lib/ddk/trace/event.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <zircon/errors.h> |
| #include <zircon/types.h> |
| |
| #include "src/camera/drivers/controller/graph_utils.h" |
| #include "src/camera/lib/format_conversion/buffer_collection_helper.h" |
| #include "src/camera/lib/format_conversion/format_conversion.h" |
| #include "src/devices/lib/sysmem/sysmem.h" |
| |
| namespace camera { |
| |
| constexpr auto kTag = "camera_controller_input_node"; |
| |
| fit::result<std::unique_ptr<InputNode>, zx_status_t> InputNode::CreateInputNode( |
| StreamCreationData* info, const ControllerMemoryAllocator& memory_allocator, |
| async_dispatcher_t* dispatcher, const ddk::IspProtocolClient& isp) { |
| uint8_t isp_stream_type; |
| if (info->node.input_stream_type == fuchsia::camera2::CameraStreamType::FULL_RESOLUTION) { |
| isp_stream_type = STREAM_TYPE_FULL_RESOLUTION; |
| } else if (info->node.input_stream_type == |
| fuchsia::camera2::CameraStreamType::DOWNSCALED_RESOLUTION) { |
| isp_stream_type = STREAM_TYPE_DOWNSCALED; |
| } else { |
| return fit::error(ZX_ERR_INVALID_ARGS); |
| } |
| |
| auto result = camera::GetBuffers(memory_allocator, info->node, info, kTag); |
| if (result.is_error()) { |
| FX_PLOGST(ERROR, kTag, result.error()) << "Failed to get buffers"; |
| return fit::error(result.error()); |
| } |
| auto buffers = std::move(result.value()); |
| |
| // Use a BufferCollectionHelper to manage the conversion |
| // between buffer collection representations. |
| BufferCollectionHelper buffer_collection_helper(buffers); |
| |
| auto image_format = ConvertHlcppImageFormat2toCType(info->node.image_formats[0]); |
| |
| // Create Input Node |
| auto processing_node = |
| std::make_unique<camera::InputNode>(info, std::move(buffers), dispatcher, isp); |
| if (!processing_node) { |
| FX_LOGST(ERROR, kTag) << "Failed to create Input node"; |
| return fit::error(ZX_ERR_NO_MEMORY); |
| } |
| |
| // Create stream with ISP |
| auto isp_stream_protocol = std::make_unique<camera::IspStreamProtocol>(); |
| if (!isp_stream_protocol) { |
| FX_LOGST(ERROR, kTag) << "Failed to create ISP stream protocol"; |
| return fit::error(ZX_ERR_INTERNAL); |
| } |
| |
| buffer_collection_info_2 temp_buffer_collection; |
| image_format_2_t temp_image_format; |
| sysmem::buffer_collection_info_2_banjo_from_fidl(*buffer_collection_helper.GetC(), |
| temp_buffer_collection); |
| sysmem::image_format_2_banjo_from_fidl(image_format, temp_image_format); |
| auto status = isp.CreateOutputStream( |
| &temp_buffer_collection, &temp_image_format, |
| reinterpret_cast<const frame_rate_t*>(&info->node.output_frame_rate), isp_stream_type, |
| processing_node->isp_frame_callback(), isp_stream_protocol->protocol()); |
| if (status != ZX_OK) { |
| FX_PLOGST(ERROR, kTag, status) << "Failed to create output stream on ISP"; |
| return fit::error(status); |
| } |
| |
| // Update the input node with the ISP stream protocol |
| processing_node->set_isp_stream_protocol(std::move(isp_stream_protocol)); |
| return fit::ok(std::move(processing_node)); |
| } |
| |
| void InputNode::OnReadyToProcess(const frame_available_info_t* info) { |
| // Use the timestamp of the input as the capture time. |
| auto modified = *info; |
| modified.metadata.capture_timestamp = info->metadata.timestamp; |
| OnFrameAvailable(&modified); |
| } |
| |
| void InputNode::OnFrameAvailable(const frame_available_info_t* info) { |
| ZX_ASSERT(thread_checker_.is_thread_valid()); |
| TRACE_DURATION("camera", "InputNode::OnFrameAvailable", "buffer_index", info->buffer_id); |
| if (shutdown_requested_ || info->frame_status != FRAME_STATUS_OK) { |
| TRACE_INSTANT("camera", "bad_status", TRACE_SCOPE_THREAD, "frame_status", |
| static_cast<uint32_t>(info->frame_status)); |
| return; |
| } |
| |
| UpdateFrameCounterForAllChildren(); |
| |
| if (NeedToDropFrame()) { |
| TRACE_INSTANT("camera", "drop_frame", TRACE_SCOPE_THREAD); |
| isp_stream_protocol_->ReleaseFrame(info->buffer_id); |
| return; |
| } |
| ProcessNode::OnFrameAvailable(info); |
| } |
| |
| void InputNode::OnReleaseFrame(uint32_t buffer_index) { |
| TRACE_DURATION("camera", "InputNode::OnReleaseFrame", "buffer_index", buffer_index); |
| fbl::AutoLock al(&in_use_buffer_lock_); |
| ZX_ASSERT(buffer_index < in_use_buffer_count_.size()); |
| in_use_buffer_count_[buffer_index]--; |
| if (in_use_buffer_count_[buffer_index] != 0) { |
| return; |
| } |
| if (!shutdown_requested_) { |
| isp_stream_protocol_->ReleaseFrame(buffer_index); |
| } |
| } |
| |
| void InputNode::OnStartStreaming() { |
| enabled_ = true; |
| isp_stream_protocol_->Start(); |
| } |
| |
| void InputNode::OnStopStreaming() { |
| if (AllChildNodesDisabled()) { |
| enabled_ = false; |
| isp_stream_protocol_->Stop(); |
| } |
| } |
| |
| void InputNode::OnShutdown(fit::function<void(void)> shutdown_callback) { |
| shutdown_callback_ = std::move(shutdown_callback); |
| |
| // After a shutdown request has been made, |
| // no other calls should be made to the ISP driver. |
| shutdown_requested_ = true; |
| |
| isp_stream_shutdown_callback_t isp_stream_shutdown_cb = { |
| .shutdown_complete = |
| [](void* ctx, zx_status_t /*status*/) { |
| auto* input_node = static_cast<decltype(this)>(ctx); |
| input_node->node_callback_received_ = true; |
| input_node->OnCallbackReceived(); |
| }, |
| .ctx = this, |
| }; |
| |
| zx_status_t status = isp_stream_protocol_->Shutdown(&isp_stream_shutdown_cb); |
| if (status != ZX_OK) { |
| FX_PLOGST(ERROR, kTag, status) << "Failure during stream shutdown"; |
| return; |
| } |
| |
| auto child_shutdown_completion_callback = [this]() { |
| child_node_callback_received_ = true; |
| OnCallbackReceived(); |
| }; |
| |
| ZX_ASSERT_MSG(configured_streams().size() == 1, |
| "Cannot shutdown a stream which supports multiple streams"); |
| |
| // Forward the shutdown request to child node. |
| if (child_nodes().empty()) { |
| // If an incomplete graph is shut down, invoke the completion directly. |
| child_shutdown_completion_callback(); |
| } else { |
| // Otherwise, propagate the shutdown command and completion to the next child. |
| child_nodes().at(0)->OnShutdown(child_shutdown_completion_callback); |
| } |
| } |
| |
| } // namespace camera |