[libcamera-devel] [RFC PATCH v2 3/7] libcamera: add IPA proxy

Paul Elder paul.elder at ideasonboard.com
Wed Jul 3 10:00:03 CEST 2019


Add a Proxy class whose implementations will act as a proxy between a
pipeline handler and an isolated IPA interface. Also add a ProxyFactory
that will construct the Proxy implementations as necessary.

Update Doxygen to ignore the directory where Proxy implementations will
reside.

Signed-off-by: Paul Elder <paul.elder at ideasonboard.com>
---
New in v2
- replaces shims from v1
- build into libcamera, hence the register macro (similar to what
  PipelineHandler uses)
- no longer builds separate .so

 Documentation/Doxyfile.in         |   3 +-
 src/libcamera/include/ipa_proxy.h |  68 ++++++++++
 src/libcamera/ipa_proxy.cpp       | 219 ++++++++++++++++++++++++++++++
 src/libcamera/meson.build         |   6 +
 test/libtest/test.cpp             |   4 +
 5 files changed, 299 insertions(+), 1 deletion(-)
 create mode 100644 src/libcamera/include/ipa_proxy.h
 create mode 100644 src/libcamera/ipa_proxy.cpp

diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
index 9ca3224..8c24f94 100644
--- a/Documentation/Doxyfile.in
+++ b/Documentation/Doxyfile.in
@@ -837,7 +837,8 @@ EXCLUDE                = @TOP_SRCDIR@/src/libcamera/device_enumerator_sysfs.cpp
 			 @TOP_SRCDIR@/src/libcamera/device_enumerator_udev.cpp \
 			 @TOP_SRCDIR@/src/libcamera/include/device_enumerator_sysfs.h \
 			 @TOP_SRCDIR@/src/libcamera/include/device_enumerator_udev.h \
-			 @TOP_SRCDIR@/src/libcamera/pipeline/
+			 @TOP_SRCDIR@/src/libcamera/pipeline/ \
+			 @TOP_SRCDIR@/src/libcamera/proxy/
 
 # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
 # directories that are symbolic links (a Unix file system feature) are excluded
