blob: ede6784eaaf2e6d015e61f3f525da65f1721154a [file] [log] [blame]
// Copyright 2016 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 "garnet/bin/media/camera_manager/camera_manager_impl.h"
#include <string>
#include <ddk/debug.h>
#include <ddk/driver.h>
#include <fbl/algorithm.h>
namespace camera {
static const char* kCameraDevicePath = "/dev/class/camera";
CameraManagerImpl::CameraManagerImpl(async::Loop* loop) {
// Step #1: Begin monitoring for plug/unplug events for pluggable cameras
bool idle_seen = false;
device_watcher_ = fsl::DeviceWatcher::CreateWithIdleCallback(
kCameraDevicePath,
// Function to be called when adding a camera:
fbl::BindMember(this, &CameraManagerImpl::OnDeviceAdded),
// Function to be called when all existing devices have been found:
// This function captures the local variable idle_seen
// because it will only be called while we are in this constructor.
[&idle_seen] { idle_seen = true; });
if (device_watcher_ == nullptr) {
FXL_LOG(ERROR) << " failed to create DeviceWatcher.";
return;
}
// Now we need to wait until:
// #1 we have received the IDLE notification, indicating that all the devices
// that were plugged in have been added, and
// #2 all of those devices have finished gathering information from their
// drivers.
// We do this by spinning the async loop one event at a time until the devices
// all report in. VideoDeviceClients are guaranteed to either gather all the
// necessary information, or time out (or error out), so we know that this
// loop will finish.
zx_status_t status;
while (!idle_seen || !inactive_devices_.empty()) {
status = loop->Run(zx::time::infinite(), true);
if (status != ZX_OK) {
return;
}
}
// Now bind our context, and publish the public service:
// Note that CreateFromStartupInfo must be called after we do our looping
// above, or the request for the CameraManager may fail.
// TODO(CAM-18): Change the interface to encourage dynamic detections by the
// application, and we won't have to do this.
context_ = component::StartupContext::CreateFromStartupInfo();
context_->outgoing().AddPublicService(bindings_.GetHandler(this));
}
// The dispatcher loop should be shut down when this destructor is called.
// No further messages should be handled after this destructor is called.
CameraManagerImpl::~CameraManagerImpl() {
// Stop monitoring plug/unplug events. We are shutting down and
// no longer care about devices coming and going.
device_watcher_ = nullptr;
// In case we just discovered any new devices:
inactive_devices_.clear();
// Shut down each currently active device in the system.
active_devices_.clear();
}
void CameraManagerImpl::OnDeviceAdded(int dir_fd, std::string filename) {
auto video_device = VideoDeviceClient::Create(dir_fd, filename);
if (video_device) {
// Initially add the device to the inactive devices until we get all
// the information we need from it.
inactive_devices_.push_back(std::move(video_device));
inactive_devices_.back()->Startup(
[this, id = inactive_devices_.back()->id()](zx_status_t status) {
OnDeviceStartupComplete(id, status);
});
} else {
// If we can't create the device, drop it.
FXL_LOG(ERROR) << "Failed to create device " << filename;
}
}
void CameraManagerImpl::OnDeviceStartupComplete(uint64_t camera_id,
zx_status_t status) {
for (auto iter = inactive_devices_.begin(); iter != inactive_devices_.end();
iter++) {
if ((*iter)->id() == camera_id) {
// Now that we found the device, either put it in the active list,
// or shut it down, depending on the status.
if (status == ZX_OK) {
// We put the newly active device in the front of active_devices_,
// but it doesn't really matter what order the devices are stored in.
active_devices_.splice(active_devices_.begin(), inactive_devices_,
iter);
} else {
inactive_devices_.erase(iter);
}
break;
}
}
}
void CameraManagerImpl::GetDevices(GetDevicesCallback callback) {
fidl::VectorPtr<fuchsia::camera::DeviceInfo> device_descriptions;
for (auto& device : active_devices_) {
device_descriptions.push_back(device->GetDeviceInfo());
}
callback(std::move(device_descriptions));
}
void CameraManagerImpl::GetFormats(uint64_t camera_id,
GetFormatsCallback callback) {
fidl::VectorPtr<fuchsia::camera::VideoFormat> formats;
for (auto& device : active_devices_) {
if (device->id() == camera_id) {
formats = device->GetFormats();
break;
}
}
callback(std::move(formats));
}
void CameraManagerImpl::CreateStream(
fuchsia::camera::CameraAccessRequest request,
fuchsia::sysmem::BufferCollectionInfo buffer_collection,
fidl::InterfaceRequest<fuchsia::camera::Stream> stream) {
for (auto& device : active_devices_) {
if (device->id() == request.camera_id) {
device->CreateStream(std::move(buffer_collection), request.format.rate,
std::move(stream));
break;
}
}
}
} // namespace camera