blob: fddf7349ec153f93bf18902e0eba9dea1992eb10 [file] [log] [blame]
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2018, Google Inc.
*
* camera_manager.h - Camera management
*/
#include <libcamera/camera_manager.h>
#include <condition_variable>
#include <map>
#include <libcamera/camera.h>
#include <libcamera/event_dispatcher.h>
#include "device_enumerator.h"
#include "event_dispatcher_poll.h"
#include "log.h"
#include "pipeline_handler.h"
#include "thread.h"
#include "utils.h"
/**
* \file camera_manager.h
* \brief The camera manager
*/
namespace libcamera {
LOG_DEFINE_CATEGORY(Camera)
class CameraManager::Private : public Thread
{
public:
Private(CameraManager *cm);
int start();
void addCamera(std::shared_ptr<Camera> &camera, dev_t devnum);
void removeCamera(Camera *camera);
/*
* This mutex protects
*
* - initialized_ and status_ during initialization
* - cameras_ and camerasByDevnum_ after initialization
*/
Mutex mutex_;
std::vector<std::shared_ptr<Camera>> cameras_;
std::map<dev_t, std::weak_ptr<Camera>> camerasByDevnum_;
protected:
void run() override;
private:
int init();
void cleanup();
CameraManager *cm_;
std::condition_variable cv_;
bool initialized_;
int status_;
std::vector<std::shared_ptr<PipelineHandler>> pipes_;
std::unique_ptr<DeviceEnumerator> enumerator_;
};
CameraManager::Private::Private(CameraManager *cm)
: cm_(cm), initialized_(false)
{
}
int CameraManager::Private::start()
{
int status;
/* Start the thread and wait for initialization to complete. */
Thread::start();
{
MutexLocker locker(mutex_);
cv_.wait(locker, [&] { return initialized_; });
status = status_;
}
/* If a failure happened during initialization, stop the thread. */
if (status < 0) {
exit();
wait();
return status;
}
return 0;
}
void CameraManager::Private::run()
{
LOG(Camera, Debug) << "Starting camera manager";
int ret = init();
mutex_.lock();
status_ = ret;
initialized_ = true;
mutex_.unlock();
cv_.notify_one();
if (ret < 0)
return;
/* Now start processing events and messages. */
exec();
cleanup();
}
int CameraManager::Private::init()
{
enumerator_ = DeviceEnumerator::create();
if (!enumerator_ || enumerator_->enumerate())
return -ENODEV;
/*
* TODO: Try to read handlers and order from configuration
* file and only fallback on all handlers if there is no
* configuration file.
*/
std::vector<PipelineHandlerFactory *> &factories = PipelineHandlerFactory::factories();
for (PipelineHandlerFactory *factory : factories) {
/*
* Try each pipeline handler until it exhaust
* all pipelines it can provide.
*/
while (1) {
std::shared_ptr<PipelineHandler> pipe = factory->create(cm_);
if (!pipe->match(enumerator_.get()))
break;
LOG(Camera, Debug)
<< "Pipeline handler \"" << factory->name()
<< "\" matched";
pipes_.push_back(std::move(pipe));
}
}
/* TODO: register hot-plug callback here */
return 0;
}
void CameraManager::Private::cleanup()
{
/* TODO: unregister hot-plug callback here */
/*
* Release all references to cameras and pipeline handlers to ensure
* they all get destroyed before the device enumerator deletes the
* media devices.
*/
pipes_.clear();
cameras_.clear();
enumerator_.reset(nullptr);
}
void CameraManager::Private::addCamera(std::shared_ptr<Camera> &camera,
dev_t devnum)
{
MutexLocker locker(mutex_);
for (std::shared_ptr<Camera> c : cameras_) {
if (c->name() == camera->name()) {
LOG(Camera, Warning)
<< "Registering camera with duplicate name '"
<< camera->name() << "'";
break;
}
}
cameras_.push_back(std::move(camera));
if (devnum) {
unsigned int index = cameras_.size() - 1;
camerasByDevnum_[devnum] = cameras_[index];
}
}
void CameraManager::Private::removeCamera(Camera *camera)
{
MutexLocker locker(mutex_);
auto iter = std::find_if(cameras_.begin(), cameras_.end(),
[camera](std::shared_ptr<Camera> &c) {
return c.get() == camera;
});
if (iter == cameras_.end())
return;
LOG(Camera, Debug)
<< "Unregistering camera '" << camera->name() << "'";
auto iter_d = std::find_if(camerasByDevnum_.begin(), camerasByDevnum_.end(),
[camera](const std::pair<dev_t, std::weak_ptr<Camera>> &p) {
return p.second.lock().get() == camera;
});
if (iter_d != camerasByDevnum_.end())
camerasByDevnum_.erase(iter_d);
cameras_.erase(iter);
}
/**
* \class CameraManager
* \brief Provide access and manage all cameras in the system
*
* The camera manager is the entry point to libcamera. It enumerates devices,
* associates them with pipeline managers, and provides access to the cameras
* in the system to applications. The manager owns all Camera objects and
* handles hot-plugging and hot-unplugging to manage the lifetime of cameras.
*
* To interact with libcamera, an application starts by creating a camera
* manager instance. Only a single instance of the camera manager may exist at
* a time. Attempting to create a second instance without first deleting the
* existing instance results in undefined behaviour.
*
* The manager is initially stopped, and shall be configured before being
* started. In particular a custom event dispatcher shall be installed if
* needed with CameraManager::setEventDispatcher().
*
* Once the camera manager is configured, it shall be started with start().
* This will enumerate all the cameras present in the system, which can then be
* listed with list() and retrieved with get().
*
* Cameras are shared through std::shared_ptr<>, ensuring that a camera will
* stay valid until the last reference is released without requiring any special
* action from the application. Once the application has released all the
* references it held to cameras, the camera manager can be stopped with
* stop().
*
* \todo Add interface to register a notification callback to the user to be
* able to inform it new cameras have been hot-plugged or cameras have been
* removed due to hot-unplug.
*/
CameraManager *CameraManager::self_ = nullptr;
CameraManager::CameraManager()
: p_(new CameraManager::Private(this))
{
if (self_)
LOG(Camera, Fatal)
<< "Multiple CameraManager objects are not allowed";
self_ = this;
}
CameraManager::~CameraManager()
{
stop();
self_ = nullptr;
}
/**
* \brief Start the camera manager
*
* Start the camera manager and enumerate all devices in the system. Once
* the start has been confirmed the user is free to list and otherwise
* interact with cameras in the system until either the camera manager
* is stopped or the camera is unplugged from the system.
*
* \return 0 on success or a negative error code otherwise
*/
int CameraManager::start()
{
LOG(Camera, Info) << "libcamera " << version_;
int ret = p_->start();
if (ret)
LOG(Camera, Error) << "Failed to start camera manager: "
<< strerror(-ret);
return ret;
}
/**
* \brief Stop the camera manager
*
* Before stopping the camera manager the caller is responsible for making
* sure all cameras provided by the manager are returned to the manager.
*
* After the manager has been stopped no resource provided by the camera
* manager should be consider valid or functional even if they for one
* reason or another have yet to be deleted.
*/
void CameraManager::stop()
{
p_->exit();
p_->wait();
}
/**
* \fn CameraManager::cameras()
* \brief Retrieve all available cameras
*
* Before calling this function the caller is responsible for ensuring that
* the camera manager is running.
*
* \context This function is \threadsafe.
*
* \return List of all available cameras
*/
std::vector<std::shared_ptr<Camera>> CameraManager::cameras() const
{
MutexLocker locker(p_->mutex_);
return p_->cameras_;
}
/**
* \brief Get a camera based on name
* \param[in] name Name of camera to get
*
* Before calling this function the caller is responsible for ensuring that
* the camera manager is running.
*
* \context This function is \threadsafe.
*
* \return Shared pointer to Camera object or nullptr if camera not found
*/
std::shared_ptr<Camera> CameraManager::get(const std::string &name)
{
MutexLocker locker(p_->mutex_);
for (std::shared_ptr<Camera> camera : p_->cameras_) {
if (camera->name() == name)
return camera;
}
return nullptr;
}
/**
* \brief Retrieve a camera based on device number
* \param[in] devnum Device number of camera to get
*
* This method is meant solely for the use of the V4L2 compatibility
* layer, to map device nodes to Camera instances. Applications shall
* not use it and shall instead retrieve cameras by name.
*
* Before calling this function the caller is responsible for ensuring that
* the camera manager is running.
*
* \context This function is \threadsafe.
*
* \return Shared pointer to Camera object, which is empty if the camera is
* not found
*/
std::shared_ptr<Camera> CameraManager::get(dev_t devnum)
{
MutexLocker locker(p_->mutex_);
auto iter = p_->camerasByDevnum_.find(devnum);
if (iter == p_->camerasByDevnum_.end())
return nullptr;
return iter->second.lock();
}
/**
* \brief Add a camera to the camera manager
* \param[in] camera The camera to be added
* \param[in] devnum The device number to associate with \a camera
*
* This function is called by pipeline handlers to register the cameras they
* handle with the camera manager. Registered cameras are immediately made
* available to the system.
*
* \a devnum is used by the V4L2 compatibility layer to map V4L2 device nodes
* to Camera instances.
*
* \context This function shall be called from the CameraManager thread.
*/
void CameraManager::addCamera(std::shared_ptr<Camera> camera, dev_t devnum)
{
ASSERT(Thread::current() == p_.get());
p_->addCamera(camera, devnum);
}
/**
* \brief Remove a camera from the camera manager
* \param[in] camera The camera to be removed
*
* This function is called by pipeline handlers to unregister cameras from the
* camera manager. Unregistered cameras won't be reported anymore by the
* cameras() and get() calls, but references may still exist in applications.
*
* \context This function shall be called from the CameraManager thread.
*/
void CameraManager::removeCamera(Camera *camera)
{
ASSERT(Thread::current() == p_.get());
p_->removeCamera(camera);
}
/**
* \fn const std::string &CameraManager::version()
* \brief Retrieve the libcamera version string
* \context This function is \a threadsafe.
* \return The libcamera version string
*/
/**
* \brief Set the event dispatcher
* \param[in] dispatcher Pointer to the event dispatcher
*
* libcamera requires an event dispatcher to integrate event notification and
* timers with the application event loop. Applications that want to provide
* their own event dispatcher shall call this function once and only once before
* the camera manager is started with start(). If no event dispatcher is
* provided, a default poll-based implementation will be used.
*
* The CameraManager takes ownership of the event dispatcher and will delete it
* when the application terminates.
*/
void CameraManager::setEventDispatcher(std::unique_ptr<EventDispatcher> dispatcher)
{
thread()->setEventDispatcher(std::move(dispatcher));
}
/**
* \brief Retrieve the event dispatcher
*
* This function retrieves the event dispatcher set with setEventDispatcher().
* If no dispatcher has been set, a default poll-based implementation is created
* and returned, and no custom event dispatcher may be installed anymore.
*
* The returned event dispatcher is valid until the camera manager is destroyed.
*
* \return Pointer to the event dispatcher
*/
EventDispatcher *CameraManager::eventDispatcher()
{
return thread()->eventDispatcher();
}
} /* namespace libcamera */