blob: 4566241f5930a24d68cd2d1354ad4dbe59d7eb13 [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 "virtual_camera2_control.h"
#include <lib/async/default.h>
#include "src/lib/syslog/cpp/logger.h"
namespace camera {
constexpr auto kTag = "virtual_camera";
static constexpr uint32_t kBufferCountForCamping = 5;
static constexpr uint32_t kFakeImageCodedWidth = 640;
static constexpr uint32_t kFakeImageMinWidth = 640;
static constexpr uint32_t kFakeImageMaxWidth = 2048;
static constexpr uint32_t kFakeImageCodedHeight = 480;
static constexpr uint32_t kFakeImageMinHeight = 480;
static constexpr uint32_t kFakeImageMaxHeight = 1280;
static constexpr uint32_t kFakeImageMinBytesPerRow = 480;
static constexpr uint32_t kFakeImageMaxBytesPerRow = 0xfffffff;
static constexpr uint32_t kFakeImageBytesPerRowDivisor = 128;
static constexpr uint32_t kFakeImageFps = 30;
static constexpr uint32_t kNumberOfLayers = 1;
static constexpr uint64_t kNanosecondsPerSecond = 1e9;
void VirtualCamera2ControllerImpl::OnFrameAvailable(fuchsia::camera2::FrameAvailableInfo frame) {
// TODO(36747): don't send multiple error frames unless the client calls
// AcknowledgeErrorFrame().
stream_->OnFrameAvailable(std::move(frame));
}
fuchsia::sysmem::BufferCollectionConstraints GetFakeConstraints() {
fuchsia::sysmem::BufferCollectionConstraints constraints;
constraints.min_buffer_count_for_camping = kBufferCountForCamping;
// TODO(36757): Add tests for allocating contiguous memory, with the following:
// constraints.has_buffer_memory_constraints = true;
// constraints.buffer_memory_constraints.physically_contiguous_required = true;
constraints.image_format_constraints_count = 1;
auto& image_constraints = constraints.image_format_constraints[0];
image_constraints.pixel_format.type = fuchsia::sysmem::PixelFormatType::NV12;
image_constraints.min_coded_width = kFakeImageMinWidth;
image_constraints.max_coded_width = kFakeImageMaxWidth;
image_constraints.min_coded_height = kFakeImageMinHeight;
image_constraints.max_coded_height = kFakeImageMaxHeight;
image_constraints.min_bytes_per_row = kFakeImageMinBytesPerRow;
image_constraints.max_bytes_per_row = kFakeImageMaxBytesPerRow;
image_constraints.layers = kNumberOfLayers;
image_constraints.bytes_per_row_divisor = kFakeImageBytesPerRowDivisor;
image_constraints.color_spaces_count = 1;
image_constraints.color_space[0].type = fuchsia::sysmem::ColorSpaceType::REC601_PAL;
constraints.usage.cpu = fuchsia::sysmem::cpuUsageWrite | fuchsia::sysmem::cpuUsageRead;
return constraints;
}
std::vector<fuchsia::sysmem::ImageFormat_2> GetImageFormats() {
fuchsia::sysmem::ImageFormat_2 ret;
ret.coded_width = kFakeImageCodedWidth;
ret.coded_height = kFakeImageCodedHeight;
ret.pixel_format.type = fuchsia::sysmem::PixelFormatType::NV12;
std::vector<fuchsia::sysmem::ImageFormat_2> ret_vec;
ret_vec.push_back(ret);
return ret_vec;
}
fuchsia::camera2::StreamProperties GetStreamProperties(fuchsia::camera2::CameraStreamType type) {
fuchsia::camera2::StreamProperties ret{};
ret.set_stream_type(type);
return ret;
}
VirtualCamera2ControllerImpl::VirtualCamera2ControllerImpl(
fidl::InterfaceRequest<fuchsia::camera2::hal::Controller> control,
async_dispatcher_t* dispatcher, fit::closure on_connection_closed)
: binding_(this) {
// Make up some configs:
fuchsia::camera2::hal::Config config1;
fuchsia::camera2::hal::StreamConfig sconfig = {
.frame_rate =
{
.frames_per_sec_numerator = kFakeImageFps,
.frames_per_sec_denominator = 1,
},
.constraints = GetFakeConstraints(),
.properties = GetStreamProperties(fuchsia::camera2::CameraStreamType::MACHINE_LEARNING),
.image_formats = GetImageFormats(),
};
config1.stream_configs.push_back(std::move(sconfig));
configs_.push_back(std::move(config1));
binding_.set_error_handler([on_connection_closed = std::move(on_connection_closed)](
zx_status_t status) { on_connection_closed(); });
binding_.Bind(std::move(control), dispatcher);
}
void VirtualCamera2ControllerImpl::PostNextCaptureTask() {
// Set the next frame time to be start + frame_count / frames per second.
int64_t next_frame_time = frame_to_timestamp_.Apply(frame_count_++);
FX_DCHECK(next_frame_time > 0) << "TimelineFunction gave negative result!";
FX_DCHECK(next_frame_time != media::TimelineRate::kOverflow)
<< "TimelineFunction gave negative result!";
task_.PostForTime(async_get_default_dispatcher(), zx::time(next_frame_time));
FX_VLOGS(4) << "VirtualCameraSource: setting next frame to: " << next_frame_time << " "
<< next_frame_time - static_cast<int64_t>(zx_clock_get_monotonic())
<< " nsec from now";
}
// Checks which buffer can be written to,
// writes it, then signals it ready.
// Then sleeps until next cycle.
void VirtualCamera2ControllerImpl::ProduceFrame() {
fuchsia::camera2::FrameAvailableInfo event = {};
// For realism, give the frame a timestamp that is kFramesOfDelay frames
// in the past:
event.metadata.set_timestamp(frame_to_timestamp_.Apply(frame_count_ - kFramesOfDelay));
FX_DCHECK(event.metadata.timestamp()) << "TimelineFunction gave negative result!";
FX_DCHECK(event.metadata.timestamp() != media::TimelineRate::kOverflow)
<< "TimelineFunction gave negative result!";
// As per the camera driver spec, we always send an OnFrameAvailable message,
// even if there is an error.
auto buffer = buffers_.LockBufferForWrite();
if (!buffer) {
FX_LOGST(ERROR, kTag) << "no available frames, dropping frame #" << frame_count_;
event.frame_status = fuchsia::camera2::FrameStatus::ERROR_BUFFER_FULL;
} else { // Got a buffer. Fill it with color:
color_source_.FillARGB(buffer->virtual_address(), buffer->size());
event.buffer_id = buffer->ReleaseWriteLockAndGetIndex();
}
OnFrameAvailable(std::move(event));
// Schedule next frame:
PostNextCaptureTask();
}
void VirtualCamera2ControllerImpl::GetConfigs(GetConfigsCallback callback) {
callback(fidl::Clone(configs_), ZX_OK);
}
void VirtualCamera2ControllerImpl::GetDeviceInfo(GetDeviceInfoCallback callback) {
fuchsia::camera2::DeviceInfo camera_device_info;
camera_device_info.set_vendor_name(kVirtualCameraVendorName);
camera_device_info.set_product_name(kVirtualCameraProductName);
camera_device_info.set_type(fuchsia::camera2::DeviceType::VIRTUAL);
callback(std::move(camera_device_info));
}
void VirtualCamera2ControllerImpl::CreateStream(
uint32_t config_index, uint32_t stream_type, uint32_t /*image_format_index*/,
fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection,
fidl::InterfaceRequest<fuchsia::camera2::Stream> stream) {
auto& stream_config = configs_[config_index].stream_configs[stream_type];
rate_ = stream_config.frame_rate;
// Pull all the vmos out of the structs that BufferCollection_2 stores them in:
std::array<zx::vmo, buffer_collection.buffers.size()> vmos;
for (uint32_t i = 0; i < buffer_collection.buffer_count; ++i) {
vmos[i] = std::move(buffer_collection.buffers[i].vmo);
}
// If we fail here we return, which drops the stream request, closing the channel.
zx_status_t status = buffers_.Init(vmos.data(), buffer_collection.buffer_count);
if (status != ZX_OK) {
FX_PLOGST(ERROR, kTag, status) << "Init buffers failed!";
return;
}
status = buffers_.MapVmos();
if (status != ZX_OK) {
FX_PLOGST(ERROR, kTag, status) << "Map buffers failed!";
return;
}
stream_ = std::make_unique<VirtualCamera2StreamImpl>(*this, std::move(stream));
}
void VirtualCamera2ControllerImpl::VirtualCamera2StreamImpl::OnFrameAvailable(
fuchsia::camera2::FrameAvailableInfo frame) {
binding_.events().OnFrameAvailable(std::move(frame));
}
void VirtualCamera2ControllerImpl::VirtualCamera2StreamImpl::Start() {
// Set a timeline function to convert from framecount to monotonic time.
// The start time is now, the start frame number is 0, and the
// conversion function from frame to time is:
// frames_per_sec_denominator * 1e9 * num_frames) / frames_per_sec_numerator
owner_.frame_to_timestamp_ = media::TimelineFunction(
zx_clock_get_monotonic(), 0, owner_.rate_.frames_per_sec_denominator * kNanosecondsPerSecond,
owner_.rate_.frames_per_sec_numerator);
owner_.frame_count_ = 0;
// Set the first time at which we will generate a frame:
owner_.PostNextCaptureTask();
}
void VirtualCamera2ControllerImpl::VirtualCamera2StreamImpl::Stop() { owner_.task_.Cancel(); }
void VirtualCamera2ControllerImpl::VirtualCamera2StreamImpl::ReleaseFrame(uint32_t buffer_index) {
owner_.buffers_.ReleaseBuffer(buffer_index);
}
VirtualCamera2ControllerImpl::VirtualCamera2StreamImpl::VirtualCamera2StreamImpl(
VirtualCamera2ControllerImpl& owner, fidl::InterfaceRequest<fuchsia::camera2::Stream> stream)
: owner_(owner), binding_(this, std::move(stream)) {
binding_.set_error_handler([](zx_status_t status) {
// Anything to do here?
});
}
} // namespace camera