blob: 286a2ea14a462c6207cb662b1976b562b0077553 [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 "src/camera/camera_manager2/camera_manager_app.h"
#include <fcntl.h>
#include <fuchsia/hardware/camera/c/fidl.h>
#include <lib/fdio/directory.h>
#include <string>
#include <ddk/debug.h>
#include <ddk/driver.h>
#include <fbl/auto_call.h>
#include <fbl/function.h>
#include "src/lib/syslog/cpp/logger.h"
namespace camera {
std::unique_ptr<CameraManagerApp> CameraManagerApp::Create(
std::unique_ptr<sys::ComponentContext> context) {
FX_LOGS(INFO) << "Starting";
auto camera_manager = std::make_unique<CameraManagerApp>(std::move(context));
zx_status_t status =
camera_manager->context_->svc()->Connect(camera_manager->sysmem_allocator_.NewRequest());
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to connect to sysmem service";
return nullptr;
}
// Begin monitoring for plug/unplug events for pluggable cameras.
status = camera_manager->plug_detector_.Start(
fbl::BindMember(camera_manager.get(), &CameraManagerApp::OnDeviceFound));
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to start plug_detector";
return nullptr;
}
camera_manager->context_->outgoing()->AddPublicService<fuchsia::camera2::Manager>(
camera_manager->bindings_.GetHandler(camera_manager.get()));
return camera_manager;
}
// The dispatcher loop should be shut down when this destructor is called.
// No further messages should be handled after this destructor is called.
CameraManagerApp::~CameraManagerApp() {
// Stop monitoring plug/unplug events. We are shutting down and
// no longer care about devices coming and going.
plug_detector_.Stop();
}
void CameraManagerApp::OnDeviceFound(
fidl::InterfaceHandle<fuchsia::camera2::hal::Controller> controller) {
auto device = VideoDeviceClient::Create(std::move(controller));
if (!device) {
FX_LOGS(ERROR) << "Failed to create device";
return;
}
// The device is fully set up at this point. The camera manager now needs to
// assign a camera_id.
// TODO(41683): If a device's info marks it as a previously known device,
// it should be merged with that device instance. That will allow the device
// to maintain its id, mute status, and any other properties that must be retained
// across boot.
device->set_id(device_id_counter_++);
devices_.push_back(std::move(device));
}
VideoDeviceClient *CameraManagerApp::GetActiveDevice(int32_t camera_id) {
for (auto &device : devices_) {
if (device->id() == camera_id) {
return device.get();
}
}
return nullptr;
}
// Currently, this function handles all the connections itself, and only
// connects to the first stream and format of the device.
void CameraManagerApp::ConnectToStream(
int32_t camera_id, fuchsia::camera2::StreamConstraints constraints,
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token,
fidl::InterfaceRequest<fuchsia::camera2::Stream> client_request,
fuchsia::camera2::Manager::ConnectToStreamCallback callback) {
auto cleanup = fbl::MakeAutoCall([&callback]() { callback({}); });
// 1: Check that the camera exists:
auto device = GetActiveDevice(camera_id);
if (!device) {
return;
}
uint32_t config_index = 0;
uint32_t stream_type = 0;
uint32_t image_format_index = constraints.has_format_index() ? constraints.format_index() : 0;
// 2: Check constraints against the configs that the device offers. If incompatible, fail.
// 3: Pick a config, stream and image_format_index
zx_status_t status = device->MatchConstraints(constraints, &config_index, &stream_type);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to match constraints";
return;
}
FX_LOGS(INFO) << "Picked config " << config_index << " stream index: " << stream_type
<< " format index: " << image_format_index;
// Get configs from the device:
auto &out_configs = device->configs();
ZX_ASSERT(out_configs.size() > config_index);
ZX_ASSERT(out_configs[config_index].stream_configs.size() > stream_type);
auto &stream_config = out_configs[config_index].stream_configs[stream_type];
// 4: Now check if the stream is currently being used. If it is, we could:
// - A) Close the other stream
// - B) Have two clients of one stream
// - C) Choose another compatible stream
// - D) Refuse this request.
// For now, we will do:
// E) Don't even check
// TODO(36260): Check if other streams are active, and apply some policy.
// 5: Allocate the buffer collection. The constraints from the device must be applied, as well as
// constraints for all the image formats being offered. These should be checked at some point by
// the camera manager.
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollection> sysmem_collection;
status =
sysmem_allocator_->BindSharedCollection(std::move(token), sysmem_collection.NewRequest());
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "BindSharedCollection failed";
return;
}
// Now connect the client directly to the device
status = device->CreateStream(config_index, stream_type, image_format_index,
std::move(sysmem_collection), std::move(client_request));
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "CreateStream failed";
return;
}
// Return the image format that was selected.
callback(stream_config.image_formats[image_format_index]);
cleanup.cancel();
}
} // namespace camera