[libcamera-devel] [PATCH v2 3/7] libcamera: camera_sensor: Introduce CameraSensorFactory
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Fri Feb 7 23:35:59 CET 2020
Hi Jacopo,
Thank you for the patch.
On Thu, Feb 06, 2020 at 07:52:43PM +0100, Jacopo Mondi wrote:
> Introduce a factory to create CameraSensor derived classes instances by
> inspecting the sensor media entity name and provide a convenience macro
> to register specialized sensor handlers.
I really like the factory :-)
> Signed-off-by: Jacopo Mondi <jacopo at jmondi.org>
> ---
> src/libcamera/camera_sensor.cpp | 134 ++++++++++++++++++
> src/libcamera/include/camera_sensor.h | 39 ++++-
> src/libcamera/pipeline/ipu3/ipu3.cpp | 2 +-
> src/libcamera/pipeline/rkisp1/rkisp1.cpp | 2 +-
> src/libcamera/pipeline/vimc.cpp | 2 +-
> test/camera-sensor.cpp | 2 +-
> .../v4l2_videodevice_test.cpp | 2 +-
> 7 files changed, 177 insertions(+), 6 deletions(-)
>
> diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
> index 2219a4307436..fc8452b607a0 100644
> --- a/src/libcamera/camera_sensor.cpp
> +++ b/src/libcamera/camera_sensor.cpp
> @@ -11,7 +11,9 @@
> #include <float.h>
> #include <iomanip>
> #include <limits.h>
> +#include <map>
No need to include map here, no function in this file make use of it
directly. The header is included in camera_sensor.h for the usage of
std::map there, that's enough.
> #include <math.h>
> +#include <string.h>
>
> #include <libcamera/property_ids.h>
>
> @@ -42,6 +44,18 @@ LOG_DEFINE_CATEGORY(CameraSensor);
> * devices as the needs arise.
> */
>
> +/**
> + * \fn CameraSensor::entityName()
I wonder, wouldn't it be simpler to pass the entity name string
directly to the REGISTER_CAMERA_SENSOR macro ? It would avoid declaring
and undefined static method in the base class.
If we ever end up needing more complex matching code, a static match
function that takes a media entity pointer would make sense, but for a
fully static name, this seems a bit overkill.
> + * \brief Retrieve the name of the entity which identifies the supported sensor
> + *
> + * Camera sensor handlers have to report with this function the name of the
> + * media entity which represents the camera sensor they support. The here
> + * reported name is then matched against the name of the MediaEntity provided
> + * at createSensor() time to identify the correct CameraSensorFactory.
> + *
> + * \return The name of the media entity that identifies the sensor
> + */
> +
> /**
> * \brief Construct a CameraSensor
> * \param[in] entity The media entity backing the camera sensor
> @@ -366,4 +380,124 @@ std::string CameraSensor::logPrefix() const
> return "'" + subdev_->entity()->name() + "'";
> }
>
> +/**
> + * \class CameraSensorFactory
> + * \brief Factory of camera sensor handlers
"camera sensor handlers" or "camera sensors" ? We have pipeline
handlers, and if we could avoid using the name handler here, it could
help reducing confusion. This implies s/sensor handler/sensor below.
> + *
> + * The class provides to camera sensor handlers the ability to register
> + * themselves to the factory to allow the creation of their instances by
> + * matching the name of the media entity which represents the sensor.
> + *
> + * Camera sensor handlers use the REGISTER_CAMERA_SENSOR() macro to
> + * add themselves to the camera sensor factory. Users of the factory
> + * creates camera sensor handler instances by using the static
> + * CameraSensorFactory::createInstance() method, which returns a pointer
> + * to the opportune CameraSensor sub-class if any, or a newly created instance
> + * of the generic CameraSensor class.
> + */
> +
> +LOG_DEFINE_CATEGORY(CameraSensorFactory);
I think you can use the CameraSensor log category, I don't really see a
need to split messages in two categories here.
> +
> +/**
> + * \typedef CameraSensorFactory::factoriesMap
factoriesMap is a type, it should thus be called FactoriesMap.
> + * \brief An std::map of sensor handler factories
"A map of entity names to camera sensor factories"
> + *
> + * The registered sensor handler factories are associated in a map with the
> + * names of the media entity that represent the sensor
* This map associates the name of the media entities that represent
* camera sensors to the corresponding camera sensor factory.
But I think we can just drop the documentation as the type can be made
private.
> + */
> +
> +/**
> + * \brief Register a camera sensor handler factory
This is the constructor, so
* \brief Construct a camera sensor factory
> + * \param[in] name The name of the media entity representing the sensor
> + *
> + * Register a new camera sensor factory which creates sensor handler instances
> + * that support camera sensors represented by the media entity with \a name.
How about being consistent with the pipeline handler factory
documentation ?
* Creating an instance of the factory registers is with the global list of
* factories, accessible through the factories() function.
> + */
> +CameraSensorFactory::CameraSensorFactory(const char *name)
> +{
> + CameraSensorFactory::registerSensorFactory(name, this);
No need for the CameraSensorFactory:: prefix.
> +}
> +
> +/**
> + * \brief Retrieve the list of registered camera sensor factories
> + *
> + * The static factories map is defined inside the function to ensures it gets
> + * initialized on first use, without any dependency on link order.
> + *
> + * \return The static list of registered camera sensor factories
> + */
> +CameraSensorFactory::factoriesMap &CameraSensorFactory::factories()
> +{
> + static factoriesMap factories;
> + return factories;
> +}
> +
> +/**
> + * \brief Register a camera sensor handler factory
> + * \param[in] name The name of the media entity that represents the sensor
> + * \param[in] factory The factory instance to register
> + *
> + * The newly registered sensor handler \a factory is associated with \a name,
> + * when a new sensor handler is instantiated with createSensor() the name of
> + * the media entity is matched against the \a name registered here to
> + * retrieve the correct factory.
Should we explain the uniqueness requirement for the name parameter, as
done in the pipeline handler factory documentation ?
* The camera sensor \a factory is registered associated with an entity \a name.
* When a camera sensor is created for a media entity with createSensor(), the
* name of the media entity is used to select the corresponding factory.
*
* The caller is responsible for guaranteeing the uniqueness of the
* camera sensor entity name.
> + */
> +void CameraSensorFactory::registerSensorFactory(const char *name,
> + CameraSensorFactory *factory)
Maybe registerFactory() to shorten the name a bit ?
> +{
> + factoriesMap &map = CameraSensorFactory::factories();
No need for the prefix here either.
Should we log an error if the name is already present in the map ?
> + map[name] = factory;
> +}
> +
> +/**
> + * \brief Create and return a CameraSensor
Maybe "Create a camera sensor corresponding to an entity" ?
> + * \param[in] entity The media entity that represents the camera sensor
> + *
> + * Create a new instance of an handler for the sensor represented by \a
> + * entity using one of the registered factories. If no specific handler is
> + * available for the sensor, or creating the handler fails, a newly created
s/, or creating the handler fails//
as that's not what's implemented :-)
> + * instance of the generic CameraSensor base class is returned.
"... fails, an instance of the generic CameraSensor class is created and
returned."
> + *
> + * Ownership of the created camera sensor instance is passed to the caller
> + * which is reponsible for deleting the instance.
> + * FIXME: is it worth using a smart pointer here ?
I think it is. Could you give it a try ?
> + *
> + * \return A newly created camera sensor handler instance
> + */
> +CameraSensor *CameraSensorFactory::createSensor(const MediaEntity *entity)
> +{
> + /*
> + * The entity name contains both the sensor name and the I2C bus ID.
> + *
> + * Remove the i2c bus part and use the sensor name only as key to
s/i2c/I2C/
> + * search for on the sensor handlers map.
s/on/in/ ?
> + */
> + const char *entityName = entity->name().c_str();
> + const char *delim = strchrnul(entityName, ' ');
> + std::string sensorName(entityName, delim);
Should you split after the last space, not the first space ? I think the
following would avoid including string.h.
std::string::size_type delim = entity->name().find_last_of(' ');
std::string name = entity->name().substr(0, delim);
> +
> + auto it = CameraSensorFactory::factories().find(sensorName);
> + if (it == CameraSensorFactory::factories().end()) {
> + LOG(CameraSensorFactory, Info)
> + << "Unsupported sensor '" << entity->name()
> + << "': using generic sensor handler";
> + return new CameraSensor(entity);
> + }
> +
> + LOG(CameraSensorFactory, Info) << "Create handler for '"
> + << entity->name() << "' sensor";
> +
> + CameraSensorFactory *factory = it->second;
> + return factory->create(entity);
> +}
> +
> +/**
> + * \def REGISTER_CAMERA_SENSOR(handler)
> + * \brief Register a camera sensor handler to the sensor factory
s/to the/with the/
> + * \param[in] handler The name of the sensor handler
s/sensor handler/camera sensor class/
> + *
> + * Register a camera sensor handler to the sensor factory to make it available
s/to the/with the/
> + * to the factory users.
> + */
> +
> } /* namespace libcamera */
> diff --git a/src/libcamera/include/camera_sensor.h b/src/libcamera/include/camera_sensor.h
> index 99cff98128dc..2f4a0cc8ad3f 100644
> --- a/src/libcamera/include/camera_sensor.h
> +++ b/src/libcamera/include/camera_sensor.h
> @@ -7,6 +7,7 @@
> #ifndef __LIBCAMERA_CAMERA_SENSOR_H__
> #define __LIBCAMERA_CAMERA_SENSOR_H__
>
> +#include <map>
> #include <string>
> #include <vector>
>
> @@ -25,7 +26,8 @@ struct V4L2SubdeviceFormat;
> class CameraSensor : protected Loggable
> {
> public:
> - explicit CameraSensor(const MediaEntity *entity);
> + static const char *entityName();
> +
> ~CameraSensor();
>
> CameraSensor(const CameraSensor &) = delete;
> @@ -49,6 +51,8 @@ public:
> const ControlList &properties() const { return properties_; }
>
> protected:
> + friend class CameraSensorFactory;
> + explicit CameraSensor(const MediaEntity *entity);
You can make the constructor private.
> std::string logPrefix() const;
>
> private:
> @@ -61,6 +65,39 @@ private:
> ControlList properties_;
> };
>
> +class CameraSensorFactory
> +{
> +public:
> + using factoriesMap = std::map<const std::string, CameraSensorFactory *>;
You can make this type private.
> +
> + CameraSensorFactory(const char *name);
> + virtual ~CameraSensorFactory() {}
> +
> + static CameraSensor *createSensor(const MediaEntity *entity = nullptr);
Why the default = nullptr ? The argument should be mandatory.
> +
> +private:
> + static factoriesMap &factories();
> + static void registerSensorFactory(const char *name,
> + CameraSensorFactory *factory);
> + virtual CameraSensor *create(const MediaEntity *entity) = 0;
> +
> +};
> +
> +#define REGISTER_CAMERA_SENSOR(handler) \
s/handler/sensor/
> +class handler##CameraSensorFactory final : public CameraSensorFactory \
To shorten lines,
class sensor##Factory ...
> +{ \
> +public: \
> + handler##CameraSensorFactory() \
> + : CameraSensorFactory(handler##CameraSensor::entityName()) {}\
Let's not add ##CameraSensor, it makes the code more confusing:
class FooCameraSensor : CameraSensor
{
...
};
REGISTER_CAMERA_SENSOR(Foo);
I'd rather write
class FooCameraSensor : CameraSensor
{
...
};
REGISTER_CAMERA_SENSOR(FooCameraSensor);
and make it possible for the author to name the class without a
CameraSensor suffix.
> + \
> +private: \
> + CameraSensor *create(const MediaEntity *entity) \
> + { \
> + return new handler##CameraSensor(entity); \
> + } \
> +}; \
> +static handler##CameraSensorFactory global_##handler##CameraSensorFactory
> +
> } /* namespace libcamera */
>
> #endif /* __LIBCAMERA_CAMERA_SENSOR_H__ */
> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> index 387bb070b505..21934e72eba7 100644
> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> @@ -1322,7 +1322,7 @@ int CIO2Device::init(const MediaDevice *media, unsigned int index)
>
> MediaLink *link = links[0];
> MediaEntity *sensorEntity = link->source()->entity();
> - sensor_ = new CameraSensor(sensorEntity);
> + sensor_ = CameraSensorFactory::createSensor(sensorEntity);
> ret = sensor_->init();
> if (ret)
> return ret;
> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> index 68f16f03a81e..f2f054596257 100644
> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> @@ -901,7 +901,7 @@ int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
>
> data->controlInfo_ = std::move(ctrls);
>
> - data->sensor_ = new CameraSensor(sensor);
> + data->sensor_ = CameraSensorFactory::createSensor(sensor);
> ret = data->sensor_->init();
> if (ret)
> return ret;
> diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp
> index fd4df0b03c26..cfeec1aac751 100644
> --- a/src/libcamera/pipeline/vimc.cpp
> +++ b/src/libcamera/pipeline/vimc.cpp
> @@ -403,7 +403,7 @@ int VimcCameraData::init(MediaDevice *media)
> return ret;
>
> /* Create and open the camera sensor, debayer, scaler and video device. */
> - sensor_ = new CameraSensor(media->getEntityByName("Sensor B"));
> + sensor_ = CameraSensorFactory::createSensor(media->getEntityByName("Sensor B"));
> ret = sensor_->init();
> if (ret)
> return ret;
> diff --git a/test/camera-sensor.cpp b/test/camera-sensor.cpp
> index 27c190fe7ace..24b83a45b656 100644
> --- a/test/camera-sensor.cpp
> +++ b/test/camera-sensor.cpp
> @@ -50,7 +50,7 @@ protected:
> return TestFail;
> }
>
> - sensor_ = new CameraSensor(entity);
> + sensor_ = CameraSensorFactory::createSensor(entity);
> if (sensor_->init() < 0) {
> cerr << "Unable to initialise camera sensor" << endl;
> return TestFail;
> diff --git a/test/v4l2_videodevice/v4l2_videodevice_test.cpp b/test/v4l2_videodevice/v4l2_videodevice_test.cpp
> index 577da4cb601c..102a8b1b6c1c 100644
> --- a/test/v4l2_videodevice/v4l2_videodevice_test.cpp
> +++ b/test/v4l2_videodevice/v4l2_videodevice_test.cpp
> @@ -61,7 +61,7 @@ int V4L2VideoDeviceTest::init()
> return TestFail;
>
> if (driver_ == "vimc") {
> - sensor_ = new CameraSensor(media_->getEntityByName("Sensor A"));
> + sensor_ = CameraSensorFactory::createSensor(media_->getEntityByName("Sensor A"));
> if (sensor_->init())
> return TestFail;
>
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list