[libcamera-devel] [PATCH 1/2] libcamera: Declare generic converter interface

Xavier Roumegue (OSS) xavier.roumegue at oss.nxp.com
Tue Nov 15 10:51:36 CET 2022


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 at 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 at 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


More information about the libcamera-devel mailing list