{"id":3092,"url":"https://patchwork.libcamera.org/api/patches/3092/?format=json","web_url":"https://patchwork.libcamera.org/patch/3092/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20200313233856.25202-9-laurent.pinchart@ideasonboard.com>","date":"2020-03-13T23:38:56","name":"[libcamera-devel,8/8] libcamera: pipeline: Add a simple pipeline handler","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"9af6b09a0b15fbb2562fa71a208d36e780ee188b","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/?format=json","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/3092/mbox/","series":[{"id":718,"url":"https://patchwork.libcamera.org/api/series/718/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=718","date":"2020-03-13T23:38:48","name":"Simple pipeline handler","version":1,"mbox":"https://patchwork.libcamera.org/series/718/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/3092/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/3092/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 C2AEA62930\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 14 Mar 2020 00:39:10 +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 54E531912;\n\tSat, 14 Mar 2020 00:39:09 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1584142750;\n\tbh=gz2Mmziu1XJlRtUFXVy5H3cJZ3fnTCVPsLV5fHO+TrU=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=WiSpa/f7V4XmzouKjanu2neYZIprrSPCrgNpbxzKdNwJWkJv+CIQ1FgM9xremQ3nf\n\t7cgHduCY2HGglICXD6lt8IqFCXF21Hw8n+/t5SZcIRjTSFgey5ov1L24S6YDsDLu2S\n\tZHbIbSJUResLjbg5JS0wGZ1WZtMtGYLjL5PsbkFs=","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Cc":"Martijn Braam <martijn@brixit.nl>,\n\tAndrey Konovalov <andrey.konovalov@linaro.org>,\n\tMickael GUENE <mickael.guene@st.com>,\n\tBenjamin GAIGNARD <benjamin.gaignard@st.com>","Date":"Sat, 14 Mar 2020 01:38:56 +0200","Message-Id":"<20200313233856.25202-9-laurent.pinchart@ideasonboard.com>","X-Mailer":"git-send-email 2.24.1","In-Reply-To":"<20200313233856.25202-1-laurent.pinchart@ideasonboard.com>","References":"<20200313233856.25202-1-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"[libcamera-devel] [PATCH 8/8] libcamera: pipeline: Add a simple\n\tpipeline 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":"Fri, 13 Mar 2020 23:39:11 -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---\n src/libcamera/pipeline/meson.build        |   1 +\n src/libcamera/pipeline/simple/meson.build |   3 +\n src/libcamera/pipeline/simple/simple.cpp  | 693 ++++++++++++++++++++++\n 3 files changed, 697 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..a0c4e77906e9\n--- /dev/null\n+++ b/src/libcamera/pipeline/simple/simple.cpp\n@@ -0,0 +1,693 @@\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 <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\tstd::vector<unsigned int> formats;\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+\tint importFrameBuffers(Camera *camera, Stream *stream) override;\n+\tvoid freeFrameBuffers(Camera *camera, Stream *stream) 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+\tconfigs_.reserve(sensor_->mbusCodes().size());\n+\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\tImageFormats formats = video->formats(format.mbus_code);\n+\n+\t\tConfiguration config;\n+\t\tconfig.code = code;\n+\t\tconfig.formats = formats.formats();\n+\t\tconfig.size = format.size;\n+\n+\t\tLOG(SimplePipeline, Debug)\n+\t\t\t<< \"Adding configuration for \" << config.size.toString()\n+\t\t\t<< \" in pixel formats [ \"\n+\t\t\t<< utils::join(config.formats, \", \",\n+\t\t\t\t       [](unsigned int f) { return std::to_string(f); })\n+\t\t\t<< \" ]\";\n+\n+\t\tconfigs_.emplace_back(std::move(config));\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 format : formats.formats()) {\n+\t\t\tPixelFormat pixelFormat = video->toPixelFormat(format);\n+\t\t\tif (pixelFormat)\n+\t\t\t\tformats_[pixelFormat] = &configs_.back();\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+\tconst auto it = data->formats_.begin();\n+\tconst PixelFormat &pixelFormat = it->first;\n+\tconst SimpleCameraData::Configuration *pipeConfig = it->second;\n+\n+\tStreamConfiguration cfg{};\n+\tcfg.pixelFormat = pixelFormat;\n+\tcfg.size = pipeConfig->size;\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::importFrameBuffers(Camera *camera, Stream *stream)\n+{\n+\tunsigned int count = stream->configuration().bufferCount;\n+\n+\treturn video_->importBuffers(count);\n+}\n+\n+void SimplePipelineHandler::freeFrameBuffers(Camera *camera, Stream *stream)\n+{\n+\tvideo_->releaseBuffers();\n+}\n+\n+int SimplePipelineHandler::start(Camera *camera)\n+{\n+\tint ret = video_->streamOn();\n+\tif (ret < 0)\n+\t\treturn 0;\n+\n+\tactiveCamera_ = camera;\n+\n+\treturn 0;\n+}\n+\n+void SimplePipelineHandler::stop(Camera *camera)\n+{\n+\tvideo_->streamOff();\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","8/8"]}