| // 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/media/codec/examples/encode_camera/camera_client.h" |
| |
| #include <lib/async-loop/default.h> |
| #include <zircon/types.h> |
| |
| #include <iostream> |
| |
| static void FatalError(std::string message) { |
| std::cerr << message << std::endl; |
| abort(); |
| } |
| |
| // Sets the error handler on the provided interface to log an error and abort the process. |
| template <class T> |
| static void SetAbortOnError(fidl::InterfacePtr<T>& p, std::string message) { |
| p.set_error_handler([message](zx_status_t status) { FatalError(message); }); |
| } |
| |
| CameraClient::CameraClient(bool list_configs, uint32_t config_index, uint32_t stream_index) |
| : list_configs_(list_configs), config_index_(config_index), stream_index_(stream_index) { |
| SetAbortOnError(watcher_, "fuchsia.camera3.DeviceWatcher disconnected."); |
| SetAbortOnError(allocator_, "fuchsia.sysmem.Allocator disconnected."); |
| SetAbortOnError(device_, "fuchsia.camera3.Device disconnected."); |
| } |
| |
| CameraClient::~CameraClient() {} |
| |
| fit::result<std::unique_ptr<CameraClient>, zx_status_t> CameraClient::Create( |
| fuchsia::camera3::DeviceWatcherHandle watcher, fuchsia::sysmem::AllocatorHandle allocator, |
| bool list_configs, uint32_t config_index, uint32_t stream_index) { |
| auto cycler = |
| std::unique_ptr<CameraClient>(new CameraClient(list_configs, config_index, stream_index)); |
| |
| zx_status_t status = cycler->watcher_.Bind(std::move(watcher)); |
| if (status != ZX_OK) { |
| return fit::error(status); |
| } |
| |
| status = cycler->allocator_.Bind(std::move(allocator)); |
| if (status != ZX_OK) { |
| return fit::error(status); |
| } |
| |
| cycler->watcher_->WatchDevices( |
| fit::bind_member(cycler.get(), &CameraClient::WatchDevicesCallback)); |
| |
| return fit::ok(std::move(cycler)); |
| } |
| |
| void CameraClient::SetHandlers(CameraClient::AddCollectionHandler on_add_collection, |
| CameraClient::RemoveCollectionHandler on_remove_collection, |
| CameraClient::ShowBufferHandler on_show_buffer, |
| CameraClient::MuteStateHandler on_mute_changed) { |
| add_collection_handler_ = std::move(on_add_collection); |
| remove_collection_handler_ = std::move(on_remove_collection); |
| show_buffer_handler_ = std::move(on_show_buffer); |
| mute_state_handler_ = std::move(on_mute_changed); |
| } |
| |
| void CameraClient::WatchDevicesCallback(std::vector<fuchsia::camera3::WatchDevicesEvent> events) { |
| for (auto& event : events) { |
| if (event.is_added()) { |
| // Connect to device. |
| watcher_->ConnectToDevice(event.added(), device_.NewRequest()); |
| |
| // Watch for mute changes. |
| device_->WatchMuteState(fit::bind_member(this, &CameraClient::WatchMuteStateHandler)); |
| |
| // Fetch camera configurations |
| device_->GetConfigurations( |
| [this](std::vector<fuchsia::camera3::Configuration> configurations) { |
| configurations_ = std::move(configurations); |
| |
| if (list_configs_) { |
| DumpConfigs(); |
| exit(0); |
| } |
| |
| ZX_ASSERT(configurations_.size() > config_index_); |
| ZX_ASSERT(!configurations_[config_index_].streams.empty()); |
| device_->SetCurrentConfiguration(config_index_); |
| device_->WatchCurrentConfiguration( |
| [this](uint32_t index) { ConnectToStream(config_index_, stream_index_); }); |
| }); |
| } |
| } |
| |
| // Hanging get. |
| watcher_->WatchDevices(fit::bind_member(this, &CameraClient::WatchDevicesCallback)); |
| } |
| |
| void CameraClient::DumpConfigs() { |
| for (size_t i = 0; i < configurations_.size(); i++) { |
| auto& c = configurations_[i]; |
| std::cout << "Configuration " << i << std::endl; |
| for (size_t j = 0; j < c.streams.size(); j++) { |
| auto& s = c.streams[j]; |
| std::cout << "Stream " << j << std::endl; |
| std::cout << " " << s.image_format.display_width << "x" << s.image_format.display_height |
| << std::endl; |
| std::cout << " framerate " << s.frame_rate.numerator << "/" << s.frame_rate.denominator |
| << std::endl; |
| } |
| std::cout << std::endl; |
| } |
| } |
| |
| void CameraClient::WatchMuteStateHandler(bool software_muted, bool hardware_muted) { |
| mute_state_handler_(software_muted | hardware_muted); |
| device_->WatchMuteState(fit::bind_member(this, &CameraClient::WatchMuteStateHandler)); |
| } |
| |
| void CameraClient::ConnectToStream(uint32_t config_index, uint32_t stream_index) { |
| ZX_ASSERT(configurations_.size() > config_index); |
| ZX_ASSERT(configurations_[config_index].streams.size() > stream_index); |
| auto image_format = configurations_[config_index].streams[stream_index].image_format; |
| auto frame_rate = configurations_[config_index].streams[stream_index].frame_rate; |
| |
| // Connect to specific stream |
| StreamInfo new_stream_info; |
| stream_infos_.emplace(stream_index, std::move(new_stream_info)); |
| auto& stream = stream_infos_[stream_index].stream; |
| auto stream_request = stream.NewRequest(); |
| |
| // Allocate buffer collection |
| fuchsia::sysmem::BufferCollectionTokenHandle token_orig; |
| allocator_->AllocateSharedCollection(token_orig.NewRequest()); |
| stream->SetBufferCollection(std::move(token_orig)); |
| stream->WatchBufferCollection([this, image_format, stream_index, frame_rate, |
| &stream](fuchsia::sysmem::BufferCollectionTokenHandle token_back) { |
| if (add_collection_handler_) { |
| auto& stream_info = stream_infos_[stream_index]; |
| stream_info.add_collection_handler_returned_value = |
| add_collection_handler_(std::move(token_back), image_format, frame_rate); |
| } else { |
| token_back.BindSync()->Close(); |
| } |
| // Kick start the stream |
| stream->GetNextFrame([this](fuchsia::camera3::FrameInfo frame_info) { |
| OnNextFrame(stream_index_, std::move(frame_info)); |
| }); |
| }); |
| |
| device_->ConnectToStream(stream_index, std::move(stream_request)); |
| } |
| |
| void CameraClient::OnNextFrame(uint32_t stream_index, fuchsia::camera3::FrameInfo frame_info) { |
| if (show_buffer_handler_) { |
| auto& stream_info = stream_infos_[stream_index]; |
| show_buffer_handler_(stream_info.add_collection_handler_returned_value, frame_info.buffer_index, |
| std::move(frame_info.release_fence)); |
| } else { |
| frame_info.release_fence.reset(); |
| } |
| auto& stream = stream_infos_[stream_index].stream; |
| stream->GetNextFrame([this, stream_index](fuchsia::camera3::FrameInfo frame_info) { |
| OnNextFrame(stream_index, std::move(frame_info)); |
| }); |
| } |