diff --git a/src/libcamera/include/ipa_proxy.h b/src/libcamera/include/ipa_proxy.h
new file mode 100644
index 0000000..b493c1a
--- /dev/null
+++ b/src/libcamera/include/ipa_proxy.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * ipa_proxy.h - Image Processing Algorithm proxy
+ */
+#ifndef __LIBCAMERA_IPA_PROXY_H__
+#define __LIBCAMERA_IPA_PROXY_H__
+
+#include <vector>
+
+#include <libcamera/ipa/ipa_interface.h>
+
+#include "ipa_module.h"
+#include "utils.h"
+
+namespace libcamera {
+
+class Proxy : public IPAInterface
+{
+public:
+	Proxy(IPAModule *ipam);
+	virtual ~Proxy();
+
+	virtual int init() = 0;
+
+	bool isValid() const;
+
+protected:
+	const std::string resolvePath(const std::string &file) const;
+
+	std::unique_ptr<IPAInterface> ipa_;
+
+	bool valid_;
+};
+
+class ProxyFactory
+{
+public:
+	ProxyFactory(const char *name);
+	virtual ~ProxyFactory() { };
+
+	virtual std::unique_ptr<Proxy> create(IPAModule *ipam) = 0;
+
+	const std::string &name() const { return name_; }
+
+	static void registerType(ProxyFactory *factory);
+	static std::vector<ProxyFactory *> &factories();
+
+private:
+	std::string name_;
+};
+
+#define REGISTER_IPA_PROXY(proxy)			\
+class proxy##Factory final : public ProxyFactory	\
+{							\
+public:							\
+	proxy##Factory() : ProxyFactory(#proxy) {}	\
+	std::unique_ptr<Proxy> create(IPAModule *ipam)	\
+	{						\
+		return utils::make_unique<proxy>(ipam);	\
+	}						\
+};							\
+static proxy##Factory global_##proxy##Factory;
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_IPA_PROXY_H__ */
diff --git a/src/libcamera/ipa_proxy.cpp b/src/libcamera/ipa_proxy.cpp
new file mode 100644
index 0000000..8bb3622
--- /dev/null
+++ b/src/libcamera/ipa_proxy.cpp
@@ -0,0 +1,219 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * ipa_proxy.cpp - Image Processing Algorithm proxy
+ */
+
+#include "ipa_proxy.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "utils.h"
+
+#include <iostream>
+
+/**
+ * \file ipa_proxy.h
+ * \brief IPA Proxy
+ *
+ * TODO write this
+ *
+ * Isolate IPA into separate process.
+ *
+ * Every subclass of proxy shall be registered with libcamera using
+ * the REGISTER_IPA_PROXY() macro.
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Proxy)
+
+/**
+ * \class Proxy
+ * \brief IPA Proxy
+ *
+ * TODO write this
+ *
+ * Isolate IPA into separate process.
+ */
+
+/**
+ * \brief Construct a Proxy instance
+ * \param[in] ipam The IPA module to wrap around
+ *
+ * Proxy instances shall be constructed through the ProxyFactory::create()
+ * method implemented by the respective factories.
+ */
+Proxy::Proxy(IPAModule *ipam)
+	: valid_(false)
+{
+}
+
+Proxy::~Proxy()
+{
+}
+
+/**
+ * \brief Check if the Proxy instance is valid
+ *
+ * A Proxy instance is valid if the IPA interface is successfully created in
+ * isolation, and IPC is successfully set up.
+ *
+ * \return true if the the Proxy is valid, false otherwise
+ */
+bool Proxy::isValid() const
+{
+	return valid_;
+}
+
+/**
+ * \brief Find a valid full path for a proxy worker for a given executable name
+ * \param[in] file File name of proxy worker executable
+ *
+ * A proxy worker's executable could be found in either the global installation
+ * directory, or in the paths specified by the environment variable
+ * LIBCAMERA_IPA_PROXY_PATH. This method checks the global install directory
+ * first, then LIBCAMERA_IPA_PROXY_PATH in order, and returns the full path to
+ * the proxy worker executable that is specified by file. The proxy worker
+ * executable must be have exec permission.
+ *
+ * \return full path to proxy worker executable, or empty string if no valid
+ * executable path
+ */
+const std::string Proxy::resolvePath(const std::string &file) const
+{
+	/* Try finding the exec target from the install directory first */
+	std::string proxyFile = "/" + file;
+	std::string proxyPath = std::string(IPA_PROXY_DIR) + proxyFile;
+	if (!access(proxyPath.c_str(), X_OK))
+		return proxyPath;
+
+	/* No exec target in install directory; check env variable*/
+	const char *execPaths = utils::secure_getenv("LIBCAMERA_IPA_PROXY_PATH");
+	while (execPaths) {
+		const char *delim = strchrnul(execPaths, ':');
+		size_t count = delim - execPaths;
+
+		if (count) {
+			std::string proxyPath(execPaths, count);
+			proxyPath += proxyFile;
+			if (!access(proxyPath.c_str(), X_OK))
+				return proxyPath;
+		}
+
+		if (*delim == '\0')
+			break;
+
+		execPaths += count + 1;
+	}
+
+	return std::string();
+}
+
+/**
+ * \var Proxy::ipa_
+ * \brief The IPA interface that the Proxy is isolating
+ */
+
+/**
+ * \var Proxy::valid_
+ * \brief Flag to indicate if the Proxy instance is valid
+ *
+ * A Proxy instance is valid if the IPA interface is successfully created in
+ * isolation, and IPC is successfully set up.
+ *
+ * This flag can be read via Proxy::isValid()
+ *
+ * Implementations of the Proxy class should set this flag upon successful
+ * construction.
+ */
+
+/**
+ * \class ProxyFactory
+ * \brief Registration of Proxy classes and creation of instances
+ *
+ * To facilitate discovery and instantiation of Proxy classes, the
+ * ProxyFactory class maintains a registry of Proxy classes. Each
+ * Proxy subclass shall register itself using the REGISTER_IPA_PROXY()
+ * macro, which will create a corresponding instance of a ProxyFactory
+ * subclass and register it with the static list of factories.
+ */
+
+/**
+ * \brief Construct a Proxy factory
+ * \param[in] name Name of the Proxy class
+ *
+ * Creating an instance of the factory registers is with the global list of
+ * factories, accessible through the factories() function.
+ *
+ * The factory \a name is used for debugging and Proxy matching purposes
+ * and shall be unique.
+ */
+ProxyFactory::ProxyFactory(const char *name)
+	: name_(name)
+{
+	registerType(this);
+}
+
+/**
+ * \fn ProxyFactory::create()
+ * \brief Create an instance of the Proxy corresponding to the factory
+ * \param[in] ipam The IPA module
+ *
+ * This virtual function is implemented by the REGISTER_IPA_PROXY() macro.
+ * It creates a Proxy instance that isolates an IPA interface designated
+ * by the IPA module \a ipam.
+ *
+ * \return a pointer to a newly constructed instance of the Proxy subclass
+ * corresponding to the factory
+ */
+
+/**
+ * \fn ProxyFactory::name()
+ * \brief Retrieve the factory name
+ * \return The factory name
+ */
+
+/**
+ * \brief Add a Proxy class to the registry
+ * \param[in] factory Factory to use to construct the Proxy
+ *
+ * The caller is responsible to guarantee the uniqueness of the Proxy name.
+ */
+void ProxyFactory::registerType(ProxyFactory *factory)
+{
+	std::vector<ProxyFactory *> &factories = ProxyFactory::factories();
+
+	factories.push_back(factory);
+
+	LOG(Proxy, Debug)
+		<< "Registered proxy \"" << factory->name() << "\"";
+}
+
+/**
+ * \brief Retrieve the list of all Proxy factories
+ *
+ * The static factories map is defined inside the function to ensures it gets
+ * initialized on first use, without any dependency on link order.
+ *
+ * \return the list of pipeline handler factories
+ */
+std::vector<ProxyFactory *> &ProxyFactory::factories()
+{
+	static std::vector<ProxyFactory *> factories;
+	return factories;
+}
+
+/**
+ * \def REGISTER_IPA_PROXY
+ * \brief Register a Proxy with the Proxy factory
+ * \param[in] proxy Class name of Proxy derived class to register
+ *
+ * Register a proxy subclass with the factory and make it available to
+ * isolate IPA modules.
+ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index 087b578..412564f 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -14,6 +14,7 @@ libcamera_sources = files([
     'ipa_interface.cpp',
     'ipa_manager.cpp',
     'ipa_module.cpp',
+    'ipa_proxy.cpp',
     'ipc_unixsocket.cpp',
     'log.cpp',
     'media_device.cpp',
@@ -42,6 +43,7 @@ libcamera_headers = files([
     'include/formats.h',
     'include/ipa_manager.h',
     'include/ipa_module.h',
+    'include/ipa_proxy.h',
     'include/ipc_unixsocket.h',
     'include/log.h',
     'include/media_device.h',
@@ -64,6 +66,10 @@ includes = [
 
 subdir('pipeline')
 
+proxy_install_dir = join_paths(get_option('libdir'), 'libcamera', 'proxy')
+config_h.set('IPA_PROXY_DIR',
+             '"' + join_paths(get_option('prefix'), proxy_install_dir) + '"')
+
 libudev = dependency('libudev', required : false)
 
 if libudev.found()
diff --git a/test/libtest/test.cpp b/test/libtest/test.cpp
index b119cf1..35aa021 100644
--- a/test/libtest/test.cpp
+++ b/test/libtest/test.cpp
@@ -25,6 +25,10 @@ int Test::execute()
 	if (ret)
 		return errno;
 
+	ret = setenv("LIBCAMERA_IPA_PROXY_PATH", "src/libcamera/proxy_worker", 1);
+	if (ret)
+		return errno;
+
 	ret = init();
 	if (ret)
 		return ret;
-- 
2.20.1



More information about the libcamera-devel mailing list