Message ID | 20221010131744.513261-2-xavier.roumegue@oss.nxp.com |
---|---|
State | Accepted |
Headers | show |
Series |
|
Related | show |
HI Xavier, sorry for the delay! On Mon, Oct 10, 2022 at 03:17:43PM +0200, Xavier Roumegue (OSS) wrote: > From: Xavier Roumegue <xavier.roumegue@oss.nxp.com> > > Declare a converter Abstract Base Class intended to provide generic > interfaces to hardware offering size and format conversion services on > streams. This is mainly based on the public interfaces of the current > converter class implementation found in the simple pipeline handler. > > The main change is the introduction of loadConfiguration() function > which can be used by the concrete implementation to load hardware > specific runtime parameters defined by the application. > > Signed-off-by: Xavier Roumegue <xavier.roumegue@oss.nxp.com> > --- > include/libcamera/internal/converter.h | 108 ++++++++ > include/libcamera/internal/meson.build | 1 + > src/libcamera/converter.cpp | 337 +++++++++++++++++++++++++ > src/libcamera/meson.build | 1 + > 4 files changed, 447 insertions(+) > create mode 100644 include/libcamera/internal/converter.h > create mode 100644 src/libcamera/converter.cpp > > diff --git a/include/libcamera/internal/converter.h b/include/libcamera/internal/converter.h > new file mode 100644 > index 00000000..38c05ee9 > --- /dev/null > +++ b/include/libcamera/internal/converter.h > @@ -0,0 +1,108 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2020, Laurent Pinchart > + * Copyright 2022 NXP > + * > + * converter.h - Generic stream converter infrastructure Please align this with the .cpp one. Pick the one that you prefer * converter.h - Generic stream converter infrastructure * converter.cpp - Generic Format converter interface > + */ > + > +#pragma once > + > +#include <map> > +#include <memory> #include <initializer_list> > +#include <string> > +#include <tuple> > +#include <vector> > + > +#include <libcamera/base/class.h> > +#include <libcamera/base/signal.h> > + > +#include <libcamera/geometry.h> > +#include <libcamera/pixel_format.h> > + > +namespace libcamera { > + > +class FrameBuffer; > +class MediaDevice; > +class Size; > +class SizeRange; > +struct StreamConfiguration; > + > +class Converter > +{ > +public: > + Converter(MediaDevice *media); > + virtual ~Converter(); > + > + virtual int loadConfiguration(const std::string &filename) = 0; > + > + virtual bool isValid() const = 0; > + > + virtual std::vector<PixelFormat> formats(PixelFormat input) = 0; > + virtual SizeRange sizes(const Size &input) = 0; > + > + virtual std::tuple<unsigned int, unsigned int> > + strideAndFrameSize(const PixelFormat &pixelFormat, const Size &size) = 0; > + > + virtual int configure(const StreamConfiguration &inputCfg, > + const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfg) = 0; I wonder if using <StreamConfiguration *> would be easier than reference_wrapper, but I'll find out in the next patch I presume > + virtual int exportBuffers(unsigned int ouput, unsigned int count, > + std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0; > + > + virtual int start() = 0; > + virtual void stop() = 0; > + > + virtual int queueBuffers(FrameBuffer *input, > + const std::map<unsigned int, FrameBuffer *> &outputs) = 0; > + > + Signal<FrameBuffer *> inputBufferReady; > + Signal<FrameBuffer *> outputBufferReady; > + > + const std::string &deviceNode() const { return deviceNode_; }; > + > +private: > + std::string deviceNode_; > +}; > + > +class ConverterFactoryBase > +{ > +public: > + ConverterFactoryBase(const std::string name, std::initializer_list<std::string> aliases); > + virtual ~ConverterFactoryBase() = default; > + > + const std::vector<std::string> &aliases() const { return aliases_; } > + > + static std::unique_ptr<Converter> create(MediaDevice *media); > + static std::vector<ConverterFactoryBase *> &factories(); > + static std::vector<std::string> names(); > + > +private: > + LIBCAMERA_DISABLE_COPY_AND_MOVE(ConverterFactoryBase) > + > + static void registerType(ConverterFactoryBase *factory); > + > + virtual std::unique_ptr<Converter> createInstance(MediaDevice *media) const = 0; > + > + std::string name_; > + std::vector<std::string> aliases_; > +}; > + > +template<typename _Converter> > +class ConverterFactory : public ConverterFactoryBase > +{ > +public: > + ConverterFactory(const char *name, std::initializer_list<std::string> aliases) > + : ConverterFactoryBase(name, aliases) > + { > + } > + > + std::unique_ptr<Converter> createInstance(MediaDevice *media) const override > + { > + return std::make_unique<_Converter>(media); > + } > +}; > + > +#define REGISTER_CONVERTER(name, converter, ...) \ > + static ConverterFactory<converter> global_##converter##Factory(name, { __VA_ARGS__ }); > + > +} /* namespace libcamera */ > diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build > index 7a780d48..8f50d755 100644 > --- a/include/libcamera/internal/meson.build > +++ b/include/libcamera/internal/meson.build > @@ -19,6 +19,7 @@ libcamera_internal_headers = files([ > 'camera_sensor_properties.h', > 'control_serializer.h', > 'control_validator.h', > + 'converter.h', > 'delayed_controls.h', > 'device_enumerator.h', > 'device_enumerator_sysfs.h', > diff --git a/src/libcamera/converter.cpp b/src/libcamera/converter.cpp > new file mode 100644 > index 00000000..5e444f5f > --- /dev/null > +++ b/src/libcamera/converter.cpp I get quite a few Doxygen warnings on this class src/libcamera/converter.cpp:328: warning: argument 'aliases' of command @param is not found in the argument list of REGISTER_CONVERTER(name, converter,...) src/libcamera/converter.cpp:113: warning: argument 'output' of command @param is not found in the argument list of libcamera::Converter::exportBuffers(unsigned int ouput, unsigned int count, std::vector< std::unique_ptr< FrameBuffer >> *buffers)=0 include/libcamera/internal/converter.h:49: warning: The following parameter of libcamera::Converter::exportBuffers(unsigned int ouput, unsigned int count, std::vector< std::unique_ptr< FrameBuffer >> *buffers)=0 is not documented: parameter 'ouput' src/libcamera/converter.cpp:139: warning: argument 'buffers' of command @param is not found in the argument list of libcamera::Converter::queueBuffers(FrameBuffer *input, const std::map< unsigned int, FrameBuffer * > &outputs)=0 include/libcamera/internal/converter.h:55: warning: The following parameter of libcamera::Converter::queueBuffers(FrameBuffer *input, const std::map< unsigned int, FrameBuffer * > &outputs)=0 is not documented: parameter 'outputs' include/libcamera/internal/converter.h:73: warning: Member aliases() const (function) of class libcamera::ConverterFactoryBase is not documented. include/libcamera/internal/converter.h:73: warning: Member aliases() const (function) of class libcamera::ConverterFactoryBase is not documented. include/libcamera/internal/converter.h:70: warning: The following parameter of libcamera::ConverterFactoryBase::ConverterFactoryBase(const std::string name, std::initializer_list< std::string > aliases) is not documented: parameter 'aliases' src/libcamera/converter.cpp:199: warning: argument 'name' of command @param is not found in the argument list of libcamera::ConverterFactoryBase::create(MediaDevice *media) include/libcamera/internal/converter.h:75: warning: The following parameter of libcamera::ConverterFactoryBase::create(MediaDevice *media) is not documented: parameter 'media' With the next patch applied the list increases: src/libcamera/converter.cpp:328: warning: argument 'aliases' of command @param is not found in the argument list of REGISTER_CONVERTER(name, converter,...) src/libcamera/converter.cpp:113: warning: argument 'output' of command @param is not found in the argument list of libcamera::Converter::exportBuffers(unsigned int ouput, unsigned int count, std::vector< std::unique_ptr< FrameBuffer >> *buffers)=0 include/libcamera/internal/converter.h:49: warning: The following parameter of libcamera::Converter::exportBuffers(unsigned int ouput, unsigned int count, std::vector< std::unique_ptr< FrameBuffer >> *buffers)=0 is not documented: parameter 'ouput' src/libcamera/converter.cpp:139: warning: argument 'buffers' of command @param is not found in the argument list of libcamera::Converter::queueBuffers(FrameBuffer *input, const std::map< unsigned int, FrameBuffer * > &outputs)=0 include/libcamera/internal/converter.h:55: warning: The following parameter of libcamera::Converter::queueBuffers(FrameBuffer *input, const std::map< unsigned int, FrameBuffer * > &outputs)=0 is not documented: parameter 'outputs' include/libcamera/internal/converter.h:73: warning: Member aliases() const (function) of class libcamera::ConverterFactoryBase is not documented. include/libcamera/internal/converter.h:73: warning: Member aliases() const (function) of class libcamera::ConverterFactoryBase is not documented. include/libcamera/internal/converter.h:70: warning: The following parameter of libcamera::ConverterFactoryBase::ConverterFactoryBase(const std::string name, std::initializer_list< std::string > aliases) is not documented: parameter 'aliases' src/libcamera/converter.cpp:199: warning: argument 'name' of command @param is not found in the argument list of libcamera::ConverterFactoryBase::create(MediaDevice *media) include/libcamera/internal/converter.h:75: warning: The following parameter of libcamera::ConverterFactoryBase::create(MediaDevice *media) is not documented: parameter 'media' src/libcamera/converter/converter_v4l2_m2m.cpp:323: warning: argument 'outputCfg' of command @param is not found in the argument list of libcamera::V4L2M2MConverter::configure(const StreamConfiguration &inputCfg, const std::vector< std::reference_wrapper< StreamConfiguration >> &outputCfgs) include/libcamera/internal/converter/converter_v4l2_m2m.h:48: warning: The following parameter of libcamera::V4L2M2MConverter::configure(const StreamConfiguration &inputCfg, const std::vector< std::reference_wrapper< StreamConfiguration >> &outputCfgs) is not documented: parameter 'outputCfgs' src/libcamera/converter/converter_v4l2_m2m.cpp:217: warning: argument 'filename' of command @param is not found in the argument list of libcamera::V4L2M2MConverter::formats(PixelFormat input) src/libcamera/converter/converter_v4l2_m2m.cpp:396: warning: argument 'buffers' of command @param is not found in the argument list of libcamera::V4L2M2MConverter::queueBuffers(FrameBuffer *input, const std::map< unsigned int, FrameBuffer * > &outputs) include/libcamera/internal/converter/converter_v4l2_m2m.h:56: warning: The following parameter of libcamera::V4L2M2MConverter::queueBuffers(FrameBuffer *input, const std::map< unsigned int, FrameBuffer * > &outputs) is not documented: parameter 'outputs' Could you make sure Doxygen doesn't report any warning ? > @@ -0,0 +1,337 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright 2022 NXP > + * > + * converter.cpp - Generic Format converter interface > + */ > + > +#include "libcamera/internal/converter.h" > + > +#include <algorithm> > + > +#include <libcamera/base/log.h> > + > +#include "libcamera/internal/media_device.h" > + > +/** > + * \file internal/converter.h > + * \brief Abstract converter > + */ > + > +namespace libcamera { > + > +LOG_DEFINE_CATEGORY(Converter) > + > +/** > + * \class Converter > + * \brief Abstract Base Class for converter > + * > + * The Converter class is an Abstract Base Class defining the interfaces of > + * converter implementations. > + * > + * Converters offers scaling and pixel formats conversion services on a input Converters -> offer I would also use "pixel format" in singular form, but I can't tell if that's more correct or not. > + * stream. The converter can output multiple streams with individual conversion > + * parameters from the same input stream. > + */ > + > +/** > + * \brief Construct a Converter instance > + * \param[in] media The media device implementing the converter > + * > + * This seeks for the entity implementing data streaming function in the media > + * graph entities and use its device node as converter device node. > + */ > +Converter::Converter(MediaDevice *media) > +{ > + const std::vector<MediaEntity *> &entities = media->entities(); > + auto it = std::find_if(entities.begin(), entities.end(), > + [](MediaEntity *entity) { > + return entity->function() == MEDIA_ENT_F_IO_V4L; > + }); > + if (it == entities.end()) { > + LOG(Converter, Error) > + << "No entity suitable for implementing a converter in " > + << media->driver() << " entities list."; > + return; > + } > + > + deviceNode_ = (*it)->deviceNode(); > +} > + > +Converter::~Converter() > +{ > +} > + > +/** > + * \fn Converter::loadConfiguration() > + * \brief Load converter configuration from file > + * \param[in] filename The file name path > + * > + * Load converter dependent configuration parameters to apply on the hardware. > + * > + * \return 0 on success or a negative error code otherwise > + */ > + > +/** > + * \fn Converter::isValid() > + * \brief Check if the converter configuration is valid > + * \return True is the converter is valid, false otherwise > + */ > + > +/** > + * \fn Converter::formats() > + * \brief Retrieve the list of supported pixel formats for an input pixel format > + * \param[in] input Input pixel format to retrieve output pixel format list for > + * \return The list of output supported pixel formats I would swap "output supported" to "supported output" > + */ > + > +/** > + * \fn Converter::sizes() > + * \brief Retrieve the output range of minimum and maximum sizes for an input size Here too: "range of minimum and maximum output sizes" > + * \param[in] input Input stream size to retrieve range for > + * \return A range of output image sizes > + */ > + > +/** > + * \fn Converter::strideAndFrameSize() > + * \brief Retrieve the output stride and frame size for an input configutation > + * \param[in] pixelFormat Input stream pixel format > + * \param[in] size Input stream size > + * \return A tuple indicating the stride and frame size or an empty tuple on error > + */ > + > +/** > + * \fn Converter::configure() > + * \brief Configure a set of output stream conversion from an input stream > + * \param[in] inputCfg Input stream configuration > + * \param[in] outputCfg A list of output stream configurations Is this an [out] parameter ? > + * \return 0 on success or a negative error code otherwise > + */ > + > +/** > + * \fn Converter::exportBuffers() > + * \brief Export buffers from the converter device > + * \param[in] output Output stream index exporting the buffers > + * \param[in] count Number of buffers to allocate > + * \param[out] buffers Vector to store allocated buffers > + * > + * This function operates similarly as V4L2VideoDevice::exportBuffers() on the > + * output stream indicated by the \a output index. > + * > + * \return The number of allocated buffers on success or a negative error code > + * otherwise > + */ > + > +/** > + * \fn Converter::start() > + * \brief Start the converter streaming operation > + * \return 0 on success or a negative error code otherwise > + */ > + > +/** > + * \fn Converter::stop() > + * \brief Stop the converter streaming operation > + * \return 0 on success or a negative error code otherwise > + */ > + > +/** > + * \fn Converter::queueBuffers() > + * \brief Queue buffers to converter device > + * \param[in] input The frame buffer to apply the conversion > + * \param[out] buffers The container holding the output stream indexes and > + * their respective frame buffer outputs. > + * > + * This function queues the \a input frame buffer on the output streams of the > + * \a buffers map key and retrieve the output frame buffer indicated by the > + * \a buffer map value. "buffer" is not an argument. Maybe "buffers". I'll better understand how the output map is populated in the next patch > + * > + * \return 0 on success or a negative error code otherwise > + */ > + > +/** > + * \var Converter::inputBufferReady > + * \brief A signal emitted when the input frame buffer completes > + */ > + > +/** > + * \var Converter::outputBufferReady > + * \brief A signal emitted when the output frame buffer completes > + */ > + > +/** > + * \fn Converter::deviceNode() > + * \brief The converter device node attribute accessor > + * \return The converter device node string > + */ > + > +/** > + * \class ConverterFactoryBase > + * \brief Base class for converter factories > + * > + * The ConverterFactoryBase class is the base of all specializations of the > + * ConverterFactory class template. It implements the factory registration, > + * maintains a registry of factories, and provides access to the registered > + * factories. > + */ > + > +/** > + * \brief Construct a converter factory base > + * \param[in] name Name of the converter class > + * > + * Creating an instance of the factory base registers it with the global list of > + * factories, accessible through the factories() function. > + * > + * The factory \a name is used as unique identifier. Please don't break lines in paragraphs. Either One line. With a line break in between. Or One sentence. And the paragraph continues without breaking. > + * If the converter implemententation fully relies on a generic framework, the > + * name should be the same as the framework. > + * Otherwise, if the implementation is specialized, the factory name should match > + * the driver name implementing the function. > + * The factory \a aliases holds a list of driver names implementing a generic > + * subsystem without any personalizations. > + */ > +ConverterFactoryBase::ConverterFactoryBase(const std::string name, std::initializer_list<std::string> aliases) > + : name_(name), aliases_(aliases) > +{ > + registerType(this); > +} > + > +/** > + * \brief Create an instance of the converter corresponding to a named factory > + * \param[in] name Name of the factory > + * > + * \return A unique pointer to a new instance of the converter subclass > + * corresponding to the named factory or one of its alias. Otherwise a null > + * pointer if no such factory exists > + */ > +std::unique_ptr<Converter> ConverterFactoryBase::create(MediaDevice *media) > +{ > + const std::vector<ConverterFactoryBase *> &factories = > + ConverterFactoryBase::factories(); > + > + for (const ConverterFactoryBase *factory : factories) { > + std::vector<std::string> aliases = factory->aliases(); &aliases > + auto it = std::find(aliases.begin(), aliases.end(), media->driver()); > + > + if (it == aliases.end() && media->driver() != factory->name_) > + continue; > + > + LOG(Converter, Debug) > + << "Creating converter from " > + << factory->name_ << " factory with " > + << (it == aliases.end() ? "no" : media->driver()) << " alias."; > + > + return factory->createInstance(media); > + } > + > + return nullptr; > +} > + > +/** > + * \brief Add a converter class to the registry > + * \param[in] factory Factory to use to construct the converter class > + * > + * The caller is responsible to guarantee the uniqueness of the converter name. > + */ > +void ConverterFactoryBase::registerType(ConverterFactoryBase *factory) > +{ > + std::vector<ConverterFactoryBase *> &factories = > + ConverterFactoryBase::factories(); > + > + factories.push_back(factory); > +} > + > +/** > + * \brief Retrieve the list of all converter factory names > + * \return The list of all converter factory names > + */ > +std::vector<std::string> ConverterFactoryBase::names() What is this function for ? Return the names of all converters registered in the system ? Am I missing where it is used ? > +{ > + std::vector<std::string> list; > + > + std::vector<ConverterFactoryBase *> &factories = > + ConverterFactoryBase::factories(); > + > + for (ConverterFactoryBase *factory : factories) { > + list.push_back(factory->name_); > + for (auto alias : factory->aliases()) > + list.push_back(alias); > + } > + > + return list; > +} > + > +/** > + * \brief Retrieve the list of all converter factories > + * \return The list of converter factories > + */ > +std::vector<ConverterFactoryBase *> &ConverterFactoryBase::factories() > +{ > + /* > + * The static factories map is defined inside the function to ensure > + * it gets initialized on first use, without any dependency on link > + * order. > + */ > + static std::vector<ConverterFactoryBase *> factories; > + return factories; > +} > + > +/** > + * \var ConverterFactoryBase::name_ > + * \brief The name of the factory > + */ > + > +/** > + * \var ConverterFactoryBase::aliases_ > + * \brief The list holding the factory aliases > + */ > + > +/** > + * \class ConverterFactory > + * \brief Registration of ConverterFactory classes and creation of instances > + * \param _Converter The converter class type for this factory > + * > + * To facilitate discovery and instantiation of Converter classes, the > + * ConverterFactory class implements auto-registration of converter helpers. > + * Each Converter subclass shall register itself using the REGISTER_CONVERTER() > + * macro, which will create a corresponding instance of a ConverterFactory > + * subclass and register it with the static list of factories. > + */ > + > +/** > + * \fn ConverterFactory::ConverterFactory(const char *name, std::initializer_list<std::string> aliases) > + * \brief Construct a converter factory > + * \param[in] name Name of the converter class > + * \param[in] aliases Aliases of the converter class > + * > + * Creating an instance of the factory registers it with the global list of > + * factories, accessible through the factories() function. > + * > + * The factory \a name is used as unique identifier. > + * If the converter implemententation fully relies on a generic framework, the > + * name should be the same as the framework. > + * Otherwise, if the implementation is specialized, the factory name should match > + * the driver name implementing the function. > + * The factory \a aliases holds a list of driver names implementing a generic > + * subsystem without any personalizations. Same comment as above. Also, you can use \copydoc > + */ > + > +/** > + * \fn ConverterFactory::createInstance() const > + * \brief Create an instance of the Converter corresponding to the factory > + * \param[in] media Media device pointer > + * \return A unique pointer to a newly constructed instance of the Converter > + * subclass corresponding to the factory > + */ > + > +/** > + * \def REGISTER_CONVERTER > + * \brief Register a converter with the Converter factory > + * \param[in] name Converter name used to register the class > + * \param[in] converter Class name of Converter derived class to register > + * \param[in] aliases Optional list of alias names > + * > + * Register a Converter subclass with the factory and make it available to try > + * and match converters. > + */ > + > +} /* namespace libcamera */ > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build > index 7fcbb2dd..3a9fc31f 100644 > --- a/src/libcamera/meson.build > +++ b/src/libcamera/meson.build > @@ -13,6 +13,7 @@ libcamera_sources = files([ > 'controls.cpp', > 'control_serializer.cpp', > 'control_validator.cpp', > + 'converter.cpp', > 'delayed_controls.cpp', > 'device_enumerator.cpp', > 'device_enumerator_sysfs.cpp', > -- > 2.37.3 >
Hi Jacopo, On 11/9/22 08:43, Jacopo Mondi wrote: > HI Xavier, > sorry for the delay! > > On Mon, Oct 10, 2022 at 03:17:43PM +0200, Xavier Roumegue (OSS) wrote: >> From: Xavier Roumegue <xavier.roumegue@oss.nxp.com> >> >> Declare a converter Abstract Base Class intended to provide generic >> interfaces to hardware offering size and format conversion services on >> streams. This is mainly based on the public interfaces of the current >> converter class implementation found in the simple pipeline handler. >> >> The main change is the introduction of loadConfiguration() function >> which can be used by the concrete implementation to load hardware >> specific runtime parameters defined by the application. >> >> Signed-off-by: Xavier Roumegue <xavier.roumegue@oss.nxp.com> >> --- >> include/libcamera/internal/converter.h | 108 ++++++++ >> include/libcamera/internal/meson.build | 1 + >> src/libcamera/converter.cpp | 337 +++++++++++++++++++++++++ >> src/libcamera/meson.build | 1 + >> 4 files changed, 447 insertions(+) >> create mode 100644 include/libcamera/internal/converter.h >> create mode 100644 src/libcamera/converter.cpp >> >> diff --git a/include/libcamera/internal/converter.h b/include/libcamera/internal/converter.h >> new file mode 100644 >> index 00000000..38c05ee9 >> --- /dev/null >> +++ b/include/libcamera/internal/converter.h >> @@ -0,0 +1,108 @@ >> +/* SPDX-License-Identifier: LGPL-2.1-or-later */ >> +/* >> + * Copyright (C) 2020, Laurent Pinchart >> + * Copyright 2022 NXP >> + * >> + * converter.h - Generic stream converter infrastructure > > Please align this with the .cpp one. Pick the one that you prefer > > * converter.h - Generic stream converter infrastructure > * converter.cpp - Generic Format converter interface > > >> + */ >> + >> +#pragma once >> + >> +#include <map> >> +#include <memory> > > #include <initializer_list> > >> +#include <string> >> +#include <tuple> >> +#include <vector> >> + >> +#include <libcamera/base/class.h> >> +#include <libcamera/base/signal.h> >> + >> +#include <libcamera/geometry.h> >> +#include <libcamera/pixel_format.h> >> + >> +namespace libcamera { >> + >> +class FrameBuffer; >> +class MediaDevice; >> +class Size; >> +class SizeRange; >> +struct StreamConfiguration; >> + >> +class Converter >> +{ >> +public: >> + Converter(MediaDevice *media); >> + virtual ~Converter(); >> + >> + virtual int loadConfiguration(const std::string &filename) = 0; >> + >> + virtual bool isValid() const = 0; >> + >> + virtual std::vector<PixelFormat> formats(PixelFormat input) = 0; >> + virtual SizeRange sizes(const Size &input) = 0; >> + >> + virtual std::tuple<unsigned int, unsigned int> >> + strideAndFrameSize(const PixelFormat &pixelFormat, const Size &size) = 0; >> + >> + virtual int configure(const StreamConfiguration &inputCfg, >> + const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfg) = 0; > > I wonder if using <StreamConfiguration *> would be easier than > reference_wrapper, but I'll find out in the next patch I presume > >> + virtual int exportBuffers(unsigned int ouput, unsigned int count, >> + std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0; >> + >> + virtual int start() = 0; >> + virtual void stop() = 0; >> + >> + virtual int queueBuffers(FrameBuffer *input, >> + const std::map<unsigned int, FrameBuffer *> &outputs) = 0; >> + >> + Signal<FrameBuffer *> inputBufferReady; >> + Signal<FrameBuffer *> outputBufferReady; >> + >> + const std::string &deviceNode() const { return deviceNode_; }; >> + >> +private: >> + std::string deviceNode_; >> +}; >> + >> +class ConverterFactoryBase >> +{ >> +public: >> + ConverterFactoryBase(const std::string name, std::initializer_list<std::string> aliases); >> + virtual ~ConverterFactoryBase() = default; >> + >> + const std::vector<std::string> &aliases() const { return aliases_; } >> + >> + static std::unique_ptr<Converter> create(MediaDevice *media); >> + static std::vector<ConverterFactoryBase *> &factories(); >> + static std::vector<std::string> names(); >> + >> +private: >> + LIBCAMERA_DISABLE_COPY_AND_MOVE(ConverterFactoryBase) >> + >> + static void registerType(ConverterFactoryBase *factory); >> + >> + virtual std::unique_ptr<Converter> createInstance(MediaDevice *media) const = 0; >> + >> + std::string name_; >> + std::vector<std::string> aliases_; >> +}; >> + >> +template<typename _Converter> >> +class ConverterFactory : public ConverterFactoryBase >> +{ >> +public: >> + ConverterFactory(const char *name, std::initializer_list<std::string> aliases) >> + : ConverterFactoryBase(name, aliases) >> + { >> + } >> + >> + std::unique_ptr<Converter> createInstance(MediaDevice *media) const override >> + { >> + return std::make_unique<_Converter>(media); >> + } >> +}; >> + >> +#define REGISTER_CONVERTER(name, converter, ...) \ >> + static ConverterFactory<converter> global_##converter##Factory(name, { __VA_ARGS__ }); >> + >> +} /* namespace libcamera */ >> diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build >> index 7a780d48..8f50d755 100644 >> --- a/include/libcamera/internal/meson.build >> +++ b/include/libcamera/internal/meson.build >> @@ -19,6 +19,7 @@ libcamera_internal_headers = files([ >> 'camera_sensor_properties.h', >> 'control_serializer.h', >> 'control_validator.h', >> + 'converter.h', >> 'delayed_controls.h', >> 'device_enumerator.h', >> 'device_enumerator_sysfs.h', >> diff --git a/src/libcamera/converter.cpp b/src/libcamera/converter.cpp >> new file mode 100644 >> index 00000000..5e444f5f >> --- /dev/null >> +++ b/src/libcamera/converter.cpp > > I get quite a few Doxygen warnings on this class > > src/libcamera/converter.cpp:328: warning: argument 'aliases' of command @param is not found in the argument list of REGISTER_CONVERTER(name, converter,...) > src/libcamera/converter.cpp:113: warning: argument 'output' of command @param is not found in the argument list of libcamera::Converter::exportBuffers(unsigned int ouput, unsigned int count, std::vector< std::unique_ptr< FrameBuffer >> *buffers)=0 > include/libcamera/internal/converter.h:49: warning: The following parameter of libcamera::Converter::exportBuffers(unsigned int ouput, unsigned int count, std::vector< std::unique_ptr< FrameBuffer >> *buffers)=0 is not documented: > parameter 'ouput' > src/libcamera/converter.cpp:139: warning: argument 'buffers' of command @param is not found in the argument list of libcamera::Converter::queueBuffers(FrameBuffer *input, const std::map< unsigned int, FrameBuffer * > &outputs)=0 > include/libcamera/internal/converter.h:55: warning: The following parameter of libcamera::Converter::queueBuffers(FrameBuffer *input, const std::map< unsigned int, FrameBuffer * > &outputs)=0 is not documented: > parameter 'outputs' > include/libcamera/internal/converter.h:73: warning: Member aliases() const (function) of class libcamera::ConverterFactoryBase is not documented. > include/libcamera/internal/converter.h:73: warning: Member aliases() const (function) of class libcamera::ConverterFactoryBase is not documented. > include/libcamera/internal/converter.h:70: warning: The following parameter of libcamera::ConverterFactoryBase::ConverterFactoryBase(const std::string name, std::initializer_list< std::string > aliases) is not documented: > parameter 'aliases' > src/libcamera/converter.cpp:199: warning: argument 'name' of command @param is not found in the argument list of libcamera::ConverterFactoryBase::create(MediaDevice *media) > include/libcamera/internal/converter.h:75: warning: The following parameter of libcamera::ConverterFactoryBase::create(MediaDevice *media) is not documented: > parameter 'media' > > > With the next patch applied the list increases: > > > src/libcamera/converter.cpp:328: warning: argument 'aliases' of command @param is not found in the argument list of REGISTER_CONVERTER(name, converter,...) > src/libcamera/converter.cpp:113: warning: argument 'output' of command @param is not found in the argument list of libcamera::Converter::exportBuffers(unsigned int ouput, unsigned int count, std::vector< std::unique_ptr< FrameBuffer >> *buffers)=0 > include/libcamera/internal/converter.h:49: warning: The following parameter of libcamera::Converter::exportBuffers(unsigned int ouput, unsigned int count, std::vector< std::unique_ptr< FrameBuffer >> *buffers)=0 is not documented: > parameter 'ouput' > src/libcamera/converter.cpp:139: warning: argument 'buffers' of command @param is not found in the argument list of libcamera::Converter::queueBuffers(FrameBuffer *input, const std::map< unsigned int, FrameBuffer * > &outputs)=0 > include/libcamera/internal/converter.h:55: warning: The following parameter of libcamera::Converter::queueBuffers(FrameBuffer *input, const std::map< unsigned int, FrameBuffer * > &outputs)=0 is not documented: > parameter 'outputs' > include/libcamera/internal/converter.h:73: warning: Member aliases() const (function) of class libcamera::ConverterFactoryBase is not documented. > include/libcamera/internal/converter.h:73: warning: Member aliases() const (function) of class libcamera::ConverterFactoryBase is not documented. > include/libcamera/internal/converter.h:70: warning: The following parameter of libcamera::ConverterFactoryBase::ConverterFactoryBase(const std::string name, std::initializer_list< std::string > aliases) is not documented: > parameter 'aliases' > src/libcamera/converter.cpp:199: warning: argument 'name' of command @param is not found in the argument list of libcamera::ConverterFactoryBase::create(MediaDevice *media) > include/libcamera/internal/converter.h:75: warning: The following parameter of libcamera::ConverterFactoryBase::create(MediaDevice *media) is not documented: > parameter 'media' > src/libcamera/converter/converter_v4l2_m2m.cpp:323: warning: argument 'outputCfg' of command @param is not found in the argument list of libcamera::V4L2M2MConverter::configure(const StreamConfiguration &inputCfg, const std::vector< std::reference_wrapper< StreamConfiguration >> &outputCfgs) > include/libcamera/internal/converter/converter_v4l2_m2m.h:48: warning: The following parameter of libcamera::V4L2M2MConverter::configure(const StreamConfiguration &inputCfg, const std::vector< std::reference_wrapper< StreamConfiguration >> &outputCfgs) is not documented: > parameter 'outputCfgs' > src/libcamera/converter/converter_v4l2_m2m.cpp:217: warning: argument 'filename' of command @param is not found in the argument list of libcamera::V4L2M2MConverter::formats(PixelFormat input) > src/libcamera/converter/converter_v4l2_m2m.cpp:396: warning: argument 'buffers' of command @param is not found in the argument list of libcamera::V4L2M2MConverter::queueBuffers(FrameBuffer *input, const std::map< unsigned int, FrameBuffer * > &outputs) > include/libcamera/internal/converter/converter_v4l2_m2m.h:56: warning: The following parameter of libcamera::V4L2M2MConverter::queueBuffers(FrameBuffer *input, const std::map< unsigned int, FrameBuffer * > &outputs) is not documented: parameter 'outputs' > > Could you make sure Doxygen doesn't report any warning ? Fixed in v2. > >> @@ -0,0 +1,337 @@ >> +/* SPDX-License-Identifier: LGPL-2.1-or-later */ >> +/* >> + * Copyright 2022 NXP >> + * >> + * converter.cpp - Generic Format converter interface >> + */ >> + >> +#include "libcamera/internal/converter.h" >> + >> +#include <algorithm> >> + >> +#include <libcamera/base/log.h> >> + >> +#include "libcamera/internal/media_device.h" >> + >> +/** >> + * \file internal/converter.h >> + * \brief Abstract converter >> + */ >> + >> +namespace libcamera { >> + >> +LOG_DEFINE_CATEGORY(Converter) >> + >> +/** >> + * \class Converter >> + * \brief Abstract Base Class for converter >> + * >> + * The Converter class is an Abstract Base Class defining the interfaces of >> + * converter implementations. >> + * >> + * Converters offers scaling and pixel formats conversion services on a input > > Converters -> offer > > I would also use "pixel format" in singular form, but I can't tell if > that's more correct or not. > > >> + * stream. The converter can output multiple streams with individual conversion >> + * parameters from the same input stream. >> + */ >> + >> +/** >> + * \brief Construct a Converter instance >> + * \param[in] media The media device implementing the converter >> + * >> + * This seeks for the entity implementing data streaming function in the media >> + * graph entities and use its device node as converter device node. >> + */ >> +Converter::Converter(MediaDevice *media) >> +{ >> + const std::vector<MediaEntity *> &entities = media->entities(); >> + auto it = std::find_if(entities.begin(), entities.end(), >> + [](MediaEntity *entity) { >> + return entity->function() == MEDIA_ENT_F_IO_V4L; >> + }); >> + if (it == entities.end()) { >> + LOG(Converter, Error) >> + << "No entity suitable for implementing a converter in " >> + << media->driver() << " entities list."; >> + return; >> + } >> + >> + deviceNode_ = (*it)->deviceNode(); >> +} >> + >> +Converter::~Converter() >> +{ >> +} >> + >> +/** >> + * \fn Converter::loadConfiguration() >> + * \brief Load converter configuration from file >> + * \param[in] filename The file name path >> + * >> + * Load converter dependent configuration parameters to apply on the hardware. >> + * >> + * \return 0 on success or a negative error code otherwise >> + */ >> + >> +/** >> + * \fn Converter::isValid() >> + * \brief Check if the converter configuration is valid >> + * \return True is the converter is valid, false otherwise >> + */ >> + >> +/** >> + * \fn Converter::formats() >> + * \brief Retrieve the list of supported pixel formats for an input pixel format >> + * \param[in] input Input pixel format to retrieve output pixel format list for >> + * \return The list of output supported pixel formats > > I would swap "output supported" to "supported output" > >> + */ >> + >> +/** >> + * \fn Converter::sizes() >> + * \brief Retrieve the output range of minimum and maximum sizes for an input size > > Here too: "range of minimum and maximum output sizes" > >> + * \param[in] input Input stream size to retrieve range for >> + * \return A range of output image sizes >> + */ >> + >> +/** >> + * \fn Converter::strideAndFrameSize() >> + * \brief Retrieve the output stride and frame size for an input configutation >> + * \param[in] pixelFormat Input stream pixel format >> + * \param[in] size Input stream size >> + * \return A tuple indicating the stride and frame size or an empty tuple on error >> + */ >> + >> +/** >> + * \fn Converter::configure() >> + * \brief Configure a set of output stream conversion from an input stream >> + * \param[in] inputCfg Input stream configuration >> + * \param[in] outputCfg A list of output stream configurations > > Is this an [out] parameter ? Yes, changed in v2 > >> + * \return 0 on success or a negative error code otherwise >> + */ >> + >> +/** >> + * \fn Converter::exportBuffers() >> + * \brief Export buffers from the converter device >> + * \param[in] output Output stream index exporting the buffers >> + * \param[in] count Number of buffers to allocate >> + * \param[out] buffers Vector to store allocated buffers >> + * >> + * This function operates similarly as V4L2VideoDevice::exportBuffers() on the >> + * output stream indicated by the \a output index. >> + * >> + * \return The number of allocated buffers on success or a negative error code >> + * otherwise >> + */ >> + >> +/** >> + * \fn Converter::start() >> + * \brief Start the converter streaming operation >> + * \return 0 on success or a negative error code otherwise >> + */ >> + >> +/** >> + * \fn Converter::stop() >> + * \brief Stop the converter streaming operation >> + * \return 0 on success or a negative error code otherwise >> + */ >> + >> +/** >> + * \fn Converter::queueBuffers() >> + * \brief Queue buffers to converter device >> + * \param[in] input The frame buffer to apply the conversion >> + * \param[out] buffers The container holding the output stream indexes and >> + * their respective frame buffer outputs. >> + * >> + * This function queues the \a input frame buffer on the output streams of the >> + * \a buffers map key and retrieve the output frame buffer indicated by the >> + * \a buffer map value. > > "buffer" is not an argument. Maybe "buffers". > > I'll better understand how the output map is populated in the next > patch > >> + * >> + * \return 0 on success or a negative error code otherwise >> + */ >> + >> +/** >> + * \var Converter::inputBufferReady >> + * \brief A signal emitted when the input frame buffer completes >> + */ >> + >> +/** >> + * \var Converter::outputBufferReady >> + * \brief A signal emitted when the output frame buffer completes >> + */ >> + >> +/** >> + * \fn Converter::deviceNode() >> + * \brief The converter device node attribute accessor >> + * \return The converter device node string >> + */ >> + >> +/** >> + * \class ConverterFactoryBase >> + * \brief Base class for converter factories >> + * >> + * The ConverterFactoryBase class is the base of all specializations of the >> + * ConverterFactory class template. It implements the factory registration, >> + * maintains a registry of factories, and provides access to the registered >> + * factories. >> + */ >> + >> +/** >> + * \brief Construct a converter factory base >> + * \param[in] name Name of the converter class >> + * >> + * Creating an instance of the factory base registers it with the global list of >> + * factories, accessible through the factories() function. >> + * >> + * The factory \a name is used as unique identifier. > > Please don't break lines in paragraphs. > > Either > One line. > > With a line break in between. > > Or > One sentence. And the paragraph continues without breaking. > >> + * If the converter implemententation fully relies on a generic framework, the >> + * name should be the same as the framework. >> + * Otherwise, if the implementation is specialized, the factory name should match >> + * the driver name implementing the function. >> + * The factory \a aliases holds a list of driver names implementing a generic >> + * subsystem without any personalizations. >> + */ >> +ConverterFactoryBase::ConverterFactoryBase(const std::string name, std::initializer_list<std::string> aliases) >> + : name_(name), aliases_(aliases) >> +{ >> + registerType(this); >> +} >> + >> +/** >> + * \brief Create an instance of the converter corresponding to a named factory >> + * \param[in] name Name of the factory >> + * >> + * \return A unique pointer to a new instance of the converter subclass >> + * corresponding to the named factory or one of its alias. Otherwise a null >> + * pointer if no such factory exists >> + */ >> +std::unique_ptr<Converter> ConverterFactoryBase::create(MediaDevice *media) >> +{ >> + const std::vector<ConverterFactoryBase *> &factories = >> + ConverterFactoryBase::factories(); >> + >> + for (const ConverterFactoryBase *factory : factories) { >> + std::vector<std::string> aliases = factory->aliases(); > > &aliases > >> + auto it = std::find(aliases.begin(), aliases.end(), media->driver()); >> + >> + if (it == aliases.end() && media->driver() != factory->name_) >> + continue; >> + >> + LOG(Converter, Debug) >> + << "Creating converter from " >> + << factory->name_ << " factory with " >> + << (it == aliases.end() ? "no" : media->driver()) << " alias."; >> + >> + return factory->createInstance(media); >> + } >> + >> + return nullptr; >> +} >> + >> +/** >> + * \brief Add a converter class to the registry >> + * \param[in] factory Factory to use to construct the converter class >> + * >> + * The caller is responsible to guarantee the uniqueness of the converter name. >> + */ >> +void ConverterFactoryBase::registerType(ConverterFactoryBase *factory) >> +{ >> + std::vector<ConverterFactoryBase *> &factories = >> + ConverterFactoryBase::factories(); >> + >> + factories.push_back(factory); >> +} >> + >> +/** >> + * \brief Retrieve the list of all converter factory names >> + * \return The list of all converter factory names >> + */ >> +std::vector<std::string> ConverterFactoryBase::names() > > What is this function for ? Return the names of all converters > registered in the system ? Yes. Am I missing where it is used ? Currently not used but will be used in next series involving rkisp1 pipeline. Pipeline handlers can retrieve a converter available in the system without hardcoding its name. > >> +{ >> + std::vector<std::string> list; >> + >> + std::vector<ConverterFactoryBase *> &factories = >> + ConverterFactoryBase::factories(); >> + >> + for (ConverterFactoryBase *factory : factories) { >> + list.push_back(factory->name_); >> + for (auto alias : factory->aliases()) >> + list.push_back(alias); >> + } >> + >> + return list; >> +} >> + >> +/** >> + * \brief Retrieve the list of all converter factories >> + * \return The list of converter factories >> + */ >> +std::vector<ConverterFactoryBase *> &ConverterFactoryBase::factories() >> +{ >> + /* >> + * The static factories map is defined inside the function to ensure >> + * it gets initialized on first use, without any dependency on link >> + * order. >> + */ >> + static std::vector<ConverterFactoryBase *> factories; >> + return factories; >> +} >> + >> +/** >> + * \var ConverterFactoryBase::name_ >> + * \brief The name of the factory >> + */ >> + >> +/** >> + * \var ConverterFactoryBase::aliases_ >> + * \brief The list holding the factory aliases >> + */ >> + >> +/** >> + * \class ConverterFactory >> + * \brief Registration of ConverterFactory classes and creation of instances >> + * \param _Converter The converter class type for this factory >> + * >> + * To facilitate discovery and instantiation of Converter classes, the >> + * ConverterFactory class implements auto-registration of converter helpers. >> + * Each Converter subclass shall register itself using the REGISTER_CONVERTER() >> + * macro, which will create a corresponding instance of a ConverterFactory >> + * subclass and register it with the static list of factories. >> + */ >> + >> +/** >> + * \fn ConverterFactory::ConverterFactory(const char *name, std::initializer_list<std::string> aliases) >> + * \brief Construct a converter factory >> + * \param[in] name Name of the converter class >> + * \param[in] aliases Aliases of the converter class >> + * >> + * Creating an instance of the factory registers it with the global list of >> + * factories, accessible through the factories() function. >> + * >> + * The factory \a name is used as unique identifier. >> + * If the converter implemententation fully relies on a generic framework, the >> + * name should be the same as the framework. >> + * Otherwise, if the implementation is specialized, the factory name should match >> + * the driver name implementing the function. >> + * The factory \a aliases holds a list of driver names implementing a generic >> + * subsystem without any personalizations. > > Same comment as above. > Also, you can use \copydoc > >> + */ >> + >> +/** >> + * \fn ConverterFactory::createInstance() const >> + * \brief Create an instance of the Converter corresponding to the factory >> + * \param[in] media Media device pointer >> + * \return A unique pointer to a newly constructed instance of the Converter >> + * subclass corresponding to the factory >> + */ >> + >> +/** >> + * \def REGISTER_CONVERTER >> + * \brief Register a converter with the Converter factory >> + * \param[in] name Converter name used to register the class >> + * \param[in] converter Class name of Converter derived class to register >> + * \param[in] aliases Optional list of alias names >> + * >> + * Register a Converter subclass with the factory and make it available to try >> + * and match converters. >> + */ >> + >> +} /* namespace libcamera */ >> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build >> index 7fcbb2dd..3a9fc31f 100644 >> --- a/src/libcamera/meson.build >> +++ b/src/libcamera/meson.build >> @@ -13,6 +13,7 @@ libcamera_sources = files([ >> 'controls.cpp', >> 'control_serializer.cpp', >> 'control_validator.cpp', >> + 'converter.cpp', >> 'delayed_controls.cpp', >> 'device_enumerator.cpp', >> 'device_enumerator_sysfs.cpp', >> -- >> 2.37.3 >> Regards, Xavier
diff --git a/include/libcamera/internal/converter.h b/include/libcamera/internal/converter.h new file mode 100644 index 00000000..38c05ee9 --- /dev/null +++ b/include/libcamera/internal/converter.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Laurent Pinchart + * Copyright 2022 NXP + * + * converter.h - Generic stream converter infrastructure + */ + +#pragma once + +#include <map> +#include <memory> +#include <string> +#include <tuple> +#include <vector> + +#include <libcamera/base/class.h> +#include <libcamera/base/signal.h> + +#include <libcamera/geometry.h> +#include <libcamera/pixel_format.h> + +namespace libcamera { + +class FrameBuffer; +class MediaDevice; +class Size; +class SizeRange; +struct StreamConfiguration; + +class Converter +{ +public: + Converter(MediaDevice *media); + virtual ~Converter(); + + virtual int loadConfiguration(const std::string &filename) = 0; + + virtual bool isValid() const = 0; + + virtual std::vector<PixelFormat> formats(PixelFormat input) = 0; + virtual SizeRange sizes(const Size &input) = 0; + + virtual std::tuple<unsigned int, unsigned int> + strideAndFrameSize(const PixelFormat &pixelFormat, const Size &size) = 0; + + virtual int configure(const StreamConfiguration &inputCfg, + const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfg) = 0; + virtual int exportBuffers(unsigned int ouput, unsigned int count, + std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0; + + virtual int start() = 0; + virtual void stop() = 0; + + virtual int queueBuffers(FrameBuffer *input, + const std::map<unsigned int, FrameBuffer *> &outputs) = 0; + + Signal<FrameBuffer *> inputBufferReady; + Signal<FrameBuffer *> outputBufferReady; + + const std::string &deviceNode() const { return deviceNode_; }; + +private: + std::string deviceNode_; +}; + +class ConverterFactoryBase +{ +public: + ConverterFactoryBase(const std::string name, std::initializer_list<std::string> aliases); + virtual ~ConverterFactoryBase() = default; + + const std::vector<std::string> &aliases() const { return aliases_; } + + static std::unique_ptr<Converter> create(MediaDevice *media); + static std::vector<ConverterFactoryBase *> &factories(); + static std::vector<std::string> names(); + +private: + LIBCAMERA_DISABLE_COPY_AND_MOVE(ConverterFactoryBase) + + static void registerType(ConverterFactoryBase *factory); + + virtual std::unique_ptr<Converter> createInstance(MediaDevice *media) const = 0; + + std::string name_; + std::vector<std::string> aliases_; +}; + +template<typename _Converter> +class ConverterFactory : public ConverterFactoryBase +{ +public: + ConverterFactory(const char *name, std::initializer_list<std::string> aliases) + : ConverterFactoryBase(name, aliases) + { + } + + std::unique_ptr<Converter> createInstance(MediaDevice *media) const override + { + return std::make_unique<_Converter>(media); + } +}; + +#define REGISTER_CONVERTER(name, converter, ...) \ + static ConverterFactory<converter> global_##converter##Factory(name, { __VA_ARGS__ }); + +} /* namespace libcamera */ diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build index 7a780d48..8f50d755 100644 --- a/include/libcamera/internal/meson.build +++ b/include/libcamera/internal/meson.build @@ -19,6 +19,7 @@ libcamera_internal_headers = files([ 'camera_sensor_properties.h', 'control_serializer.h', 'control_validator.h', + 'converter.h', 'delayed_controls.h', 'device_enumerator.h', 'device_enumerator_sysfs.h', diff --git a/src/libcamera/converter.cpp b/src/libcamera/converter.cpp new file mode 100644 index 00000000..5e444f5f --- /dev/null +++ b/src/libcamera/converter.cpp @@ -0,0 +1,337 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright 2022 NXP + * + * converter.cpp - Generic Format converter interface + */ + +#include "libcamera/internal/converter.h" + +#include <algorithm> + +#include <libcamera/base/log.h> + +#include "libcamera/internal/media_device.h" + +/** + * \file internal/converter.h + * \brief Abstract converter + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(Converter) + +/** + * \class Converter + * \brief Abstract Base Class for converter + * + * The Converter class is an Abstract Base Class defining the interfaces of + * converter implementations. + * + * Converters offers scaling and pixel formats conversion services on a input + * stream. The converter can output multiple streams with individual conversion + * parameters from the same input stream. + */ + +/** + * \brief Construct a Converter instance + * \param[in] media The media device implementing the converter + * + * This seeks for the entity implementing data streaming function in the media + * graph entities and use its device node as converter device node. + */ +Converter::Converter(MediaDevice *media) +{ + const std::vector<MediaEntity *> &entities = media->entities(); + auto it = std::find_if(entities.begin(), entities.end(), + [](MediaEntity *entity) { + return entity->function() == MEDIA_ENT_F_IO_V4L; + }); + if (it == entities.end()) { + LOG(Converter, Error) + << "No entity suitable for implementing a converter in " + << media->driver() << " entities list."; + return; + } + + deviceNode_ = (*it)->deviceNode(); +} + +Converter::~Converter() +{ +} + +/** + * \fn Converter::loadConfiguration() + * \brief Load converter configuration from file + * \param[in] filename The file name path + * + * Load converter dependent configuration parameters to apply on the hardware. + * + * \return 0 on success or a negative error code otherwise + */ + +/** + * \fn Converter::isValid() + * \brief Check if the converter configuration is valid + * \return True is the converter is valid, false otherwise + */ + +/** + * \fn Converter::formats() + * \brief Retrieve the list of supported pixel formats for an input pixel format + * \param[in] input Input pixel format to retrieve output pixel format list for + * \return The list of output supported pixel formats + */ + +/** + * \fn Converter::sizes() + * \brief Retrieve the output range of minimum and maximum sizes for an input size + * \param[in] input Input stream size to retrieve range for + * \return A range of output image sizes + */ + +/** + * \fn Converter::strideAndFrameSize() + * \brief Retrieve the output stride and frame size for an input configutation + * \param[in] pixelFormat Input stream pixel format + * \param[in] size Input stream size + * \return A tuple indicating the stride and frame size or an empty tuple on error + */ + +/** + * \fn Converter::configure() + * \brief Configure a set of output stream conversion from an input stream + * \param[in] inputCfg Input stream configuration + * \param[in] outputCfg A list of output stream configurations + * \return 0 on success or a negative error code otherwise + */ + +/** + * \fn Converter::exportBuffers() + * \brief Export buffers from the converter device + * \param[in] output Output stream index exporting the buffers + * \param[in] count Number of buffers to allocate + * \param[out] buffers Vector to store allocated buffers + * + * This function operates similarly as V4L2VideoDevice::exportBuffers() on the + * output stream indicated by the \a output index. + * + * \return The number of allocated buffers on success or a negative error code + * otherwise + */ + +/** + * \fn Converter::start() + * \brief Start the converter streaming operation + * \return 0 on success or a negative error code otherwise + */ + +/** + * \fn Converter::stop() + * \brief Stop the converter streaming operation + * \return 0 on success or a negative error code otherwise + */ + +/** + * \fn Converter::queueBuffers() + * \brief Queue buffers to converter device + * \param[in] input The frame buffer to apply the conversion + * \param[out] buffers The container holding the output stream indexes and + * their respective frame buffer outputs. + * + * This function queues the \a input frame buffer on the output streams of the + * \a buffers map key and retrieve the output frame buffer indicated by the + * \a buffer map value. + * + * \return 0 on success or a negative error code otherwise + */ + +/** + * \var Converter::inputBufferReady + * \brief A signal emitted when the input frame buffer completes + */ + +/** + * \var Converter::outputBufferReady + * \brief A signal emitted when the output frame buffer completes + */ + +/** + * \fn Converter::deviceNode() + * \brief The converter device node attribute accessor + * \return The converter device node string + */ + +/** + * \class ConverterFactoryBase + * \brief Base class for converter factories + * + * The ConverterFactoryBase class is the base of all specializations of the + * ConverterFactory class template. It implements the factory registration, + * maintains a registry of factories, and provides access to the registered + * factories. + */ + +/** + * \brief Construct a converter factory base + * \param[in] name Name of the converter class + * + * Creating an instance of the factory base registers it with the global list of + * factories, accessible through the factories() function. + * + * The factory \a name is used as unique identifier. + * If the converter implemententation fully relies on a generic framework, the + * name should be the same as the framework. + * Otherwise, if the implementation is specialized, the factory name should match + * the driver name implementing the function. + * The factory \a aliases holds a list of driver names implementing a generic + * subsystem without any personalizations. + */ +ConverterFactoryBase::ConverterFactoryBase(const std::string name, std::initializer_list<std::string> aliases) + : name_(name), aliases_(aliases) +{ + registerType(this); +} + +/** + * \brief Create an instance of the converter corresponding to a named factory + * \param[in] name Name of the factory + * + * \return A unique pointer to a new instance of the converter subclass + * corresponding to the named factory or one of its alias. Otherwise a null + * pointer if no such factory exists + */ +std::unique_ptr<Converter> ConverterFactoryBase::create(MediaDevice *media) +{ + const std::vector<ConverterFactoryBase *> &factories = + ConverterFactoryBase::factories(); + + for (const ConverterFactoryBase *factory : factories) { + std::vector<std::string> aliases = factory->aliases(); + auto it = std::find(aliases.begin(), aliases.end(), media->driver()); + + if (it == aliases.end() && media->driver() != factory->name_) + continue; + + LOG(Converter, Debug) + << "Creating converter from " + << factory->name_ << " factory with " + << (it == aliases.end() ? "no" : media->driver()) << " alias."; + + return factory->createInstance(media); + } + + return nullptr; +} + +/** + * \brief Add a converter class to the registry + * \param[in] factory Factory to use to construct the converter class + * + * The caller is responsible to guarantee the uniqueness of the converter name. + */ +void ConverterFactoryBase::registerType(ConverterFactoryBase *factory) +{ + std::vector<ConverterFactoryBase *> &factories = + ConverterFactoryBase::factories(); + + factories.push_back(factory); +} + +/** + * \brief Retrieve the list of all converter factory names + * \return The list of all converter factory names + */ +std::vector<std::string> ConverterFactoryBase::names() +{ + std::vector<std::string> list; + + std::vector<ConverterFactoryBase *> &factories = + ConverterFactoryBase::factories(); + + for (ConverterFactoryBase *factory : factories) { + list.push_back(factory->name_); + for (auto alias : factory->aliases()) + list.push_back(alias); + } + + return list; +} + +/** + * \brief Retrieve the list of all converter factories + * \return The list of converter factories + */ +std::vector<ConverterFactoryBase *> &ConverterFactoryBase::factories() +{ + /* + * The static factories map is defined inside the function to ensure + * it gets initialized on first use, without any dependency on link + * order. + */ + static std::vector<ConverterFactoryBase *> factories; + return factories; +} + +/** + * \var ConverterFactoryBase::name_ + * \brief The name of the factory + */ + +/** + * \var ConverterFactoryBase::aliases_ + * \brief The list holding the factory aliases + */ + +/** + * \class ConverterFactory + * \brief Registration of ConverterFactory classes and creation of instances + * \param _Converter The converter class type for this factory + * + * To facilitate discovery and instantiation of Converter classes, the + * ConverterFactory class implements auto-registration of converter helpers. + * Each Converter subclass shall register itself using the REGISTER_CONVERTER() + * macro, which will create a corresponding instance of a ConverterFactory + * subclass and register it with the static list of factories. + */ + +/** + * \fn ConverterFactory::ConverterFactory(const char *name, std::initializer_list<std::string> aliases) + * \brief Construct a converter factory + * \param[in] name Name of the converter class + * \param[in] aliases Aliases of the converter class + * + * Creating an instance of the factory registers it with the global list of + * factories, accessible through the factories() function. + * + * The factory \a name is used as unique identifier. + * If the converter implemententation fully relies on a generic framework, the + * name should be the same as the framework. + * Otherwise, if the implementation is specialized, the factory name should match + * the driver name implementing the function. + * The factory \a aliases holds a list of driver names implementing a generic + * subsystem without any personalizations. + */ + +/** + * \fn ConverterFactory::createInstance() const + * \brief Create an instance of the Converter corresponding to the factory + * \param[in] media Media device pointer + * \return A unique pointer to a newly constructed instance of the Converter + * subclass corresponding to the factory + */ + +/** + * \def REGISTER_CONVERTER + * \brief Register a converter with the Converter factory + * \param[in] name Converter name used to register the class + * \param[in] converter Class name of Converter derived class to register + * \param[in] aliases Optional list of alias names + * + * Register a Converter subclass with the factory and make it available to try + * and match converters. + */ + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 7fcbb2dd..3a9fc31f 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -13,6 +13,7 @@ libcamera_sources = files([ 'controls.cpp', 'control_serializer.cpp', 'control_validator.cpp', + 'converter.cpp', 'delayed_controls.cpp', 'device_enumerator.cpp', 'device_enumerator_sysfs.cpp',