blob: fcbe9d41f2d17072967c3c185105bceb0c1dc5df [file] [log] [blame]
// Copyright 2020 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 <lib/async-loop/default.h>
#include <lib/async/cpp/task.h>
#include <lib/fpromise/result.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/trace/event.h>
#include <sstream>
#include "src/camera/bin/usb_device/messages.h"
#include "src/camera/bin/usb_device/size_util.h"
#include "src/camera/bin/usb_device/stream_impl.h"
#include "src/lib/fsl/handles/object_info.h"
namespace camera {
namespace {
using fpromise::make_error_promise;
using fpromise::make_ok_promise;
using fpromise::make_promise;
using fuchsia::camera3::FrameInfo2;
using fuchsia::sysmem::BufferCollectionInfo;
using fuchsia::sysmem::BufferCollectionTokenPtr;
} // namespace
StreamImpl::Client::Client(StreamImpl& stream, uint64_t id,
fidl::InterfaceRequest<fuchsia::camera3::Stream> request)
: stream_(stream), id_(id), binding_(this, std::move(request)), resolution_(SizeEqual) {
log_prefix_ = "stream " + stream_.description_ +
" client (koid = " + std::to_string(GetRelatedKoid(binding_)) + "): ";
FX_LOGS(INFO) << log_prefix_ << "new stream client, id = " << id_;
binding_.set_error_handler(fit::bind_member(this, &StreamImpl::Client::OnClientDisconnected));
}
StreamImpl::Client::~Client() = default;
void StreamImpl::Client::AddFrame(FrameInfo2 frame) {
TRACE_DURATION("camera", "StreamImpl::Client::AddFrame");
frames_.push(std::move(frame));
if (!frame_logging_state_.available) {
FX_LOGS(INFO) << log_prefix_ << "frame available";
frame_logging_state_.available = true;
}
MaybeSendFrame();
}
void StreamImpl::Client::MaybeSendFrame() {
TRACE_DURATION("camera", "StreamImpl::Client::MaybeSendFrame");
if (frames_.empty() || !frame_callback_) {
return;
}
auto& frame = frames_.front();
// This Flow can be connected on the client end to allow tracing the flow of frames
// into the client.
TRACE_FLOW_BEGIN("camera", "camera3::Stream::GetNextFrame",
fsl::GetKoid(frame.release_fence().get()));
// Note: this message is logged immediately prior to invoking the frame callback to ensure that no
// client logs emitted as a result will have earlier timestamps than this message.
if (!frame_logging_state_.sent) {
FX_LOGS(INFO) << log_prefix_ << "sent frame";
frame_logging_state_.sent = true;
}
frame_callback_(std::move(frame));
frames_.pop();
frame_callback_ = nullptr;
}
void StreamImpl::Client::ReceiveBufferCollection(
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token) {
TRACE_DURATION("camera", "StreamImpl::Client::ReceiveBufferCollection");
buffers_.Set(std::move(token));
}
void StreamImpl::Client::ReceiveResolution(fuchsia::math::Size coded_size) {
TRACE_DURATION("camera", "StreamImpl::Client::ReceiveResolution");
resolution_.Set(coded_size);
}
void StreamImpl::Client::ReceiveCropRegion(std::unique_ptr<fuchsia::math::RectF> region) {
TRACE_DURATION("camera", "StreamImpl::Client::ReceiveCropRegion");
crop_region_.Set(std::move(region));
}
bool& StreamImpl::Client::Participant() { return participant_; }
void StreamImpl::Client::ClearFrames() {
while (!frames_.empty()) {
frames_.pop();
}
}
void StreamImpl::Client::OnClientDisconnected(zx_status_t status) {
FX_PLOGS(INFO, status) << log_prefix_ << "closed connection";
stream_.Schedule(stream_.RemoveClient(id_));
}
void StreamImpl::Client::CloseConnection(zx_status_t status) {
binding_.Close(status);
}
void StreamImpl::Client::GetProperties(GetPropertiesCallback callback) {
FX_LOGS(ERROR) << log_prefix_ << "FIDL call not supported";
}
void StreamImpl::Client::GetProperties2(GetProperties2Callback callback) {
FX_LOGS(ERROR) << log_prefix_ << "FIDL call not supported";
}
void StreamImpl::Client::SetCropRegion(std::unique_ptr<fuchsia::math::RectF> region) {
FX_LOGS(ERROR) << log_prefix_ << "FIDL call not supported";
}
void StreamImpl::Client::WatchCropRegion(WatchCropRegionCallback callback) {
FX_LOGS(ERROR) << log_prefix_ << "FIDL call not supported";
}
void StreamImpl::Client::SetResolution(fuchsia::math::Size coded_size) {
FX_LOGS(ERROR) << log_prefix_ << "FIDL call not supported";
}
void StreamImpl::Client::WatchResolution(WatchResolutionCallback callback) {
FX_LOGS(ERROR) << log_prefix_ << "FIDL call not supported";
}
void StreamImpl::Client::SetBufferCollection(
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token) {
auto nonce = TRACE_NONCE();
TRACE_ASYNC_BEGIN("camera", "StreamImpl::Client::SetBufferCollection", nonce);
FX_LOGS(INFO) << log_prefix_ << "called SetBufferCollection(koid = " << GetRelatedKoid(token)
<< ")";
auto promise =
stream_.SetClientParticipation(id_, std::move(token))
.then([this,
nonce](fpromise::result<std::optional<BufferCollectionTokenPtr>>& result) mutable
-> fpromise::promise<void> {
// If the result of SetClientParticipation was nullopt, we're done.
if (!result.value())
return make_ok_promise();
return stream_.InitBufferCollections(std::move(*result.value()))
.then([this](fpromise::result<BufferCollectionInfo, zx_status_t>& result)
-> fpromise::promise<void> {
if (result.is_ok()) {
return stream_.ConnectAndStartStream(std::move(result.value()));
} else {
stream_.OnError(result.error(), ":failed to initialize buffer collections.");
return make_ok_promise();
}
})
.and_then([nonce]() {
TRACE_ASYNC_END("camera", "StreamImpl::Client::SetBufferCollection", nonce);
});
});
stream_.Schedule(std::move(promise));
}
void StreamImpl::Client::WatchBufferCollection(WatchBufferCollectionCallback callback) {
TRACE_DURATION("camera", "StreamImpl::Client::WatchBufferCollection");
FX_LOGS(INFO) << log_prefix_ << "called WatchBufferCollection()";
if (buffers_.Get(std::move(callback))) {
CloseConnection(ZX_ERR_BAD_STATE);
stream_.Schedule(stream_.RemoveClient(id_));
}
}
void StreamImpl::Client::WatchOrientation(WatchOrientationCallback callback) {
TRACE_DURATION("camera", "StreamImpl::Client::WatchOrientation");
// It is invalid to have multiple Watch requests in flight.
if (orientation_callback_) {
CloseConnection(ZX_ERR_BAD_STATE);
stream_.Schedule(stream_.RemoveClient(id_));
return;
}
// A watch call subsequent to the first will never be invoked, but the object must still be
// persisted. Destruction of captured FIDL objects prior to invocation would be interpreted as a
// server error.
orientation_callback_ = std::move(callback);
}
void StreamImpl::Client::GetNextFrame(GetNextFrameCallback callback) {
GetNextFrame2([callback = std::move(callback)](FrameInfo2 frame) {
callback({.buffer_index = frame.buffer_index(),
.frame_counter = frame.frame_counter(),
.timestamp = frame.timestamp(),
.release_fence = std::move(*frame.mutable_release_fence())});
});
}
void StreamImpl::Client::GetNextFrame2(GetNextFrame2Callback callback) {
TRACE_DURATION("camera", "StreamImpl::Client::GetNextFrame2");
if (!frame_logging_state_.requested) {
FX_LOGS(INFO) << log_prefix_ << "called GetNextFrame2()";
frame_logging_state_.requested = true;
}
if (frame_callback_) {
FX_LOGS(INFO) << stream_.description_ << ": " << id_
<< ": Client called GetNextFrame while a previous call was still pending.";
CloseConnection(ZX_ERR_BAD_STATE);
stream_.Schedule(stream_.RemoveClient(id_));
return;
}
frame_callback_ = std::move(callback);
MaybeSendFrame();
}
void StreamImpl::Client::Rebind(fidl::InterfaceRequest<Stream> request) {
FX_LOGS(INFO) << log_prefix_ << "called Rebind(koid = " << GetRelatedKoid(request) << ")";
stream_.OnNewRequest(std::move(request));
}
} // namespace camera