| // 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 <fidl/fuchsia.sysmem/cpp/hlcpp_conversion.h> |
| #include <fidl/fuchsia.sysmem2/cpp/hlcpp_conversion.h> |
| #include <fuchsia/hardware/isp/c/banjo.h> |
| #include <lib/ddk/debug.h> |
| #include <lib/ddk/trace/event.h> |
| #include <lib/sysmem-version/sysmem-version.h> |
| #include <zircon/errors.h> |
| #include <zircon/types.h> |
| |
| #include "src/camera/lib/format_conversion/format_conversion.h" |
| #include "src/devices/lib/sysmem/sysmem.h" |
| |
| namespace camera { |
| |
| InputNode::InputNode(async_dispatcher_t* dispatcher, BufferAttachments attachments, |
| FrameCallback frame_callback, const ddk::IspProtocolClient& isp) |
| : ProcessNode(dispatcher, NodeType::kInputStream, attachments, std::move(frame_callback)) {} |
| |
| fpromise::result<std::unique_ptr<InputNode>, zx_status_t> InputNode::Create( |
| async_dispatcher_t* dispatcher, BufferAttachments attachments, FrameCallback frame_callback, |
| const ddk::IspProtocolClient& isp, const StreamCreationData& info) { |
| // TODO(100525): this makes very specific assumptions about the layout of the monitoring config |
| const auto& inode = info.stream_type() == fuchsia::camera2::CameraStreamType::MONITORING |
| ? info.roots[1] |
| : info.roots[0]; |
| // Create Input Node |
| auto pnode = |
| std::make_unique<camera::InputNode>(dispatcher, attachments, std::move(frame_callback), isp); |
| |
| uint8_t isp_stream_type = 0; |
| if (inode.input_stream_type == fuchsia::camera2::CameraStreamType::FULL_RESOLUTION) { |
| isp_stream_type = STREAM_TYPE_FULL_RESOLUTION; |
| } else if (inode.input_stream_type == fuchsia::camera2::CameraStreamType::DOWNSCALED_RESOLUTION) { |
| isp_stream_type = STREAM_TYPE_DOWNSCALED; |
| } else { |
| return fpromise::error(ZX_ERR_INVALID_ARGS); |
| } |
| |
| buffer_collection_info_2 temp_buffer_collection = sysmem::fidl_to_banjo(pnode->OutputBuffers()); |
| image_format_2_t temp_image_format = |
| sysmem::fidl_to_banjo(ConvertV2ToV1WireType(inode.image_formats[0])); |
| output_stream_protocol_t isp_stream_protocol{}; |
| auto status = isp.CreateOutputStream( |
| &temp_buffer_collection, &temp_image_format, |
| reinterpret_cast<const frame_rate_t*>(&inode.output_frame_rate), isp_stream_type, |
| pnode->GetHwFrameReadyCallback(), &isp_stream_protocol); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "Failed to create output stream on ISP"); |
| return fpromise::error(status); |
| } |
| pnode->stream_ = ddk::OutputStreamProtocolClient(&isp_stream_protocol); |
| |
| // Note that as clients can only access the camera via camera3, and camera3 only exposes a |
| // device-wide mute state (corresponding to StartStreaming/StopStreaming), individual camera2 |
| // stream start/stop requests from the client are ignored. |
| ZX_ASSERT(pnode->stream_.Start() == ZX_OK); |
| |
| return fpromise::ok(std::move(pnode)); |
| } |
| |
| void InputNode::ProcessFrame(FrameToken token, frame_metadata_t metadata) { |
| ZX_PANIC("InputNode cannot process frames!"); |
| } |
| |
| void InputNode::SetOutputFormat(uint32_t output_format_index, fit::closure callback) { |
| ZX_PANIC("InputNode cannot set output format!"); |
| } |
| |
| void InputNode::ShutdownImpl(fit::closure callback) { |
| TRACE_DURATION("camera", "InputNode::ShutdownImpl"); |
| ZX_ASSERT(!shutdown_callback_); |
| shutdown_callback_ = std::move(callback); |
| |
| isp_stream_shutdown_callback_t isp_stream_shutdown_cb{ |
| .shutdown_complete = |
| [](void* ctx, zx_status_t status) { |
| ZX_ASSERT(status == ZX_OK); |
| auto node = static_cast<decltype(this)>(ctx); |
| node->PostTask([node] { node->shutdown_callback_(); }); |
| }, |
| .ctx = this, |
| }; |
| StopStreaming(); |
| stream_.Shutdown(&isp_stream_shutdown_cb); |
| } |
| |
| void InputNode::HwFrameReady(frame_available_info_t info) { |
| TRACE_DURATION("camera", "InputNode::HwFrameReady", "status", |
| static_cast<uint32_t>(info.frame_status), "buffer_index", info.buffer_id); |
| // Don't do anything further with error frames. |
| if (info.frame_status != FRAME_STATUS_OK) { |
| constexpr auto kErrorFrameMinLogInterval = zx::sec(1); |
| auto now = zx::clock::get_monotonic(); |
| if (now >= last_frame_error_logged_ + kErrorFrameMinLogInterval) { |
| zxlogf(ERROR, "failed input frame: %i", static_cast<int>(info.frame_status)); |
| TRACE_INSTANT("camera", "bad_status", TRACE_SCOPE_THREAD, "frame_status", |
| static_cast<uint32_t>(info.frame_status)); |
| last_frame_error_logged_ = now; |
| } |
| return; |
| } |
| if (info.metadata.timestamp == 0) { |
| zxlogf(ERROR, "missing timestamp on input buffer %u", info.buffer_id); |
| stream_.ReleaseFrame(info.buffer_id); |
| return; |
| } |
| |
| // Use the timestamp of the input as the capture time. |
| auto modified = info; |
| modified.metadata.capture_timestamp = info.metadata.timestamp; |
| |
| // Send the frame onward. |
| SendFrame(modified.buffer_id, modified.metadata, |
| [this, buffer_index = modified.buffer_id] { stream_.ReleaseFrame(buffer_index); }); |
| } |
| |
| void InputNode::HwFrameResolutionChanged(frame_available_info_t info) {} |
| |
| void InputNode::HwTaskRemoved(task_remove_status_t status) {} |
| |
| void InputNode::StartStreaming() { |
| TRACE_DURATION("camera", "InputNode::StartStreaming"); |
| stream_.Start(); |
| } |
| |
| void InputNode::StopStreaming() { |
| TRACE_DURATION("camera", "InputNode::StopStreaming"); |
| stream_.Stop(); |
| } |
| |
| } // namespace camera |