[{"id":111,"web_url":"https://patchwork.libcamera.org/comment/111/","msgid":"<2446472.FRuzvD9pZ1@avalon>","date":"2018-12-29T00:37:38","subject":"Re: [libcamera-devel] [PATCH 09/12] libcamera: pipelinehandler: add\n\tPipelineHandler class","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Niklas,\n\nThank you for the patch.\n\nOn Sunday, 23 December 2018 01:00:38 EET Niklas Söderlund wrote:\n> Provide a PipelineHandler which represents a handler for one or more\n> media devices and provider of one or more cameras.\n> \n> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> ---\n>  src/libcamera/include/pipelinehandler.h |  71 ++++++++++++++\n>  src/libcamera/meson.build               |   2 +\n>  src/libcamera/pipelinehandler.cpp       | 122 ++++++++++++++++++++++++\n>  3 files changed, 195 insertions(+)\n>  create mode 100644 src/libcamera/include/pipelinehandler.h\n>  create mode 100644 src/libcamera/pipelinehandler.cpp\n> \n> diff --git a/src/libcamera/include/pipelinehandler.h\n> b/src/libcamera/include/pipelinehandler.h new file mode 100644\n> index 0000000000000000..0e2f497a4fda3caa\n> --- /dev/null\n> +++ b/src/libcamera/include/pipelinehandler.h\n> @@ -0,0 +1,71 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2018, Google Inc.\n> + *\n> + * pipelinehandler.h - Pipeline handler infrastructure\n> + */\n> +#ifndef __LIBCAMERA_PIPELINEHANDLER_H__\n> +#define __LIBCAMERA_PIPELINEHANDLER_H__\n> +\n> +#include <map>\n> +#include <string>\n> +#include <vector>\n> +\n> +#include <libcamera/camera.h>\n> +\n> +namespace libcamera {\n> +\n> +class DeviceEnumerator;\n> +class PipelineHandlerFactory;\n> +\n> +class PipelineHandler\n> +{\n> +public:\n> +\tvirtual ~PipelineHandler() { };\n> +\n> +\tvirtual bool match(DeviceEnumerator *enumerator) = 0;\n> +\n> +\tvirtual unsigned int count() = 0;\n> +\tvirtual Camera *camera(unsigned int id) = 0;\n> +\n> +\tstatic void registerType(const std::string &name, PipelineHandlerFactory\n> *factory); \n> +\tstatic PipelineHandler *create(const std::string &name,\n> DeviceEnumerator *enumerator);\n> +\tstatic void handlers(std::vector<std::string> &handlers);\n\nDon't those three methods belong to PipelineHandlerFactory ? It seems more \nlogical to me to have creation handled by the factory.\n\n> +private:\n> +\tstatic std::map<std::string, PipelineHandlerFactory *> &registry();\n> +};\n> +\n> +class PipelineHandlerFactory\n> +{\n> +public:\n> +\n\nNo need for a blank line.\n\n> +\tvirtual ~PipelineHandlerFactory() { };\n> +\n> +\tvirtual PipelineHandler *create() = 0;\n> +};\n> +\n> +/**\n> + * \\brief Register a pipeline hander with the global list\n> + *\n> + * \\param[in] handler Class name of PipelineHandler subclass to register\n> + *\n> + * Register a specifc pipline handler with the global list and make it\n> + * avaiable to try and match devices for libcamera.\n> + */\n\nLet's move the documentation to the .cpp file.\n\n> +#define REGISTER_PIPELINE(handler) \\\n\nREGISTER_PIPELINE_HANDLER ?\n\n> +\tclass handler##Factory : public PipelineHandlerFactory { \\\n> +\tpublic: \\\n> +\t\thandler##Factory() \\\n> +\t\t{ \\\n> +\t\t\tPipelineHandler::registerType(#handler, this); \\\n> +\t\t} \\\n> +\t\tvirtual PipelineHandler *create() { \\\n> +\t\t\treturn new handler(); \\\n> +\t\t} \\\n> +\t}; \\\n> +\tstatic handler##Factory global_##handler##Factory;\n> +\n> +} /* namespace libcamera */\n> +\n> +#endif /* __LIBCAMERA_PIPELINEHANDLER_H__ */\n\n[snip]\n\n> diff --git a/src/libcamera/pipelinehandler.cpp\n> b/src/libcamera/pipelinehandler.cpp new file mode 100644\n> index 0000000000000000..3c47f1ceb72eb6f6\n> --- /dev/null\n> +++ b/src/libcamera/pipelinehandler.cpp\n> @@ -0,0 +1,122 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2018, Google Inc.\n> + *\n> + * pipelinehandler.cpp - Pipeline handler infrastructure\n> + */\n> +\n> +#include \"deviceenumerator.h\"\n> +#include \"log.h\"\n> +#include \"pipelinehandler.h\"\n> +\n> +/**\n> + * \\file pipelinehandler.h\n> + * \\brief Create pipelines and cameras from one or more media device\n> + *\n> + * Each pipeline supported by libcamera needs to be backed by a pipeline\n> + * handler implementation which describes the one or many media devices\n> + * needed for a pipeline to function properly.\n> + *\n> + * The pipeline handler is responsible to find all media devices it\n> requires + * to operate and once it retrieves them create all the camera\n> devices + * it is able to support with the that set of devices.\n> + *\n> + * To make it a bit less bit complicated to write pipe line handlers a\n> + * important macro REGISTER_PIPELINE() is provided which allows a pipeline\n> + * hander implementation to register itself with the library with ease.\n> + *\n> + * \\todo Figure out how and if the PipelineHandler should be involved in\n> + *       controlling cameras with resource dependencies and if the handler\n> + *       should provide helpers for cameras to control media graph links.\n> + */\n> +\n> +namespace libcamera {\n> +\n> +\n> +/**\n> + * \\class PipelineHandler\n> + * \\brief Find a set of media devices and provide cameras\n> + *\n> + * The responsibility of a PipelineHandler is to describe all media\n> + * devices it would need in order to provide cameras to the system.\n> + */\n> +\n> +/**\n> + * \\brief Add a pipeline hander to the global list\n> + *\n> + * \\param[in] name Name of the pipeline handler to add\n> + * \\param[in] factory Factory to use to construct the pipeline\n> + *\n> + * The caller is responsible to guarantee the uniqueness of the pipeline\n> name.\n> + */\n> +void PipelineHandler::registerType(const std::string &name,\n> PipelineHandlerFactory *factory)\n> +{\n> +\tstd::map<std::string, PipelineHandlerFactory *> &factories = registry();\n\nHow about adding a name member variable to the PipelineHandlerFactory class \nand storing factories in an std::vector ? You could then return the full list \nof factories to CameraManager and pass a factory pointer to \nPipelineHandler::create(), avoiding the lookup by name operation.\n\n> +\tif (factories.count(name)) {\n> +\t\tLOG(Error) <<  \"Registering '\" << name << \"' pipeline twice\";\n> +\t\treturn;\n> +\t}\n> +\n> +\tfactories[name] = factory;\n> +}\n> +\n> +/**\n> + * \\brief Create a new pipeline hander and try to match it\n\ns/hander/handler/\n\n> + *\n> + * \\param[in] name Name of the pipeline handler to try\n> + * \\param[in] enumerator  Numerator to to search for a match for the\n> handler\n> + *\n> + * Search \\a enumerator for a match for a pipeline handler named \\a name.\n> + *\n> + * \\return Pipeline handler if a match was found else NULL\n> + */\n> +PipelineHandler *PipelineHandler::create(const std::string &name,\n> DeviceEnumerator *enumerator)\n> +{\n> +\tstd::map<std::string, PipelineHandlerFactory *> &factories = registry();\n> +\n> +\tif (!factories.count(name)) {\n> +\t\tLOG(Error) << \"Trying to create non-existing pipeline handler \" << name;\n> +\t\treturn NULL;\n> +\t}\n> +\n> +\tPipelineHandler *pipe;\n> +\n> +\tpipe = factories[name]->create();\n\nUse an iterator to lookup in one operation, removing the need to count() and \n[] separately.\n\n> +\n> +\tif (pipe->match(enumerator))\n> +\t\treturn pipe;\n> +\n> +\tdelete pipe;\n> +\treturn NULL;\n\nI wonder whether REGISTER_PIPELINE should register a new instance of the \nPipelineHandler instead of the PipelineHandlerFactory. This would avoid \ncreating and deleting instance in a tight loop during matching, and would \nallow for storing data shared by multiple pipelines from the same pipeline \nhandler (I'm not sure we'll have a use case for this though). The pipeline \nhandler would create one pipeline per match, and pipelines would be handled \ninternally in the pipeline handler and not exposed to the camera manager. The \npipeline handler would then need to register the camera instances it creates \nwith the camera manager, as the camera manager wouldn't have a list of pipes \nanymore.\n\n> +}\n> +\n> +/**\n> + * \\brief List all names of handlers from the global list\n> + *\n> + * \\param[out] handlers Names of all handlers registered with the global\n> list + */\n> +void PipelineHandler::handlers(std::vector<std::string> &handlers)\n> +{\n> +\tstd::map<std::string, PipelineHandlerFactory *> &factories = registry();\n> +\n> +\tfor (auto const &handler : factories)\n> +\t\thandlers.push_back(handler.first);\n> +}\n> +\n> +/**\n> + * \\brief Static global list of pipeline handlers\n> + *\n> + * It might seem odd to hide the static map inside a function.\n> + * This is needed to make sure the list is created before anyone\n> + * tries to access it and creating problems at link time.\n> + *\n> + * \\return Global list of pipeline handlers\n> + */\n> +std::map<std::string, PipelineHandlerFactory *>\n> &PipelineHandler::registry()\n> +{\n> +\tstatic std::map<std::string, PipelineHandlerFactory *> factories;\n> +\treturn factories;\n> +}\n> +\n> +} /* namespace libcamera */","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B261660B2E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 29 Dec 2018 01:36:42 +0100 (CET)","from avalon.localnet (unknown\n\t[IPv6:2a02:2788:66a:3eb:2624:a446:f4b7:b19d])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 4D31BDF;\n\tSat, 29 Dec 2018 01:36:42 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1546043802;\n\tbh=YcPPuj1eD4vzbkHif/xQm22FgWdn42iFiEkxcR5GDOw=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=GLIdjxtua5jK5jzvKEfDuUL/VQx7jPijAxW+v6sZlWc6MGcWaftowfBzk9yBS2swj\n\tFjo6z12Q4f2n6WZ9UPaIEJvVFIJLiCJ49LLZdEQX0SK8KWR1b9SeOqjmlGKHL2aTR9\n\t1/z5DG1uCX7VTvtuPGGqZ32Mbs4Qloen+2MSI5+Q=","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Date":"Sat, 29 Dec 2018 02:37:38 +0200","Message-ID":"<2446472.FRuzvD9pZ1@avalon>","Organization":"Ideas on Board Oy","In-Reply-To":"<20181222230041.29999-10-niklas.soderlund@ragnatech.se>","References":"<20181222230041.29999-1-niklas.soderlund@ragnatech.se>\n\t<20181222230041.29999-10-niklas.soderlund@ragnatech.se>","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","Content-Type":"text/plain; charset=\"iso-8859-1\"","Subject":"Re: [libcamera-devel] [PATCH 09/12] libcamera: pipelinehandler: add\n\tPipelineHandler class","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Sat, 29 Dec 2018 00:36:42 -0000"}},{"id":118,"web_url":"https://patchwork.libcamera.org/comment/118/","msgid":"<20181229023918.GD19796@bigcity.dyn.berto.se>","date":"2018-12-29T02:39:18","subject":"Re: [libcamera-devel] [PATCH 09/12] libcamera: pipelinehandler: add\n\tPipelineHandler class","submitter":{"id":5,"url":"https://patchwork.libcamera.org/api/people/5/","name":"Niklas Söderlund","email":"niklas.soderlund@ragnatech.se"},"content":"Hi Laurent,\n\nThanks for your comments.\n\nOn 2018-12-29 02:37:38 +0200, Laurent Pinchart wrote:\n> Hi Niklas,\n> \n> Thank you for the patch.\n> \n> On Sunday, 23 December 2018 01:00:38 EET Niklas Söderlund wrote:\n> > Provide a PipelineHandler which represents a handler for one or more\n> > media devices and provider of one or more cameras.\n> > \n> > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> > ---\n> >  src/libcamera/include/pipelinehandler.h |  71 ++++++++++++++\n> >  src/libcamera/meson.build               |   2 +\n> >  src/libcamera/pipelinehandler.cpp       | 122 ++++++++++++++++++++++++\n> >  3 files changed, 195 insertions(+)\n> >  create mode 100644 src/libcamera/include/pipelinehandler.h\n> >  create mode 100644 src/libcamera/pipelinehandler.cpp\n> > \n> > diff --git a/src/libcamera/include/pipelinehandler.h\n> > b/src/libcamera/include/pipelinehandler.h new file mode 100644\n> > index 0000000000000000..0e2f497a4fda3caa\n> > --- /dev/null\n> > +++ b/src/libcamera/include/pipelinehandler.h\n> > @@ -0,0 +1,71 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2018, Google Inc.\n> > + *\n> > + * pipelinehandler.h - Pipeline handler infrastructure\n> > + */\n> > +#ifndef __LIBCAMERA_PIPELINEHANDLER_H__\n> > +#define __LIBCAMERA_PIPELINEHANDLER_H__\n> > +\n> > +#include <map>\n> > +#include <string>\n> > +#include <vector>\n> > +\n> > +#include <libcamera/camera.h>\n> > +\n> > +namespace libcamera {\n> > +\n> > +class DeviceEnumerator;\n> > +class PipelineHandlerFactory;\n> > +\n> > +class PipelineHandler\n> > +{\n> > +public:\n> > +\tvirtual ~PipelineHandler() { };\n> > +\n> > +\tvirtual bool match(DeviceEnumerator *enumerator) = 0;\n> > +\n> > +\tvirtual unsigned int count() = 0;\n> > +\tvirtual Camera *camera(unsigned int id) = 0;\n> > +\n> > +\tstatic void registerType(const std::string &name, PipelineHandlerFactory\n> > *factory); \n> > +\tstatic PipelineHandler *create(const std::string &name,\n> > DeviceEnumerator *enumerator);\n> > +\tstatic void handlers(std::vector<std::string> &handlers);\n> \n> Don't those three methods belong to PipelineHandlerFactory ? It seems more \n> logical to me to have creation handled by the factory.\n\nAgreed, they are moved for v2.\n\n\n> \n> > +private:\n> > +\tstatic std::map<std::string, PipelineHandlerFactory *> &registry();\n> > +};\n> > +\n> > +class PipelineHandlerFactory\n> > +{\n> > +public:\n> > +\n> \n> No need for a blank line.\n\nThanks.\n\n> \n> > +\tvirtual ~PipelineHandlerFactory() { };\n> > +\n> > +\tvirtual PipelineHandler *create() = 0;\n> > +};\n> > +\n> > +/**\n> > + * \\brief Register a pipeline hander with the global list\n> > + *\n> > + * \\param[in] handler Class name of PipelineHandler subclass to register\n> > + *\n> > + * Register a specifc pipline handler with the global list and make it\n> > + * avaiable to try and match devices for libcamera.\n> > + */\n> \n> Let's move the documentation to the .cpp file.\n\nDone.\n\n> \n> > +#define REGISTER_PIPELINE(handler) \\\n> \n> REGISTER_PIPELINE_HANDLER ?\n\nYes.\n\n> \n> > +\tclass handler##Factory : public PipelineHandlerFactory { \\\n> > +\tpublic: \\\n> > +\t\thandler##Factory() \\\n> > +\t\t{ \\\n> > +\t\t\tPipelineHandler::registerType(#handler, this); \\\n> > +\t\t} \\\n> > +\t\tvirtual PipelineHandler *create() { \\\n> > +\t\t\treturn new handler(); \\\n> > +\t\t} \\\n> > +\t}; \\\n> > +\tstatic handler##Factory global_##handler##Factory;\n> > +\n> > +} /* namespace libcamera */\n> > +\n> > +#endif /* __LIBCAMERA_PIPELINEHANDLER_H__ */\n> \n> [snip]\n> \n> > diff --git a/src/libcamera/pipelinehandler.cpp\n> > b/src/libcamera/pipelinehandler.cpp new file mode 100644\n> > index 0000000000000000..3c47f1ceb72eb6f6\n> > --- /dev/null\n> > +++ b/src/libcamera/pipelinehandler.cpp\n> > @@ -0,0 +1,122 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2018, Google Inc.\n> > + *\n> > + * pipelinehandler.cpp - Pipeline handler infrastructure\n> > + */\n> > +\n> > +#include \"deviceenumerator.h\"\n> > +#include \"log.h\"\n> > +#include \"pipelinehandler.h\"\n> > +\n> > +/**\n> > + * \\file pipelinehandler.h\n> > + * \\brief Create pipelines and cameras from one or more media device\n> > + *\n> > + * Each pipeline supported by libcamera needs to be backed by a pipeline\n> > + * handler implementation which describes the one or many media devices\n> > + * needed for a pipeline to function properly.\n> > + *\n> > + * The pipeline handler is responsible to find all media devices it\n> > requires + * to operate and once it retrieves them create all the camera\n> > devices + * it is able to support with the that set of devices.\n> > + *\n> > + * To make it a bit less bit complicated to write pipe line handlers a\n> > + * important macro REGISTER_PIPELINE() is provided which allows a pipeline\n> > + * hander implementation to register itself with the library with ease.\n> > + *\n> > + * \\todo Figure out how and if the PipelineHandler should be involved in\n> > + *       controlling cameras with resource dependencies and if the handler\n> > + *       should provide helpers for cameras to control media graph links.\n> > + */\n> > +\n> > +namespace libcamera {\n> > +\n> > +\n> > +/**\n> > + * \\class PipelineHandler\n> > + * \\brief Find a set of media devices and provide cameras\n> > + *\n> > + * The responsibility of a PipelineHandler is to describe all media\n> > + * devices it would need in order to provide cameras to the system.\n> > + */\n> > +\n> > +/**\n> > + * \\brief Add a pipeline hander to the global list\n> > + *\n> > + * \\param[in] name Name of the pipeline handler to add\n> > + * \\param[in] factory Factory to use to construct the pipeline\n> > + *\n> > + * The caller is responsible to guarantee the uniqueness of the pipeline\n> > name.\n> > + */\n> > +void PipelineHandler::registerType(const std::string &name,\n> > PipelineHandlerFactory *factory)\n> > +{\n> > +\tstd::map<std::string, PipelineHandlerFactory *> &factories = registry();\n> \n> How about adding a name member variable to the PipelineHandlerFactory class \n> and storing factories in an std::vector ? You could then return the full list \n> of factories to CameraManager and pass a factory pointer to \n> PipelineHandler::create(), avoiding the lookup by name operation.\n\nThis is a nice idea, I would like to implement the configuration file \nwhich allows the user to specify which pipeline handlers should be used \nor if all available should be used. I had this in mind when implementing \nthis in the first place. I made a note of it and will think of it when I \nadd the configuration feature.\n\n> \n> > +\tif (factories.count(name)) {\n> > +\t\tLOG(Error) <<  \"Registering '\" << name << \"' pipeline twice\";\n> > +\t\treturn;\n> > +\t}\n> > +\n> > +\tfactories[name] = factory;\n> > +}\n> > +\n> > +/**\n> > + * \\brief Create a new pipeline hander and try to match it\n> \n> s/hander/handler/\n\nWops, I manage to misspell this in quiet a few locations.\n\n> \n> > + *\n> > + * \\param[in] name Name of the pipeline handler to try\n> > + * \\param[in] enumerator  Numerator to to search for a match for the\n> > handler\n> > + *\n> > + * Search \\a enumerator for a match for a pipeline handler named \\a name.\n> > + *\n> > + * \\return Pipeline handler if a match was found else NULL\n> > + */\n> > +PipelineHandler *PipelineHandler::create(const std::string &name,\n> > DeviceEnumerator *enumerator)\n> > +{\n> > +\tstd::map<std::string, PipelineHandlerFactory *> &factories = registry();\n> > +\n> > +\tif (!factories.count(name)) {\n> > +\t\tLOG(Error) << \"Trying to create non-existing pipeline handler \" << name;\n> > +\t\treturn NULL;\n> > +\t}\n> > +\n> > +\tPipelineHandler *pipe;\n> > +\n> > +\tpipe = factories[name]->create();\n> \n> Use an iterator to lookup in one operation, removing the need to count() and \n> [] separately.\n\nThanks.\n\n> \n> > +\n> > +\tif (pipe->match(enumerator))\n> > +\t\treturn pipe;\n> > +\n> > +\tdelete pipe;\n> > +\treturn NULL;\n> \n> I wonder whether REGISTER_PIPELINE should register a new instance of the \n> PipelineHandler instead of the PipelineHandlerFactory. This would avoid \n> creating and deleting instance in a tight loop during matching, and would \n> allow for storing data shared by multiple pipelines from the same pipeline \n> handler (I'm not sure we'll have a use case for this though). The pipeline \n> handler would create one pipeline per match, and pipelines would be handled \n> internally in the pipeline handler and not exposed to the camera manager. The \n> pipeline handler would then need to register the camera instances it creates \n> with the camera manager, as the camera manager wouldn't have a list of pipes \n> anymore.\n\nThe current design is that each pipeline handler is fully independent.  \nIf a pipeline requires more then one media device where resources are \nshard between two semi independent video sources, the pipeline handler \nshould still request all media devices needed and provide two or more \ncameras. This is supported in the design in this series and for now I \nfeel this approach is simpler.\n\nI do see your point that there is waste in creating and destroying \nobjects in the matching loop. I will meditate a bit over this and see \nwhat can be done.\n\n> \n> > +}\n> > +\n> > +/**\n> > + * \\brief List all names of handlers from the global list\n> > + *\n> > + * \\param[out] handlers Names of all handlers registered with the global\n> > list + */\n> > +void PipelineHandler::handlers(std::vector<std::string> &handlers)\n> > +{\n> > +\tstd::map<std::string, PipelineHandlerFactory *> &factories = registry();\n> > +\n> > +\tfor (auto const &handler : factories)\n> > +\t\thandlers.push_back(handler.first);\n> > +}\n> > +\n> > +/**\n> > + * \\brief Static global list of pipeline handlers\n> > + *\n> > + * It might seem odd to hide the static map inside a function.\n> > + * This is needed to make sure the list is created before anyone\n> > + * tries to access it and creating problems at link time.\n> > + *\n> > + * \\return Global list of pipeline handlers\n> > + */\n> > +std::map<std::string, PipelineHandlerFactory *>\n> > &PipelineHandler::registry()\n> > +{\n> > +\tstatic std::map<std::string, PipelineHandlerFactory *> factories;\n> > +\treturn factories;\n> > +}\n> > +\n> > +} /* namespace libcamera */\n> \n> -- \n> Regards,\n> \n> Laurent Pinchart\n> \n> \n>","headers":{"Return-Path":"<niklas.soderlund@ragnatech.se>","Received":["from mail-lf1-x142.google.com (mail-lf1-x142.google.com\n\t[IPv6:2a00:1450:4864:20::142])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 07B2460B30\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 29 Dec 2018 03:39:21 +0100 (CET)","by mail-lf1-x142.google.com with SMTP id i26so15560350lfc.0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 28 Dec 2018 18:39:20 -0800 (PST)","from localhost (89-233-230-99.cust.bredband2.com. [89.233.230.99])\n\tby smtp.gmail.com with ESMTPSA id\n\te13-v6sm8897911ljk.53.2018.12.28.18.39.18\n\t(version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256);\n\tFri, 28 Dec 2018 18:39:19 -0800 (PST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ragnatech-se.20150623.gappssmtp.com; s=20150623;\n\th=date:from:to:cc:subject:message-id:references:mime-version\n\t:content-disposition:content-transfer-encoding:in-reply-to\n\t:user-agent; bh=2Li8amUi/3RTrksrPjcuCO8DvRHYKsXZMVYVbBiQxbU=;\n\tb=f68Db5HuSepYBns4Vtizpf4A24QfJC8dXDq9Vt7a/l4vKvlyJCNdrOkq/ZP+zAPgvb\n\tSMI5Cvpp/cQFJWUEFx4pX4qyJ0w6f6NTFsaFxUYCRhylF0NQGL3oz0rarfKbBEAcaz4L\n\tyU7yYshjseUV9R7gVfFXZ8aH30vePzruCAqQpMwQ7J0N2Sh3zjDFMLSdfFEVZeZbSfko\n\tjmn3Ogahy09R+prekHYp8Ur4e3+b1AMr8nOD9DmrPwMxjcqRhHNzkA/E+8WdFrL22DKN\n\t7i0EhpVAjXejzYrIaAd0px9rGF6UJ/hMZgmj21RwKxDSGm3PVmSVs5j8GpZLMnLkZ17H\n\tAMiA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:date:from:to:cc:subject:message-id:references\n\t:mime-version:content-disposition:content-transfer-encoding\n\t:in-reply-to:user-agent;\n\tbh=2Li8amUi/3RTrksrPjcuCO8DvRHYKsXZMVYVbBiQxbU=;\n\tb=rX4rHSUuIx6M9GEFmiR0oWNIT2shERWnPHVN6YKIKIudblJEzzP6oRm64hVQ0tlc7E\n\tyEHKpipQzH3P3aGXI5tN6HMl7u1+RAuQPayG88HGU1jC/aMZKqZNwZED5sW8+6uJ09n9\n\t+EW4A+FEUpFpE95dacy3+Tim6bd9i9smas6OAyDsYPQNNXcG7lAFqH72q03oAmd08z/f\n\tKW7q52jNwwOm7uEarEaDDiJIsMKUBbnk5d30//9wn81vjgs8zsj2Lt83vxHJsmTz49/3\n\twyQkeSX9EC71CXiNlScY8Iuq1ktWh5DabamliU+COh7I11jewCAxqMIg6M2J1SiPpV/u\n\tnMqw==","X-Gm-Message-State":"AA+aEWbx0XYAfUGwq2M31LE+vBeYZA61UEXmd48WsGTp03qtxokyF24Y\n\tRLxgBN/F4fXZfIWi9oeisX/YqFa5N90=","X-Google-Smtp-Source":"AFSGD/V4UhvOCMoxuSv9FEm8ItQ9ugZOjSVYq7NWVyrvpHfeIuqwcNUah1uK4DzDMkZgBf7mhcFZQw==","X-Received":"by 2002:a19:945b:: with SMTP id\n\tw88mr14264688lfd.15.1546051160167; \n\tFri, 28 Dec 2018 18:39:20 -0800 (PST)","Date":"Sat, 29 Dec 2018 03:39:18 +0100","From":"Niklas =?iso-8859-1?q?S=F6derlund?= <niklas.soderlund@ragnatech.se>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20181229023918.GD19796@bigcity.dyn.berto.se>","References":"<20181222230041.29999-1-niklas.soderlund@ragnatech.se>\n\t<20181222230041.29999-10-niklas.soderlund@ragnatech.se>\n\t<2446472.FRuzvD9pZ1@avalon>","MIME-Version":"1.0","Content-Type":"text/plain; charset=iso-8859-1","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<2446472.FRuzvD9pZ1@avalon>","User-Agent":"Mutt/1.10.1 (2018-07-13)","Subject":"Re: [libcamera-devel] [PATCH 09/12] libcamera: pipelinehandler: add\n\tPipelineHandler class","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Sat, 29 Dec 2018 02:39:21 -0000"}}]