Patch Detail
Show a patch.
GET /api/patches/17595/?format=api
{ "id": 17595, "url": "https://patchwork.libcamera.org/api/patches/17595/?format=api", "web_url": "https://patchwork.libcamera.org/patch/17595/", "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": "<20221014103612.241629-2-chenghaoyang@google.com>", "date": "2022-10-14T10:36:12", "name": "[libcamera-devel,v2,1/1] Add fake pipeline handler", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "54e8b7f9182cd06fa236ecb720eb74ecb06e7242", "submitter": { "id": 117, "url": "https://patchwork.libcamera.org/api/people/117/?format=api", "name": "Cheng-Hao Yang", "email": "chenghaoyang@chromium.org" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/17595/mbox/", "series": [ { "id": 3554, "url": "https://patchwork.libcamera.org/api/series/3554/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3554", "date": "2022-10-14T10:36:11", "name": "Fake pipeline handler", "version": 2, "mbox": "https://patchwork.libcamera.org/series/3554/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/17595/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/17595/checks/", "tags": {}, "headers": { "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>", "X-Original-To": "parsemail@patchwork.libcamera.org", "Delivered-To": "parsemail@patchwork.libcamera.org", "Received": [ "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id C1C37C327C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 14 Oct 2022 10:36:23 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8376C62DBD;\n\tFri, 14 Oct 2022 12:36:23 +0200 (CEST)", "from mail-pj1-x1030.google.com (mail-pj1-x1030.google.com\n\t[IPv6:2607:f8b0:4864:20::1030])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 436C862DB2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 14 Oct 2022 12:36:20 +0200 (CEST)", "by mail-pj1-x1030.google.com with SMTP id 70so4552011pjo.4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 14 Oct 2022 03:36:20 -0700 (PDT)", "from chenghaoyang-low.c.googlers.com.com\n\t(231.137.80.34.bc.googleusercontent.com. [34.80.137.231])\n\tby smtp.gmail.com with ESMTPSA id\n\te15-20020a170902784f00b00178b9c997e5sm1405704pln.138.2022.10.14.03.36.17\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tFri, 14 Oct 2022 03:36:17 -0700 (PDT)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1665743783;\n\tbh=BQdZdP5h3dEKLVbksBt8YSucoVWeUyzm+F8vO82kE8I=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=uEAtd2E3UB5g4+b6FwE6pPefJ+rFPh6/QJ+ARzSQy2+oKDgzun3zrPxqbphoZe1cg\n\tBdHoj2DrdnOlDvK4BIliOjTsA5iRd/eYJ8S0s6yGC9GCtBoAqh5Q3EpCxlAe3hVBLD\n\t/DtGk3bY4En8LJN6bW3XRSwBQTxNmWsCp+mjfcLUnxgSJV2jL2lVCYIbfGikm8SZOE\n\tpPtPhBwP3Gl8NeoKsBu9ZU4gq9UevYf8ea0mO+OYfaTgMseLhIHFT0EUulyJQJsCpW\n\tvCDLhReBb/532cSH6KT9rv0SWay7EPV1gmsqBztWYycDU+BL2XYCo3WkeWELN/FFr5\n\tg8vmwwt8l8wPw==", "v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=u00/zmljhCbFyVJOd8MnOWM5DsTO44YCqHj/Umyxvi8=;\n\tb=K7K1UbD1pAWXrN2BQDj1SXqRbX3Mpr/PvuE8kXMEPWrN3S3Kquckl9jjKNR3txHVPo\n\tjwSexoRWEGHs46QgkP5CuFUi3eopj9iSEIl7H65odv5dr9YqcrkJJGBJ/CIddcOXuV0Z\n\tcHBuwAHg0Q94I0+LxGqZz9lXVqnb0833IcqKg=" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=chromium.org\n\theader.i=@chromium.org header.b=\"K7K1UbD1\"; \n\tdkim-atps=neutral", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n\t:subject:date:message-id:reply-to;\n\tbh=u00/zmljhCbFyVJOd8MnOWM5DsTO44YCqHj/Umyxvi8=;\n\tb=m9AkP2dZpFnIeklvCuY4V5GNOevQqzisIG+NgP7Zx8GSVHsCkMuyhzFRhqAydNQgDn\n\tcehVag1MeVYqAskIK/ChJ8eT1EwjJfKPQoDO2iZbJplPfRuJ5GESYUxq9MQEWhiZdaUV\n\tWnSPop1WpOwiNzl4+rzv3Vla9V8DUxb0VqWdttSgUz5hBJlT4w24asizJUs1ENXlaGaf\n\tpSIyJRU2QCDdyylJf8LEal1pO5IkNv/y2xEbJso3c4Fce7iivI/XssNT+UqQxd2Ovt+U\n\tPfBAZ+VE2ZHKq12QVdKzji5h29bFfwMqNxROiqISesD+4oRtv6Pf3h+5xmEDhBQk6gi9\n\tkq1g==", "X-Gm-Message-State": "ACrzQf25r3DJMioBvymWp7f/hib7cgWeO3UfloYi56ChC76yg2kKEMd7\n\tYEzuVAZ3byxS4dqMlLo022CRCDkUdU8Cmg==", "X-Google-Smtp-Source": "AMsMyM4qlrruLVbOZC95HU4cIXBbkpgavfHhPK+TZ3HlhXKij16En0WTKwHX7hEpjvCn+BWg6NwklQ==", "X-Received": "by 2002:a17:902:c405:b0:181:83e4:490e with SMTP id\n\tk5-20020a170902c40500b0018183e4490emr4385886plk.4.1665743778320; \n\tFri, 14 Oct 2022 03:36:18 -0700 (PDT)", "X-Google-Original-From": "Harvey Yang <chenghaoyang@google.com>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Fri, 14 Oct 2022 10:36:12 +0000", "Message-Id": "<20221014103612.241629-2-chenghaoyang@google.com>", "X-Mailer": "git-send-email 2.38.0.413.g74048e4d9e-goog", "In-Reply-To": "<20221014103612.241629-1-chenghaoyang@google.com>", "References": "<20221014103612.241629-1-chenghaoyang@google.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v2 1/1] Add fake 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>", "From": "Harvey Yang via libcamera-devel <libcamera-devel@lists.libcamera.org>", "Reply-To": "Harvey Yang <chenghaoyang@chromium.org>", "Cc": "Harvey Yang <chenghaoyang@google.com>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "---\n meson_options.txt | 2 +-\n src/libcamera/pipeline/fake/fake.cpp | 441 ++++++++++++++++++++++++\n src/libcamera/pipeline/fake/meson.build | 3 +\n test/camera/camera_reconfigure.cpp | 2 +-\n 4 files changed, 446 insertions(+), 2 deletions(-)\n create mode 100644 src/libcamera/pipeline/fake/fake.cpp\n create mode 100644 src/libcamera/pipeline/fake/meson.build", "diff": "diff --git a/meson_options.txt b/meson_options.txt\nindex f1d67808..f08dfc5f 100644\n--- a/meson_options.txt\n+++ b/meson_options.txt\n@@ -37,7 +37,7 @@ option('lc-compliance',\n \n option('pipelines',\n type : 'array',\n- choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo', 'vimc'],\n+ choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo', 'vimc', 'fake'],\n description : 'Select which pipeline handlers to include')\n \n option('qcam',\ndiff --git a/src/libcamera/pipeline/fake/fake.cpp b/src/libcamera/pipeline/fake/fake.cpp\nnew file mode 100644\nindex 00000000..1ee24e3b\n--- /dev/null\n+++ b/src/libcamera/pipeline/fake/fake.cpp\n@@ -0,0 +1,441 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * fake.cpp - Pipeline handler for fake cameras\n+ */\n+\n+#include <algorithm>\n+#include <iomanip>\n+#include <memory>\n+#include <queue>\n+#include <set>\n+#include <vector>\n+\n+#include <libcamera/base/log.h>\n+#include <libcamera/base/utils.h>\n+\n+#include <libcamera/camera_manager.h>\n+#include <libcamera/controls.h>\n+#include <libcamera/control_ids.h>\n+#include <libcamera/formats.h>\n+#include <libcamera/property_ids.h>\n+#include <libcamera/request.h>\n+#include <libcamera/stream.h>\n+\n+#include \"libcamera/internal/camera.h\"\n+#include \"libcamera/internal/device_enumerator.h\"\n+#include \"libcamera/internal/formats.h\"\n+#include \"libcamera/internal/framebuffer.h\"\n+#include \"libcamera/internal/media_device.h\"\n+#include \"libcamera/internal/pipeline_handler.h\"\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(Fake)\n+\n+uint64_t CurrentTimestamp()\n+{\n+\tstruct timespec ts;\n+\tif (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {\n+\t\tLOG(Fake, Error) << \"Get clock time fails\";\n+\t\treturn 0;\n+\t}\n+\n+\treturn ts.tv_sec * 1'000'000'000LL + ts.tv_nsec;\n+}\n+\n+static const ControlInfoMap::Map FakeControls = {\n+\t{ &controls::draft::PipelineDepth, ControlInfo(2, 3) },\n+};\n+\n+class FakeCameraData : public Camera::Private\n+{\n+public:\n+\tstruct Resolution {\n+\t\tSize size;\n+\t\tstd::vector<int> frame_rates;\n+\t\tstd::vector<PixelFormat> formats;\n+\t};\n+\n+\tFakeCameraData(PipelineHandler *pipe)\n+\t\t: Camera::Private(pipe)\n+\t{\n+\t}\n+\n+\tstd::vector<Resolution> supportedResolutions_;\n+\n+\tStream outStream_;\n+\tStream vfStream_;\n+\tStream rawStream_;\n+\n+\tbool started_ = false;\n+};\n+\n+class FakeCameraConfiguration : public CameraConfiguration\n+{\n+public:\n+\tstatic constexpr unsigned int kBufferCount = 4; // 4~6\n+\tstatic constexpr unsigned int kMaxStreams = 3;\n+\n+\tFakeCameraConfiguration(FakeCameraData *data);\n+\n+\tStatus validate() override;\n+\n+private:\n+\t/*\n+\t * The FakeCameraData instance is guaranteed to be valid as long as the\n+\t * 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+\tconst FakeCameraData *data_;\n+};\n+\n+class PipelineHandlerFake : public PipelineHandler\n+{\n+public:\n+\tPipelineHandlerFake(CameraManager *manager);\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, const ControlList *controls) override;\n+\tvoid stopDevice(Camera *camera) override;\n+\n+\tint queueRequestDevice(Camera *camera, Request *request) override;\n+\n+\tbool match(DeviceEnumerator *enumerator) override;\n+\n+private:\n+\tFakeCameraData *cameraData(Camera *camera)\n+\t{\n+\t\treturn static_cast<FakeCameraData *>(camera->_d());\n+\t}\n+\n+\tint registerCameras();\n+\n+\tstatic bool registered_;\n+};\n+\n+bool PipelineHandlerFake::registered_ = false;\n+\n+FakeCameraConfiguration::FakeCameraConfiguration(FakeCameraData *data)\n+\t: CameraConfiguration()\n+{\n+\tdata_ = data;\n+}\n+\n+CameraConfiguration::Status FakeCameraConfiguration::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() > kMaxStreams) {\n+\t\tconfig_.resize(kMaxStreams);\n+\t\tstatus = Adjusted;\n+\t}\n+\n+\t/*\n+\t * Validate the requested stream configuration and select the sensor\n+\t * format by collecting the maximum RAW stream width and height and\n+\t * picking the closest larger match.\n+\t *\n+\t * If no RAW stream is requested use the one of the largest YUV stream,\n+\t * plus margin pixels for the IF and BDS rectangle to downscale.\n+\t *\n+\t * \\todo Clarify the IF and BDS margins requirements.\n+\t */\n+\tunsigned int rawCount = 0;\n+\tunsigned int yuvCount = 0;\n+\tSize rawRequirement;\n+\tSize maxYuvSize;\n+\tSize rawSize;\n+\n+\tfor (const StreamConfiguration &cfg : config_) {\n+\t\tconst PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat);\n+\n+\t\tif (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW) {\n+\t\t\trawCount++;\n+\t\t\trawSize = std::max(rawSize, cfg.size);\n+\t\t} else {\n+\t\t\tyuvCount++;\n+\t\t\tmaxYuvSize = std::max(maxYuvSize, cfg.size);\n+\t\t\trawRequirement.expandTo(cfg.size);\n+\t\t}\n+\t}\n+\n+\t// TODO: Check the configuration file.\n+\tif (rawCount > 1 || yuvCount > 2) {\n+\t\tLOG(Fake, Debug) << \"Camera configuration not supported\";\n+\t\treturn Invalid;\n+\t}\n+\n+\t/*\n+\t * Adjust the configurations if needed and assign streams while\n+\t * iterating them.\n+\t */\n+\tbool mainOutputAvailable = true;\n+\tfor (unsigned int i = 0; i < config_.size(); ++i) {\n+\t\tconst PixelFormatInfo &info = PixelFormatInfo::info(config_[i].pixelFormat);\n+\t\tconst StreamConfiguration originalCfg = config_[i];\n+\t\tStreamConfiguration *cfg = &config_[i];\n+\n+\t\tLOG(Fake, Debug) << \"Validating stream: \" << config_[i].toString();\n+\n+\t\tif (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW) {\n+\t\t\t/* Initialize the RAW stream with the CIO2 configuration. */\n+\t\t\tcfg->size = rawSize;\n+\t\t\t// TODO: check\n+\t\t\tcfg->pixelFormat = formats::SBGGR10;\n+\t\t\tcfg->bufferCount = FakeCameraConfiguration::kBufferCount;\n+\t\t\tcfg->stride = info.stride(cfg->size.width, 0, 64);\n+\t\t\tcfg->frameSize = info.frameSize(cfg->size, 64);\n+\t\t\tcfg->setStream(const_cast<Stream *>(&data_->rawStream_));\n+\n+\t\t\tLOG(Fake, Debug) << \"Assigned \" << cfg->toString()\n+\t\t\t\t\t << \" to the raw stream\";\n+\t\t} else {\n+\t\t\t/* Assign and configure the main and viewfinder outputs. */\n+\n+\t\t\tcfg->pixelFormat = formats::NV12;\n+\t\t\tcfg->bufferCount = kBufferCount;\n+\t\t\tcfg->stride = info.stride(cfg->size.width, 0, 1);\n+\t\t\tcfg->frameSize = info.frameSize(cfg->size, 1);\n+\n+\t\t\t/*\n+\t\t\t * Use the main output stream in case only one stream is\n+\t\t\t * requested or if the current configuration is the one\n+\t\t\t * with the maximum YUV output size.\n+\t\t\t */\n+\t\t\tif (mainOutputAvailable &&\n+\t\t\t (originalCfg.size == maxYuvSize || yuvCount == 1)) {\n+\t\t\t\tcfg->setStream(const_cast<Stream *>(&data_->outStream_));\n+\t\t\t\tmainOutputAvailable = false;\n+\n+\t\t\t\tLOG(Fake, Debug) << \"Assigned \" << cfg->toString()\n+\t\t\t\t\t\t << \" to the main output\";\n+\t\t\t} else {\n+\t\t\t\tcfg->setStream(const_cast<Stream *>(&data_->vfStream_));\n+\n+\t\t\t\tLOG(Fake, Debug) << \"Assigned \" << cfg->toString()\n+\t\t\t\t\t\t << \" to the viewfinder output\";\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (cfg->pixelFormat != originalCfg.pixelFormat ||\n+\t\t cfg->size != originalCfg.size) {\n+\t\t\tLOG(Fake, Debug)\n+\t\t\t\t<< \"Stream \" << i << \" configuration adjusted to \"\n+\t\t\t\t<< cfg->toString();\n+\t\t\tstatus = Adjusted;\n+\t\t}\n+\t}\n+\n+\treturn status;\n+}\n+\n+PipelineHandlerFake::PipelineHandlerFake(CameraManager *manager)\n+\t: PipelineHandler(manager)\n+{\n+\t// TODO: read the fake hal spec file.\n+}\n+\n+CameraConfiguration *PipelineHandlerFake::generateConfiguration(Camera *camera,\n+\t\t\t\t\t\t\t\tconst StreamRoles &roles)\n+{\n+\tFakeCameraData *data = cameraData(camera);\n+\tFakeCameraConfiguration *config = new FakeCameraConfiguration(data);\n+\n+\tif (roles.empty())\n+\t\treturn config;\n+\n+\tSize minSize, sensorResolution;\n+\tfor (const auto& resolution : data->supportedResolutions_) {\n+\t\tif (minSize.isNull() || minSize > resolution.size)\n+\t\t\tminSize = resolution.size;\n+\n+\t\tsensorResolution = std::max(sensorResolution, resolution.size);\n+\t}\n+\n+\tfor (const StreamRole role : roles) {\n+\t\tstd::map<PixelFormat, std::vector<SizeRange>> streamFormats;\n+\t\tunsigned int bufferCount;\n+\t\tPixelFormat pixelFormat;\n+\t\tSize size;\n+\n+\t\tswitch (role) {\n+\t\tcase StreamRole::StillCapture:\n+\t\t\tsize = sensorResolution;\n+\t\t\tpixelFormat = formats::NV12;\n+\t\t\tbufferCount = FakeCameraConfiguration::kBufferCount;\n+\t\t\tstreamFormats[pixelFormat] = { { minSize, size } };\n+\n+\t\t\tbreak;\n+\n+\t\tcase StreamRole::Raw: {\n+\t\t\t// TODO: check\n+\t\t\tpixelFormat = formats::SBGGR10;\n+\t\t\tsize = sensorResolution;\n+\t\t\tbufferCount = FakeCameraConfiguration::kBufferCount;\n+\t\t\tstreamFormats[pixelFormat] = { { minSize, size } };\n+\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tcase StreamRole::Viewfinder:\n+\t\tcase StreamRole::VideoRecording: {\n+\t\t\t/*\n+\t\t\t * Default viewfinder and videorecording to 1280x720,\n+\t\t\t * capped to the maximum sensor resolution and aligned\n+\t\t\t * to the ImgU output constraints.\n+\t\t\t */\n+\t\t\tsize = sensorResolution;\n+\t\t\tpixelFormat = formats::NV12;\n+\t\t\tbufferCount = FakeCameraConfiguration::kBufferCount;\n+\t\t\tstreamFormats[pixelFormat] = { { minSize, size } };\n+\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tdefault:\n+\t\t\tLOG(Fake, Error)\n+\t\t\t\t<< \"Requested stream role not supported: \" << role;\n+\t\t\tdelete config;\n+\t\t\treturn nullptr;\n+\t\t}\n+\n+\t\tStreamFormats formats(streamFormats);\n+\t\tStreamConfiguration cfg(formats);\n+\t\tcfg.size = size;\n+\t\tcfg.pixelFormat = pixelFormat;\n+\t\tcfg.bufferCount = bufferCount;\n+\t\tconfig->addConfiguration(cfg);\n+\t}\n+\n+\tif (config->validate() == CameraConfiguration::Invalid)\n+\t\treturn {};\n+\n+\treturn config;\n+}\n+\n+int PipelineHandlerFake::configure(Camera *camera, CameraConfiguration *c)\n+{\n+\tif (camera || c)\n+\t\treturn 0;\n+\treturn 0;\n+}\n+\n+int PipelineHandlerFake::exportFrameBuffers(Camera *camera, Stream *stream,\n+\t\t\t\t\t std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n+{\n+\t// Assume it's never called.\n+\tLOG(Fake, Fatal) << \"exportFrameBuffers should never be called\";\n+\tif (camera || stream || buffers)\n+\t\treturn -EINVAL;\n+\treturn -EINVAL;\n+}\n+\n+int PipelineHandlerFake::start(Camera *camera, [[maybe_unused]] const ControlList *controls)\n+{\n+\tFakeCameraData *data = cameraData(camera);\n+\tdata->started_ = true;\n+\n+\treturn 0;\n+}\n+\n+void PipelineHandlerFake::stopDevice(Camera *camera)\n+{\n+\tFakeCameraData *data = cameraData(camera);\n+\n+\tdata->started_ = false;\n+}\n+\n+int PipelineHandlerFake::queueRequestDevice(Camera *camera, Request *request)\n+{\n+\tif (!camera)\n+\t\treturn -EINVAL;\n+\n+\tfor (auto it : request->buffers())\n+\t\tcompleteBuffer(request, it.second);\n+\n+\t// TODO: request.metadata()\n+\trequest->metadata().set(controls::SensorTimestamp, CurrentTimestamp());\n+\tcompleteRequest(request);\n+\n+\treturn 0;\n+}\n+\n+bool PipelineHandlerFake::match(DeviceEnumerator *enumerator)\n+{\n+\t// TODO: exhaust all devices in |enumerator|.\n+\tif (!enumerator)\n+\t\tLOG(Fake, Info) << \"Invalid enumerator\";\n+\n+\tif (registered_)\n+\t\treturn false;\n+\n+\tregistered_ = true;\n+\treturn registerCameras() == 0;\n+}\n+\n+/**\n+ * \\brief Initialise ImgU and CIO2 devices associated with cameras\n+ *\n+ * Initialise the two ImgU instances and create cameras with an associated\n+ * CIO2 device instance.\n+ *\n+ * \\return 0 on success or a negative error code for error or if no camera\n+ * has been created\n+ * \\retval -ENODEV no camera has been created\n+ */\n+int PipelineHandlerFake::registerCameras()\n+{\n+\tstd::unique_ptr<FakeCameraData> data =\n+\t\tstd::make_unique<FakeCameraData>(this);\n+\tstd::set<Stream *> streams = {\n+\t\t&data->outStream_,\n+\t\t&data->vfStream_,\n+\t\t&data->rawStream_,\n+\t};\n+\n+\t// TODO: Read from config or from IPC.\n+\t// TODO: Check with Han-lin: Can this function be called more than once?\n+\tdata->supportedResolutions_.resize(2);\n+\tdata->supportedResolutions_[0].size = Size(1920, 1080);\n+\tdata->supportedResolutions_[0].frame_rates.push_back(30);\n+\tdata->supportedResolutions_[0].formats.push_back(formats::NV12);\n+\tdata->supportedResolutions_[0].formats.push_back(formats::MJPEG);\n+\tdata->supportedResolutions_[1].size = Size(1280, 720);\n+\tdata->supportedResolutions_[1].frame_rates.push_back(30);\n+\tdata->supportedResolutions_[1].frame_rates.push_back(60);\n+\tdata->supportedResolutions_[1].formats.push_back(formats::NV12);\n+\tdata->supportedResolutions_[1].formats.push_back(formats::MJPEG);\n+\n+\t// TODO: Assign different locations for different cameras based on config.\n+\tdata->properties_.set(properties::Location, properties::CameraLocationFront);\n+\tdata->properties_.set(properties::PixelArrayActiveAreas, { Rectangle(Size(1920, 1080)) });\n+\n+\t// TODO: Set FrameDurationLimits based on config.\n+\tControlInfoMap::Map controls = FakeControls;\n+\tint64_t min_frame_duration = 30, max_frame_duration = 60;\n+\tcontrols[&controls::FrameDurationLimits] = ControlInfo(min_frame_duration, max_frame_duration);\n+\tdata->controlInfo_ = ControlInfoMap(std::move(controls), controls::controls);\n+\n+\tstd::shared_ptr<Camera> camera =\n+\t\tCamera::create(std::move(data), \"\\\\_SB_.PCI0.I2C4.CAM1\" /* cameraId */, streams);\n+\n+\tmanager_->addCamera(std::move(camera), {});\n+\n+\treturn 0;\n+}\n+\n+REGISTER_PIPELINE_HANDLER(PipelineHandlerFake)\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/pipeline/fake/meson.build b/src/libcamera/pipeline/fake/meson.build\nnew file mode 100644\nindex 00000000..13980b4a\n--- /dev/null\n+++ b/src/libcamera/pipeline/fake/meson.build\n@@ -0,0 +1,3 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+libcamera_sources += files([ 'fake.cpp' ])\ndiff --git a/test/camera/camera_reconfigure.cpp b/test/camera/camera_reconfigure.cpp\nindex d12e2413..06c87730 100644\n--- a/test/camera/camera_reconfigure.cpp\n+++ b/test/camera/camera_reconfigure.cpp\n@@ -98,7 +98,7 @@ private:\n \t\t\t\treturn TestFail;\n \t\t\t}\n \n-\t\t\trequests_.push_back(move(request));\n+\t\t\trequests_.push_back(std::move(request));\n \t\t}\n \n \t\tcamera_->requestCompleted.connect(this, &CameraReconfigure::requestComplete);\n", "prefixes": [ "libcamera-devel", "v2", "1/1" ] }