| // 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 "src/camera/lib/fake_controller/fake_controller.h" |
| |
| #include <lib/async-loop/default.h> |
| #include <lib/async/cpp/task.h> |
| #include <lib/fidl/cpp/optional.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <zircon/errors.h> |
| |
| #include "src/camera/lib/fake_legacy_stream/fake_legacy_stream.h" |
| |
| static fuchsia::camera2::DeviceInfo DefaultDeviceInfo() { |
| fuchsia::camera2::DeviceInfo device_info{}; |
| device_info.set_vendor_id(0xFFFF); |
| device_info.set_vendor_name("Fake Vendor Name"); |
| device_info.set_product_id(0x0ABC); |
| device_info.set_product_name("Fake Product Name"); |
| device_info.set_type(fuchsia::camera2::DeviceType::VIRTUAL); |
| return device_info; |
| } |
| |
| static std::vector<fuchsia::camera2::hal::Config> DefaultConfigs() { |
| constexpr fuchsia::sysmem::BufferCollectionConstraints kBufferCollectionConstraints{ |
| .usage = {.cpu = fuchsia::sysmem::cpuUsageRead}, |
| .min_buffer_count_for_camping = 3, |
| .image_format_constraints_count = 1, |
| .image_format_constraints = {{{ |
| .pixel_format = |
| { |
| .type = fuchsia::sysmem::PixelFormatType::NV12, |
| }, |
| .color_spaces_count = 1, |
| .color_space = {{{ |
| .type = fuchsia::sysmem::ColorSpaceType::REC601_NTSC, |
| }}}, |
| .min_coded_width = 128, |
| .max_coded_width = 4096, |
| .min_coded_height = 1, |
| .max_coded_height = 4096, |
| .coded_width_divisor = 128, |
| }}}}; |
| fuchsia::camera2::hal::Config config1; |
| config1.stream_configs.push_back( |
| {.frame_rate = |
| { |
| .frames_per_sec_numerator = 30, |
| .frames_per_sec_denominator = 1, |
| }, |
| .constraints = kBufferCollectionConstraints, |
| .image_formats = { |
| { |
| .pixel_format = |
| kBufferCollectionConstraints.image_format_constraints[0].pixel_format, |
| .coded_width = 1920, |
| .coded_height = 1080, |
| .bytes_per_row = 1920, |
| }, |
| { |
| .pixel_format = |
| kBufferCollectionConstraints.image_format_constraints[0].pixel_format, |
| .coded_width = 1280, |
| .coded_height = 720, |
| .bytes_per_row = 1920, |
| }, |
| { |
| .pixel_format = |
| kBufferCollectionConstraints.image_format_constraints[0].pixel_format, |
| .coded_width = 1024, |
| .coded_height = 576, |
| .bytes_per_row = 1920, |
| }}}); |
| fuchsia::camera2::hal::Config config2; |
| config2.stream_configs.push_back( |
| {.frame_rate = |
| { |
| .frames_per_sec_numerator = 30, |
| .frames_per_sec_denominator = 1, |
| }, |
| .constraints = kBufferCollectionConstraints, |
| .image_formats = {{ |
| .pixel_format = kBufferCollectionConstraints.image_format_constraints[0].pixel_format, |
| .coded_width = 1280, |
| .coded_height = 720, |
| .bytes_per_row = 1280, |
| }}}); |
| std::vector<fuchsia::camera2::hal::Config> configs; |
| configs.push_back(std::move(config1)); |
| configs.push_back(std::move(config2)); |
| return configs; |
| } |
| |
| fit::result<std::unique_ptr<FakeController>, zx_status_t> FakeController::Create( |
| fidl::InterfaceRequest<fuchsia::camera2::hal::Controller> request) { |
| auto controller = std::make_unique<FakeController>(); |
| |
| zx_status_t status = controller->loop_.StartThread("Fake Controller Loop"); |
| if (status != ZX_OK) { |
| FX_PLOGS(ERROR, status); |
| return fit::error(status); |
| } |
| |
| status = controller->binding_.Bind(std::move(request), controller->loop_.dispatcher()); |
| if (status != ZX_OK) { |
| FX_PLOGS(ERROR, status); |
| return fit::error(status); |
| } |
| |
| return fit::ok(std::move(controller)); |
| } |
| |
| std::vector<fuchsia::camera2::hal::Config> FakeController::GetDefaultConfigs() { |
| return DefaultConfigs(); |
| } |
| |
| FakeController::FakeController() |
| : loop_(&kAsyncLoopConfigNoAttachToCurrentThread), binding_(this) {} |
| |
| FakeController::~FakeController() { loop_.Shutdown(); } |
| |
| bool FakeController::LegacyStreamBufferIsOutstanding(uint32_t id) { |
| bool is_outstanding = false; |
| zx::event event; |
| zx::event::create(0, &event); |
| async::PostTask(loop_.dispatcher(), [&, this, id] { |
| is_outstanding = stream_->IsOutstanding(id); |
| event.signal(0, ZX_USER_SIGNAL_0); |
| }); |
| event.wait_one(ZX_USER_SIGNAL_0, zx::time::infinite(), nullptr); |
| return is_outstanding; |
| } |
| |
| zx_status_t FakeController::SendFrameViaLegacyStream(fuchsia::camera2::FrameAvailableInfo info) { |
| zx_status_t status = ZX_ERR_INTERNAL; |
| zx::event event; |
| zx::event::create(0, &event); |
| async::PostTask(loop_.dispatcher(), [this, &status, &event, info = std::move(info)]() mutable { |
| if (!stream_ || !stream_->IsStreaming()) { |
| status = ZX_ERR_SHOULD_WAIT; |
| } else { |
| status = stream_->SendFrameAvailable(std::move(info)); |
| } |
| event.signal(0, ZX_USER_SIGNAL_0); |
| }); |
| event.wait_one(ZX_USER_SIGNAL_0, zx::time::infinite(), nullptr); |
| return status; |
| } |
| |
| void FakeController::GetNextConfig( |
| fuchsia::camera2::hal::Controller::GetNextConfigCallback callback) { |
| if (get_configs_call_count_ >= DefaultConfigs().size()) { |
| callback(nullptr, ZX_ERR_STOP); |
| return; |
| } |
| callback(fidl::MakeOptional(std::move(DefaultConfigs()[get_configs_call_count_++])), ZX_OK); |
| } |
| |
| void FakeController::CreateStream(uint32_t config_index, uint32_t stream_index, |
| uint32_t image_format_index, |
| fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection, |
| fidl::InterfaceRequest<fuchsia::camera2::Stream> stream) { |
| auto result = camera::FakeLegacyStream::Create(std::move(stream), loop_.dispatcher()); |
| if (result.is_error()) { |
| FX_PLOGS(ERROR, result.error()); |
| return; |
| } |
| stream_ = result.take_value(); |
| } |
| |
| void FakeController::EnableStreaming() { |
| if (streaming_enabled_) { |
| FX_LOGS(ERROR) << "Called EnableStreaming when already enabled."; |
| binding_.Close(ZX_ERR_BAD_STATE); |
| return; |
| } |
| streaming_enabled_ = true; |
| } |
| |
| void FakeController::DisableStreaming() { |
| if (!streaming_enabled_) { |
| FX_LOGS(ERROR) << "Called DisableStreaming when already disabled."; |
| binding_.Close(ZX_ERR_BAD_STATE); |
| return; |
| } |
| streaming_enabled_ = false; |
| } |
| |
| void FakeController::GetDeviceInfo( |
| fuchsia::camera2::hal::Controller::GetDeviceInfoCallback callback) { |
| callback(DefaultDeviceInfo()); |
| } |