@@ -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);
@@ -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_;
@@ -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_;
@@ -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>
@@ -120,13 +122,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 dots
* \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, ".")) {
@@ -134,7 +145,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>());
}
/**
@@ -161,7 +172,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);
}
/**
@@ -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()
@@ -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";
@@ -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;
}
/*
@@ -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_);
@@ -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 */
@@ -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);
@@ -1150,7 +1150,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;
@@ -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() },
@@ -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);
@@ -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" },
@@ -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";
@@ -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/base/global_configuration.cpp | 47 +++++++++++- src/libcamera/camera_manager.cpp | 2 +- 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(-)