blob: b600260c4cd1f01ab4d44b707310cd762a4eb80d [file] [log] [blame]
// 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/output_node.h"
#include <lib/ddk/debug.h>
#include <lib/trace/event.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <sstream>
#include <safemath/safe_conversions.h>
namespace camera {
OutputNode::OutputNode(async_dispatcher_t* dispatcher, BufferAttachments attachments)
: ProcessNode(dispatcher, NodeType::kOutputStream, attachments, [](auto, auto) { abort(); }),
dispatcher_(dispatcher),
binding_(this) {
binding_.set_error_handler([this](auto status) { callbacks_.disconnect(); });
}
fpromise::result<std::unique_ptr<OutputNode>, zx_status_t> OutputNode::Create(
async_dispatcher_t* dispatcher, BufferAttachments attachments,
fidl::InterfaceRequest<fuchsia::camera2::Stream> request, Callbacks callbacks) {
ZX_ASSERT(request.is_valid());
TRACE_DURATION("camera", "OutputNode::Create");
auto node = std::make_unique<camera::OutputNode>(dispatcher, attachments);
node->callbacks_ = std::move(callbacks);
ZX_ASSERT(node->binding_.Bind(std::move(request), node->dispatcher_) == ZX_OK);
return fpromise::ok(std::move(node));
}
void OutputNode::ProcessFrame(FrameToken token, frame_metadata_t metadata) {
TRACE_DURATION("camera", "OutputNode::ProcessFrame", "buffer_index", *token);
if (!started_) {
return; // ~token
}
frame_count_++;
if (frame_count_ % kFrameCountHeartbeatInterval == 0) {
zxlogf(INFO, "Camera node %s has produced %u frames", GetLabel().c_str(), frame_count_);
}
// Throttle the rate of binding warning messages.
// Once a connection is unbound, every frame from the camera can produce multiple
// of these warnings - one per stream.
if (!binding_.is_bound()) {
binding_warnings_++;
if (binding_warnings_ <= kBindingWarningsInitial ||
binding_warnings_ % kBindingWarningsInterval == 0) {
std::stringstream ss;
ss << this << ": client disconnected? returning frame... (seen " << binding_warnings_
<< " times)";
ss << "Camera node " << this << " client reconnected? delivering frame...";
zxlogf(WARNING, "%s", ss.str().c_str());
}
return; // ~token
}
if (binding_warnings_ > 0) {
std::stringstream ss;
ss << "Camera node " << this << " client reconnected? delivering frame...";
zxlogf(INFO, "%s", ss.str().c_str());
binding_warnings_ = 0;
}
TRACE_FLOW_BEGIN("camera", "camera_stream_on_frame_available", metadata.timestamp);
client_tokens_.emplace(*token, token);
fuchsia::camera2::FrameAvailableInfo frame_info{};
frame_info.frame_status = fuchsia::camera2::FrameStatus::OK;
frame_info.buffer_id = *token;
frame_info.metadata.set_image_format_index(metadata.image_format_index);
frame_info.metadata.set_timestamp(safemath::checked_cast<int64_t>(metadata.timestamp));
frame_info.metadata.set_capture_timestamp(
safemath::checked_cast<int64_t>(metadata.capture_timestamp));
ZX_ASSERT(binding_.is_bound());
binding_.events().OnFrameAvailable(std::move(frame_info));
}
void OutputNode::SetOutputFormat(uint32_t output_format_index, fit::closure callback) {
TRACE_DURATION("camera", "OutputNode::SetOutputFormat", "format_index", output_format_index);
callback();
}
void OutputNode::ShutdownImpl(fit::closure callback) {
TRACE_DURATION("camera", "OutputNode::ShutdownImpl");
client_tokens_.clear();
callback();
}
void OutputNode::HwFrameReady(frame_available_info_t info) { abort(); }
void OutputNode::HwFrameResolutionChanged(frame_available_info_t info) { abort(); }
void OutputNode::HwTaskRemoved(task_remove_status_t status) { abort(); }
void OutputNode::Stop() {
std::stringstream ss;
ss << "Camera node " << this << " Stop()";
zxlogf(INFO, "%s", ss.str().c_str());
started_ = false;
}
void OutputNode::Start() {
std::stringstream ss;
ss << "Camera node " << this << " Start()";
zxlogf(INFO, "%s", ss.str().c_str());
started_ = true;
}
void OutputNode::ReleaseFrame(uint32_t buffer_id) {
auto element = client_tokens_.extract(buffer_id);
if (element.empty()) {
zxlogf(INFO, "Client called ReleaseFrame on non-held buffer %u", buffer_id);
}
}
void OutputNode::AcknowledgeFrameError() {
zxlogf(ERROR, "Camera node %s not implemented", __PRETTY_FUNCTION__);
}
void OutputNode::SetRegionOfInterest(float x_min, float y_min, float x_max, float y_max,
SetRegionOfInterestCallback callback) {
TRACE_DURATION("camera", "OutputNode::SetRegionOfInterest");
callbacks_.set_region_of_interest(x_min, y_min, x_max, y_max, std::move(callback));
}
void OutputNode::SetImageFormat(uint32_t image_format_index, SetImageFormatCallback callback) {
TRACE_DURATION("camera", "OutputNode::SetImageFormat");
callbacks_.set_image_format(image_format_index, std::move(callback));
}
void OutputNode::GetImageFormats(GetImageFormatsCallback callback) {
TRACE_DURATION("camera", "OutputNode::GetImageFormats");
callbacks_.get_image_formats(std::move(callback));
}
void OutputNode::GetBuffers(GetBuffersCallback callback) {
TRACE_DURATION("camera", "OutputNode::GetBuffers");
callbacks_.get_buffers(std::move(callback));
}
} // namespace camera