Patch Detail
Show a patch.
GET /api/1.1/patches/3386/?format=api
{ "id": 3386, "url": "https://patchwork.libcamera.org/api/1.1/patches/3386/?format=api", "web_url": "https://patchwork.libcamera.org/patch/3386/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/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": "<20200404004438.17992-10-laurent.pinchart@ideasonboard.com>", "date": "2020-04-04T00:44:36", "name": "[libcamera-devel,v4,09/11] libcamera: pipeline: Add a simple pipeline handler", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "9e0ed73330a39be3d665402c2427f359ad95f902", "submitter": { "id": 2, "url": "https://patchwork.libcamera.org/api/1.1/people/2/?format=api", "name": "Laurent Pinchart", "email": "laurent.pinchart@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/3386/mbox/", "series": [ { "id": 796, "url": "https://patchwork.libcamera.org/api/1.1/series/796/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=796", "date": "2020-04-04T00:44:27", "name": "Simple pipeline handler", "version": 4, "mbox": "https://patchwork.libcamera.org/series/796/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/3386/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/3386/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 85FC862DF2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 4 Apr 2020 02:44:56 +0200 (CEST)", "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 E5A30321;\n\tSat, 4 Apr 2020 02:44:55 +0200 (CEST)" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"QppwGjpR\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1585961096;\n\tbh=82qV4qvKWo9Cyc7ALWvQcxzWX8f87fC+vOoi6j+WmZU=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=QppwGjpRP/1q/v1Tf3qel2+48NzEBQGum+t9DW7T6nJMEi9+WYjthY1IU/dQvhLJq\n\touTBqeNxjafTDdyVGAeQz4eK17C6M8suCzJu2dGioZzH0NOdCQgPK+jPu8KHDplFtl\n\tZ2+u3NCjo1HwryQ3VxVUM5cQx6jIdzZNXwGqHR6M=", "From": "Laurent Pinchart <laurent.pinchart@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Cc": "Martijn Braam <martijn@brixit.nl>,\n\tBenjamin GAIGNARD <benjamin.gaignard@st.com>,\n\tAndrey Konovalov <andrey.konovalov@linaro.org>", "Date": "Sat, 4 Apr 2020 03:44:36 +0300", "Message-Id": "<20200404004438.17992-10-laurent.pinchart@ideasonboard.com>", "X-Mailer": "git-send-email 2.24.1", "In-Reply-To": "<20200404004438.17992-1-laurent.pinchart@ideasonboard.com>", "References": "<20200404004438.17992-1-laurent.pinchart@ideasonboard.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v4 09/11] 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": "Sat, 04 Apr 2020 00:44:56 -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>\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n---\nChanges since v3:\n\n- Fix typos\n- Improve comments\n- Add local variable to increase readability\n- Move list of supported drivers to the top of the file\n\nChanges since v2:\n\n- Log an error when setupFormats() fail\n- Propagate getFormat() and setFormat() errors to the caller of\n setupFormats()\n- Reorder variable declarations in validate()\n- Add \\todo comment related to the selection of the default format\n- Use log Error instead of Info if pipeline isn't valid\n- Rebase on top of V4L2PixelFormat\n\nChanges since v1:\n\n- Rebase on top of buffer API rework\n- Expose stream formats\n- Rework camera data config\n---\n meson_options.txt | 2 +-\n src/libcamera/pipeline/simple/meson.build | 3 +\n src/libcamera/pipeline/simple/simple.cpp | 717 ++++++++++++++++++++++\n 3 files changed, 721 insertions(+), 1 deletion(-)\n create mode 100644 src/libcamera/pipeline/simple/meson.build\n create mode 100644 src/libcamera/pipeline/simple/simple.cpp", "diff": "diff --git a/meson_options.txt b/meson_options.txt\nindex 6464df837cc3..166429f8583e 100644\n--- a/meson_options.txt\n+++ b/meson_options.txt\n@@ -14,7 +14,7 @@ option('gstreamer',\n \n option('pipelines',\n type : 'array',\n- choices : ['ipu3', 'rkisp1', 'uvcvideo', 'vimc'],\n+ choices : ['ipu3', 'rkisp1', 'simple', 'uvcvideo', 'vimc'],\n description : 'Select which pipeline handlers to include')\n \n option('test',\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..e4f33f6ff531\n--- /dev/null\n+++ b/src/libcamera/pipeline/simple/simple.cpp\n@@ -0,0 +1,717 @@\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+namespace {\n+\n+static const char * const drivers[] = {\n+\t\"imx7-csi\",\n+\t\"sun6i-csi\",\n+};\n+\n+} /* namespace */\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+\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 propagate TRY formats. Such 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\tLOG(SimplePipeline, Error)\n+\t\t\t\t<< \"Failed to setup pipeline for media bus code \"\n+\t\t\t\t<< utils::hex(code, 4);\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tstd::map<V4L2PixelFormat, std::vector<SizeRange>> videoFormats =\n+\t\t\tvideo->formats(format.mbus_code);\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(videoFormats, \", \",\n+\t\t\t\t [](const auto &f) {\n+\t\t\t\t\t return f.first.toString();\n+\t\t\t\t })\n+\t\t\t<< \" ]\";\n+\n+\t\t/*\n+\t\t * Store the configuration in the formats_ map, mapping the\n+\t\t * PixelFormat to the corresponding configuration. Any\n+\t\t * previously stored value is overwritten, as the pipeline\n+\t\t * handler currently doesn't care about how a particular\n+\t\t * PixelFormat is achieved.\n+\t\t */\n+\t\tfor (const auto &videoFormat : videoFormats) {\n+\t\t\tPixelFormat pixelFormat = video->toPixelFormat(videoFormat.first);\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\tMediaEntity *remote = e.link->sink()->entity();\n+\t\tfor (MediaPad *pad : remote->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\tret = subdev->getFormat(source->index(), format, whence);\n+\t\t\tif (ret < 0)\n+\t\t\t\treturn ret;\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\tret = subdev->setFormat(sink->index(), format, whence);\n+\t\t\tif (ret < 0)\n+\t\t\t\treturn ret;\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+\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+\tconst SimpleCameraData::Configuration &pipeConfig = it->second;\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+\t * \\todo Implement a better way to pick the default format\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+\tV4L2PixelFormat videoFormat = video_->toV4L2PixelFormat(cfg.pixelFormat);\n+\n+\tV4L2DeviceFormat outputFormat = {};\n+\toutputFormat.fourcc = videoFormat;\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 != videoFormat) {\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+\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 capture 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, Error) << \"No sensor found\";\n+\t\treturn false;\n+\t}\n+\n+\tif (videos.size() != 1) {\n+\t\tLOG(SimplePipeline, Error)\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, Error)\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, Error)\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", "v4", "09/11" ] }