Message ID | 20250624083612.27230-6-mzamazal@redhat.com |
---|---|
State | New |
Headers | show |
Series |
|
Related | show |
Hi 2025. 06. 24. 10:36 keltezéssel, Milan Zamazal írta: > This patch adds configuration options for environment variables used in > the IPA proxy. Two utility functions configuration retrieval functions > are added, to retrieve lists of values. > > The configuration snippet: > > configuration: > ipa: > config_paths: > - config path 1 > - config path 2 > - ... > module_paths: > - module path 1 > - module path 2 > - ... > proxy_paths: > - proxy path 1 > - proxy path 2 > - ... > force_isolation: BOOL > ipas: > IPA_NAME: > tuning_file: TUNING FILE > > Signed-off-by: Milan Zamazal <mzamazal@redhat.com> > --- > .../libcamera/internal/global_configuration.h | 35 ++++++++- > include/libcamera/internal/ipa_manager.h | 8 ++- > include/libcamera/internal/ipa_proxy.h | 5 +- > src/libcamera/camera_manager.cpp | 2 +- > src/libcamera/global_configuration.cpp | 47 +++++++++++- > src/libcamera/ipa_manager.cpp | 37 ++++++---- > src/libcamera/ipa_proxy.cpp | 72 ++++++++++--------- > src/libcamera/pipeline/ipu3/ipu3.cpp | 3 +- > src/libcamera/pipeline/mali-c55/mali-c55.cpp | 2 + > src/libcamera/pipeline/rkisp1/rkisp1.cpp | 4 +- > .../pipeline/rpi/common/pipeline_base.cpp | 3 +- > src/libcamera/pipeline/vimc/vimc.cpp | 3 +- > src/libcamera/software_isp/software_isp.cpp | 4 +- > test/ipa/ipa_interface_test.cpp | 3 +- > .../module_ipa_proxy.cpp.tmpl | 4 +- > .../module_ipa_proxy.h.tmpl | 2 +- > 16 files changed, 169 insertions(+), 65 deletions(-) > > diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h > index 357119628..cc436703e 100644 > --- a/include/libcamera/internal/global_configuration.h > +++ b/include/libcamera/internal/global_configuration.h > @@ -11,6 +11,8 @@ > #include <optional> > #include <string> > > +#include <libcamera/base/utils.h> > + > #include "libcamera/internal/yaml_parser.h" > > namespace libcamera { > @@ -24,9 +26,40 @@ public: > > unsigned int version() const; > Configuration configuration() const; > - std::optional<std::string> option(const std::string &confPath) const; > + > +#ifndef __DOXYGEN__ > + template<typename T, > + std::enable_if_t< > + std::is_same_v<bool, T> || > + std::is_same_v<double, T> || > + std::is_same_v<int8_t, T> || > + std::is_same_v<uint8_t, T> || > + std::is_same_v<int16_t, T> || > + std::is_same_v<uint16_t, T> || > + std::is_same_v<int32_t, T> || > + std::is_same_v<uint32_t, T> || > + std::is_same_v<std::string, T> || > + std::is_same_v<Size, T>> * = nullptr> Is this needed? `YamlObject::get()` does not have any constraints, so I think it can be omitted here as well? > +#else > + template<typename T> > +#endif > + std::optional<T> option(const std::string &confPath) const > + { > + const YamlObject *c = &configuration(); > + for (auto part : utils::split(confPath, "/")) { > + c = &(*c)[part]; > + if (!*c) > + return {}; > + } > + return c->get<T>(); > + } > + > + std::vector<std::string> listOption(const std::string &confPath) const; > std::optional<std::string> envOption(const char *const envVariable, > const std::string &confPath) const; > + std::vector<std::string> envListOption( > + const char *const envVariable, > + const std::string &confPath) const; > > private: > bool loadFile(const std::filesystem::path &fileName); > diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h > index a0d448cf9..8ad717919 100644 > --- a/include/libcamera/internal/ipa_manager.h > +++ b/include/libcamera/internal/ipa_manager.h > @@ -17,6 +17,7 @@ > #include <libcamera/ipa/ipa_module_info.h> > > #include "libcamera/internal/camera_manager.h" > +#include "libcamera/internal/global_configuration.h" > #include "libcamera/internal/ipa_module.h" > #include "libcamera/internal/pipeline_handler.h" > #include "libcamera/internal/pub_key.h" > @@ -28,7 +29,7 @@ LOG_DECLARE_CATEGORY(IPAManager) > class IPAManager > { > public: > - IPAManager(); > + IPAManager(const GlobalConfiguration &configuration); > ~IPAManager(); > > template<typename T> > @@ -42,7 +43,8 @@ public: > if (!m) > return nullptr; > > - std::unique_ptr<T> proxy = std::make_unique<T>(m, !self->isSignatureValid(m)); > + const GlobalConfiguration &configuration = cm->_d()->configuration(); > + std::unique_ptr<T> proxy = std::make_unique<T>(m, !self->isSignatureValid(m, configuration), configuration); > if (!proxy->isValid()) { > LOG(IPAManager, Error) << "Failed to load proxy"; > return nullptr; > @@ -66,7 +68,7 @@ private: > IPAModule *module(PipelineHandler *pipe, uint32_t minVersion, > uint32_t maxVersion); > > - bool isSignatureValid(IPAModule *ipa) const; > + bool isSignatureValid(IPAModule *ipa, const GlobalConfiguration &configuration) const; > > std::vector<std::unique_ptr<IPAModule>> modules_; > > diff --git a/include/libcamera/internal/ipa_proxy.h b/include/libcamera/internal/ipa_proxy.h > index 983bcc5fa..01cc5deff 100644 > --- a/include/libcamera/internal/ipa_proxy.h > +++ b/include/libcamera/internal/ipa_proxy.h > @@ -11,6 +11,8 @@ > > #include <libcamera/ipa/ipa_interface.h> > > +#include "libcamera/internal/global_configuration.h" > + > namespace libcamera { > > class IPAModule; > @@ -30,10 +32,11 @@ public: > bool isValid() const { return valid_; } > > std::string configurationFile(const std::string &name, > + const GlobalConfiguration &configuration, > const std::string &fallbackName = std::string()) const; > > protected: > - std::string resolvePath(const std::string &file) const; > + std::string resolvePath(const std::string &file, const GlobalConfiguration &configuration) const; I feel like `IPAProxy` could take the `GlobalConfiguration` in the constructor? > > bool valid_; > ProxyState state_; > diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp > index f3b4ec708..c47fd3677 100644 > --- a/src/libcamera/camera_manager.cpp > +++ b/src/libcamera/camera_manager.cpp > @@ -41,7 +41,7 @@ LOG_DEFINE_CATEGORY(Camera) > CameraManager::Private::Private() > : initialized_(false) > { > - ipaManager_ = std::make_unique<IPAManager>(); > + ipaManager_ = std::make_unique<IPAManager>(this->configuration()); > } > > int CameraManager::Private::start() > diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp > index 068ddd5a5..f7c69890c 100644 > --- a/src/libcamera/global_configuration.cpp > +++ b/src/libcamera/global_configuration.cpp > @@ -9,9 +9,11 @@ > > #include <filesystem> > #include <memory> > +#include <optional> > #include <string> > #include <string_view> > #include <sys/types.h> > +#include <vector> > > #include <libcamera/base/file.h> > #include <libcamera/base/log.h> > @@ -116,13 +118,22 @@ GlobalConfiguration::GlobalConfiguration() > */ > > /** > + * \fn std::optional<T> GlobalConfiguration::option(const std::string &confPath) const > * \brief Return value of the configuration option identified by \a confPath > * \param[in] confPath Sequence of the YAML section names (excluding > * `configuration') leading to the requested option separated by slashes > * \return A value if an item corresponding to \a confPath exists in the > * configuration file, no value otherwise > */ > -std::optional<std::string> GlobalConfiguration::option(const std::string &confPath) const > + > +/** > + * \brief Return values of the configuration option identified by \a confPath > + * \tparam T The type of the retrieved configuration value > + * \param[in] confPath Sequence of the YAML section names (excluding > + * `configuration') leading to the requested list option, separated by dots > + * \return A vector of strings (empty one if the option is not found) > + */ > +std::vector<std::string> GlobalConfiguration::listOption(const std::string &confPath) const > { > const YamlObject *c = &configuration(); > for (auto part : utils::split(confPath, "/")) { > @@ -130,7 +141,7 @@ std::optional<std::string> GlobalConfiguration::option(const std::string &confPa > if (!*c) > return {}; > } > - return c->get<std::string>(); > + return c->getList<std::string>().value_or(std::vector<std::string>()); > } > > /** > @@ -157,7 +168,37 @@ std::optional<std::string> GlobalConfiguration::envOption( > const char *envValue = utils::secure_getenv(envVariable); > if (envValue) > return std::optional{ std::string{ envValue } }; > - return option(confPath); > + return option<std::string>(confPath); > +} > + > +/** > + * \brief Return values of the configuration option from a file or environment > + * \param[in] envVariable Environment variable to get the value from > + * \param[in] confPath The same as in GlobalConfiguration::option > + * > + * This helper looks first at the given environment variable and if it is > + * defined (even if it is empty) then it splits its value by semicolons and > + * returns the resulting list of strings. Otherwise it looks for \a confPath the > + * same way as in GlobalConfiguration::option, value of which must be a list of > + * strings. > + * > + * \note Support for using environment variables to configure libcamera behavior > + * is provided here mostly for backward compatibility reasons. Introducing new > + * configuration environment variables is discouraged. > + * > + * \return A vector of strings retrieved from the given environment option or > + * configuration file (an empty vector is returned if nothing is found) I think we want to distinguish "not present" and "empty". I could imagine that someone might want to inhibit some behaviour by setting the env var to empty. `envOption()` also differentiates the two. For example, with the new "layers" proposal, I could image that we can set the layers in a configuration file, etc, as well as an environmental variable. In that case doing `LIBCAMERA_LAYERS= cam ...` might be useful for testing to temporarily disable all layers. > + */ > +std::vector<std::string> GlobalConfiguration::envListOption( > + const char *const envVariable, > + const std::string &confPath) const > +{ > + const char *envValue = utils::secure_getenv(envVariable); > + if (envValue) { > + auto items = utils::split(envValue, ":"); > + return std::vector<std::string>(items.begin(), items.end()); > + } > + return listOption(confPath); > } > > /** > diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp > index 830750dcc..67c1b23d2 100644 > --- a/src/libcamera/ipa_manager.cpp > +++ b/src/libcamera/ipa_manager.cpp > @@ -9,6 +9,7 @@ > > #include <algorithm> > #include <dirent.h> > +#include <numeric> > #include <string.h> > #include <sys/types.h> > > @@ -16,6 +17,7 @@ > #include <libcamera/base/log.h> > #include <libcamera/base/utils.h> > > +#include "libcamera/internal/global_configuration.h" > #include "libcamera/internal/ipa_module.h" > #include "libcamera/internal/ipa_proxy.h" > #include "libcamera/internal/pipeline_handler.h" > @@ -101,7 +103,7 @@ LOG_DEFINE_CATEGORY(IPAManager) > * The IPAManager class is meant to only be instantiated once, by the > * CameraManager. > */ > -IPAManager::IPAManager() > +IPAManager::IPAManager(const GlobalConfiguration &configuration) > { > #if HAVE_IPA_PUBKEY > if (!pubKey_.isValid()) > @@ -111,18 +113,25 @@ IPAManager::IPAManager() > unsigned int ipaCount = 0; > > /* User-specified paths take precedence. */ > - const char *modulePaths = utils::secure_getenv("LIBCAMERA_IPA_MODULE_PATH"); > - if (modulePaths) { > - for (const auto &dir : utils::split(modulePaths, ":")) { > - if (dir.empty()) > - continue; > + const auto modulePaths = > + configuration.envListOption( > + "LIBCAMERA_IPA_MODULE_PATH", "ipa/module_paths"); > + for (const auto &dir : modulePaths) { > + if (dir.empty()) > + continue; > > - ipaCount += addDir(dir.c_str()); > - } > + ipaCount += addDir(dir.c_str()); > + } > > - if (!ipaCount) > - LOG(IPAManager, Warning) > - << "No IPA found in '" << modulePaths << "'"; > + if (!ipaCount) { > + std::string paths; > + if (!modulePaths.empty()) { > + paths = std::accumulate(std::next(modulePaths.begin()), > + modulePaths.end(), > + modulePaths[0], > + [](std::string s1, std::string s2) { return s1 + ":" + s2; }); > + } > + LOG(IPAManager, Warning) << "No IPA found in '" << paths << "'"; #include <libcamera/base/utils.h> ... LOG(IPAManager, Warning) << "No IPA found in '" << utils::join(modulePaths, ":") << "'"; ? > } > > /* > @@ -276,11 +285,13 @@ IPAModule *IPAManager::module(PipelineHandler *pipe, uint32_t minVersion, > */ > #endif > > -bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa) const > +bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa, const GlobalConfiguration &configuration) const Maybe `configuration.option<bool>("ipa/force_isolation").value_or(false)` could be saved into a member variable in the constructor? Then this won't need the configuration parameter. > { > #if HAVE_IPA_PUBKEY > char *force = utils::secure_getenv("LIBCAMERA_IPA_FORCE_ISOLATION"); > - if (force && force[0] != '\0') { > + if ((force && force[0] != '\0') || > + (!force && configuration.option<bool>("ipa/force_isolation") > + .value_or(false))) { > LOG(IPAManager, Debug) > << "Isolation of IPA module " << ipa->path() > << " forced through environment variable"; > diff --git a/src/libcamera/ipa_proxy.cpp b/src/libcamera/ipa_proxy.cpp > index 9907b9615..77927b0d4 100644 > --- a/src/libcamera/ipa_proxy.cpp > +++ b/src/libcamera/ipa_proxy.cpp > @@ -14,6 +14,7 @@ > #include <libcamera/base/log.h> > #include <libcamera/base/utils.h> > > +#include "libcamera/internal/global_configuration.h" > #include "libcamera/internal/ipa_module.h" > > /** > @@ -71,6 +72,7 @@ IPAProxy::~IPAProxy() > /** > * \brief Retrieve the absolute path to an IPA configuration file > * \param[in] name The configuration file name > + * \param[in] configuration The global configuration > * \param[in] fallbackName The name of a fallback configuration file > * > * This function locates the configuration file for an IPA and returns its > @@ -96,6 +98,7 @@ IPAProxy::~IPAProxy() > * no configuration file can be found > */ > std::string IPAProxy::configurationFile(const std::string &name, > + const GlobalConfiguration &configuration, > const std::string &fallbackName) const > { > /* > @@ -105,37 +108,37 @@ std::string IPAProxy::configurationFile(const std::string &name, > const std::string ipaName = ipam_->info().name; > > /* > - * Start with any user override through the module-specific environment > - * variable. Use the name of the IPA module up to the first '/' to > - * construct the variable name. > + * Start with any user override through the module-specific configuration or > + * environment variable. Use the name of the IPA module up to the first '/' > + * to construct the configuration and variable names. > */ > - std::string ipaEnvName = ipaName.substr(0, ipaName.find('/')); > + std::string ipaBaseName = ipaName.substr(0, ipaName.find('/')); > + std::string ipaConfigName = "ipa/ipas/" + ipaBaseName + "/tuning_file"; I feel like we're unnecessarily limiting outself wrt. rpi here. Since the IPA names are "rpi/vc4" and "rpi/pisp", thus `ipaBaseName == "rpi"`. Which means that it is impossible to specify different tuning files for the different platforms. I think this is fine for an env var, but for a configuration file I think this flexibility is expected. But I don't quite see the point of overriding the tuning file like this from a configuration file. So maybe leaving just the env var override is sufficient? > + std::string ipaEnvName = ipaBaseName; > std::transform(ipaEnvName.begin(), ipaEnvName.end(), ipaEnvName.begin(), > [](unsigned char c) { return std::toupper(c); }); > ipaEnvName = "LIBCAMERA_" + ipaEnvName + "_TUNING_FILE"; > > - char const *configFromEnv = utils::secure_getenv(ipaEnvName.c_str()); > - if (configFromEnv && *configFromEnv != '\0') > - return { configFromEnv }; > + auto config = configuration.envOption(ipaEnvName.c_str(), ipaConfigName); > + if (config) > + return { config.value() }; > > struct stat statbuf; > int ret; > > /* > - * Check the directory pointed to by the IPA config path environment > - * variable next. > + * Check the directory pointed to by the IPA config path next. > */ > - const char *confPaths = utils::secure_getenv("LIBCAMERA_IPA_CONFIG_PATH"); > - if (confPaths) { > - for (const auto &dir : utils::split(confPaths, ":")) { > - if (dir.empty()) > - continue; > - > - std::string confPath = dir + "/" + ipaName + "/" + name; > - ret = stat(confPath.c_str(), &statbuf); > - if (ret == 0 && (statbuf.st_mode & S_IFMT) == S_IFREG) > - return confPath; > - } > + auto confPaths = > + configuration.envListOption( > + "LIBCAMERA_IPA_CONFIG_PATH", "ipa/config_paths"); > + for (const auto &dir : confPaths) { > + if (dir.empty()) > + continue; > + std::string confPath = dir + "/" + ipaName + "/" + name; > + ret = stat(confPath.c_str(), &statbuf); > + if (ret == 0 && (statbuf.st_mode & S_IFMT) == S_IFREG) > + return confPath; > } > > std::string root = utils::libcameraSourcePath(); > @@ -178,12 +181,13 @@ std::string IPAProxy::configurationFile(const std::string &name, > << "Configuration file '" << name > << "' not found for IPA module '" << ipaName > << "', falling back to '" << fallbackName << "'"; > - return configurationFile(fallbackName); > + return configurationFile(fallbackName, configuration); > } > > /** > * \brief Find a valid full path for a proxy worker for a given executable name > * \param[in] file File name of proxy worker executable > + * \param[in] configuration The global configuration > * > * A proxy worker's executable could be found in either the global installation > * directory, or in the paths specified by the environment variable > @@ -195,22 +199,22 @@ std::string IPAProxy::configurationFile(const std::string &name, > * \return The full path to the proxy worker executable, or an empty string if > * no valid executable path > */ > -std::string IPAProxy::resolvePath(const std::string &file) const > +std::string IPAProxy::resolvePath(const std::string &file, const GlobalConfiguration &configuration) const > { > std::string proxyFile = "/" + file; > > - /* Check env variable first. */ > - const char *execPaths = utils::secure_getenv("LIBCAMERA_IPA_PROXY_PATH"); > - if (execPaths) { > - for (const auto &dir : utils::split(execPaths, ":")) { > - if (dir.empty()) > - continue; > - > - std::string proxyPath = dir; > - proxyPath += proxyFile; > - if (!access(proxyPath.c_str(), X_OK)) > - return proxyPath; > - } > + /* Check the configuration first. */ > + const auto execPaths = > + configuration.envListOption( > + "LIBCAMERA_IPA_PROXY_PATH", "ipa/proxy_paths"); > + for (const auto &dir : execPaths) { > + if (dir.empty()) > + continue; > + > + std::string proxyPath = dir; std::string proxyPath = dir + proxyFile; Regards, Barnabás Pőcze > + proxyPath += proxyFile; > + if (!access(proxyPath.c_str(), X_OK)) > + return proxyPath; > } > > /* > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp > index e31e3879d..723f7665b 100644 > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp > @@ -1183,8 +1183,9 @@ int IPU3CameraData::loadIPA() > * The API tuning file is made from the sensor name. If the tuning file > * isn't found, fall back to the 'uncalibrated' file. > */ > + const GlobalConfiguration &configuration = pipe()->cameraManager()->_d()->configuration(); > std::string ipaTuningFile = > - ipa_->configurationFile(sensor->model() + ".yaml", "uncalibrated.yaml"); > + ipa_->configurationFile(sensor->model() + ".yaml", configuration, "uncalibrated.yaml"); > > ret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() }, > sensorInfo, sensor->controls(), &ipaControls_); > diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp > index 4acc091bd..d346fa25f 100644 > --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp > +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp > @@ -388,7 +388,9 @@ int MaliC55CameraData::loadIPA() > > ipa_->setSensorControls.connect(this, &MaliC55CameraData::setSensorControls); > > + const GlobalConfiguration &configuration = pipe()->cameraManager()->_d()->configuration(); > std::string ipaTuningFile = ipa_->configurationFile(sensor_->model() + ".yaml", > + configuration, > "uncalibrated.yaml"); > > /* We need to inform the IPA of the sensor configuration */ > diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp > index 675f0a749..aded4cb43 100644 > --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp > +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp > @@ -40,6 +40,7 @@ > #include "libcamera/internal/delayed_controls.h" > #include "libcamera/internal/device_enumerator.h" > #include "libcamera/internal/framebuffer.h" > +#include "libcamera/internal/global_configuration.h" > #include "libcamera/internal/ipa_manager.h" > #include "libcamera/internal/media_device.h" > #include "libcamera/internal/media_pipeline.h" > @@ -383,8 +384,9 @@ int RkISP1CameraData::loadIPA(unsigned int hwRevision) > ipa_->metadataReady.connect(this, &RkISP1CameraData::metadataReady); > > /* The IPA tuning file is made from the sensor name. */ > + const GlobalConfiguration &configuration = pipe()->cameraManager()->_d()->configuration(); > std::string ipaTuningFile = > - ipa_->configurationFile(sensor_->model() + ".yaml", "uncalibrated.yaml"); > + ipa_->configurationFile(sensor_->model() + ".yaml", configuration, "uncalibrated.yaml"); > > IPACameraSensorInfo sensorInfo{}; > int ret = sensor_->sensorInfo(&sensorInfo); > diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp > index a316ef297..3da919ee7 100644 > --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp > +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp > @@ -1157,7 +1157,8 @@ int CameraData::loadIPA(ipa::RPi::InitResult *result) > std::string model = sensor_->model(); > if (isMonoSensor(sensor_)) > model += "_mono"; > - std::string configurationFile = ipa_->configurationFile(model + ".json"); > + const GlobalConfiguration &configuration = pipe()->cameraManager()->_d()->configuration(); > + std::string configurationFile = ipa_->configurationFile(model + ".json", configuration); > > IPASettings settings(configurationFile, sensor_->model()); > ipa::RPi::InitParams params; > diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp > index 07273bd2b..555d51bb6 100644 > --- a/src/libcamera/pipeline/vimc/vimc.cpp > +++ b/src/libcamera/pipeline/vimc/vimc.cpp > @@ -494,7 +494,8 @@ bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator) > > data->ipa_->paramsComputed.connect(data.get(), &VimcCameraData::paramsComputed); > > - std::string conf = data->ipa_->configurationFile("vimc.conf"); > + const GlobalConfiguration &configuration = manager_->_d()->configuration(); > + std::string conf = data->ipa_->configurationFile("vimc.conf", configuration); > Flags<ipa::vimc::TestFlag> inFlags = ipa::vimc::TestFlag::Flag2; > Flags<ipa::vimc::TestFlag> outFlags; > data->ipa_->init(IPASettings{ conf, data->sensor_->model() }, > diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp > index 28e2a360e..3ce354111 100644 > --- a/src/libcamera/software_isp/software_isp.cpp > +++ b/src/libcamera/software_isp/software_isp.cpp > @@ -21,6 +21,7 @@ > #include <libcamera/stream.h> > > #include "libcamera/internal/framebuffer.h" > +#include "libcamera/internal/global_configuration.h" > #include "libcamera/internal/ipa_manager.h" > #include "libcamera/internal/software_isp/debayer_params.h" > > @@ -130,8 +131,9 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, > * The API tuning file is made from the sensor name. If the tuning file > * isn't found, fall back to the 'uncalibrated' file. > */ > + const GlobalConfiguration &configuration = pipe->cameraManager()->_d()->configuration(); > std::string ipaTuningFile = > - ipa_->configurationFile(sensor->model() + ".yaml", "uncalibrated.yaml"); > + ipa_->configurationFile(sensor->model() + ".yaml", configuration, "uncalibrated.yaml"); > > IPACameraSensorInfo sensorInfo{}; > int ret = sensor->sensorInfo(&sensorInfo); > diff --git a/test/ipa/ipa_interface_test.cpp b/test/ipa/ipa_interface_test.cpp > index b81783664..1afcbf106 100644 > --- a/test/ipa/ipa_interface_test.cpp > +++ b/test/ipa/ipa_interface_test.cpp > @@ -106,7 +106,8 @@ protected: > } > > /* Test initialization of IPA module. */ > - std::string conf = ipa_->configurationFile("vimc.conf"); > + const GlobalConfiguration configuration; > + std::string conf = ipa_->configurationFile("vimc.conf", configuration); > Flags<ipa::vimc::TestFlag> inFlags; > Flags<ipa::vimc::TestFlag> outFlags; > int ret = ipa_->init(IPASettings{ conf, "vimc" }, > diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl > index 9a3aadbd2..71e920eb1 100644 > --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl > +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl > @@ -45,7 +45,7 @@ namespace {{ns}} { > {% endfor %} > {%- endif %} > > -{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate) > +{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration) > : IPAProxy(ipam), isolate_(isolate), > controlSerializer_(ControlSerializer::Role::Proxy), seq_(0) > { > @@ -54,7 +54,7 @@ namespace {{ns}} { > << ipam->path(); > > if (isolate_) { > - const std::string proxyWorkerPath = resolvePath("{{module_name}}_ipa_proxy"); > + const std::string proxyWorkerPath = resolvePath("{{module_name}}_ipa_proxy", configuration); > if (proxyWorkerPath.empty()) { > LOG(IPAProxy, Error) > << "Failed to get proxy worker path"; > diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl > index a0312a7c1..057c3ab03 100644 > --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl > +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl > @@ -37,7 +37,7 @@ namespace {{ns}} { > class {{proxy_name}} : public IPAProxy, public {{interface_name}}, public Object > { > public: > - {{proxy_name}}(IPAModule *ipam, bool isolate); > + {{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration); > ~{{proxy_name}}(); > > {% for method in interface_main.methods %}
Hi Barnabás, thank you for review. Barnabás Pőcze <barnabas.pocze@ideasonboard.com> writes: > Hi > > 2025. 06. 24. 10:36 keltezéssel, Milan Zamazal írta: >> This patch adds configuration options for environment variables used in >> the IPA proxy. Two utility functions configuration retrieval functions >> are added, to retrieve lists of values. >> The configuration snippet: >> configuration: >> ipa: >> config_paths: >> - config path 1 >> - config path 2 >> - ... >> module_paths: >> - module path 1 >> - module path 2 >> - ... >> proxy_paths: >> - proxy path 1 >> - proxy path 2 >> - ... >> force_isolation: BOOL >> ipas: >> IPA_NAME: >> tuning_file: TUNING FILE >> Signed-off-by: Milan Zamazal <mzamazal@redhat.com> >> --- >> .../libcamera/internal/global_configuration.h | 35 ++++++++- >> include/libcamera/internal/ipa_manager.h | 8 ++- >> include/libcamera/internal/ipa_proxy.h | 5 +- >> src/libcamera/camera_manager.cpp | 2 +- >> src/libcamera/global_configuration.cpp | 47 +++++++++++- >> src/libcamera/ipa_manager.cpp | 37 ++++++---- >> src/libcamera/ipa_proxy.cpp | 72 ++++++++++--------- >> src/libcamera/pipeline/ipu3/ipu3.cpp | 3 +- >> src/libcamera/pipeline/mali-c55/mali-c55.cpp | 2 + >> src/libcamera/pipeline/rkisp1/rkisp1.cpp | 4 +- >> .../pipeline/rpi/common/pipeline_base.cpp | 3 +- >> src/libcamera/pipeline/vimc/vimc.cpp | 3 +- >> src/libcamera/software_isp/software_isp.cpp | 4 +- >> test/ipa/ipa_interface_test.cpp | 3 +- >> .../module_ipa_proxy.cpp.tmpl | 4 +- >> .../module_ipa_proxy.h.tmpl | 2 +- >> 16 files changed, 169 insertions(+), 65 deletions(-) >> diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h >> index 357119628..cc436703e 100644 >> --- a/include/libcamera/internal/global_configuration.h >> +++ b/include/libcamera/internal/global_configuration.h >> @@ -11,6 +11,8 @@ >> #include <optional> >> #include <string> >> +#include <libcamera/base/utils.h> >> + >> #include "libcamera/internal/yaml_parser.h" >> namespace libcamera { >> @@ -24,9 +26,40 @@ public: >> unsigned int version() const; >> Configuration configuration() const; >> - std::optional<std::string> option(const std::string &confPath) const; >> + >> +#ifndef __DOXYGEN__ >> + template<typename T, >> + std::enable_if_t< >> + std::is_same_v<bool, T> || >> + std::is_same_v<double, T> || >> + std::is_same_v<int8_t, T> || >> + std::is_same_v<uint8_t, T> || >> + std::is_same_v<int16_t, T> || >> + std::is_same_v<uint16_t, T> || >> + std::is_same_v<int32_t, T> || >> + std::is_same_v<uint32_t, T> || >> + std::is_same_v<std::string, T> || >> + std::is_same_v<Size, T>> * = nullptr> > > Is this needed? `YamlObject::get()` does not have any constraints, > so I think it can be omitted here as well? Yes; YamlObject::get() used to have the constraints but doesn't have them any more. >> +#else >> + template<typename T> >> +#endif >> + std::optional<T> option(const std::string &confPath) const >> + { >> + const YamlObject *c = &configuration(); >> + for (auto part : utils::split(confPath, "/")) { >> + c = &(*c)[part]; >> + if (!*c) >> + return {}; >> + } >> + return c->get<T>(); >> + } >> + >> + std::vector<std::string> listOption(const std::string &confPath) const; >> std::optional<std::string> envOption(const char *const envVariable, >> const std::string &confPath) const; >> + std::vector<std::string> envListOption( >> + const char *const envVariable, >> + const std::string &confPath) const; >> private: >> bool loadFile(const std::filesystem::path &fileName); >> diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h >> index a0d448cf9..8ad717919 100644 >> --- a/include/libcamera/internal/ipa_manager.h >> +++ b/include/libcamera/internal/ipa_manager.h >> @@ -17,6 +17,7 @@ >> #include <libcamera/ipa/ipa_module_info.h> >> #include "libcamera/internal/camera_manager.h" >> +#include "libcamera/internal/global_configuration.h" >> #include "libcamera/internal/ipa_module.h" >> #include "libcamera/internal/pipeline_handler.h" >> #include "libcamera/internal/pub_key.h" >> @@ -28,7 +29,7 @@ LOG_DECLARE_CATEGORY(IPAManager) >> class IPAManager >> { >> public: >> - IPAManager(); >> + IPAManager(const GlobalConfiguration &configuration); >> ~IPAManager(); >> template<typename T> >> @@ -42,7 +43,8 @@ public: >> if (!m) >> return nullptr; >> - std::unique_ptr<T> proxy = std::make_unique<T>(m, !self->isSignatureValid(m)); >> + const GlobalConfiguration &configuration = cm->_d()->configuration(); >> + std::unique_ptr<T> proxy = std::make_unique<T>(m, !self->isSignatureValid(m, configuration), configuration); >> if (!proxy->isValid()) { >> LOG(IPAManager, Error) << "Failed to load proxy"; >> return nullptr; >> @@ -66,7 +68,7 @@ private: >> IPAModule *module(PipelineHandler *pipe, uint32_t minVersion, >> uint32_t maxVersion); >> - bool isSignatureValid(IPAModule *ipa) const; >> + bool isSignatureValid(IPAModule *ipa, const GlobalConfiguration &configuration) const; >> std::vector<std::unique_ptr<IPAModule>> modules_; >> diff --git a/include/libcamera/internal/ipa_proxy.h b/include/libcamera/internal/ipa_proxy.h >> index 983bcc5fa..01cc5deff 100644 >> --- a/include/libcamera/internal/ipa_proxy.h >> +++ b/include/libcamera/internal/ipa_proxy.h >> @@ -11,6 +11,8 @@ >> #include <libcamera/ipa/ipa_interface.h> >> +#include "libcamera/internal/global_configuration.h" >> + >> namespace libcamera { >> class IPAModule; >> @@ -30,10 +32,11 @@ public: >> bool isValid() const { return valid_; } >> std::string configurationFile(const std::string &name, >> + const GlobalConfiguration &configuration, >> const std::string &fallbackName = std::string()) const; >> protected: >> - std::string resolvePath(const std::string &file) const; >> + std::string resolvePath(const std::string &file, const GlobalConfiguration &configuration) const; > > I feel like `IPAProxy` could take the `GlobalConfiguration` > in the constructor? I.e. copy the configuration and changing std::unique_ptr<YamlObject> inside to shared_ptr? Possible, I think, and I don't have a particular preference -- up to your judgement what's better. >> bool valid_; >> ProxyState state_; >> diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp >> index f3b4ec708..c47fd3677 100644 >> --- a/src/libcamera/camera_manager.cpp >> +++ b/src/libcamera/camera_manager.cpp >> @@ -41,7 +41,7 @@ LOG_DEFINE_CATEGORY(Camera) >> CameraManager::Private::Private() >> : initialized_(false) >> { >> - ipaManager_ = std::make_unique<IPAManager>(); >> + ipaManager_ = std::make_unique<IPAManager>(this->configuration()); >> } >> int CameraManager::Private::start() >> diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp >> index 068ddd5a5..f7c69890c 100644 >> --- a/src/libcamera/global_configuration.cpp >> +++ b/src/libcamera/global_configuration.cpp >> @@ -9,9 +9,11 @@ >> #include <filesystem> >> #include <memory> >> +#include <optional> >> #include <string> >> #include <string_view> >> #include <sys/types.h> >> +#include <vector> >> #include <libcamera/base/file.h> >> #include <libcamera/base/log.h> >> @@ -116,13 +118,22 @@ GlobalConfiguration::GlobalConfiguration() >> */ >> /** >> + * \fn std::optional<T> GlobalConfiguration::option(const std::string &confPath) const >> * \brief Return value of the configuration option identified by \a confPath >> * \param[in] confPath Sequence of the YAML section names (excluding >> * `configuration') leading to the requested option separated by slashes >> * \return A value if an item corresponding to \a confPath exists in the >> * configuration file, no value otherwise >> */ >> -std::optional<std::string> GlobalConfiguration::option(const std::string &confPath) const >> + >> +/** >> + * \brief Return values of the configuration option identified by \a confPath >> + * \tparam T The type of the retrieved configuration value >> + * \param[in] confPath Sequence of the YAML section names (excluding >> + * `configuration') leading to the requested list option, separated by dots >> + * \return A vector of strings (empty one if the option is not found) >> + */ >> +std::vector<std::string> GlobalConfiguration::listOption(const std::string &confPath) const >> { >> const YamlObject *c = &configuration(); >> for (auto part : utils::split(confPath, "/")) { >> @@ -130,7 +141,7 @@ std::optional<std::string> GlobalConfiguration::option(const std::string &confPa >> if (!*c) >> return {}; >> } >> - return c->get<std::string>(); >> + return c->getList<std::string>().value_or(std::vector<std::string>()); >> } >> /** >> @@ -157,7 +168,37 @@ std::optional<std::string> GlobalConfiguration::envOption( >> const char *envValue = utils::secure_getenv(envVariable); >> if (envValue) >> return std::optional{ std::string{ envValue } }; >> - return option(confPath); >> + return option<std::string>(confPath); >> +} >> + >> +/** >> + * \brief Return values of the configuration option from a file or environment >> + * \param[in] envVariable Environment variable to get the value from >> + * \param[in] confPath The same as in GlobalConfiguration::option >> + * >> + * This helper looks first at the given environment variable and if it is >> + * defined (even if it is empty) then it splits its value by semicolons and >> + * returns the resulting list of strings. Otherwise it looks for \a confPath the >> + * same way as in GlobalConfiguration::option, value of which must be a list of >> + * strings. >> + * >> + * \note Support for using environment variables to configure libcamera behavior >> + * is provided here mostly for backward compatibility reasons. Introducing new >> + * configuration environment variables is discouraged. >> + * >> + * \return A vector of strings retrieved from the given environment option or >> + * configuration file (an empty vector is returned if nothing is found) > > I think we want to distinguish "not present" and "empty". I could imagine > that someone might want to inhibit some behaviour by setting the env var > to empty. Well, introducing new environment variables for configuration is discouraged. > `envOption()` also differentiates the two. For example, with the new > "layers" proposal, I could image that we can set the layers in a > configuration file, etc, as well as an environmental variable. In that > case doing `LIBCAMERA_LAYERS= cam ...` might be useful for testing to > temporarily disable all layers. Hmm, sounds reasonable but I'm still not sure we want to encourage this. But technically, I can change the method signature. >> + */ >> +std::vector<std::string> GlobalConfiguration::envListOption( >> + const char *const envVariable, >> + const std::string &confPath) const >> +{ >> + const char *envValue = utils::secure_getenv(envVariable); >> + if (envValue) { >> + auto items = utils::split(envValue, ":"); >> + return std::vector<std::string>(items.begin(), items.end()); >> + } >> + return listOption(confPath); >> } >> /** >> diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp >> index 830750dcc..67c1b23d2 100644 >> --- a/src/libcamera/ipa_manager.cpp >> +++ b/src/libcamera/ipa_manager.cpp >> @@ -9,6 +9,7 @@ >> #include <algorithm> >> #include <dirent.h> >> +#include <numeric> >> #include <string.h> >> #include <sys/types.h> >> @@ -16,6 +17,7 @@ >> #include <libcamera/base/log.h> >> #include <libcamera/base/utils.h> >> +#include "libcamera/internal/global_configuration.h" >> #include "libcamera/internal/ipa_module.h" >> #include "libcamera/internal/ipa_proxy.h" >> #include "libcamera/internal/pipeline_handler.h" >> @@ -101,7 +103,7 @@ LOG_DEFINE_CATEGORY(IPAManager) >> * The IPAManager class is meant to only be instantiated once, by the >> * CameraManager. >> */ >> -IPAManager::IPAManager() >> +IPAManager::IPAManager(const GlobalConfiguration &configuration) >> { >> #if HAVE_IPA_PUBKEY >> if (!pubKey_.isValid()) >> @@ -111,18 +113,25 @@ IPAManager::IPAManager() >> unsigned int ipaCount = 0; >> /* User-specified paths take precedence. */ >> - const char *modulePaths = utils::secure_getenv("LIBCAMERA_IPA_MODULE_PATH"); >> - if (modulePaths) { >> - for (const auto &dir : utils::split(modulePaths, ":")) { >> - if (dir.empty()) >> - continue; >> + const auto modulePaths = >> + configuration.envListOption( >> + "LIBCAMERA_IPA_MODULE_PATH", "ipa/module_paths"); >> + for (const auto &dir : modulePaths) { >> + if (dir.empty()) >> + continue; >> - ipaCount += addDir(dir.c_str()); >> - } >> + ipaCount += addDir(dir.c_str()); >> + } >> - if (!ipaCount) >> - LOG(IPAManager, Warning) >> - << "No IPA found in '" << modulePaths << "'"; >> + if (!ipaCount) { >> + std::string paths; >> + if (!modulePaths.empty()) { >> + paths = std::accumulate(std::next(modulePaths.begin()), >> + modulePaths.end(), >> + modulePaths[0], >> + [](std::string s1, std::string s2) { return s1 + ":" + s2; }); >> + } >> + LOG(IPAManager, Warning) << "No IPA found in '" << paths << "'"; > > #include <libcamera/base/utils.h> > ... > LOG(IPAManager, Warning) << "No IPA found in '" << utils::join(modulePaths, ":") << "'"; > > ? OK. >> } >> /* >> @@ -276,11 +285,13 @@ IPAModule *IPAManager::module(PipelineHandler *pipe, uint32_t minVersion, >> */ >> #endif >> -bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa) const >> +bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa, const GlobalConfiguration &configuration) const > > Maybe `configuration.option<bool>("ipa/force_isolation").value_or(false)` could be > saved into a member variable in the constructor? Then this won't need > the configuration parameter. Yes. Assuming the `pipe' CameraManager instance is always the same that was used to create the IPAManager instance. >> { >> #if HAVE_IPA_PUBKEY >> char *force = utils::secure_getenv("LIBCAMERA_IPA_FORCE_ISOLATION"); >> - if (force && force[0] != '\0') { >> + if ((force && force[0] != '\0') || >> + (!force && configuration.option<bool>("ipa/force_isolation") >> + .value_or(false))) { >> LOG(IPAManager, Debug) >> << "Isolation of IPA module " << ipa->path() >> << " forced through environment variable"; >> diff --git a/src/libcamera/ipa_proxy.cpp b/src/libcamera/ipa_proxy.cpp >> index 9907b9615..77927b0d4 100644 >> --- a/src/libcamera/ipa_proxy.cpp >> +++ b/src/libcamera/ipa_proxy.cpp >> @@ -14,6 +14,7 @@ >> #include <libcamera/base/log.h> >> #include <libcamera/base/utils.h> >> +#include "libcamera/internal/global_configuration.h" >> #include "libcamera/internal/ipa_module.h" >> /** >> @@ -71,6 +72,7 @@ IPAProxy::~IPAProxy() >> /** >> * \brief Retrieve the absolute path to an IPA configuration file >> * \param[in] name The configuration file name >> + * \param[in] configuration The global configuration >> * \param[in] fallbackName The name of a fallback configuration file >> * >> * This function locates the configuration file for an IPA and returns its >> @@ -96,6 +98,7 @@ IPAProxy::~IPAProxy() >> * no configuration file can be found >> */ >> std::string IPAProxy::configurationFile(const std::string &name, >> + const GlobalConfiguration &configuration, >> const std::string &fallbackName) const >> { >> /* >> @@ -105,37 +108,37 @@ std::string IPAProxy::configurationFile(const std::string &name, >> const std::string ipaName = ipam_->info().name; >> /* >> - * Start with any user override through the module-specific environment >> - * variable. Use the name of the IPA module up to the first '/' to >> - * construct the variable name. >> + * Start with any user override through the module-specific configuration or >> + * environment variable. Use the name of the IPA module up to the first '/' >> + * to construct the configuration and variable names. >> */ >> - std::string ipaEnvName = ipaName.substr(0, ipaName.find('/')); >> + std::string ipaBaseName = ipaName.substr(0, ipaName.find('/')); >> + std::string ipaConfigName = "ipa/ipas/" + ipaBaseName + "/tuning_file"; > > I feel like we're unnecessarily limiting outself wrt. rpi here. Since the IPA > names are "rpi/vc4" and "rpi/pisp", thus `ipaBaseName == "rpi"`. Which means > that it is impossible to specify different tuning files for the different > platforms. I think this is fine for an env var, but for a configuration file > I think this flexibility is expected. Right; so rather than trimming, let's replace the slash with another character here? > But I don't quite see the point of overriding the tuning file like this from a > configuration file. So maybe leaving just the env var override is sufficient? I think the initial idea was to be able to configure everything in the configuration file (why not?). Logging is currently a noticeable exception, although hopefully only "temporary". >> + std::string ipaEnvName = ipaBaseName; >> std::transform(ipaEnvName.begin(), ipaEnvName.end(), ipaEnvName.begin(), >> [](unsigned char c) { return std::toupper(c); }); >> ipaEnvName = "LIBCAMERA_" + ipaEnvName + "_TUNING_FILE"; >> - char const *configFromEnv = utils::secure_getenv(ipaEnvName.c_str()); >> - if (configFromEnv && *configFromEnv != '\0') >> - return { configFromEnv }; >> + auto config = configuration.envOption(ipaEnvName.c_str(), ipaConfigName); >> + if (config) >> + return { config.value() }; >> struct stat statbuf; >> int ret; >> /* >> - * Check the directory pointed to by the IPA config path environment >> - * variable next. >> + * Check the directory pointed to by the IPA config path next. >> */ >> - const char *confPaths = utils::secure_getenv("LIBCAMERA_IPA_CONFIG_PATH"); >> - if (confPaths) { >> - for (const auto &dir : utils::split(confPaths, ":")) { >> - if (dir.empty()) >> - continue; >> - >> - std::string confPath = dir + "/" + ipaName + "/" + name; >> - ret = stat(confPath.c_str(), &statbuf); >> - if (ret == 0 && (statbuf.st_mode & S_IFMT) == S_IFREG) >> - return confPath; >> - } >> + auto confPaths = >> + configuration.envListOption( >> + "LIBCAMERA_IPA_CONFIG_PATH", "ipa/config_paths"); >> + for (const auto &dir : confPaths) { >> + if (dir.empty()) >> + continue; >> + std::string confPath = dir + "/" + ipaName + "/" + name; >> + ret = stat(confPath.c_str(), &statbuf); >> + if (ret == 0 && (statbuf.st_mode & S_IFMT) == S_IFREG) >> + return confPath; >> } >> std::string root = utils::libcameraSourcePath(); >> @@ -178,12 +181,13 @@ std::string IPAProxy::configurationFile(const std::string &name, >> << "Configuration file '" << name >> << "' not found for IPA module '" << ipaName >> << "', falling back to '" << fallbackName << "'"; >> - return configurationFile(fallbackName); >> + return configurationFile(fallbackName, configuration); >> } >> /** >> * \brief Find a valid full path for a proxy worker for a given executable name >> * \param[in] file File name of proxy worker executable >> + * \param[in] configuration The global configuration >> * >> * A proxy worker's executable could be found in either the global installation >> * directory, or in the paths specified by the environment variable >> @@ -195,22 +199,22 @@ std::string IPAProxy::configurationFile(const std::string &name, >> * \return The full path to the proxy worker executable, or an empty string if >> * no valid executable path >> */ >> -std::string IPAProxy::resolvePath(const std::string &file) const >> +std::string IPAProxy::resolvePath(const std::string &file, const GlobalConfiguration &configuration) const >> { >> std::string proxyFile = "/" + file; >> - /* Check env variable first. */ >> - const char *execPaths = utils::secure_getenv("LIBCAMERA_IPA_PROXY_PATH"); >> - if (execPaths) { >> - for (const auto &dir : utils::split(execPaths, ":")) { >> - if (dir.empty()) >> - continue; >> - >> - std::string proxyPath = dir; >> - proxyPath += proxyFile; >> - if (!access(proxyPath.c_str(), X_OK)) >> - return proxyPath; >> - } >> + /* Check the configuration first. */ >> + const auto execPaths = >> + configuration.envListOption( >> + "LIBCAMERA_IPA_PROXY_PATH", "ipa/proxy_paths"); >> + for (const auto &dir : execPaths) { >> + if (dir.empty()) >> + continue; >> + >> + std::string proxyPath = dir; > > std::string proxyPath = dir + proxyFile; OK. > Regards, > Barnabás Pőcze > > >> + proxyPath += proxyFile; >> + if (!access(proxyPath.c_str(), X_OK)) >> + return proxyPath; >> } >> /* >> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp >> index e31e3879d..723f7665b 100644 >> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp >> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp >> @@ -1183,8 +1183,9 @@ int IPU3CameraData::loadIPA() >> * The API tuning file is made from the sensor name. If the tuning file >> * isn't found, fall back to the 'uncalibrated' file. >> */ >> + const GlobalConfiguration &configuration = pipe()->cameraManager()->_d()->configuration(); >> std::string ipaTuningFile = >> - ipa_->configurationFile(sensor->model() + ".yaml", "uncalibrated.yaml"); >> + ipa_->configurationFile(sensor->model() + ".yaml", configuration, "uncalibrated.yaml"); >> ret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() }, >> sensorInfo, sensor->controls(), &ipaControls_); >> diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp >> index 4acc091bd..d346fa25f 100644 >> --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp >> +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp >> @@ -388,7 +388,9 @@ int MaliC55CameraData::loadIPA() >> ipa_->setSensorControls.connect(this, &MaliC55CameraData::setSensorControls); >> + const GlobalConfiguration &configuration = pipe()->cameraManager()->_d()->configuration(); >> std::string ipaTuningFile = ipa_->configurationFile(sensor_->model() + ".yaml", >> + configuration, >> "uncalibrated.yaml"); >> /* We need to inform the IPA of the sensor configuration */ >> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp >> index 675f0a749..aded4cb43 100644 >> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp >> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp >> @@ -40,6 +40,7 @@ >> #include "libcamera/internal/delayed_controls.h" >> #include "libcamera/internal/device_enumerator.h" >> #include "libcamera/internal/framebuffer.h" >> +#include "libcamera/internal/global_configuration.h" >> #include "libcamera/internal/ipa_manager.h" >> #include "libcamera/internal/media_device.h" >> #include "libcamera/internal/media_pipeline.h" >> @@ -383,8 +384,9 @@ int RkISP1CameraData::loadIPA(unsigned int hwRevision) >> ipa_->metadataReady.connect(this, &RkISP1CameraData::metadataReady); >> /* The IPA tuning file is made from the sensor name. */ >> + const GlobalConfiguration &configuration = pipe()->cameraManager()->_d()->configuration(); >> std::string ipaTuningFile = >> - ipa_->configurationFile(sensor_->model() + ".yaml", "uncalibrated.yaml"); >> + ipa_->configurationFile(sensor_->model() + ".yaml", configuration, "uncalibrated.yaml"); >> IPACameraSensorInfo sensorInfo{}; >> int ret = sensor_->sensorInfo(&sensorInfo); >> diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp >> index a316ef297..3da919ee7 100644 >> --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp >> +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp >> @@ -1157,7 +1157,8 @@ int CameraData::loadIPA(ipa::RPi::InitResult *result) >> std::string model = sensor_->model(); >> if (isMonoSensor(sensor_)) >> model += "_mono"; >> - std::string configurationFile = ipa_->configurationFile(model + ".json"); >> + const GlobalConfiguration &configuration = pipe()->cameraManager()->_d()->configuration(); >> + std::string configurationFile = ipa_->configurationFile(model + ".json", configuration); >> IPASettings settings(configurationFile, sensor_->model()); >> ipa::RPi::InitParams params; >> diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp >> index 07273bd2b..555d51bb6 100644 >> --- a/src/libcamera/pipeline/vimc/vimc.cpp >> +++ b/src/libcamera/pipeline/vimc/vimc.cpp >> @@ -494,7 +494,8 @@ bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator) >> data->ipa_->paramsComputed.connect(data.get(), &VimcCameraData::paramsComputed); >> - std::string conf = data->ipa_->configurationFile("vimc.conf"); >> + const GlobalConfiguration &configuration = manager_->_d()->configuration(); >> + std::string conf = data->ipa_->configurationFile("vimc.conf", configuration); >> Flags<ipa::vimc::TestFlag> inFlags = ipa::vimc::TestFlag::Flag2; >> Flags<ipa::vimc::TestFlag> outFlags; >> data->ipa_->init(IPASettings{ conf, data->sensor_->model() }, >> diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp >> index 28e2a360e..3ce354111 100644 >> --- a/src/libcamera/software_isp/software_isp.cpp >> +++ b/src/libcamera/software_isp/software_isp.cpp >> @@ -21,6 +21,7 @@ >> #include <libcamera/stream.h> >> #include "libcamera/internal/framebuffer.h" >> +#include "libcamera/internal/global_configuration.h" >> #include "libcamera/internal/ipa_manager.h" >> #include "libcamera/internal/software_isp/debayer_params.h" >> @@ -130,8 +131,9 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, >> * The API tuning file is made from the sensor name. If the tuning file >> * isn't found, fall back to the 'uncalibrated' file. >> */ >> + const GlobalConfiguration &configuration = pipe->cameraManager()->_d()->configuration(); >> std::string ipaTuningFile = >> - ipa_->configurationFile(sensor->model() + ".yaml", "uncalibrated.yaml"); >> + ipa_->configurationFile(sensor->model() + ".yaml", configuration, "uncalibrated.yaml"); >> IPACameraSensorInfo sensorInfo{}; >> int ret = sensor->sensorInfo(&sensorInfo); >> diff --git a/test/ipa/ipa_interface_test.cpp b/test/ipa/ipa_interface_test.cpp >> index b81783664..1afcbf106 100644 >> --- a/test/ipa/ipa_interface_test.cpp >> +++ b/test/ipa/ipa_interface_test.cpp >> @@ -106,7 +106,8 @@ protected: >> } >> /* Test initialization of IPA module. */ >> - std::string conf = ipa_->configurationFile("vimc.conf"); >> + const GlobalConfiguration configuration; >> + std::string conf = ipa_->configurationFile("vimc.conf", configuration); >> Flags<ipa::vimc::TestFlag> inFlags; >> Flags<ipa::vimc::TestFlag> outFlags; >> int ret = ipa_->init(IPASettings{ conf, "vimc" }, >> diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl >> index 9a3aadbd2..71e920eb1 100644 >> --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl >> +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl >> @@ -45,7 +45,7 @@ namespace {{ns}} { >> {% endfor %} >> {%- endif %} >> -{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate) >> +{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration) >> : IPAProxy(ipam), isolate_(isolate), >> controlSerializer_(ControlSerializer::Role::Proxy), seq_(0) >> { >> @@ -54,7 +54,7 @@ namespace {{ns}} { >> << ipam->path(); >> if (isolate_) { >> - const std::string proxyWorkerPath = resolvePath("{{module_name}}_ipa_proxy"); >> + const std::string proxyWorkerPath = resolvePath("{{module_name}}_ipa_proxy", configuration); >> if (proxyWorkerPath.empty()) { >> LOG(IPAProxy, Error) >> << "Failed to get proxy worker path"; >> diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl >> index a0312a7c1..057c3ab03 100644 >> --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl >> +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl >> @@ -37,7 +37,7 @@ namespace {{ns}} { >> class {{proxy_name}} : public IPAProxy, public {{interface_name}}, public Object >> { >> public: >> - {{proxy_name}}(IPAModule *ipam, bool isolate); >> + {{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration); >> ~{{proxy_name}}(); >> {% for method in interface_main.methods %}
diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h index 357119628..cc436703e 100644 --- a/include/libcamera/internal/global_configuration.h +++ b/include/libcamera/internal/global_configuration.h @@ -11,6 +11,8 @@ #include <optional> #include <string> +#include <libcamera/base/utils.h> + #include "libcamera/internal/yaml_parser.h" namespace libcamera { @@ -24,9 +26,40 @@ public: unsigned int version() const; Configuration configuration() const; - std::optional<std::string> option(const std::string &confPath) const; + +#ifndef __DOXYGEN__ + template<typename T, + std::enable_if_t< + std::is_same_v<bool, T> || + std::is_same_v<double, T> || + std::is_same_v<int8_t, T> || + std::is_same_v<uint8_t, T> || + std::is_same_v<int16_t, T> || + std::is_same_v<uint16_t, T> || + std::is_same_v<int32_t, T> || + std::is_same_v<uint32_t, T> || + std::is_same_v<std::string, T> || + std::is_same_v<Size, T>> * = nullptr> +#else + template<typename T> +#endif + std::optional<T> option(const std::string &confPath) const + { + const YamlObject *c = &configuration(); + for (auto part : utils::split(confPath, "/")) { + c = &(*c)[part]; + if (!*c) + return {}; + } + return c->get<T>(); + } + + std::vector<std::string> listOption(const std::string &confPath) const; std::optional<std::string> envOption(const char *const envVariable, const std::string &confPath) const; + std::vector<std::string> envListOption( + const char *const envVariable, + const std::string &confPath) const; private: bool loadFile(const std::filesystem::path &fileName); diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h index a0d448cf9..8ad717919 100644 --- a/include/libcamera/internal/ipa_manager.h +++ b/include/libcamera/internal/ipa_manager.h @@ -17,6 +17,7 @@ #include <libcamera/ipa/ipa_module_info.h> #include "libcamera/internal/camera_manager.h" +#include "libcamera/internal/global_configuration.h" #include "libcamera/internal/ipa_module.h" #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/pub_key.h" @@ -28,7 +29,7 @@ LOG_DECLARE_CATEGORY(IPAManager) class IPAManager { public: - IPAManager(); + IPAManager(const GlobalConfiguration &configuration); ~IPAManager(); template<typename T> @@ -42,7 +43,8 @@ public: if (!m) return nullptr; - std::unique_ptr<T> proxy = std::make_unique<T>(m, !self->isSignatureValid(m)); + const GlobalConfiguration &configuration = cm->_d()->configuration(); + std::unique_ptr<T> proxy = std::make_unique<T>(m, !self->isSignatureValid(m, configuration), configuration); if (!proxy->isValid()) { LOG(IPAManager, Error) << "Failed to load proxy"; return nullptr; @@ -66,7 +68,7 @@ private: IPAModule *module(PipelineHandler *pipe, uint32_t minVersion, uint32_t maxVersion); - bool isSignatureValid(IPAModule *ipa) const; + bool isSignatureValid(IPAModule *ipa, const GlobalConfiguration &configuration) const; std::vector<std::unique_ptr<IPAModule>> modules_; diff --git a/include/libcamera/internal/ipa_proxy.h b/include/libcamera/internal/ipa_proxy.h index 983bcc5fa..01cc5deff 100644 --- a/include/libcamera/internal/ipa_proxy.h +++ b/include/libcamera/internal/ipa_proxy.h @@ -11,6 +11,8 @@ #include <libcamera/ipa/ipa_interface.h> +#include "libcamera/internal/global_configuration.h" + namespace libcamera { class IPAModule; @@ -30,10 +32,11 @@ public: bool isValid() const { return valid_; } std::string configurationFile(const std::string &name, + const GlobalConfiguration &configuration, const std::string &fallbackName = std::string()) const; protected: - std::string resolvePath(const std::string &file) const; + std::string resolvePath(const std::string &file, const GlobalConfiguration &configuration) const; bool valid_; ProxyState state_; diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index f3b4ec708..c47fd3677 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -41,7 +41,7 @@ LOG_DEFINE_CATEGORY(Camera) CameraManager::Private::Private() : initialized_(false) { - ipaManager_ = std::make_unique<IPAManager>(); + ipaManager_ = std::make_unique<IPAManager>(this->configuration()); } int CameraManager::Private::start() diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp index 068ddd5a5..f7c69890c 100644 --- a/src/libcamera/global_configuration.cpp +++ b/src/libcamera/global_configuration.cpp @@ -9,9 +9,11 @@ #include <filesystem> #include <memory> +#include <optional> #include <string> #include <string_view> #include <sys/types.h> +#include <vector> #include <libcamera/base/file.h> #include <libcamera/base/log.h> @@ -116,13 +118,22 @@ GlobalConfiguration::GlobalConfiguration() */ /** + * \fn std::optional<T> GlobalConfiguration::option(const std::string &confPath) const * \brief Return value of the configuration option identified by \a confPath * \param[in] confPath Sequence of the YAML section names (excluding * `configuration') leading to the requested option separated by slashes * \return A value if an item corresponding to \a confPath exists in the * configuration file, no value otherwise */ -std::optional<std::string> GlobalConfiguration::option(const std::string &confPath) const + +/** + * \brief Return values of the configuration option identified by \a confPath + * \tparam T The type of the retrieved configuration value + * \param[in] confPath Sequence of the YAML section names (excluding + * `configuration') leading to the requested list option, separated by dots + * \return A vector of strings (empty one if the option is not found) + */ +std::vector<std::string> GlobalConfiguration::listOption(const std::string &confPath) const { const YamlObject *c = &configuration(); for (auto part : utils::split(confPath, "/")) { @@ -130,7 +141,7 @@ std::optional<std::string> GlobalConfiguration::option(const std::string &confPa if (!*c) return {}; } - return c->get<std::string>(); + return c->getList<std::string>().value_or(std::vector<std::string>()); } /** @@ -157,7 +168,37 @@ std::optional<std::string> GlobalConfiguration::envOption( const char *envValue = utils::secure_getenv(envVariable); if (envValue) return std::optional{ std::string{ envValue } }; - return option(confPath); + return option<std::string>(confPath); +} + +/** + * \brief Return values of the configuration option from a file or environment + * \param[in] envVariable Environment variable to get the value from + * \param[in] confPath The same as in GlobalConfiguration::option + * + * This helper looks first at the given environment variable and if it is + * defined (even if it is empty) then it splits its value by semicolons and + * returns the resulting list of strings. Otherwise it looks for \a confPath the + * same way as in GlobalConfiguration::option, value of which must be a list of + * strings. + * + * \note Support for using environment variables to configure libcamera behavior + * is provided here mostly for backward compatibility reasons. Introducing new + * configuration environment variables is discouraged. + * + * \return A vector of strings retrieved from the given environment option or + * configuration file (an empty vector is returned if nothing is found) + */ +std::vector<std::string> GlobalConfiguration::envListOption( + const char *const envVariable, + const std::string &confPath) const +{ + const char *envValue = utils::secure_getenv(envVariable); + if (envValue) { + auto items = utils::split(envValue, ":"); + return std::vector<std::string>(items.begin(), items.end()); + } + return listOption(confPath); } /** diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp index 830750dcc..67c1b23d2 100644 --- a/src/libcamera/ipa_manager.cpp +++ b/src/libcamera/ipa_manager.cpp @@ -9,6 +9,7 @@ #include <algorithm> #include <dirent.h> +#include <numeric> #include <string.h> #include <sys/types.h> @@ -16,6 +17,7 @@ #include <libcamera/base/log.h> #include <libcamera/base/utils.h> +#include "libcamera/internal/global_configuration.h" #include "libcamera/internal/ipa_module.h" #include "libcamera/internal/ipa_proxy.h" #include "libcamera/internal/pipeline_handler.h" @@ -101,7 +103,7 @@ LOG_DEFINE_CATEGORY(IPAManager) * The IPAManager class is meant to only be instantiated once, by the * CameraManager. */ -IPAManager::IPAManager() +IPAManager::IPAManager(const GlobalConfiguration &configuration) { #if HAVE_IPA_PUBKEY if (!pubKey_.isValid()) @@ -111,18 +113,25 @@ IPAManager::IPAManager() unsigned int ipaCount = 0; /* User-specified paths take precedence. */ - const char *modulePaths = utils::secure_getenv("LIBCAMERA_IPA_MODULE_PATH"); - if (modulePaths) { - for (const auto &dir : utils::split(modulePaths, ":")) { - if (dir.empty()) - continue; + const auto modulePaths = + configuration.envListOption( + "LIBCAMERA_IPA_MODULE_PATH", "ipa/module_paths"); + for (const auto &dir : modulePaths) { + if (dir.empty()) + continue; - ipaCount += addDir(dir.c_str()); - } + ipaCount += addDir(dir.c_str()); + } - if (!ipaCount) - LOG(IPAManager, Warning) - << "No IPA found in '" << modulePaths << "'"; + if (!ipaCount) { + std::string paths; + if (!modulePaths.empty()) { + paths = std::accumulate(std::next(modulePaths.begin()), + modulePaths.end(), + modulePaths[0], + [](std::string s1, std::string s2) { return s1 + ":" + s2; }); + } + LOG(IPAManager, Warning) << "No IPA found in '" << paths << "'"; } /* @@ -276,11 +285,13 @@ IPAModule *IPAManager::module(PipelineHandler *pipe, uint32_t minVersion, */ #endif -bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa) const +bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa, const GlobalConfiguration &configuration) const { #if HAVE_IPA_PUBKEY char *force = utils::secure_getenv("LIBCAMERA_IPA_FORCE_ISOLATION"); - if (force && force[0] != '\0') { + if ((force && force[0] != '\0') || + (!force && configuration.option<bool>("ipa/force_isolation") + .value_or(false))) { LOG(IPAManager, Debug) << "Isolation of IPA module " << ipa->path() << " forced through environment variable"; diff --git a/src/libcamera/ipa_proxy.cpp b/src/libcamera/ipa_proxy.cpp index 9907b9615..77927b0d4 100644 --- a/src/libcamera/ipa_proxy.cpp +++ b/src/libcamera/ipa_proxy.cpp @@ -14,6 +14,7 @@ #include <libcamera/base/log.h> #include <libcamera/base/utils.h> +#include "libcamera/internal/global_configuration.h" #include "libcamera/internal/ipa_module.h" /** @@ -71,6 +72,7 @@ IPAProxy::~IPAProxy() /** * \brief Retrieve the absolute path to an IPA configuration file * \param[in] name The configuration file name + * \param[in] configuration The global configuration * \param[in] fallbackName The name of a fallback configuration file * * This function locates the configuration file for an IPA and returns its @@ -96,6 +98,7 @@ IPAProxy::~IPAProxy() * no configuration file can be found */ std::string IPAProxy::configurationFile(const std::string &name, + const GlobalConfiguration &configuration, const std::string &fallbackName) const { /* @@ -105,37 +108,37 @@ std::string IPAProxy::configurationFile(const std::string &name, const std::string ipaName = ipam_->info().name; /* - * Start with any user override through the module-specific environment - * variable. Use the name of the IPA module up to the first '/' to - * construct the variable name. + * Start with any user override through the module-specific configuration or + * environment variable. Use the name of the IPA module up to the first '/' + * to construct the configuration and variable names. */ - std::string ipaEnvName = ipaName.substr(0, ipaName.find('/')); + std::string ipaBaseName = ipaName.substr(0, ipaName.find('/')); + std::string ipaConfigName = "ipa/ipas/" + ipaBaseName + "/tuning_file"; + std::string ipaEnvName = ipaBaseName; std::transform(ipaEnvName.begin(), ipaEnvName.end(), ipaEnvName.begin(), [](unsigned char c) { return std::toupper(c); }); ipaEnvName = "LIBCAMERA_" + ipaEnvName + "_TUNING_FILE"; - char const *configFromEnv = utils::secure_getenv(ipaEnvName.c_str()); - if (configFromEnv && *configFromEnv != '\0') - return { configFromEnv }; + auto config = configuration.envOption(ipaEnvName.c_str(), ipaConfigName); + if (config) + return { config.value() }; struct stat statbuf; int ret; /* - * Check the directory pointed to by the IPA config path environment - * variable next. + * Check the directory pointed to by the IPA config path next. */ - const char *confPaths = utils::secure_getenv("LIBCAMERA_IPA_CONFIG_PATH"); - if (confPaths) { - for (const auto &dir : utils::split(confPaths, ":")) { - if (dir.empty()) - continue; - - std::string confPath = dir + "/" + ipaName + "/" + name; - ret = stat(confPath.c_str(), &statbuf); - if (ret == 0 && (statbuf.st_mode & S_IFMT) == S_IFREG) - return confPath; - } + auto confPaths = + configuration.envListOption( + "LIBCAMERA_IPA_CONFIG_PATH", "ipa/config_paths"); + for (const auto &dir : confPaths) { + if (dir.empty()) + continue; + std::string confPath = dir + "/" + ipaName + "/" + name; + ret = stat(confPath.c_str(), &statbuf); + if (ret == 0 && (statbuf.st_mode & S_IFMT) == S_IFREG) + return confPath; } std::string root = utils::libcameraSourcePath(); @@ -178,12 +181,13 @@ std::string IPAProxy::configurationFile(const std::string &name, << "Configuration file '" << name << "' not found for IPA module '" << ipaName << "', falling back to '" << fallbackName << "'"; - return configurationFile(fallbackName); + return configurationFile(fallbackName, configuration); } /** * \brief Find a valid full path for a proxy worker for a given executable name * \param[in] file File name of proxy worker executable + * \param[in] configuration The global configuration * * A proxy worker's executable could be found in either the global installation * directory, or in the paths specified by the environment variable @@ -195,22 +199,22 @@ std::string IPAProxy::configurationFile(const std::string &name, * \return The full path to the proxy worker executable, or an empty string if * no valid executable path */ -std::string IPAProxy::resolvePath(const std::string &file) const +std::string IPAProxy::resolvePath(const std::string &file, const GlobalConfiguration &configuration) const { std::string proxyFile = "/" + file; - /* Check env variable first. */ - const char *execPaths = utils::secure_getenv("LIBCAMERA_IPA_PROXY_PATH"); - if (execPaths) { - for (const auto &dir : utils::split(execPaths, ":")) { - if (dir.empty()) - continue; - - std::string proxyPath = dir; - proxyPath += proxyFile; - if (!access(proxyPath.c_str(), X_OK)) - return proxyPath; - } + /* Check the configuration first. */ + const auto execPaths = + configuration.envListOption( + "LIBCAMERA_IPA_PROXY_PATH", "ipa/proxy_paths"); + for (const auto &dir : execPaths) { + if (dir.empty()) + continue; + + std::string proxyPath = dir; + proxyPath += proxyFile; + if (!access(proxyPath.c_str(), X_OK)) + return proxyPath; } /* diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index e31e3879d..723f7665b 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -1183,8 +1183,9 @@ int IPU3CameraData::loadIPA() * The API tuning file is made from the sensor name. If the tuning file * isn't found, fall back to the 'uncalibrated' file. */ + const GlobalConfiguration &configuration = pipe()->cameraManager()->_d()->configuration(); std::string ipaTuningFile = - ipa_->configurationFile(sensor->model() + ".yaml", "uncalibrated.yaml"); + ipa_->configurationFile(sensor->model() + ".yaml", configuration, "uncalibrated.yaml"); ret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() }, sensorInfo, sensor->controls(), &ipaControls_); diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp index 4acc091bd..d346fa25f 100644 --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp @@ -388,7 +388,9 @@ int MaliC55CameraData::loadIPA() ipa_->setSensorControls.connect(this, &MaliC55CameraData::setSensorControls); + const GlobalConfiguration &configuration = pipe()->cameraManager()->_d()->configuration(); std::string ipaTuningFile = ipa_->configurationFile(sensor_->model() + ".yaml", + configuration, "uncalibrated.yaml"); /* We need to inform the IPA of the sensor configuration */ diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index 675f0a749..aded4cb43 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -40,6 +40,7 @@ #include "libcamera/internal/delayed_controls.h" #include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/framebuffer.h" +#include "libcamera/internal/global_configuration.h" #include "libcamera/internal/ipa_manager.h" #include "libcamera/internal/media_device.h" #include "libcamera/internal/media_pipeline.h" @@ -383,8 +384,9 @@ int RkISP1CameraData::loadIPA(unsigned int hwRevision) ipa_->metadataReady.connect(this, &RkISP1CameraData::metadataReady); /* The IPA tuning file is made from the sensor name. */ + const GlobalConfiguration &configuration = pipe()->cameraManager()->_d()->configuration(); std::string ipaTuningFile = - ipa_->configurationFile(sensor_->model() + ".yaml", "uncalibrated.yaml"); + ipa_->configurationFile(sensor_->model() + ".yaml", configuration, "uncalibrated.yaml"); IPACameraSensorInfo sensorInfo{}; int ret = sensor_->sensorInfo(&sensorInfo); diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp index a316ef297..3da919ee7 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp @@ -1157,7 +1157,8 @@ int CameraData::loadIPA(ipa::RPi::InitResult *result) std::string model = sensor_->model(); if (isMonoSensor(sensor_)) model += "_mono"; - std::string configurationFile = ipa_->configurationFile(model + ".json"); + const GlobalConfiguration &configuration = pipe()->cameraManager()->_d()->configuration(); + std::string configurationFile = ipa_->configurationFile(model + ".json", configuration); IPASettings settings(configurationFile, sensor_->model()); ipa::RPi::InitParams params; diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp index 07273bd2b..555d51bb6 100644 --- a/src/libcamera/pipeline/vimc/vimc.cpp +++ b/src/libcamera/pipeline/vimc/vimc.cpp @@ -494,7 +494,8 @@ bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator) data->ipa_->paramsComputed.connect(data.get(), &VimcCameraData::paramsComputed); - std::string conf = data->ipa_->configurationFile("vimc.conf"); + const GlobalConfiguration &configuration = manager_->_d()->configuration(); + std::string conf = data->ipa_->configurationFile("vimc.conf", configuration); Flags<ipa::vimc::TestFlag> inFlags = ipa::vimc::TestFlag::Flag2; Flags<ipa::vimc::TestFlag> outFlags; data->ipa_->init(IPASettings{ conf, data->sensor_->model() }, diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp index 28e2a360e..3ce354111 100644 --- a/src/libcamera/software_isp/software_isp.cpp +++ b/src/libcamera/software_isp/software_isp.cpp @@ -21,6 +21,7 @@ #include <libcamera/stream.h> #include "libcamera/internal/framebuffer.h" +#include "libcamera/internal/global_configuration.h" #include "libcamera/internal/ipa_manager.h" #include "libcamera/internal/software_isp/debayer_params.h" @@ -130,8 +131,9 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, * The API tuning file is made from the sensor name. If the tuning file * isn't found, fall back to the 'uncalibrated' file. */ + const GlobalConfiguration &configuration = pipe->cameraManager()->_d()->configuration(); std::string ipaTuningFile = - ipa_->configurationFile(sensor->model() + ".yaml", "uncalibrated.yaml"); + ipa_->configurationFile(sensor->model() + ".yaml", configuration, "uncalibrated.yaml"); IPACameraSensorInfo sensorInfo{}; int ret = sensor->sensorInfo(&sensorInfo); diff --git a/test/ipa/ipa_interface_test.cpp b/test/ipa/ipa_interface_test.cpp index b81783664..1afcbf106 100644 --- a/test/ipa/ipa_interface_test.cpp +++ b/test/ipa/ipa_interface_test.cpp @@ -106,7 +106,8 @@ protected: } /* Test initialization of IPA module. */ - std::string conf = ipa_->configurationFile("vimc.conf"); + const GlobalConfiguration configuration; + std::string conf = ipa_->configurationFile("vimc.conf", configuration); Flags<ipa::vimc::TestFlag> inFlags; Flags<ipa::vimc::TestFlag> outFlags; int ret = ipa_->init(IPASettings{ conf, "vimc" }, diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl index 9a3aadbd2..71e920eb1 100644 --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl @@ -45,7 +45,7 @@ namespace {{ns}} { {% endfor %} {%- endif %} -{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate) +{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration) : IPAProxy(ipam), isolate_(isolate), controlSerializer_(ControlSerializer::Role::Proxy), seq_(0) { @@ -54,7 +54,7 @@ namespace {{ns}} { << ipam->path(); if (isolate_) { - const std::string proxyWorkerPath = resolvePath("{{module_name}}_ipa_proxy"); + const std::string proxyWorkerPath = resolvePath("{{module_name}}_ipa_proxy", configuration); if (proxyWorkerPath.empty()) { LOG(IPAProxy, Error) << "Failed to get proxy worker path"; diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl index a0312a7c1..057c3ab03 100644 --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl @@ -37,7 +37,7 @@ namespace {{ns}} { class {{proxy_name}} : public IPAProxy, public {{interface_name}}, public Object { public: - {{proxy_name}}(IPAModule *ipam, bool isolate); + {{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration); ~{{proxy_name}}(); {% for method in interface_main.methods %}
This patch adds configuration options for environment variables used in the IPA proxy. Two utility functions configuration retrieval functions are added, to retrieve lists of values. The configuration snippet: configuration: ipa: config_paths: - config path 1 - config path 2 - ... module_paths: - module path 1 - module path 2 - ... proxy_paths: - proxy path 1 - proxy path 2 - ... force_isolation: BOOL ipas: IPA_NAME: tuning_file: TUNING FILE Signed-off-by: Milan Zamazal <mzamazal@redhat.com> --- .../libcamera/internal/global_configuration.h | 35 ++++++++- include/libcamera/internal/ipa_manager.h | 8 ++- include/libcamera/internal/ipa_proxy.h | 5 +- src/libcamera/camera_manager.cpp | 2 +- src/libcamera/global_configuration.cpp | 47 +++++++++++- src/libcamera/ipa_manager.cpp | 37 ++++++---- src/libcamera/ipa_proxy.cpp | 72 ++++++++++--------- src/libcamera/pipeline/ipu3/ipu3.cpp | 3 +- src/libcamera/pipeline/mali-c55/mali-c55.cpp | 2 + src/libcamera/pipeline/rkisp1/rkisp1.cpp | 4 +- .../pipeline/rpi/common/pipeline_base.cpp | 3 +- src/libcamera/pipeline/vimc/vimc.cpp | 3 +- src/libcamera/software_isp/software_isp.cpp | 4 +- test/ipa/ipa_interface_test.cpp | 3 +- .../module_ipa_proxy.cpp.tmpl | 4 +- .../module_ipa_proxy.h.tmpl | 2 +- 16 files changed, 169 insertions(+), 65 deletions(-)