blob: ad9cb972eeaec441f95c8043c8b7f24ff67889b7 [file] [log] [blame]
// Copyright 2018 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_camera_control.h"
#include <lib/async/default.h>
#include "src/lib/syslog/cpp/logger.h"
namespace camera {
constexpr auto kTag = "virtual_camera";
static const char* kVirtualCameraVendorName = "Google Inc.";
static const char* kVirtualCameraProductName = "Fuchsia Virtual Camera";
static constexpr uint32_t kFakeImageWidth = 640;
static constexpr uint32_t kFakeImageHeight = 480;
static constexpr uint32_t kFakeImageBytesPerPixel = 4;
static constexpr uint32_t kFakeImageFps = 30;
static constexpr uint64_t kNanosecondsPerSecond = 1e9;
void VirtualCameraControlImpl::OnFrameAvailable(const fuchsia::camera::FrameAvailableEvent& frame) {
stream_->OnFrameAvailable(frame);
}
VirtualCameraControlImpl::VirtualCameraControlImpl(fidl::InterfaceRequest<Control> control,
async_dispatcher_t* dispatcher,
fit::closure on_connection_closed)
: binding_(this, std::move(control), dispatcher) {
binding_.set_error_handler(
[occ = std::move(on_connection_closed)](zx_status_t /*status*/) { occ(); });
}
void VirtualCameraControlImpl::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 VirtualCameraControlImpl::ProduceFrame() {
fuchsia::camera::FrameAvailableEvent event = {};
// For realism, give the frame a timestamp that is kFramesOfDelay frames
// in the past:
event.metadata.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::camera::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(event);
// Schedule next frame:
PostNextCaptureTask();
}
void VirtualCameraControlImpl::GetFormats(uint32_t /*index*/, GetFormatsCallback callback) {
std::vector<fuchsia::camera::VideoFormat> formats;
fuchsia::camera::VideoFormat format = {
.format =
{
.width = kFakeImageWidth,
.height = kFakeImageHeight,
.pixel_format = {.type = fuchsia::sysmem::PixelFormatType::BGRA32},
},
.rate = {.frames_per_sec_numerator = kFakeImageFps, .frames_per_sec_denominator = 1}};
format.format.planes[0].bytes_per_row = kFakeImageBytesPerPixel * kFakeImageWidth;
formats.push_back(format);
callback(std::move(formats), 1, ZX_OK);
}
void VirtualCameraControlImpl::GetDeviceInfo(GetDeviceInfoCallback callback) {
fuchsia::camera::DeviceInfo camera_device_info;
camera_device_info.vendor_name = kVirtualCameraVendorName;
camera_device_info.product_name = kVirtualCameraProductName;
camera_device_info.output_capabilities = fuchsia::camera::CAMERA_OUTPUT_STREAM;
camera_device_info.max_stream_count = 1;
callback(std::move(camera_device_info));
}
void VirtualCameraControlImpl::CreateStream(fuchsia::sysmem::BufferCollectionInfo buffer_collection,
fuchsia::camera::FrameRate frame_rate,
fidl::InterfaceRequest<fuchsia::camera::Stream> stream,
zx::eventpair stream_token) {
rate_ = frame_rate;
buffers_.Init(buffer_collection.vmos.data(), buffer_collection.buffer_count);
buffers_.MapVmos();
stream_ = std::make_unique<VirtualCameraStreamImpl>(*this, std::move(stream));
stream_token_ = std::move(stream_token);
// If not triggered by the token being closed, this waiter will be cancelled
// by the destruction of this class, so the "this" pointer will be valid as
// long as the waiter is around.
stream_token_waiter_ = std::make_unique<async::Wait>(
stream_token_.get(), ZX_EVENTPAIR_PEER_CLOSED, 0, std::bind([this]() {
stream_->Stop();
stream_ = nullptr;
stream_token_.reset();
stream_token_waiter_ = nullptr;
}));
zx_status_t status = stream_token_waiter_->Begin(async_get_default_dispatcher());
// The waiter, dispatcher and token are known to be valid, so this should
// never fail.
FX_CHECK(status == ZX_OK);
}
void VirtualCameraControlImpl::VirtualCameraStreamImpl::OnFrameAvailable(
const fuchsia::camera::FrameAvailableEvent& frame) {
binding_.events().OnFrameAvailable(frame);
}
void VirtualCameraControlImpl::VirtualCameraStreamImpl::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 VirtualCameraControlImpl::VirtualCameraStreamImpl::Stop() { owner_.task_.Cancel(); }
void VirtualCameraControlImpl::VirtualCameraStreamImpl::ReleaseFrame(uint32_t buffer_index) {
owner_.buffers_.ReleaseBuffer(buffer_index);
}
VirtualCameraControlImpl::VirtualCameraStreamImpl::VirtualCameraStreamImpl(
VirtualCameraControlImpl& owner, fidl::InterfaceRequest<fuchsia::camera::Stream> stream)
: owner_(owner), binding_(this, std::move(stream)) {
binding_.set_error_handler([](zx_status_t status) {
// Anything to do here?
});
}
} // namespace camera