Patch Detail
Show a patch.
GET /api/patches/21519/?format=api
{ "id": 21519, "url": "https://patchwork.libcamera.org/api/patches/21519/?format=api", "web_url": "https://patchwork.libcamera.org/patch/21519/", "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": "<20241004120517.3572281-2-paul.elder@ideasonboard.com>", "date": "2024-10-04T12:05:16", "name": "[v2,1/2] pipeline: Add support for dumping capture script and metadata", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "31fe2ca048d762c44b82aa5182dc67f00e4f7fa8", "submitter": { "id": 17, "url": "https://patchwork.libcamera.org/api/people/17/?format=api", "name": "Paul Elder", "email": "paul.elder@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/21519/mbox/", "series": [ { "id": 4659, "url": "https://patchwork.libcamera.org/api/series/4659/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4659", "date": "2024-10-04T12:05:15", "name": "libcamera: Add support for dumping capture script", "version": 2, "mbox": "https://patchwork.libcamera.org/series/4659/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/21519/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/21519/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 C2682C3257\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 4 Oct 2024 12:05:39 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5AD716352B;\n\tFri, 4 Oct 2024 14:05:39 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 53C4B63524\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 4 Oct 2024 14:05:34 +0200 (CEST)", "from neptunite.flets-east.jp (unknown\n\t[IPv6:2404:7a81:160:2100:e3ca:2180:ae9b:1941])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 6CD6E4C9;\n\tFri, 4 Oct 2024 14:03:59 +0200 (CEST)" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"MF410xBo\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1728043440;\n\tbh=IPGZftDfOzCXfNQGGjhfWMnYl0kgQs31aaPrb8Jxi+E=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=MF410xBoqZs0+1vA+18Yf2vzTaMUgABSsFpe7bNw0uNB41i6oMRxaqDNPPPWzYyWi\n\tvb9Mj442b0xG/iC13jwsqLjnCNIughJIYj2eLolVAex5FVpedHvSxZ7DHZbXBgaZnP\n\t8MtsQcAAjR7TPSkHimV8IL4z/L+VLWC9X3pNqkR4=", "From": "Paul Elder <paul.elder@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Cc": "Paul Elder <paul.elder@ideasonboard.com>", "Subject": "[PATCH v2 1/2] pipeline: Add support for dumping capture script and\n\tmetadata", "Date": "Fri, 4 Oct 2024 21:05:16 +0900", "Message-Id": "<20241004120517.3572281-2-paul.elder@ideasonboard.com>", "X-Mailer": "git-send-email 2.39.2", "In-Reply-To": "<20241004120517.3572281-1-paul.elder@ideasonboard.com>", "References": "<20241004120517.3572281-1-paul.elder@ideasonboard.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "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>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Add support for dumping capture scripts and metadata. The capture\nscripts can then be fed into the cam application and a capture can thus\nbe \"replayed\". Metadata can also be dumped.\n\nCamera configuration is also dumped to the capture script. The cam\napplication currently does not support loading configuration from the\ncapture script, but support for that will be added in a subsequent\npatch.\n\nThese can be enabled by a new environment variable.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n\n---\nChanges in v2:\n- clean up code\n- add support for creating new dump file when restarting capture\n- document the environment variables\n---\n Documentation/environment_variables.rst | 26 ++++++\n include/libcamera/internal/camera.h | 2 +\n include/libcamera/internal/pipeline_handler.h | 19 +++++\n src/libcamera/camera.cpp | 13 +++\n src/libcamera/pipeline_handler.cpp | 85 ++++++++++++++++++-\n 5 files changed, 144 insertions(+), 1 deletion(-)", "diff": "diff --git a/Documentation/environment_variables.rst b/Documentation/environment_variables.rst\nindex 7da9883a8380..b0448d387847 100644\n--- a/Documentation/environment_variables.rst\n+++ b/Documentation/environment_variables.rst\n@@ -29,6 +29,32 @@ LIBCAMERA_IPA_CONFIG_PATH\n \n Example value: ``${HOME}/.libcamera/share/ipa:/opt/libcamera/vendor/share/ipa``\n \n+LIBCAMERA_DUMP_CAPTURE_SCRIPT\n+ The custom destination for capture script dump output.\n+\n+ The precensce of this environment variable enables capture script dumping.\n+ All controls that are set for each request will be dumped into the file\n+ specified by the environment variable as a capture script, which can later\n+ be fed into the cam application to replay a control sequence.\n+\n+ The file that is written to will be suffixed with a number indicating the\n+ number of capture. That is, if the capture is stopped and started again, a\n+ new capture script will be dumped with the suffix incremented.\n+\n+ Example value: ``/home/{user}/capture_script.yaml``\n+\n+LIBCAMERA_DUMP_METADATA\n+ The custom destination for metadata dump output.\n+\n+ This is similar to LIBCAMERA_DUMP_CAPTURE_SCRIPT, except instead of a\n+ capture script with controls for each frame, the dump will consist of all\n+ metadata that was returned for every frame.\n+\n+ Also similar to LIBCAMERA_DUMP_CAPTURE_SCRIPT, there will be a number suffix\n+ added to the filename of the dump.\n+\n+ Example value: ``/home/{user}/metadata_dump.yaml``\n+\n LIBCAMERA_IPA_FORCE_ISOLATION\n When set to a non-empty string, force process isolation of all IPA modules.\n \ndiff --git a/include/libcamera/internal/camera.h b/include/libcamera/internal/camera.h\nindex 0add0428bb5d..b029d85901a7 100644\n--- a/include/libcamera/internal/camera.h\n+++ b/include/libcamera/internal/camera.h\n@@ -16,6 +16,7 @@\n #include <libcamera/base/class.h>\n \n #include <libcamera/camera.h>\n+#include <libcamera/orientation.h>\n \n namespace libcamera {\n \n@@ -65,6 +66,7 @@ private:\n \tstd::string id_;\n \tstd::set<Stream *> streams_;\n \tstd::set<const Stream *> activeStreams_;\n+\tOrientation orientation_;\n \n \tbool disconnected_;\n \tstd::atomic<State> state_;\ndiff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h\nindex 0d38080369c5..f31aced71331 100644\n--- a/include/libcamera/internal/pipeline_handler.h\n+++ b/include/libcamera/internal/pipeline_handler.h\n@@ -9,6 +9,7 @@\n \n #include <memory>\n #include <queue>\n+#include <set>\n #include <string>\n #include <sys/types.h>\n #include <vector>\n@@ -20,6 +21,8 @@\n \n namespace libcamera {\n \n+enum class Orientation;\n+\n class Camera;\n class CameraConfiguration;\n class CameraManager;\n@@ -68,6 +71,9 @@ public:\n \n \tCameraManager *cameraManager() const { return manager_; }\n \n+\tvoid dumpConfiguration(const std::set<const Stream *> &streams,\n+\t\t\t Orientation orientation);\n+\n protected:\n \tvoid registerCamera(std::shared_ptr<Camera> camera);\n \tvoid hotplugMediaDevice(MediaDevice *media);\n@@ -81,6 +87,11 @@ protected:\n \tCameraManager *manager_;\n \n private:\n+\tenum class DumpMode {\n+\t\tControls,\n+\t\tMetadata,\n+\t};\n+\n \tvoid unlockMediaDevices();\n \n \tvoid mediaDeviceDisconnected(MediaDevice *media);\n@@ -89,6 +100,8 @@ private:\n \tvoid doQueueRequest(Request *request);\n \tvoid doQueueRequests();\n \n+\tvoid dumpRequest(Request *request, DumpMode mode);\n+\n \tstd::vector<std::shared_ptr<MediaDevice>> mediaDevices_;\n \tstd::vector<std::weak_ptr<Camera>> cameras_;\n \n@@ -97,6 +110,12 @@ private:\n \tconst char *name_;\n \tunsigned int useCount_;\n \n+\tunsigned int captureCount_;\n+\tstd::string fileCapture_;\n+\tstd::string fileMetadata_;\n+\tstd::unique_ptr<std::ostream> dumpCaptureScript_;\n+\tstd::unique_ptr<std::ostream> dumpMetadata_;\n+\n \tfriend class PipelineHandlerFactoryBase;\n };\n \ndiff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp\nindex a86f552a47bc..1282f99d839b 100644\n--- a/src/libcamera/camera.cpp\n+++ b/src/libcamera/camera.cpp\n@@ -1215,6 +1215,9 @@ int Camera::configure(CameraConfiguration *config)\n \t\td->activeStreams_.insert(stream);\n \t}\n \n+\t/* TODO Save sensor configuration for dumping it to capture script */\n+\td->orientation_ = config->orientation;\n+\n \td->setState(Private::CameraConfigured);\n \n \treturn 0;\n@@ -1356,6 +1359,16 @@ int Camera::start(const ControlList *controls)\n \n \tASSERT(d->requestSequence_ == 0);\n \n+\t/*\n+\t * Invoke method in blocking mode to avoid the risk of writing after\n+\t * streaming has started.\n+\t * This needs to be here as PipelineHandler::start is a virtual function\n+\t * so it is impractical to add the dumping there.\n+\t * TODO Pass the sensor configuration, once it is supported\n+\t */\n+\td->pipe_->invokeMethod(&PipelineHandler::dumpConfiguration,\n+\t\t\t ConnectionTypeBlocking, d->activeStreams_, d->orientation_);\n+\n \tret = d->pipe_->invokeMethod(&PipelineHandler::start,\n \t\t\t\t ConnectionTypeBlocking, this, controls);\n \tif (ret)\ndiff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\nindex e5940469127e..4df64ac90bdf 100644\n--- a/src/libcamera/pipeline_handler.cpp\n+++ b/src/libcamera/pipeline_handler.cpp\n@@ -8,6 +8,7 @@\n #include \"libcamera/internal/pipeline_handler.h\"\n \n #include <chrono>\n+#include <fstream>\n #include <sys/stat.h>\n #include <sys/sysmacros.h>\n \n@@ -68,8 +69,10 @@ LOG_DEFINE_CATEGORY(Pipeline)\n * through the PipelineHandlerFactoryBase::create() function.\n */\n PipelineHandler::PipelineHandler(CameraManager *manager)\n-\t: manager_(manager), useCount_(0)\n+\t: manager_(manager), useCount_(0), captureCount_(0)\n {\n+\tfileCapture_ = utils::secure_getenv(\"LIBCAMERA_DUMP_CAPTURE_SCRIPT\");\n+\tfileMetadata_ = utils::secure_getenv(\"LIBCAMERA_DUMP_METADATA\");\n }\n \n PipelineHandler::~PipelineHandler()\n@@ -464,6 +467,8 @@ void PipelineHandler::doQueueRequest(Request *request)\n \n \trequest->_d()->sequence_ = data->requestSequence_++;\n \n+\tdumpRequest(request, DumpMode::Controls);\n+\n \tif (request->_d()->cancelled_) {\n \t\tcompleteRequest(request);\n \t\treturn;\n@@ -555,6 +560,8 @@ void PipelineHandler::completeRequest(Request *request)\n \n \trequest->_d()->complete();\n \n+\tdumpRequest(request, DumpMode::Metadata);\n+\n \tCamera::Private *data = camera->_d();\n \n \twhile (!data->queuedRequests_.empty()) {\n@@ -758,6 +765,82 @@ void PipelineHandler::disconnect()\n * \\return The CameraManager for this pipeline handler\n */\n \n+void PipelineHandler::dumpConfiguration(const std::set<const Stream *> &streams,\n+\t\t\t\t\tOrientation orientation)\n+{\n+\tcaptureCount_++;\n+\n+\t/* These need to be done here in case capture is restarted */\n+\tif (!fileCapture_.empty()) {\n+\t\tstd::string file = fileCapture_ + \".\" + std::to_string(captureCount_);\n+\t\tLOG(Pipeline, Info) << \"Dumping capture script to \" << file;\n+\t\tdumpCaptureScript_ = std::make_unique<std::ofstream>(file);\n+\t}\n+\n+\t/*\n+\t * Metadata needs to go into a separate file because otherwise it'll\n+\t * flood the capture script\n+\t */\n+\tif (!fileMetadata_.empty()) {\n+\t\tstd::string file = fileMetadata_ + \".\" + std::to_string(captureCount_);\n+\t\tLOG(Pipeline, Info) << \"Dumping metadata to \" << file;\n+\t\tdumpMetadata_ = std::make_unique<std::ofstream>(file);\n+\t\t*dumpMetadata_ << \"frames:\" << std::endl;\n+\t\tdumpMetadata_->flush();\n+\t}\n+\n+\tif (!dumpCaptureScript_)\n+\t\treturn;\n+\n+\tstd::ostream &output = *dumpCaptureScript_;\n+\n+\toutput << \"configuration:\" << std::endl;\n+\toutput << \" orientation: \" << orientation << std::endl;\n+\n+\t/* \\todo Dump Sensor configuration */\n+\n+\toutput << \" streams:\" << std::endl;\n+\tfor (const auto &stream : streams) {\n+\t\tconst StreamConfiguration &streamConfig = stream->configuration();\n+\t\toutput << \" - pixelFormat: \" << streamConfig.pixelFormat << std::endl;\n+\t\toutput << \" size: \" << streamConfig.size << std::endl;\n+\t\toutput << \" stride: \" << streamConfig.stride << std::endl;\n+\t\toutput << \" frameSize: \" << streamConfig.frameSize << std::endl;\n+\t\toutput << \" bufferCount: \" << streamConfig.bufferCount << std::endl;\n+\t\tif (streamConfig.colorSpace) {\n+\t\t\toutput << \" colorSpace: \" << streamConfig.colorSpace->toString() << std::endl;\n+\t\t}\n+\t}\n+\n+\toutput << \"frames:\" << std::endl;\n+\tdumpCaptureScript_->flush();\n+}\n+\n+void PipelineHandler::dumpRequest(Request *request, DumpMode mode)\n+{\n+\tControlList &controls =\n+\t\tmode == DumpMode::Controls ? request->controls()\n+\t\t\t\t\t : request->metadata();\n+\tstd::ostream *output =\n+\t\tmode == DumpMode::Controls ? dumpCaptureScript_.get()\n+\t\t\t\t\t : dumpMetadata_.get();\n+\n+\tif (!output || controls.empty())\n+\t\treturn;\n+\n+\t/* \\todo Figure out PFC */\n+\t*output << \" - \" << request->sequence() << \":\" << std::endl;\n+\n+\tconst ControlIdMap *idMap = controls.idMap();\n+\tfor (const auto &pair : controls) {\n+\t\tconst ControlId *ctrlId = idMap->at(pair.first);\n+\t\t*output << \" \" << ctrlId->name() << \": \" << pair.second.toString() << std::endl;\n+\t}\n+\n+\t/* \\todo Investigate the overhead of flushing this frequently */\n+\toutput->flush();\n+}\n+\n /**\n * \\class PipelineHandlerFactoryBase\n * \\brief Base class for pipeline handler factories\n", "prefixes": [ "v2", "1/2" ] }