| // 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 <ddk/debug.h> |
| #include "src/lib/fxl/logging.h" |
| |
| #include "garnet/drivers/usb_video/camera_control_impl.h" |
| #include "garnet/drivers/usb_video/usb-video-stream.h" |
| |
| namespace camera { |
| |
| void ControlImpl::OnFrameAvailable( |
| const fuchsia::camera::FrameAvailableEvent& frame) { |
| stream_->OnFrameAvailable(frame); |
| } |
| |
| ControlImpl::ControlImpl(video::usb::UsbVideoStream* usb_video_stream, |
| fidl::InterfaceRequest<Control> control, |
| async_dispatcher_t* dispatcher, |
| fit::closure on_connection_closed) |
| : binding_(this, std::move(control), dispatcher), |
| usb_video_stream_(usb_video_stream) { |
| binding_.set_error_handler( |
| [occ = std::move(on_connection_closed)](zx_status_t status) { occ(); }); |
| } |
| |
| void ControlImpl::GetFormats(uint32_t index, GetFormatsCallback callback) { |
| if (formats_->size() == 0) { |
| zx_status_t status = usb_video_stream_->GetFormats(formats_); |
| if (status != ZX_OK) { |
| callback(std::move(formats_), formats_->size(), status); |
| return; |
| } |
| } |
| |
| size_t min_index = std::max((size_t)0, std::min((size_t)index, formats_->size() - 1)); |
| size_t max_index = |
| std::min(min_index + fuchsia::camera::MAX_FORMATS_PER_RESPONSE - 1, |
| formats_->size() - 1); |
| |
| callback(std::vector<fuchsia::camera::VideoFormat>( |
| &(*formats_)[min_index], &(*formats_)[max_index + 1]), |
| formats_->size(), ZX_OK); |
| } |
| |
| void ControlImpl::GetDeviceInfo(GetDeviceInfoCallback callback) { |
| // This is just a conversion from the format internal to the device driver |
| // to the FIDL DeviceInfo struct. |
| const auto& usb_device_info = usb_video_stream_->GetDeviceInfo(); |
| fuchsia::camera::DeviceInfo camera_device_info; |
| camera_device_info.vendor_name = usb_device_info.manufacturer; |
| camera_device_info.vendor_id = usb_device_info.vendor_id; |
| camera_device_info.product_name = usb_device_info.product_name; |
| camera_device_info.product_id = usb_device_info.product_id; |
| camera_device_info.serial_number = usb_device_info.serial_number; |
| |
| // TODO(CAM-11): add more capabilities based on usb description |
| camera_device_info.output_capabilities = |
| fuchsia::camera::CAMERA_OUTPUT_STREAM; |
| camera_device_info.max_stream_count = 1; |
| callback(std::move(camera_device_info)); |
| } |
| |
| void ControlImpl::CreateStream( |
| fuchsia::sysmem::BufferCollectionInfo buffer_collection, |
| fuchsia::camera::FrameRate frame_rate, |
| fidl::InterfaceRequest<fuchsia::camera::Stream> stream, |
| zx::eventpair stream_token) { |
| zx_status_t status = |
| usb_video_stream_->CreateStream(std::move(buffer_collection), frame_rate); |
| |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "Failed to set format. Closing channel.\n"); |
| binding_.Unbind(); // Close the channel on error. |
| return; |
| } |
| |
| stream_ = std::make_unique<StreamImpl>(*this, std::move(stream), |
| std::move(stream_token)); |
| } |
| |
| void ControlImpl::StreamImpl::OnFrameAvailable( |
| const fuchsia::camera::FrameAvailableEvent& frame) { |
| binding_.events().OnFrameAvailable(frame); |
| } |
| |
| void ControlImpl::StreamImpl::Start() { |
| zx_status_t status = owner_.usb_video_stream_->StartStreaming(); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "Failed to start. Closing channel.\n"); |
| binding_.Unbind(); // Close the channel on error. |
| } |
| } |
| |
| void ControlImpl::StreamImpl::Stop() { |
| zx_status_t status = owner_.usb_video_stream_->StopStreaming(); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "Failed to stop. Closing channel.\n"); |
| binding_.Unbind(); // Close the channel on error. |
| } |
| } |
| |
| void ControlImpl::StreamImpl::ReleaseFrame(uint32_t buffer_index) { |
| zx_status_t status = owner_.usb_video_stream_->FrameRelease(buffer_index); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "Failed to release frame. Closing channel.\n"); |
| binding_.Unbind(); // Close the channel on error. |
| } |
| } |
| |
| void ControlImpl::ShutDownStream() { |
| // This has the effect of cancelling the wait, deleting the |
| // stream_token, and unbinding from the stream channel. |
| stream_ = nullptr; |
| } |
| |
| ControlImpl::StreamImpl::~StreamImpl() { |
| // Doesn't matter what this returns: |
| (void)owner_.usb_video_stream_->StopStreaming(); |
| } |
| |
| ControlImpl::StreamImpl::StreamImpl( |
| ControlImpl& owner, fidl::InterfaceRequest<fuchsia::camera::Stream> stream, |
| zx::eventpair stream_token) |
| : owner_(owner), |
| binding_(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_( |
| stream_token_.get(), ZX_EVENTPAIR_PEER_CLOSED, std::bind([this]() { |
| zxlogf( |
| INFO, |
| "ControlImpl::StreamImpl::StreamImpl - " |
| "ZX_EVENTPAIR_PEER_CLOSED received, shutting down stream.\n"); |
| // If the peer is closed, shut down the whole |
| // stream. |
| owner_.ShutDownStream(); |
| // We just deleted ourselves. Don't do anything |
| // else. |
| })) { |
| 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. |
| FXL_CHECK(status == ZX_OK); |
| binding_.set_error_handler([this](zx_status_t status) { |
| owner_.usb_video_stream_->DeactivateVideoBuffer(); |
| }); |
| } |
| |
| } // namespace camera |