Patch Detail
Show a patch.
GET /api/patches/17589/?format=api
{ "id": 17589, "url": "https://patchwork.libcamera.org/api/patches/17589/?format=api", "web_url": "https://patchwork.libcamera.org/patch/17589/", "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": "<20221012075925.3971538-2-chenghaoyang@google.com>", "date": "2022-10-12T07:59:25", "name": "[libcamera-devel,1/1] Add fake pipeline handler", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "cfd067a2290a2c6645e9444fd83960e049fffa13", "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/17589/mbox/", "series": [ { "id": 3552, "url": "https://patchwork.libcamera.org/api/series/3552/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3552", "date": "2022-10-12T07:59:24", "name": "Fake Pipeline Handler", "version": 1, "mbox": "https://patchwork.libcamera.org/series/3552/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/17589/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/17589/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 D12FFC327C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 12 Oct 2022 07:59:36 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6B5B962D91;\n\tWed, 12 Oct 2022 09:59:36 +0200 (CEST)", "from mail-pl1-x629.google.com (mail-pl1-x629.google.com\n\t[IPv6:2607:f8b0:4864:20::629])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 99B24603D4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 12 Oct 2022 09:59:34 +0200 (CEST)", "by mail-pl1-x629.google.com with SMTP id c24so15524536pls.9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 12 Oct 2022 00:59:34 -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\tt6-20020aa79466000000b005631af4ece2sm7166177pfq.182.2022.10.12.00.59.31\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tWed, 12 Oct 2022 00:59:32 -0700 (PDT)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1665561576;\n\tbh=uHiiP+fN03fREEkkhgWSxsDMkjoD0cWIoROXxMtW/MQ=;\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=r5lDqAEfhHpmTVZNbPjpyjZHaJBS9sfmP1/PJDWxrLLJuXkK3rleQ1EKkYhPiWXcZ\n\tspCB8k21I09vWA5wg5uB8UUBopVAjvM9931nN2h7CFhqXjt5zL4tUc4nSuusZnq+jo\n\tBeSndpylgQ0UVBpRXfvyBl5u8zCU7mO+TyoYTUhSLYbz+ZjPoRp2gQjUPbm7+/xkcG\n\tKVccDNTp7gUdvQ6jNWoJCOUINDWsJ+mUa9FRb8qrVCH4ZN71hmZkZhdb0jbNYJbkiC\n\tN43w6a43AsmHzZ5KR4FI7jz4vCn5QcdFCuFyW+6WdrjOHgIoNX3mEXX8fojkToyAZ2\n\tfTYo4WVQa+0DQ==", "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=gO7TBAjs/jXyAEyFEAVcuXJHnO16F/6IXPZRuOjzfao=;\n\tb=CFHdV0W5yWCe8lmBVfuAbXY910Bs/8n1oiQg1zGzpwb0f3KY1q3F1sS9bPmVoRPJGt\n\tH/j7b5W9I5w6nkmm8GTDckR1k/70hPGTmbAiZDaJYFBQBznU/Mofh9wxRsrUNJOv6tnT\n\tQERWVndzJKKuZb8b420hZWepStMjEvFHhaTZg=" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=chromium.org\n\theader.i=@chromium.org header.b=\"CFHdV0W5\"; \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=gO7TBAjs/jXyAEyFEAVcuXJHnO16F/6IXPZRuOjzfao=;\n\tb=IrywfCOhj7n859Xb+c28DAMRAHsw8PrZ9zl1FcuMO8/VzHBBxdAXK668I9OivqKSi9\n\tIjSvZDElmIOzO69VZJ4rUSHUBYG5Hbjb1rwiwX68XbXI8JS2KTLFNGQd+k3Wd4k0/mNK\n\tC3/BMc+pZU/MbA+rpDtKCB0xY1F9i7iw/2+Sm1OIDfB0kjKqRrmHdm0z0M2NKjBTEfBS\n\tR6HYK8McX//TVwUqdTDPoACif/wzaDTRobuSkv4N4DYIGRRSS/8gB8xK5DKb+hocZpMq\n\tQcgs8A/6F4/ynQY61OzrL/GiFimDXovyvwE5rn93SeD720NzJGDt7h2uIx1ae683LtoO\n\tlOBg==", "X-Gm-Message-State": "ACrzQf2fSIpU3cbjC0XxR64PkkMlXetaxPVKpdl4EeGhG58eRPm0yXGn\n\txdy+LruIa1n7gURlw82M4nOKlfL+TocCdQ==", "X-Google-Smtp-Source": "AMsMyM6i7KwjhdUl8x9eUS8lxs9TaXSfJ8DUvbyeBm2mJhZXJO9/6KHbLTl4tNvQq6eJGu/Ajv0ctg==", "X-Received": "by 2002:a17:903:1211:b0:178:9353:9e42 with SMTP id\n\tl17-20020a170903121100b0017893539e42mr28280256plh.45.1665561572600; \n\tWed, 12 Oct 2022 00:59:32 -0700 (PDT)", "X-Google-Original-From": "Harvey Yang <chenghaoyang@google.com>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Wed, 12 Oct 2022 07:59:25 +0000", "Message-Id": "<20221012075925.3971538-2-chenghaoyang@google.com>", "X-Mailer": "git-send-email 2.38.0.rc1.362.ged0d419d3c-goog", "In-Reply-To": "<20221012075925.3971538-1-chenghaoyang@google.com>", "References": "<20221012075925.3971538-1-chenghaoyang@google.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH 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/android/camera_capabilities.cpp | 1 +\n src/libcamera/pipeline/fake/fake.cpp | 518 ++++++++++++++++++++++++\n src/libcamera/pipeline/fake/meson.build | 3 +\n src/libcamera/pipeline_handler.cpp | 2 +-\n test/camera/camera_reconfigure.cpp | 2 +-\n 6 files changed, 525 insertions(+), 3 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/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp\nindex 64bd8dde..730ceafc 100644\n--- a/src/android/camera_capabilities.cpp\n+++ b/src/android/camera_capabilities.cpp\n@@ -1077,6 +1077,7 @@ int CameraCapabilities::initializeStaticMetadata()\n \t{\n \t\tconst Span<const Rectangle> &rects =\n \t\t\tproperties.get(properties::PixelArrayActiveAreas).value_or(Span<const Rectangle>{});\n+\t\t// TODO: initialize at least one Rectangle as default.\n \t\tstd::vector<int32_t> data{\n \t\t\tstatic_cast<int32_t>(rects[0].x),\n \t\t\tstatic_cast<int32_t>(rects[0].y),\ndiff --git a/src/libcamera/pipeline/fake/fake.cpp b/src/libcamera/pipeline/fake/fake.cpp\nnew file mode 100644\nindex 00000000..518de6aa\n--- /dev/null\n+++ b/src/libcamera/pipeline/fake/fake.cpp\n@@ -0,0 +1,518 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * ipu3.cpp - Pipeline handler for Intel Fake\n+ */\n+\n+#include <algorithm>\n+#include <iomanip>\n+#include <memory>\n+#include <queue>\n+#include <vector>\n+\n+#include <libcamera/base/log.h>\n+#include <libcamera/base/utils.h>\n+\n+#include <libcamera/camera.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/camera_lens.h\"\n+#include \"libcamera/internal/camera_sensor.h\"\n+#include \"libcamera/internal/delayed_controls.h\"\n+#include \"libcamera/internal/device_enumerator.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<std::string> formats;\n+\t};\n+\n+\tFakeCameraData(PipelineHandler *pipe)\n+\t\t: Camera::Private(pipe), supportsFlips_(false)\n+\t{\n+\t}\n+\n+\tstd::vector<Resolution> supported_resolutions_;\n+\n+\tStream outStream_;\n+\tStream vfStream_;\n+\tStream rawStream_;\n+\n+\t// TODO: Check if we should support.\n+\tbool supportsFlips_;\n+\tTransform rotationTransform_;\n+\n+\t// TODO: remove\n+\t/* Requests for which no buffer has been queued to the CIO2 device yet. */\n+\tstd::queue<Request *> pendingRequests_;\n+\t/* Requests queued to the CIO2 device but not yet processed by the ImgU. */\n+\tstd::queue<Request *> processingRequests_;\n+\n+\t// ControlInfoMap ipaControls_;\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+\tstatic constexpr unsigned int V4L2_CID_Fake_PIPE_MODE = 0x009819c1;\n+\tstatic constexpr Size kViewfinderSize{ 1280, 720 };\n+\n+\tenum FakePipeModes {\n+\t\tFakePipeModeVideo = 0,\n+\t\tFakePipeModeStillCapture = 1,\n+\t};\n+\n+\tPipelineHandlerFake(CameraManager *manager);\n+\n+\tCameraConfiguration *generateConfiguration(Camera *camera,\n+\t\tconst 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// Transform combined = transform * data_->rotationTransform_;\n+\n+\t// /*\n+\t// * We combine the platform and user transform, but must \"adjust away\"\n+\t// * any combined result that includes a transposition, as we can't do\n+\t// * those. In this case, flipping only the transpose bit is helpful to\n+\t// * applications - they either get the transform they requested, or have\n+\t// * to do a simple transpose themselves (they don't have to worry about\n+\t// * the other possible cases).\n+\t// */\n+\t// if (!!(combined & Transform::Transpose)) {\n+\t// \t/*\n+\t// \t * Flipping the transpose bit in \"transform\" flips it in the\n+\t// \t * combined result too (as it's the last thing that happens),\n+\t// \t * which is of course clearing it.\n+\t// \t */\n+\t// \ttransform ^= Transform::Transpose;\n+\t// \tcombined &= ~Transform::Transpose;\n+\t// \tstatus = Adjusted;\n+\t// }\n+\n+\t// /*\n+\t// * We also check if the sensor doesn't do h/vflips at all, in which\n+\t// * case we clear them, and the application will have to do everything.\n+\t// */\n+\t// if (!data_->supportsFlips_ && !!combined) {\n+\t// \t/*\n+\t// \t * If the sensor can do no transforms, then combined must be\n+\t// \t * changed to the identity. The only user transform that gives\n+\t// \t * rise to this is the inverse of the rotation. (Recall that\n+\t// \t * combined = transform * rotationTransform.)\n+\t// \t */\n+\t// \ttransform = -data_->rotationTransform_;\n+\t// \tcombined = Transform::Identity;\n+\t// \tstatus = Adjusted;\n+\t// }\n+\n+\t/*\n+\t * Store the final combined transform that configure() will need to\n+\t * apply to the sensor to save us working it out again.\n+\t */\n+\t// combinedTransform_ = combined;\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: Base on number of cameras?\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 * Generate raw configuration from CIO2.\n+\t *\n+\t * The output YUV streams will be limited in size to the maximum frame\n+\t * size requested for the RAW stream, if present.\n+\t *\n+\t * If no raw stream is requested, generate a size from the largest YUV\n+\t * stream, aligned to the ImgU constraints and bound\n+\t * by the sensor's maximum resolution. See\n+\t * https://bugs.libcamera.org/show_bug.cgi?id=32\n+\t */\n+\t// TODO\n+\tif (rawSize.isNull())\n+\t\trawSize = rawRequirement;\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_IPU3;\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->supported_resolutions_) {\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_IPU3;\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->supported_resolutions_.resize(2);\n+\tdata->supported_resolutions_[0].size = Size(1920, 1080);\n+\tdata->supported_resolutions_[0].frame_rates.push_back(30);\n+\tdata->supported_resolutions_[0].formats.push_back(\"YCbCr_420_888\");\n+\tdata->supported_resolutions_[0].formats.push_back(\"BLOB\");\n+\tdata->supported_resolutions_[1].size = Size(1280, 720);\n+\tdata->supported_resolutions_[1].frame_rates.push_back(30);\n+\tdata->supported_resolutions_[1].frame_rates.push_back(60);\n+\tdata->supported_resolutions_[1].formats.push_back(\"YCbCr_420_888\");\n+\tdata->supported_resolutions_[1].formats.push_back(\"BLOB\");\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{};\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+\tregisterCamera(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/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\nindex e5cb751c..4261154d 100644\n--- a/src/libcamera/pipeline_handler.cpp\n+++ b/src/libcamera/pipeline_handler.cpp\n@@ -536,7 +536,7 @@ void PipelineHandler::registerCamera(std::shared_ptr<Camera> camera)\n \tcameras_.push_back(camera);\n \n \tif (mediaDevices_.empty())\n-\t\tLOG(Pipeline, Fatal)\n+\t\tLOG(Pipeline, Error)\n \t\t\t<< \"Registering camera with no media devices!\";\n \n \t/*\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", "1/1" ] }