/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2018, Google Inc. * * pipeline_handler.cpp - Pipeline handler infrastructure */ #include "libcamera/internal/pipeline_handler.h" #include #include #include #include #include #include #include #include #include "libcamera/internal/camera.h" #include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/framebuffer.h" #include "libcamera/internal/media_device.h" #include "libcamera/internal/request.h" #include "libcamera/internal/tracepoints.h" /** * \file pipeline_handler.h * \brief Create pipelines and cameras from a set of media devices * * Each pipeline supported by libcamera needs to be backed by a pipeline * handler implementation that operate on a set of media devices. The pipeline * handler is responsible for matching the media devices it requires with the * devices present in the system, and once all those devices can be acquired, * create corresponding Camera instances. * * Every subclass of PipelineHandler shall be registered with libcamera using * the REGISTER_PIPELINE_HANDLER() macro. */ using namespace std::chrono_literals; namespace libcamera { LOG_DEFINE_CATEGORY(Pipeline) /** * \class PipelineHandler * \brief Create and manage cameras based on a set of media devices * * The PipelineHandler matches the media devices provided by a DeviceEnumerator * with the pipelines it supports and creates corresponding Camera devices. * * Pipeline handler instances are reference-counted through std::shared_ptr<>. * They implement std::enable_shared_from_this<> in order to create new * std::shared_ptr<> in code paths originating from member functions of the * PipelineHandler class where only the 'this' pointer is available. */ /** * \brief Construct a PipelineHandler instance * \param[in] manager The camera manager * * In order to honour the std::enable_shared_from_this<> contract, * PipelineHandler instances shall never be constructed manually, but always * through the PipelineHandlerFactory::create() function implemented by the * respective factories. */ PipelineHandler::PipelineHandler(CameraManager *manager) : manager_(manager), lockOwner_(false) { } PipelineHandler::~PipelineHandler() { for (std::shared_ptr media : mediaDevices_) media->release(); } /** * \fn PipelineHandler::match(DeviceEnumerator *enumerator) * \brief Match media devices and create camera instances * \param[in] enumerator The enumerator providing all media devices found in the * system * * This function is the main entry point of the pipeline handler. It is called * by the camera manager with the \a enumerator passed as an argument. It shall * acquire from the \a enumerator all the media devices it needs for a single * pipeline, create one or multiple Camera instances and register them with the * camera manager. * * If all media devices needed by the pipeline handler are found, they must all * be acquired by a call to MediaDevice::acquire(). This function shall then * create the corresponding Camera instances, store them internally, and return * true. Otherwise it shall not acquire any media device (or shall release all * the media devices is has acquired by calling MediaDevice::release()) and * return false. * * If multiple instances of a pipeline are available in the system, the * PipelineHandler class will be instantiated once per instance, and its match() * function called for every instance. Each call shall acquire media devices for * one pipeline instance, until all compatible media devices are exhausted. * * If this function returns true, a new instance of the pipeline handler will * be created and its match() function called. * * \context This function is called from the CameraManager thread. * * \return true if media devices have been acquired and camera instances * created, or false otherwise */ /** * \brief Search and acquire a MediaDevice matching a device pattern * \param[in] enumerator Enumerator containing all media devices in the system * \param[in] dm Device match pattern * * Search the device \a enumerator for an available media device matching the * device match pattern \a dm. Matching media device that have previously been * acquired by MediaDevice::acquire() are not considered. If a match is found, * the media device is acquired and returned. The caller shall not release the * device explicitly, it will be automatically released when the pipeline * handler is destroyed. * * \context This function shall be called from the CameraManager thread. * * \return A pointer to the matching MediaDevice, or nullptr if no match is found */ MediaDevice *PipelineHandler::acquireMediaDevice(DeviceEnumerator *enumerator, const DeviceMatch &dm) { std::shared_ptr media = enumerator->search(dm); if (!media) return nullptr; if (!media->acquire()) return nullptr; mediaDevices_.push_back(media); return media.get(); } /** * \brief Lock all media devices acquired by the pipeline * * This function shall not be called from pipeline handler implementation, as * the Camera class handles locking directly. * * \context This function is \threadsafe. * * \return True if the devices could be locked, false otherwise * \sa unlock() * \sa MediaDevice::lock() */ bool PipelineHandler::lock() { MutexLocker locker(lock_); /* Do not allow nested locking in the same libcamera instance. */ if (lockOwner_) return false; for (std::shared_ptr &media : mediaDevices_) { if (!media->lock()) { unlock(); return false; } } lockOwner_ = true; return true; } /** * \brief Unlock all media devices acquired by the pipeline * * This function shall not be called from pipeline handler implementation, as * the Camera class handles locking directly. * * \context This function is \threadsafe. * * \sa lock() */ void PipelineHandler::unlock() { MutexLocker locker(lock_); if (!lockOwner_) return; for (std::shared_ptr &media : mediaDevices_) media->unlock(); lockOwner_ = false; } /** * \fn PipelineHandler::generateConfiguration() * \brief Generate a camera configuration for a specified camera * \param[in] camera The camera to generate a default configuration for * \param[in] roles A list of stream roles * * Generate a default configuration for the \a camera for a specified list of * stream roles. The caller shall populate the \a roles with the use-cases it * wishes to fetch the default configuration for. The returned configuration * can then be examined by the caller to learn about the selected streams and * their default parameters. * * The intended companion to this is \a configure() which can be used to change * the group of streams parameters. * * \context This function may be called from any thread and shall be * \threadsafe. It shall not modify the state of the \a camera in the pipeline * handler. * * \return A valid CameraConfiguration if the requested roles can be satisfied, * or a null pointer otherwise. The ownership of the returned configuration is * passed to the caller. */ /** * \fn PipelineHandler::configure() * \brief Configure a group of streams for capture * \param[in] camera The camera to configure * \param[in] config The camera configurations to setup * * Configure the specified group of streams for \a camera according to the * configuration specified in \a config. The intended caller of this interface * is the Camera class which will receive configuration to apply from the * application. * * The configuration is guaranteed to have been validated with * CameraConfiguration::validate(). The pipeline handler implementation shall * not perform further validation and may rely on any custom field stored in its * custom CameraConfiguration derived class. * * When configuring the camera the pipeline handler shall associate a Stream * instance to each StreamConfiguration entry in the CameraConfiguration using * the StreamConfiguration::setStream() function. * * \context This function is called from the CameraManager thread. * * \return 0 on success or a negative error code otherwise */ /** * \fn PipelineHandler::exportFrameBuffers() * \brief Allocate and export buffers for \a stream * \param[in] camera The camera * \param[in] stream The stream to allocate buffers for * \param[out] buffers Array of buffers successfully allocated * * This function allocates buffers for the \a stream from the devices associated * with the stream in the corresponding pipeline handler. Those buffers shall be * suitable to be added to a Request for the stream, and shall be mappable to * the CPU through their associated dmabufs with mmap(). * * The function may only be called after the Camera has been configured and * before it gets started, or after it gets stopped. It shall be called only for * streams that are part of the active camera configuration. * * The only intended caller is Camera::exportFrameBuffers(). * * \context This function is called from the CameraManager thread. * * \return The number of allocated buffers on success or a negative error code * otherwise */ /** * \fn PipelineHandler::start() * \brief Start capturing from a group of streams * \param[in] camera The camera to start * \param[in] controls Controls to be applied before starting the Camera * * Start the group of streams that have been configured for capture by * \a configure(). The intended caller of this function is the Camera class * which will in turn be called from the application to indicate that it has * configured the streams and is ready to capture. * * \context This fun/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2021, Google Inc. * * generic_camera_buffer.cpp - Generic Android frame buffer backend */ #include "../camera_buffer.h" #include <sys/mman.h> #include <unistd.h> #include <libcamera/base/log.h> #include "libcamera/internal/formats.h" #include "libcamera/internal/mapped_framebuffer.h" using namespace libcamera; LOG_DECLARE_CATEGORY(HAL) class CameraBuffer::Private : public Extensible::Private, public MappedBuffer { LIBCAMERA_DECLARE_PUBLIC(CameraBuffer) public: Private(CameraBuffer *cameraBuffer, buffer_handle_t camera3Buffer, PixelFormat pixelFormat, const Size &size, int flags); ~Private(); unsigned int numPlanes() const; Span<uint8_t> plane(unsigned int plane); unsigned int stride(unsigned int plane) const; unsigned int offset(unsigned int plane) const; unsigned int size(unsigned int plane) const; size_t jpegBufferSize(size_t maxJpegBufferSize) const; private: struct PlaneInfo { unsigned int stride; unsigned int offset; unsigned int size; }; void map(); int fd_; int flags_; off_t bufferLength_; bool mapped_; std::vector<PlaneInfo> planeInfo_; }; CameraBuffer::Private::Private([[maybe_unused]] CameraBuffer *cameraBuffer, buffer_handle_t camera3Buffer, PixelFormat pixelFormat, const Size &size, int flags) : fd_(-1), flags_(flags), bufferLength_(-1), mapped_(false) { error_ = 0; const auto &info = PixelFormatInfo::info(pixelFormat); if (!info.isValid()) { error_ = -EINVAL; LOG(HAL, Error) << "Invalid pixel format: " << pixelFormat; return; } /* * As Android doesn't offer an API to query buffer layouts, assume for * now that the buffer is backed by a single dmabuf, with planes being * stored contiguously. */ for (int i = 0; i < camera3Buffer->numFds; i++) { if (camera3Buffer->data[i] == -1 || camera3Buffer->data[i] == fd_) continue; if (fd_ != -1) { error_ = -EINVAL; LOG(HAL, Error) << "Discontiguous planes are not supported"; return; } fd_ = camera3Buffer->data[i]; } if (fd_ == -1) { error_ = -EINVAL; LOG(HAL, Error) << "No valid file descriptor"; return; } bufferLength_ = lseek(fd_, 0, SEEK_END); if (bufferLength_ < 0) { error_ = -errno; LOG(HAL, Error) << "Failed to get buffer length"; return; } const unsigned int numPlanes = info.numPlanes(); planeInfo_.resize(numPlanes); unsigned int offset = 0; for (unsigned int i = 0; i < numPlanes; ++i) { const unsigned int planeSize = info.planeSize(size, i); planeInfo_[i].stride = info.stride(size.width, i, 1u); planeInfo_[i].offset = offset; planeInfo_[i].size = planeSize; if (bufferLength_ < offset + planeSize) { LOG(HAL, Error) << "Plane " << i << " is out of buffer:" << " plane offset=" << offset << ", plane size=" << planeSize << ", buffer length=" << bufferLength_; return; } offset += planeSize; } } CameraBuffer::Private::~Private() { } unsigned int CameraBuffer::Private::numPlanes() const { return planeInfo_.size(); } Span<uint8_t> CameraBuffer::Private::plane(unsigned int plane) { if (!mapped_) map(); if (!mapped_) return {}; return planes_[plane]; } unsigned int CameraBuffer::Private::stride(unsigned int plane) const { if (plane >= planeInfo_.size()) return 0; return planeInfo_[plane].stride; } unsigned int CameraBuffer::Private::offset(unsigned int plane) const { if (plane >= planeInfo_.size()) return 0; return planeInfo_[plane].offset; } unsigned int CameraBuffer::Private::size(unsigned int plane) const { if (plane >= planeInfo_.size()) return 0; return planeInfo_[plane].size; } size_t CameraBuffer::Private::jpegBufferSize(size_t maxJpegBufferSize) const { ASSERT(bufferLength_ >= 0); return std::min<unsigned int>(bufferLength_, maxJpegBufferSize); } void CameraBuffer::Private::map() { ASSERT(fd_ != -1); ASSERT(bufferLength_ >= 0); void *address = mmap(nullptr, bufferLength_, flags_, MAP_SHARED, fd_, 0); if (address == MAP_FAILED) { error_ = -errno; LOG(HAL, Error) << "Failed to mmap plane"; return; } maps_.emplace_back(static_cast<uint8_t *>(address), bufferLength_); planes_.reserve(planeInfo_.size()); for (const auto &info : planeInfo_) { planes_.emplace_back( static_cast<uint8_t *>(address) + info.offset, info.size); } mapped_ = true; } PUBLIC_CAMERA_BUFFER_IMPLEMENTATION