summaryrefslogtreecommitdiff
path: root/test/libtest
AgeCommit message (Expand)Author
2022-08-03libcamera: v4l2_videodevice: Reintroduce toV4L2PixelFormat()Jacopo Mondi
2021-12-01test: Store path to the test executable in Test classLaurent Pinchart
2021-11-24test: Convert to pragma onceKieran Bingham
2021-10-15test: Remove using namespace in header filesHirokazu Honda
2021-09-07libcamera: v4l2_videodevice: Drop toV4L2PixelFormat()Laurent Pinchart
2021-08-19libtest: camera_test: Plumb constructor to set LIBCAMERA_IPA_FORCE_ISOLATIONUmang Jain
2021-06-25libcamera/base: Validate internal headers as privateKieran Bingham
2020-12-09libcamera: v4l2_device: Return a unique pointer from fromEntityName()Laurent Pinchart
2020-09-21test: Include specific headers instead of libcamera.hLaurent Pinchart
2020-08-25meson: Remove -Wno-unused-parameterLaurent Pinchart
2020-05-16libcamera: Move internal headers to include/libcamera/internal/Laurent Pinchart
2020-05-13licenses: License all meson files under CC0-1.0Laurent Pinchart
2020-04-30libcamera: v4l2_pixelformat: Move DRM/V4L2 format conversionLaurent Pinchart
2020-03-19libcamera: v4l2_videodevice: Rename toV4L2Fourcc to toV4L2PixelFormatLaurent Pinchart
2020-03-18libcamera: v4l2_videodevice: Rename exportBuffers() to allocateBuffers()Laurent Pinchart
2020-03-18test: libtest: buffer_source: Close video device right after allocationLaurent Pinchart
2020-03-18tests: remove IPA_PROXY_PATH environment variableKaaira Gupta
2020-03-06test: Extract BufferSource class out of camera tests to libtestNiklas Söderlund
2020-02-24tests: Remove IPA_MODULE_PATH environment variableKieran Bingham
2019-11-20test: Extract CameraTest class out of camera tests to libtestLaurent Pinchart
2019-11-18libcamera: Remove space between empty curly bracketsLaurent Pinchart
2019-07-12libcamera: add IPA proxyPaul Elder
2019-06-05libcamera: ipa_manager: implement class for managing IPA modulesPaul Elder
2019-05-23meson: Create and use a dependency for libcamera and its headersLaurent Pinchart
2019-01-02test: Move include definitions to libtestKieran Bingham
2019-01-01test: libtest: Return all non-zero init valuesKieran Bingham
2019-01-01test: libtest: Add test return codesKieran Bingham
2019-01-01test: Move test objects to libtestKieran Bingham
n class="hl pps">"ipa_manager.h" #include <algorithm> #include <dirent.h> #include <string.h> #include <sys/types.h> #include "ipa_module.h" #include "ipa_proxy.h" #include "log.h" #include "pipeline_handler.h" #include "utils.h" /** * \file ipa_manager.h * \brief Image Processing Algorithm module manager */ namespace libcamera { LOG_DEFINE_CATEGORY(IPAManager) /** * \class IPAManager * \brief Manager for IPA modules * * The IPA module manager discovers IPA modules from disk, queries and loads * them, and creates IPA contexts. It supports isolation of the modules in a * separate process with IPC communication and offers a unified IPAInterface * view of the IPA contexts to pipeline handlers regardless of whether the * modules are isolated or loaded in the same process. * * Module isolation is based on the module licence. Open-source modules are * loaded without isolation, while closed-source module are forcefully isolated. * The isolation mechanism ensures that no code from a closed-source module is * ever run in the libcamera process. * * To create an IPA context, pipeline handlers call the IPAManager::ipaCreate() * method. For a directly loaded module, the manager calls the module's * ipaCreate() function directly and wraps the returned context in an * IPAContextWrapper that exposes an IPAInterface. * * ~~~~ * +---------------+ * | Pipeline | * | Handler | * +---------------+ * | * v * +---------------+ +---------------+ * | IPA | | Open Source | * | Interface | | IPA Module | * | - - - - - - - | | - - - - - - - | * | IPA Context | ipa_context_ops | ipa_context | * | Wrapper | ----------------> | | * +---------------+ +---------------+ * ~~~~ * * For an isolated module, the manager instantiates an IPAProxy which spawns a * new process for an IPA proxy worker. The worker loads the IPA module and * creates the IPA context. The IPAProxy alse exposes an IPAInterface. * * ~~~~ * +---------------+ +---------------+ * | Pipeline | | Closed Source | * | Handler | | IPA Module | * +---------------+ | - - - - - - - | * | | ipa_context | * v | | * +---------------+ +---------------+ * | IPA | ipa_context_ops ^ * | Interface | | * | - - - - - - - | +---------------+ * | IPA Proxy | operations | IPA Proxy | * | | ----------------> | Worker | * +---------------+ over IPC +---------------+ * ~~~~ * * The IPAInterface implemented by the IPAContextWrapper or IPAProxy is * returned to the pipeline handler, and all interactions with the IPA context * go the same interface regardless of process isolation. * * In all cases the data passed to the IPAInterface methods is serialized to * Plain Old Data, either for the purpose of passing it to the IPA context * plain C API, or to transmit the data to the isolated process through IPC. */ 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; ipaCount += addDir(dir.c_str()); } if (!ipaCount) LOG(IPAManager, Warning) << "No IPA found in '" << modulePaths << "'"; } /* * When libcamera is used before it is installed, load IPAs from the * same build directory as the libcamera library itself. This requires * identifying the path of the libcamera.so, and referencing a relative * path for the IPA from that point. We need to recurse one level of * sub-directories to match the build tree. */ std::string root = utils::libcameraBuildPath(); if (!root.empty()) { std::string ipaBuildPath = root + "src/ipa"; constexpr int maxDepth = 1; LOG(IPAManager, Info) << "libcamera is not installed. Adding '" << ipaBuildPath << "' to the IPA search path"; ipaCount += addDir(ipaBuildPath.c_str(), maxDepth); } /* Finally try to load IPAs from the installed system path. */ ipaCount += addDir(IPA_MODULE_DIR); if (!ipaCount) LOG(IPAManager, Warning) << "No IPA found in '" IPA_MODULE_DIR "'"; } IPAManager::~IPAManager() { for (IPAModule *module : modules_) delete module; } /** * \brief Retrieve the IPA manager instance * * The IPAManager is a singleton and can't be constructed manually. This * function shall instead be used to retrieve the single global instance of the * manager. * * \return The IPA manager instance */ IPAManager *IPAManager::instance() { static IPAManager ipaManager; return &ipaManager; } /** * \brief Identify shared library objects within a directory * \param[in] libDir The directory to search for shared objects * \param[in] maxDepth The maximum depth of sub-directories to parse * \param[out] files A vector of paths to shared object library files * * Search a directory for .so files, allowing recursion down to sub-directories * no further than the depth specified by \a maxDepth. * * Discovered shared objects are added to the \a files vector. */ void IPAManager::parseDir(const char *libDir, unsigned int maxDepth, std::vector<std::string> &files) { struct dirent *ent; DIR *dir; dir = opendir(libDir); if (!dir) return; while ((ent = readdir(dir)) != nullptr) { if (ent->d_type == DT_DIR && maxDepth) { if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; std::string subdir = std::string(libDir) + "/" + ent->d_name; /* Recursion is limited to maxDepth. */ parseDir(subdir.c_str(), maxDepth - 1, files); continue; } int offset = strlen(ent->d_name) - 3; if (offset < 0) continue; if (strcmp(&ent->d_name[offset], ".so")) continue; files.push_back(std::string(libDir) + "/" + ent->d_name); } closedir(dir); } /** * \brief Load IPA modules from a directory * \param[in] libDir The directory to search for IPA modules * \param[in] maxDepth The maximum depth of sub-directories to search * * This method tries to create an IPAModule instance for every shared object * found in \a libDir, and skips invalid IPA modules. * * Sub-directories are searched up to a depth of \a maxDepth. A \a maxDepth * value of 0 only searches the directory specified in \a libDir. * * \return Number of modules loaded by this call */ unsigned int IPAManager::addDir(const char *libDir, unsigned int maxDepth) { std::vector<std::string> files; parseDir(libDir, maxDepth, files); /* Ensure a stable ordering of modules. */ std::sort(files.begin(), files.end()); unsigned int count = 0; for (const std::string &file : files) { IPAModule *ipaModule = new IPAModule(file); if (!ipaModule->isValid()) { delete ipaModule; continue; } LOG(IPAManager, Debug) << "Loaded IPA module '" << file << "'"; modules_.push_back(ipaModule); count++; } return count; } /** * \brief Create an IPA interface that matches a given pipeline handler * \param[in] pipe The pipeline handler that wants a matching IPA interface * \param[in] minVersion Minimum acceptable version of IPA module * \param[in] maxVersion Maximum acceptable version of IPA module * * \return A newly created IPA interface, or nullptr if no matching * IPA module is found or if the IPA interface fails to initialize */ std::unique_ptr<IPAInterface> IPAManager::createIPA(PipelineHandler *pipe, uint32_t maxVersion, uint32_t minVersion) { IPAModule *m = nullptr; for (IPAModule *module : modules_) { if (module->match(pipe, minVersion, maxVersion)) { m = module; break; } } if (!m) return nullptr; /* * Load and run the IPA module in a thread if it is open-source, or * isolate it in a separate process otherwise. * * \todo Implement a better proxy selection */ const char *proxyName = m->isOpenSource() ? "IPAProxyThread" : "IPAProxyLinux"; IPAProxyFactory *pf = nullptr; for (IPAProxyFactory *factory : IPAProxyFactory::factories()) { if (!strcmp(factory->name().c_str(), proxyName)) { pf = factory; break; } } if (!pf) { LOG(IPAManager, Error) << "Failed to get proxy factory"; return nullptr; } std::unique_ptr<IPAProxy> proxy = pf->create(m); if (!proxy->isValid()) { LOG(IPAManager, Error) << "Failed to load proxy"; return nullptr; } return proxy; } } /* namespace libcamera */