From 7645083c720f419d6188b40fa94af95146c346cb Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 10 Apr 2019 01:18:49 +0300 Subject: libcamera: camera_sensor: Add a new class to model a camera sensor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CameraSensor class abstracts camera sensors and provides helper functions to ease interactions with them. It is currently limited to sensors that expose a single subdev, and offer the same frame sizes for all media bus codes, but will be extended to support more complex sensors as the needs arise. Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Reviewed-by: Jacopo Mondi --- src/libcamera/camera_sensor.cpp | 258 ++++++++++++++++++++++++++++++++++ src/libcamera/include/camera_sensor.h | 56 ++++++++ src/libcamera/meson.build | 2 + 3 files changed, 316 insertions(+) create mode 100644 src/libcamera/camera_sensor.cpp create mode 100644 src/libcamera/include/camera_sensor.h diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp new file mode 100644 index 00000000..ca4dd0c9 --- /dev/null +++ b/src/libcamera/camera_sensor.cpp @@ -0,0 +1,258 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * camera_sensor.cpp - A camera sensor + */ + +#include +#include +#include +#include +#include + +#include "camera_sensor.h" +#include "formats.h" +#include "v4l2_subdevice.h" + +/** + * \file camera_sensor.h + * \brief A camera sensor + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(CameraSensor); + +/** + * \class CameraSensor + * \brief A camera sensor based on V4L2 subdevices + * + * The CameraSensor class eases handling of sensors for pipeline handlers by + * hiding the details of the V4L2 subdevice kernel API and caching sensor + * information. + * + * The implementation is currently limited to sensors that expose a single V4L2 + * subdevice with a single pad, and support the same frame sizes for all + * supported media bus codes. It will be extended to support more complex + * devices as the needs arise. + */ + +/** + * \brief Construct a CameraSensor + * \param[in] entity The media entity backing the camera sensor + * + * Once constructed the instance must be initialized with init(). + */ +CameraSensor::CameraSensor(const MediaEntity *entity) + : entity_(entity) +{ + subdev_ = new V4L2Subdevice(entity); +} + +/** + * \brief Destroy a CameraSensor + */ +CameraSensor::~CameraSensor() +{ + delete subdev_; +} + +/** + * \brief Initialize the camera sensor instance + * + * This method performs the initialisation steps of the CameraSensor that may + * fail. It shall be called once and only once after constructing the instance. + * + * \return 0 on success or a negative error code otherwise + */ +int CameraSensor::init() +{ + int ret; + + if (entity_->pads().size() != 1) { + LOG(CameraSensor, Error) + << "Sensors with more than one pad are not supported"; + return -EINVAL; + } + + if (entity_->function() != MEDIA_ENT_F_CAM_SENSOR) { + LOG(CameraSensor, Error) + << "Invalid sensor function 0x" + << std::hex << std::setfill('0') << std::setw(8) + << entity_->function(); + return -EINVAL; + } + + ret = subdev_->open(); + if (ret < 0) + return ret; + + /* Enumerate and cache media bus codes and sizes. */ + const FormatEnum formats = subdev_->formats(0); + if (formats.empty()) { + LOG(CameraSensor, Error) << "No image format found"; + return -EINVAL; + } + + std::transform(formats.begin(), formats.end(), + std::back_inserter(mbusCodes_), + [](decltype(*formats.begin()) f) { return f.first; }); + + /* + * Extract the supported sizes from the first format as we only support + * sensors that offer the same frame sizes for all media bus codes. + * Verify this assumption and reject the sensor if it isn't true. + */ + const std::vector &sizes = formats.begin()->second; + std::transform(sizes.begin(), sizes.end(), std::back_inserter(sizes_), + [](const SizeRange &range) { return range.max; }); + + for (auto it = ++formats.begin(); it != formats.end(); ++it) { + if (it->second != sizes) { + LOG(CameraSensor, Error) + << "Frame sizes differ between media bus codes"; + return -EINVAL; + } + } + + /* Sort the media bus codes and sizes. */ + std::sort(mbusCodes_.begin(), mbusCodes_.end()); + std::sort(sizes_.begin(), sizes_.end()); + + return 0; +} + +/** + * \fn CameraSensor::entity() + * \brief Retrieve the sensor media entity + * \return The sensor media entity + */ + +/** + * \fn CameraSensor::mbusCodes() + * \brief Retrieve the media bus codes supported by the camera sensor + * \return The supported media bus codes sorted in increasing order + */ + +/** + * \fn CameraSensor::sizes() + * \brief Retrieve the frame sizes supported by the camera sensor + * \return The supported frame sizes sorted in increasing order + */ + +/** + * \brief Retrieve the camera sensor resolution + * \return The camera sensor resolution in pixels + */ +const Size &CameraSensor::resolution() const +{ + /* + * The sizes_ vector is sorted in ascending order, the resolution is + * thus the last element of the vector. + */ + return sizes_.back(); +} + +/** + * \brief Retrieve the best sensor format for a desired output + * \param[in] mbusCodes The list of acceptable media bus codes + * \param[in] size The desired size + * + * Media bus codes are selected from \a mbusCodes, which lists all acceptable + * codes in decreasing order of preference. This method selects the first code + * from the list that is supported by the sensor. If none of the desired codes + * is supported, it returns an error. + * + * \a size indicates the desired size at the output of the sensor. This method + * selects the best size supported by the sensor according to the following + * criteria. + * + * - The desired \a size shall fit in the sensor output size to avoid the need + * to up-scale. + * - The sensor output size shall match the desired aspect ratio to avoid the + * need to crop the field of view. + * - The sensor output size shall be as small as possible to lower the required + * bandwidth. + * + * The use of this method is optional, as the above criteria may not match the + * needs of all pipeline handlers. Pipeline handlers may implement custom + * sensor format selection when needed. + * + * The returned sensor output format is guaranteed to be acceptable by the + * setFormat() method without any modification. + * + * \return The best sensor output format matching the desired media bus codes + * and size on success, or an empty format otherwise. + */ +V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector &mbusCodes, + const Size &size) const +{ + V4L2SubdeviceFormat format{}; + + for (unsigned int code : mbusCodes_) { + if (std::any_of(mbusCodes.begin(), mbusCodes.end(), + [code](unsigned int c) { return c == code; })) { + format.mbus_code = code; + break; + } + } + + if (!format.mbus_code) { + LOG(CameraSensor, Debug) << "No supported format found"; + return format; + } + + unsigned int desiredArea = size.width * size.height; + unsigned int bestArea = UINT_MAX; + float desiredRatio = static_cast(size.width) / size.height; + float bestRatio = FLT_MAX; + const Size *bestSize = nullptr; + + for (const Size &sz : sizes_) { + if (sz.width < size.width || sz.height < size.height) + continue; + + float ratio = static_cast(sz.width) / sz.height; + float ratioDiff = fabsf(ratio - desiredRatio); + unsigned int area = sz.width * sz.height; + unsigned int areaDiff = area - desiredArea; + + if (ratioDiff > bestRatio) + continue; + + if (ratioDiff < bestRatio || areaDiff < bestArea) { + bestRatio = ratioDiff; + bestArea = areaDiff; + bestSize = &sz; + } + } + + if (!bestSize) { + LOG(CameraSensor, Debug) << "No supported size found"; + return format; + } + + format.width = bestSize->width; + format.height = bestSize->height; + + return format; +} + +/** + * \brief Set the sensor output format + * \param[in] format The desired sensor output format + * + * \return 0 on success or a negative error code otherwise + */ +int CameraSensor::setFormat(V4L2SubdeviceFormat *format) +{ + return subdev_->setFormat(0, format); +} + +std::string CameraSensor::logPrefix() const +{ + return "'" + subdev_->entity()->name() + "'"; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/include/camera_sensor.h b/src/libcamera/include/camera_sensor.h new file mode 100644 index 00000000..7f2f906b --- /dev/null +++ b/src/libcamera/include/camera_sensor.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * camera_sensor.h - A camera sensor + */ +#ifndef __LIBCAMERA_CAMERA_SENSOR_H__ +#define __LIBCAMERA_CAMERA_SENSOR_H__ + +#include +#include + +#include + +#include "log.h" + +namespace libcamera { + +class MediaEntity; +class V4L2Subdevice; +class V4L2SubdeviceFormat; + +class CameraSensor : protected Loggable +{ +public: + explicit CameraSensor(const MediaEntity *entity); + ~CameraSensor(); + + CameraSensor(const CameraSensor &) = delete; + CameraSensor &operator=(const CameraSensor &) = delete; + + int init(); + + const MediaEntity *entity() const { return entity_; } + const std::vector &mbusCodes() const { return mbusCodes_; } + const std::vector &sizes() const { return sizes_; } + const Size &resolution() const; + + V4L2SubdeviceFormat getFormat(const std::vector &mbusCodes, + const Size &size) const; + int setFormat(V4L2SubdeviceFormat *format); + +protected: + std::string logPrefix() const; + +private: + const MediaEntity *entity_; + V4L2Subdevice *subdev_; + + std::vector mbusCodes_; + std::vector sizes_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_CAMERA_SENSOR_H__ */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index cd36ac30..cf4edec0 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -2,6 +2,7 @@ libcamera_sources = files([ 'buffer.cpp', 'camera.cpp', 'camera_manager.cpp', + 'camera_sensor.cpp', 'device_enumerator.cpp', 'event_dispatcher.cpp', 'event_dispatcher_poll.cpp', @@ -23,6 +24,7 @@ libcamera_sources = files([ ]) libcamera_headers = files([ + 'include/camera_sensor.h', 'include/device_enumerator.h', 'include/event_dispatcher_poll.h', 'include/formats.h', -- cgit v1.2.1