Show a patch.

GET /api/patches/3125/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 3125,
    "url": "https://patchwork.libcamera.org/api/patches/3125/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/3125/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20200316214310.27665-9-laurent.pinchart@ideasonboard.com>",
    "date": "2020-03-16T21:43:08",
    "name": "[libcamera-devel,v2,08/10] libcamera: pipeline: Add a simple pipeline handler",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "c7489aa2f3cc26bc69f1f3483b025bf86628425d",
    "submitter": {
        "id": 2,
        "url": "https://patchwork.libcamera.org/api/people/2/?format=api",
        "name": "Laurent Pinchart",
        "email": "laurent.pinchart@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/3125/mbox/",
    "series": [
        {
            "id": 724,
            "url": "https://patchwork.libcamera.org/api/series/724/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=724",
            "date": "2020-03-16T21:43:00",
            "name": "Simple pipeline handler",
            "version": 2,
            "mbox": "https://patchwork.libcamera.org/series/724/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/3125/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/3125/checks/",
    "tags": {},
    "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 432CD62923\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Mar 2020 22:43:29 +0100 (CET)",
            "from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi\n\t[81.175.216.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 81750AC1;\n\tMon, 16 Mar 2020 22:43:28 +0100 (CET)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1584395008;\n\tbh=UvNlc3+H7nmSZA9CeI63GH7eeffB/qxRdFaTWNnsQPo=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=DEijf6b/fdTFKVqF5AL/EflYoa6euufDhMlXRsJ49+FhHY8rZqsv8UgMpEpE9vPI/\n\tTlfuioiPt6TAHp/OKJfW87hp+2AJj5hVW7kYsDGMbTeSZvqGa0ZBV5R/d5IndO61j7\n\tIMsAv3+yuZfIzfdq+cGWH6NSsszff5vtj067qFCo=",
        "From": "Laurent Pinchart <laurent.pinchart@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "Martijn Braam <martijn@brixit.nl>, Mickael GUENE <mickael.guene@st.com>, \n\tBenjamin GAIGNARD <benjamin.gaignard@st.com>,\n\tAndrey Konovalov <andrey.konovalov@linaro.org>",
        "Date": "Mon, 16 Mar 2020 23:43:08 +0200",
        "Message-Id": "<20200316214310.27665-9-laurent.pinchart@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.24.1",
        "In-Reply-To": "<20200316214310.27665-1-laurent.pinchart@ideasonboard.com>",
        "References": "<20200316214310.27665-1-laurent.pinchart@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH v2 08/10] libcamera: pipeline: Add a\n\tsimple pipeline handler",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.29",
        "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": "Mon, 16 Mar 2020 21:43:29 -0000"
    },
    "content": "From: Martijn Braam <martijn@brixit.nl>\n\nThis new pipeline handler aims at supporting any simple device without\nrequiring any device-specific code. Simple devices are currently defined\nas a graph made of one or multiple camera sensors and a single video\nnode, with each sensor connected to the video node through a linear\npipeline.\n\nThe simple pipeline handler will automatically parse the media graph,\nenumerate sensors, build supported stream configurations, and configure\nthe pipeline, without any device-specific knowledge. It doesn't support\nconfiguration of any processing in the pipeline at the moment, but may\nbe extended to support simple processing such as format conversion or\nscaling in the future.\n\nThe only device-specific information in the pipeline handler is the list\nof supported drivers, required for device matching. We may be able to\nremove this in the future by matching with the simple pipeline handler\nas a last resort option, after all other pipeline handlers have been\ntried.\n\nSigned-off-by: Martijn Braam <martijn@brixit.nl>\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\nChanges since v1:\n\n- Rebase on top of buffer API rework\n- Expose stream formats\n- Rework camera data config\n---\n src/libcamera/pipeline/meson.build        |   1 +\n src/libcamera/pipeline/simple/meson.build |   3 +\n src/libcamera/pipeline/simple/simple.cpp  | 699 ++++++++++++++++++++++\n 3 files changed, 703 insertions(+)\n create mode 100644 src/libcamera/pipeline/simple/meson.build\n create mode 100644 src/libcamera/pipeline/simple/simple.cpp",
    "diff": "diff --git a/src/libcamera/pipeline/meson.build b/src/libcamera/pipeline/meson.build\nindex 0d466225a72e..606ba31a0319 100644\n--- a/src/libcamera/pipeline/meson.build\n+++ b/src/libcamera/pipeline/meson.build\n@@ -5,3 +5,4 @@ libcamera_sources += files([\n \n subdir('ipu3')\n subdir('rkisp1')\n+subdir('simple')\ndiff --git a/src/libcamera/pipeline/simple/meson.build b/src/libcamera/pipeline/simple/meson.build\nnew file mode 100644\nindex 000000000000..4945a3e173cf\n--- /dev/null\n+++ b/src/libcamera/pipeline/simple/meson.build\n@@ -0,0 +1,3 @@\n+libcamera_sources += files([\n+    'simple.cpp',\n+])\ndiff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\nnew file mode 100644\nindex 000000000000..2126799c54eb\n--- /dev/null\n+++ b/src/libcamera/pipeline/simple/simple.cpp\n@@ -0,0 +1,699 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2020, Laurent Pinchart\n+ * Copyright (C) 2019, Martijn Braam\n+ *\n+ * simple.cpp - Pipeline handler for simple pipelines\n+ */\n+\n+#include <algorithm>\n+#include <iterator>\n+#include <list>\n+#include <map>\n+#include <memory>\n+#include <set>\n+#include <string>\n+#include <string.h>\n+#include <utility>\n+#include <vector>\n+\n+#include <linux/media-bus-format.h>\n+\n+#include <libcamera/camera.h>\n+#include <libcamera/request.h>\n+#include <libcamera/stream.h>\n+\n+#include \"camera_sensor.h\"\n+#include \"device_enumerator.h\"\n+#include \"log.h\"\n+#include \"media_device.h\"\n+#include \"pipeline_handler.h\"\n+#include \"v4l2_subdevice.h\"\n+#include \"v4l2_videodevice.h\"\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(SimplePipeline)\n+\n+class SimplePipelineHandler;\n+\n+class SimpleCameraData : public CameraData\n+{\n+public:\n+\tSimpleCameraData(PipelineHandler *pipe, MediaEntity *sensor,\n+\t\t\t MediaEntity *video);\n+\n+\tbool isValid() const { return sensor_ != nullptr; }\n+\tstd::set<Stream *> streams() { return { &stream_ }; }\n+\n+\tint init();\n+\tint setupLinks();\n+\tint setupFormats(V4L2SubdeviceFormat *format,\n+\t\t\t V4L2Subdevice::Whence whence);\n+\n+\tstruct Entity {\n+\t\tMediaEntity *entity;\n+\t\tMediaLink *link;\n+\t};\n+\n+\tstruct Configuration {\n+\t\tuint32_t code;\n+\t\tPixelFormat pixelFormat;\n+\t\tSize size;\n+\t};\n+\n+\tStream stream_;\n+\tstd::unique_ptr<CameraSensor> sensor_;\n+\tstd::list<Entity> entities_;\n+\n+\tstd::vector<Configuration> configs_;\n+\tstd::map<PixelFormat, Configuration> formats_;\n+};\n+\n+class SimpleCameraConfiguration : public CameraConfiguration\n+{\n+public:\n+\tSimpleCameraConfiguration(Camera *camera, SimpleCameraData *data);\n+\n+\tStatus validate() override;\n+\n+\tconst V4L2SubdeviceFormat &sensorFormat() { return sensorFormat_; }\n+\n+private:\n+\t/*\n+\t * The SimpleCameraData instance is guaranteed to be valid as long as\n+\t * the corresponding Camera instance is valid. In order to borrow a\n+\t * reference to the camera data, store a new reference to the camera.\n+\t */\n+\tstd::shared_ptr<Camera> camera_;\n+\tconst SimpleCameraData *data_;\n+\n+\tV4L2SubdeviceFormat sensorFormat_;\n+};\n+\n+class SimplePipelineHandler : public PipelineHandler\n+{\n+public:\n+\tSimplePipelineHandler(CameraManager *manager);\n+\t~SimplePipelineHandler();\n+\n+\tCameraConfiguration *generateConfiguration(Camera *camera,\n+\t\t\t\t\t\t   const StreamRoles &roles) override;\n+\tint configure(Camera *camera, CameraConfiguration *config) override;\n+\n+\tint exportFrameBuffers(Camera *camera, Stream *stream,\n+\t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;\n+\n+\tint start(Camera *camera) override;\n+\tvoid stop(Camera *camera) override;\n+\n+\tbool match(DeviceEnumerator *enumerator) override;\n+\n+\tV4L2VideoDevice *video() { return video_; }\n+\tV4L2Subdevice *subdev(const MediaEntity *entity);\n+\n+protected:\n+\tint queueRequestDevice(Camera *camera, Request *request) override;\n+\n+private:\n+\tSimpleCameraData *cameraData(const Camera *camera)\n+\t{\n+\t\treturn static_cast<SimpleCameraData *>(\n+\t\t\tPipelineHandler::cameraData(camera));\n+\t}\n+\n+\tint initLinks();\n+\n+\tint createCamera(MediaEntity *sensor);\n+\n+\tvoid bufferReady(FrameBuffer *buffer);\n+\n+\tMediaDevice *media_;\n+\tV4L2VideoDevice *video_;\n+\tstd::map<const MediaEntity *, V4L2Subdevice> subdevs_;\n+\n+\tCamera *activeCamera_;\n+};\n+\n+/* -----------------------------------------------------------------------------\n+ * Camera Data\n+ */\n+SimpleCameraData::SimpleCameraData(PipelineHandler *pipe, MediaEntity *sensor,\n+\t\t\t\t   MediaEntity *video)\n+\t: CameraData(pipe)\n+{\n+\tint ret;\n+\n+\t/*\n+\t * Walk the pipeline towards the video node and store all entities\n+\t * along the way.\n+\t */\n+\tMediaEntity *source = sensor;\n+\n+\twhile (source) {\n+\t\t/* If we have reached the video node, we're done. */\n+\t\tif (source == video)\n+\t\t\tbreak;\n+\n+\t\t/* Use the first output pad that has links. */\n+\t\tMediaPad *sourcePad = nullptr;\n+\t\tfor (MediaPad *pad : source->pads()) {\n+\t\t\tif ((pad->flags() & MEDIA_PAD_FL_SOURCE) &&\n+\t\t\t    !pad->links().empty()) {\n+\t\t\t\tsourcePad = pad;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (!sourcePad)\n+\t\t\treturn;\n+\n+\t\t/* Use the first link that isn't immutable and disabled. */\n+\t\tMediaLink *sourceLink = nullptr;\n+\t\tfor (MediaLink *link : sourcePad->links()) {\n+\t\t\tif ((link->flags() & MEDIA_LNK_FL_ENABLED) ||\n+\t\t\t    !(link->flags() & MEDIA_LNK_FL_IMMUTABLE)) {\n+\t\t\t\tsourceLink = link;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (!sourceLink)\n+\t\t\treturn;\n+\n+\t\tentities_.push_back({ source, sourceLink });\n+\n+\t\tsource = sourceLink->sink()->entity();\n+\n+\t\t/* Avoid infinite loops. */\n+\t\tauto iter = std::find_if(entities_.begin(), entities_.end(),\n+\t\t\t\t\t [&](const Entity &entity) {\n+\t\t\t\t\t\t return entity.entity == source;\n+\t\t\t\t\t });\n+\t\tif (iter != entities_.end()) {\n+\t\t\tLOG(SimplePipeline, Info) << \"Loop detected in pipeline\";\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\t/* We have a valid pipeline, create the camera sensor. */\n+\tsensor_ = std::make_unique<CameraSensor>(sensor);\n+\tret = sensor_->init();\n+\tif (ret) {\n+\t\tsensor_.reset();\n+\t\treturn;\n+\t}\n+}\n+\n+int SimpleCameraData::init()\n+{\n+\tSimplePipelineHandler *pipe = static_cast<SimplePipelineHandler *>(pipe_);\n+\tV4L2VideoDevice *video = pipe->video();\n+\tint ret;\n+\n+\t/*\n+\t * Enumerate the possible pipeline configurations. For each media bus\n+\t * format supported by the sensor, propagate the formats through the\n+\t * pipeline, and enumerate the corresponding possible V4L2 pixel\n+\t * formats on the video node.\n+\t */\n+\tfor (unsigned int code : sensor_->mbusCodes()) {\n+\t\tV4L2SubdeviceFormat format{ code, sensor_->resolution() };\n+\n+\t\t/*\n+\t\t * Setup links first as some subdev drivers take active links\n+\t\t * into account to propaget TRY formats. So is life :-(\n+\t\t */\n+\t\tret = setupLinks();\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\n+\t\tret = setupFormats(&format, V4L2Subdevice::TryFormat);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\n+\t\tstd::vector<unsigned int> formats =\n+\t\t\tvideo->formats(format.mbus_code).formats();\n+\n+\t\tLOG(SimplePipeline, Debug)\n+\t\t\t<< \"Adding configuration for \" << format.size.toString()\n+\t\t\t<< \" in pixel formats [ \"\n+\t\t\t<< utils::join(formats, \", \",\n+\t\t\t\t       [](unsigned int f) { return std::to_string(f); })\n+\t\t\t<< \" ]\";\n+\n+\t\t/*\n+\t\t * Store the configuration in the formats_ map, mapping\n+\t\t * PixelFormat to configuration. Any previously stored value is\n+\t\t * overwritten, as the pipeline handler currently doesn't care\n+\t\t * about how a particular PixelFormat is achieved.\n+\t\t */\n+\t\tfor (unsigned int v4l2Format : formats) {\n+\t\t\tPixelFormat pixelFormat = video->toPixelFormat(v4l2Format);\n+\t\t\tif (!pixelFormat)\n+\t\t\t\tcontinue;\n+\n+\t\t\tConfiguration config;\n+\t\t\tconfig.code = code;\n+\t\t\tconfig.pixelFormat = pixelFormat;\n+\t\t\tconfig.size = format.size;\n+\n+\t\t\tformats_[pixelFormat] = config;\n+\t\t}\n+\t}\n+\n+\tif (formats_.empty()) {\n+\t\tLOG(SimplePipeline, Error) << \"No valid configuration found\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int SimpleCameraData::setupLinks()\n+{\n+\tint ret;\n+\n+\t/*\n+\t * Configure all links along the pipeline. Some entities may not allow\n+\t * multiple sink links to be enabled together, even on different sink\n+\t * pads. We must thus start by disabling all sink links (but the one we\n+\t * want to enable) before enabling the pipeline link.\n+\t */\n+\tfor (SimpleCameraData::Entity &e : entities_) {\n+\t\tfor (MediaPad *pad : e.link->sink()->entity()->pads()) {\n+\t\t\tfor (MediaLink *link : pad->links()) {\n+\t\t\t\tif (link == e.link)\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tif ((link->flags() & MEDIA_LNK_FL_ENABLED) &&\n+\t\t\t\t    !(link->flags() & MEDIA_LNK_FL_IMMUTABLE)) {\n+\t\t\t\t\tret = link->setEnabled(false);\n+\t\t\t\t\tif (ret < 0)\n+\t\t\t\t\t\treturn ret;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (!(e.link->flags() & MEDIA_LNK_FL_ENABLED)) {\n+\t\t\tret = e.link->setEnabled(true);\n+\t\t\tif (ret < 0)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int SimpleCameraData::setupFormats(V4L2SubdeviceFormat *format,\n+\t\t\t\t   V4L2Subdevice::Whence whence)\n+{\n+\tSimplePipelineHandler *pipe = static_cast<SimplePipelineHandler *>(pipe_);\n+\tint ret;\n+\n+\t/*\n+\t * Configure the format on the sensor output and propagate it through\n+\t * the pipeline.\n+\t */\n+\tret = sensor_->setFormat(format);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tfor (const Entity &e : entities_) {\n+\t\tMediaLink *link = e.link;\n+\t\tMediaPad *source = link->source();\n+\t\tMediaPad *sink = link->sink();\n+\n+\t\tif (source->entity() != sensor_->entity()) {\n+\t\t\tV4L2Subdevice *subdev = pipe->subdev(source->entity());\n+\t\t\tsubdev->getFormat(source->index(), format, whence);\n+\t\t}\n+\n+\t\tif (sink->entity()->function() != MEDIA_ENT_F_IO_V4L) {\n+\t\t\tV4L2Subdevice *subdev = pipe->subdev(sink->entity());\n+\t\t\tsubdev->setFormat(sink->index(), format, whence);\n+\t\t}\n+\n+\t\tLOG(SimplePipeline, Debug)\n+\t\t\t<< \"Link '\" << source->entity()->name()\n+\t\t\t<< \"':\" << source->index()\n+\t\t\t<< \" -> '\" << sink->entity()->name()\n+\t\t\t<< \"':\" << sink->index()\n+\t\t\t<< \" configured with format \" << format->toString();\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* -----------------------------------------------------------------------------\n+ * Camera Configuration\n+ */\n+\n+SimpleCameraConfiguration::SimpleCameraConfiguration(Camera *camera,\n+\t\t\t\t\t\t     SimpleCameraData *data)\n+\t: CameraConfiguration(), camera_(camera->shared_from_this()),\n+\t  data_(data)\n+{\n+}\n+\n+CameraConfiguration::Status SimpleCameraConfiguration::validate()\n+{\n+\tStatus status = Valid;\n+\n+\tif (config_.empty())\n+\t\treturn Invalid;\n+\n+\t/* Cap the number of entries to the available streams. */\n+\tif (config_.size() > 1) {\n+\t\tconfig_.resize(1);\n+\t\tstatus = Adjusted;\n+\t}\n+\n+\tStreamConfiguration &cfg = config_[0];\n+\n+\t/* Adjust the pixel format. */\n+\tauto it = data_->formats_.find(cfg.pixelFormat);\n+\tif (it == data_->formats_.end())\n+\t\tit = data_->formats_.begin();\n+\n+\tPixelFormat pixelFormat = it->first;\n+\tconst SimpleCameraData::Configuration &pipeConfig = it->second;\n+\n+\tif (cfg.pixelFormat != pixelFormat) {\n+\t\tLOG(SimplePipeline, Debug) << \"Adjusting pixel format\";\n+\t\tcfg.pixelFormat = pixelFormat;\n+\t\tstatus = Adjusted;\n+\t}\n+\n+\tif (cfg.size != pipeConfig.size) {\n+\t\tLOG(SimplePipeline, Debug)\n+\t\t\t<< \"Adjusting size from \" << cfg.size.toString()\n+\t\t\t<< \" to \" << pipeConfig.size.toString();\n+\t\tcfg.size = pipeConfig.size;\n+\t\tstatus = Adjusted;\n+\t}\n+\n+\tcfg.bufferCount = 3;\n+\n+\treturn status;\n+}\n+\n+/* -----------------------------------------------------------------------------\n+ * Pipeline Handler\n+ */\n+\n+SimplePipelineHandler::SimplePipelineHandler(CameraManager *manager)\n+\t: PipelineHandler(manager), video_(nullptr)\n+{\n+}\n+\n+SimplePipelineHandler::~SimplePipelineHandler()\n+{\n+\tdelete video_;\n+}\n+\n+CameraConfiguration *SimplePipelineHandler::generateConfiguration(Camera *camera,\n+\t\t\t\t\t\t\t\t  const StreamRoles &roles)\n+{\n+\tSimpleCameraData *data = cameraData(camera);\n+\tCameraConfiguration *config =\n+\t\tnew SimpleCameraConfiguration(camera, data);\n+\n+\tif (roles.empty())\n+\t\treturn config;\n+\n+\t/* Create the formats map. */\n+\tstd::map<PixelFormat, std::vector<SizeRange>> formats;\n+\tstd::transform(data->formats_.begin(), data->formats_.end(),\n+\t\t       std::inserter(formats, formats.end()),\n+\t\t       [](const auto &format) -> decltype(formats)::value_type {\n+\t\t\t       const PixelFormat &pixelFormat = format.first;\n+\t\t\t       const Size &size = format.second.size;\n+\t\t\t       return { pixelFormat, { size } };\n+\t\t       });\n+\n+\t/*\n+\t * Create the stream configuration. Take the first entry in the formats\n+\t * map as the default, for lack of a better option.\n+\t */\n+\tStreamConfiguration cfg{ StreamFormats{ formats } };\n+\tcfg.pixelFormat = formats.begin()->first;\n+\tcfg.size = formats.begin()->second[0].max;\n+\n+\tconfig->addConfiguration(cfg);\n+\n+\tconfig->validate();\n+\n+\treturn config;\n+}\n+\n+int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)\n+{\n+\tSimpleCameraConfiguration *config =\n+\t\tstatic_cast<SimpleCameraConfiguration *>(c);\n+\tSimpleCameraData *data = cameraData(camera);\n+\tStreamConfiguration &cfg = config->at(0);\n+\tint ret;\n+\n+\t/*\n+\t * Configure links on the pipeline and propagate formats from the\n+\t * sensor to the video node.\n+\t */\n+\tret = data->setupLinks();\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tconst SimpleCameraData::Configuration &pipeConfig =\n+\t\tdata->formats_[cfg.pixelFormat];\n+\n+\tV4L2SubdeviceFormat format{ pipeConfig.code, data->sensor_->resolution() };\n+\n+\tret = data->setupFormats(&format, V4L2Subdevice::ActiveFormat);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\t/* Configure the video node. */\n+\tuint32_t fourcc = video_->toV4L2Fourcc(cfg.pixelFormat);\n+\n+\tV4L2DeviceFormat outputFormat = {};\n+\toutputFormat.fourcc = fourcc;\n+\toutputFormat.size = cfg.size;\n+\n+\tret = video_->setFormat(&outputFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (outputFormat.size != cfg.size || outputFormat.fourcc != fourcc) {\n+\t\tLOG(SimplePipeline, Error)\n+\t\t\t<< \"Unable to configure capture in \" << cfg.toString();\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tcfg.setStream(&data->stream_);\n+\n+\treturn 0;\n+}\n+\n+int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,\n+\t\t\t\t\t      std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n+{\n+\tunsigned int count = stream->configuration().bufferCount;\n+\n+\treturn video_->exportBuffers(count, buffers);\n+}\n+\n+int SimplePipelineHandler::start(Camera *camera)\n+{\n+\tSimpleCameraData *data = cameraData(camera);\n+\tunsigned int count = data->stream_.configuration().bufferCount;\n+\n+\tint ret = video_->importBuffers(count);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tret = video_->streamOn();\n+\tif (ret < 0) {\n+\t\tvideo_->releaseBuffers();\n+\t\treturn ret;\n+\t}\n+\n+\tactiveCamera_ = camera;\n+\n+\treturn 0;\n+}\n+\n+void SimplePipelineHandler::stop(Camera *camera)\n+{\n+\tvideo_->streamOff();\n+\tvideo_->releaseBuffers();\n+\tactiveCamera_ = nullptr;\n+}\n+\n+int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)\n+{\n+\tSimpleCameraData *data = cameraData(camera);\n+\tStream *stream = &data->stream_;\n+\n+\tFrameBuffer *buffer = request->findBuffer(stream);\n+\tif (!buffer) {\n+\t\tLOG(SimplePipeline, Error)\n+\t\t\t<< \"Attempt to queue request with invalid stream\";\n+\t\treturn -ENOENT;\n+\t}\n+\n+\treturn video_->queueBuffer(buffer);\n+}\n+\n+/* -----------------------------------------------------------------------------\n+ * Match and Setup\n+ */\n+\n+bool SimplePipelineHandler::match(DeviceEnumerator *enumerator)\n+{\n+\tstatic const char * const drivers[] = {\n+\t\t\"imx7-csi\",\n+\t\t\"sun6i-csi\",\n+\t};\n+\n+\tfor (const char *driver : drivers) {\n+\t\tDeviceMatch dm(driver);\n+\t\tmedia_ = acquireMediaDevice(enumerator, dm);\n+\t\tif (media_)\n+\t\t\tbreak;\n+\t}\n+\n+\tif (!media_)\n+\t\treturn false;\n+\n+\t/*\n+\t * Locate sensors and video nodes. We only support pipelines with at\n+\t * least one sensor and exactly one video captude node.\n+\t */\n+\tstd::vector<MediaEntity *> sensors;\n+\tstd::vector<MediaEntity *> videos;\n+\n+\tfor (MediaEntity *entity : media_->entities()) {\n+\t\tswitch (entity->function()) {\n+\t\tcase MEDIA_ENT_F_CAM_SENSOR:\n+\t\t\tsensors.push_back(entity);\n+\t\t\tbreak;\n+\n+\t\tcase MEDIA_ENT_F_IO_V4L:\n+\t\t\tif (entity->pads().size() == 1 &&\n+\t\t\t    (entity->pads()[0]->flags() & MEDIA_PAD_FL_SINK))\n+\t\t\t\tvideos.push_back(entity);\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (sensors.empty()) {\n+\t\tLOG(SimplePipeline, Info) << \"No sensor found\";\n+\t\treturn false;\n+\t}\n+\n+\tif (videos.size() != 1) {\n+\t\tLOG(SimplePipeline, Info)\n+\t\t\t<< \"Pipeline with \" << videos.size()\n+\t\t\t<< \" video capture nodes is not supported\";\n+\t\treturn false;\n+\t}\n+\n+\t/* Locate and open the capture video node. */\n+\tvideo_ = new V4L2VideoDevice(videos[0]);\n+\tif (video_->open() < 0)\n+\t\treturn false;\n+\n+\tif (video_->caps().isMultiplanar()) {\n+\t\tLOG(SimplePipeline, Info)\n+\t\t\t<< \"V4L2 multiplanar devices are not supported\";\n+\t\treturn false;\n+\t}\n+\n+\tvideo_->bufferReady.connect(this, &SimplePipelineHandler::bufferReady);\n+\n+\t/*\n+\t * Create one camera data instance for each sensor and gather all\n+\t * entities in all pipelines.\n+\t */\n+\tstd::vector<std::unique_ptr<SimpleCameraData>> pipelines;\n+\tstd::set<MediaEntity *> entities;\n+\n+\tpipelines.reserve(sensors.size());\n+\n+\tfor (MediaEntity *sensor : sensors) {\n+\t\tstd::unique_ptr<SimpleCameraData> data =\n+\t\t\tstd::make_unique<SimpleCameraData>(this, sensor,\n+\t\t\t\t\t\t\t   videos[0]);\n+\t\tif (!data->isValid()) {\n+\t\t\tLOG(SimplePipeline, Info)\n+\t\t\t\t<< \"No valid pipeline for sensor '\"\n+\t\t\t\t<< sensor->name() << \"', skipping\";\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tfor (SimpleCameraData::Entity &entity : data->entities_)\n+\t\t\tentities.insert(entity.entity);\n+\n+\t\tpipelines.push_back(std::move(data));\n+\t}\n+\n+\tif (entities.empty())\n+\t\treturn false;\n+\n+\t/* Create and open V4L2Subdev instances for all the entities. */\n+\tfor (MediaEntity *entity : entities) {\n+\t\tauto elem = subdevs_.emplace(std::piecewise_construct,\n+\t\t\t\t\t     std::forward_as_tuple(entity),\n+\t\t\t\t\t     std::forward_as_tuple(entity));\n+\t\tV4L2Subdevice *subdev = &elem.first->second;\n+\t\tint ret = subdev->open();\n+\t\tif (ret < 0) {\n+\t\t\tLOG(SimplePipeline, Error)\n+\t\t\t\t<< \"Failed to open \" << subdev->deviceNode()\n+\t\t\t\t<< \": \" << strerror(-ret);\n+\t\t\treturn false;\n+\t\t}\n+\t}\n+\n+\t/* Initialize each pipeline and register a corresponding camera. */\n+\tfor (std::unique_ptr<SimpleCameraData> &data : pipelines) {\n+\t\tint ret = data->init();\n+\t\tif (ret < 0)\n+\t\t\tcontinue;\n+\n+\t\tstd::shared_ptr<Camera> camera =\n+\t\t\tCamera::create(this, data->sensor_->entity()->name(),\n+\t\t\t\t       data->streams());\n+\t\tregisterCamera(std::move(camera), std::move(data));\n+\t}\n+\n+\treturn true;\n+}\n+\n+V4L2Subdevice *SimplePipelineHandler::subdev(const MediaEntity *entity)\n+{\n+\tauto iter = subdevs_.find(entity);\n+\tif (iter == subdevs_.end())\n+\t\treturn nullptr;\n+\n+\treturn &iter->second;\n+}\n+\n+/* -----------------------------------------------------------------------------\n+ * Buffer Handling\n+ */\n+\n+void SimplePipelineHandler::bufferReady(FrameBuffer *buffer)\n+{\n+\tASSERT(activeCamera_);\n+\tRequest *request = buffer->request();\n+\tcompleteBuffer(activeCamera_, request, buffer);\n+\tcompleteRequest(activeCamera_, request);\n+}\n+\n+REGISTER_PIPELINE_HANDLER(SimplePipelineHandler);\n+\n+} /* namespace libcamera */\n",
    "prefixes": [
        "libcamera-devel",
        "v2",
        "08/10"
    ]
}