| /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
| /* | |
| * Copyright (C) 2018, Google Inc. | |
| * | |
| * camera_manager.h - Camera management | |
| */ | |
| #include <dlfcn.h> | |
| #include <string.h> | |
| #include <dirent.h> | |
| #include "libcamera/internal/camera_manager.h" | |
| #include <libcamera/base/log.h> | |
| #include <libcamera/base/utils.h> | |
| #include <libcamera/camera.h> | |
| #include <libcamera/property_ids.h> | |
| #include "libcamera/internal/camera.h" | |
| #include "libcamera/internal/device_enumerator.h" | |
| #include "libcamera/internal/pipeline_handler.h" | |
| /** | |
| * \file libcamera/camera_manager.h | |
| * \brief The camera manager | |
| */ | |
| /** | |
| * \file libcamera/internal/camera_manager.h | |
| * \brief Internal camera manager support | |
| */ | |
| /** | |
| * \brief Top-level libcamera namespace | |
| */ | |
| namespace libcamera { | |
| LOG_DEFINE_CATEGORY(Camera) | |
| CameraManager::Private::Private() | |
| : 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, [&]() LIBCAMERA_TSA_REQUIRES(mutex_) { | |
| 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; | |
| loadPipelineHandlerFromLibrary(); | |
| createPipelineHandlers(); | |
| enumerator_->devicesAdded.connect(this, &Private::createPipelineHandlers); | |
| return 0; | |
| } | |
| void CameraManager::Private::loadPipelineHandlerFromLibrary() { | |
| LOG(Camera, Info) << "pipeline handler loader"; | |
| std::string libpath = PIPELINE_PROXY_DIR; | |
| std::vector<std::string> libfiles; | |
| DIR *dir = opendir(libpath.c_str()); | |
| if (dir) { | |
| struct dirent *ent; | |
| while ((ent = readdir(dir)) != nullptr) { | |
| if (ent->d_type == DT_DIR) | |
| continue; | |
| if (strlen(ent->d_name) <= 3) | |
| continue; | |
| size_t offset = strlen(ent->d_name) - 3; | |
| if (strcmp(&ent->d_name[offset], ".so")) | |
| continue; | |
| libfiles.push_back(libpath + "/" + ent->d_name); | |
| LOG(Camera, Info) << ent->d_name << " is found"; | |
| } | |
| closedir(dir); | |
| } | |
| for (const std::string &libfile : libfiles) { | |
| void* dlHandler = dlopen(libfile.c_str(), RTLD_LAZY | RTLD_GLOBAL); | |
| if (dlHandler) { | |
| LOG(Camera, Info) << "dlopen " << libfile.c_str() << " successfully"; | |
| handleVector_.push_back(dlHandler); | |
| } | |
| } | |
| } | |
| void CameraManager::Private::closeLibraryHandles() { | |
| for (const auto handle : handleVector_) { | |
| dlclose(handle); | |
| } | |
| handleVector_.clear(); | |
| } | |
| void CameraManager::Private::createPipelineHandlers() | |
| { | |
| CameraManager *const o = LIBCAMERA_O_PTR(); | |
| /* | |
| * \todo Try to read handlers and order from configuration | |
| * file and only fallback on all handlers if there is no | |
| * configuration file. | |
| */ | |
| const std::vector<PipelineHandlerFactoryBase *> &factories = | |
| PipelineHandlerFactoryBase::factories(); | |
| for (const PipelineHandlerFactoryBase *factory : factories) { | |
| LOG(Camera, Debug) | |
| << "Found registered pipeline handler '" | |
| << factory->name() << "'"; | |
| /* | |
| * Try each pipeline handler until it exhaust | |
| * all pipelines it can provide. | |
| */ | |
| while (1) { | |
| std::shared_ptr<PipelineHandler> pipe = factory->create(o); | |
| if (!pipe->match(enumerator_.get())) | |
| break; | |
| LOG(Camera, Debug) | |
| << "Pipeline handler \"" << factory->name() | |
| << "\" matched"; | |
| } | |
| } | |
| } | |
| void CameraManager::Private::cleanup() | |
| { | |
| enumerator_->devicesAdded.disconnect(this); | |
| /* | |
| * Release all references to cameras to ensure they all get destroyed | |
| * before the device enumerator deletes the media devices. Cameras are | |
| * destroyed via Object::deleteLater() API, hence we need to explicitly | |
| * process deletion requests from the thread's message queue as the event | |
| * loop is not in action here. | |
| */ | |
| { | |
| MutexLocker locker(mutex_); | |
| cameras_.clear(); | |
| } | |
| dispatchMessages(Message::Type::DeferredDelete); | |
| enumerator_.reset(nullptr); | |
| closeLibraryHandles(); | |
| } | |
| /** | |
| * \brief Add a camera to the camera manager | |
| * \param[in] camera The camera to be added | |
| * | |
| * 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. | |
| * | |
| * Device numbers from the SystemDevices property are 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::Private::addCamera(std::shared_ptr<Camera> camera) | |
| { | |
| ASSERT(Thread::current() == this); | |
| MutexLocker locker(mutex_); | |
| for (const std::shared_ptr<Camera> &c : cameras_) { | |
| if (c->id() == camera->id()) { | |
| LOG(Camera, Fatal) | |
| << "Trying to register a camera with a duplicated ID '" | |
| << camera->id() << "'"; | |
| return; | |
| } | |
| } | |
| cameras_.push_back(std::move(camera)); | |
| unsigned int index = cameras_.size() - 1; | |
| /* Report the addition to the public signal */ | |
| CameraManager *const o = LIBCAMERA_O_PTR(); | |
| o->cameraAdded.emit(cameras_[index]); | |
| } | |
| /** | |
| * \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::Private::removeCamera(std::shared_ptr<Camera> camera) | |
| { | |
| ASSERT(Thread::current() == this); | |
| MutexLocker locker(mutex_); | |
| auto iter = std::find_if(cameras_.begin(), cameras_.end(), | |
| [camera](std::shared_ptr<Camera> &c) { | |
| return c.get() == camera.get(); | |
| }); | |
| if (iter == cameras_.end()) | |
| return; | |
| LOG(Camera, Debug) | |
| << "Unregistering camera '" << camera->id() << "'"; | |
| cameras_.erase(iter); | |
| /* Report the removal to the public signal */ | |
| CameraManager *const o = LIBCAMERA_O_PTR(); | |
| o->cameraRemoved.emit(camera); | |
| } | |
| /** | |
| * \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 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(). | |
| */ | |
| CameraManager *CameraManager::self_ = nullptr; | |
| CameraManager::CameraManager() | |
| : Extensible(std::make_unique<CameraManager::Private>()) | |
| { | |
| if (self_) | |
| LOG(Camera, Fatal) | |
| << "Multiple CameraManager objects are not allowed"; | |
| self_ = this; | |
| } | |
| /** | |
| * \brief Destroy the camera manager | |
| * | |
| * Destroying the camera manager stops it if it is currently running. | |
| */ | |
| 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 = _d()->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() | |
| { | |
| Private *const d = _d(); | |
| d->exit(); | |
| d->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 | |
| { | |
| const Private *const d = _d(); | |
| MutexLocker locker(d->mutex_); | |
| return d->cameras_; | |
| } | |
| /** | |
| * \brief Get a camera based on ID | |
| * \param[in] id ID 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 &id) | |
| { | |
| Private *const d = _d(); | |
| MutexLocker locker(d->mutex_); | |
| for (std::shared_ptr<Camera> camera : d->cameras_) { | |
| if (camera->id() == id) | |
| return camera; | |
| } | |
| return nullptr; | |
| } | |
| /** | |
| * \var CameraManager::cameraAdded | |
| * \brief Notify of a new camera added to the system | |
| * | |
| * This signal is emitted when a new camera is detected and successfully handled | |
| * by the camera manager. The notification occurs alike for cameras detected | |
| * when the manager is started with start() or when new cameras are later | |
| * connected to the system. When the signal is emitted the new camera is already | |
| * available from the list of cameras(). | |
| * | |
| * The signal is emitted from the CameraManager thread. Applications shall | |
| * minimize the time spent in the signal handler and shall in particular not | |
| * perform any blocking operation. | |
| */ | |
| /** | |
| * \var CameraManager::cameraRemoved | |
| * \brief Notify of a new camera removed from the system | |
| * | |
| * This signal is emitted when a camera is removed from the system. When the | |
| * signal is emitted the camera is not available from the list of cameras() | |
| * anymore. | |
| * | |
| * The signal is emitted from the CameraManager thread. Applications shall | |
| * minimize the time spent in the signal handler and shall in particular not | |
| * perform any blocking operation. | |
| */ | |
| /** | |
| * \fn const std::string &CameraManager::version() | |
| * \brief Retrieve the libcamera version string | |
| * \context This function is \a threadsafe. | |
| * \return The libcamera version string | |
| */ | |
| } /* namespace libcamera */ |