Show a patch.

GET /api/patches/15839/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 15839,
    "url": "https://patchwork.libcamera.org/api/patches/15839/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/15839/",
    "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": "<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>",
    "date": "2022-05-10T07:16:17",
    "name": "[libcamera-devel] cam: convert to libfmt",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "aab1e1d7aa3fb4a82972a7ee5a1de9367d86ca7c",
    "submitter": {
        "id": 109,
        "url": "https://patchwork.libcamera.org/api/people/109/?format=api",
        "name": "Tomi Valkeinen",
        "email": "tomi.valkeinen@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/15839/mbox/",
    "series": [
        {
            "id": 3105,
            "url": "https://patchwork.libcamera.org/api/series/3105/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3105",
            "date": "2022-05-10T07:16:17",
            "name": "[libcamera-devel] cam: convert to libfmt",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/3105/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/15839/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/15839/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 DAE7FC0F2A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 10 May 2022 07:16:36 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 41AA765643;\n\tTue, 10 May 2022 09:16:36 +0200 (CEST)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8A3356563E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 May 2022 09:16:34 +0200 (CEST)",
            "from deskari.lan (91-156-85-209.elisa-laajakaista.fi\n\t[91.156.85.209])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id A234A55A;\n\tTue, 10 May 2022 09:16:33 +0200 (CEST)"
        ],
        "DKIM-Signature": [
            "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1652166996;\n\tbh=X2H9NK3pii7qA1TGO6AL1ZItGb26FOZyO4dSjxXnfIg=;\n\th=To:Date:Subject:List-Id:List-Unsubscribe:List-Archive:List-Post:\n\tList-Help:List-Subscribe:From:Reply-To:From;\n\tb=tmbeHW35ZvrkelcfniRJ2XTqny0eM+7jKofwkjwxP0+XpL6qHTwj17lqDFglrlLIl\n\t2T69DkCOTYOimJJsEtS95K9xTYS5ylhU2fNqcw79SZVs77hzNgUxIgsCy1pbb69BtE\n\taeGC6UoidYVw3t/q9a3Ocovt4B6geZYvXHxRUFb0+nbiIRl8IKPmYLw6o9IBE3Ib0y\n\tPWCkImTL+pobxQFl4ZGfSICjkqPfU4JybQBZ/KaDap2mR9L1Hwsg0rkjU/WGAEZiYm\n\t7YsfBR7VC1cpcwnssyv3oAK712fReRHnd7NA6VILR7lOnxGlmqa7jVXnHmMvUl5g2k\n\tbYQ7b9rgSU3ug==",
            "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1652166994;\n\tbh=X2H9NK3pii7qA1TGO6AL1ZItGb26FOZyO4dSjxXnfIg=;\n\th=From:To:Cc:Subject:Date:From;\n\tb=C+gJ2+vcJrANN5BLbHH3f1eTPbzNehUWHI3pGTwIPkZtlVd9Fnnl36PnOoAJ0CgCP\n\tWdmbI6Pr7ldlh0LoFJCG8CyoObDiq1AKY+hh4EXzgc+UQpeBnNg3ef2tF39MdvnlTK\n\tpZTEEJs0cMLL/xpu+9/4Gys8JFhlUS1zGkHgXEYQ="
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"C+gJ2+vc\"; dkim-atps=neutral",
        "To": "libcamera-devel@lists.libcamera.org,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>,\n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>",
        "Date": "Tue, 10 May 2022 10:16:17 +0300",
        "Message-Id": "<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH] cam: convert to libfmt",
        "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": "Tomi Valkeinen via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>",
        "Reply-To": "Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "This is just a conversation starter, not for merging. I really like\nlibfmt, and I really hate the C++ stream-based string formatting. libfmt\nis the first library I add to any new C++ project I make.\n\nYou can find more information about libfmt from:\n\nhttps://github.com/fmtlib/fmt\nhttps://fmt.dev/latest/index.html\n\nThis patch is just a crude conversion with ugly macros to showcase what\nthe formatting code might look like.\n\nlibfmt pages suggest that libfmt would be faster and smaller (exe size)\nthan iostreams, but for the size it didn't seem to be true in cam's case\nas the tests below show. However, simple prints did reduce the exe size,\nbut the few more complex ones increased the size.\n\nSize tests with gcc 11.2.0-19ubuntu1\n\n- Without libfmt\n\ndebug\t\t3523400\ndebug lto\t3269368\nrelease\t\t223056\nrelease lto\t172280\n\n- With libfmt\n\ndebug\t\t4424256\ndebug lto\t4143840\nrelease\t\t303952\nrelease lto\t252640\n\nAbove shows that cam's size clearly increases with libfmt. However, the\nincrease really comes only from one case, the use of fmt::memory_buffer\nand std::back_inserter. Converting that code to use fmt::format() and\nnaive string append gives:\n\nrelease with string append\t233680\nrelease lto with string append\t186936\n\nAlso, if I add another use of fmt::memory_buffer and std::back_inserter\nto another file, I see much less increase in the size:\n\nrelease lto with two uses of memory_buffer, back_inserter\t256736\n\nSigned-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n---\n src/cam/camera_session.cpp | 105 ++++++++++++++++---------------------\n src/cam/drm.cpp            |  68 ++++++++----------------\n src/cam/event_loop.cpp     |   9 ++--\n src/cam/file_sink.cpp      |  31 +++++------\n src/cam/image.cpp          |  15 +++---\n src/cam/kms_sink.cpp       |  38 ++++++--------\n src/cam/main.cpp           |  28 +++++-----\n src/cam/meson.build        |   5 +-\n src/cam/options.cpp        |  66 ++++++++++-------------\n src/cam/stream_options.cpp |  18 +++----\n 10 files changed, 165 insertions(+), 218 deletions(-)",
    "diff": "diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp\nindex efffafbf..7843c3fd 100644\n--- a/src/cam/camera_session.cpp\n+++ b/src/cam/camera_session.cpp\n@@ -5,10 +5,9 @@\n  * camera_session.cpp - Camera capture session\n  */\n \n-#include <iomanip>\n-#include <iostream>\n #include <limits.h>\n-#include <sstream>\n+#include <fmt/format.h>\n+#include <fmt/ostream.h>\n \n #include <libcamera/control_ids.h>\n #include <libcamera/property_ids.h>\n@@ -22,6 +21,9 @@\n #include \"main.h\"\n #include \"stream_options.h\"\n \n+#define PR(...) fmt::print(__VA_ARGS__)\n+#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n+\n using namespace libcamera;\n \n CameraSession::CameraSession(CameraManager *cm,\n@@ -40,13 +42,12 @@ CameraSession::CameraSession(CameraManager *cm,\n \t\tcamera_ = cm->get(cameraId);\n \n \tif (!camera_) {\n-\t\tstd::cerr << \"Camera \" << cameraId << \" not found\" << std::endl;\n+\t\tEPR(\"Camera {} not found\\n\", cameraId);\n \t\treturn;\n \t}\n \n \tif (camera_->acquire()) {\n-\t\tstd::cerr << \"Failed to acquire camera \" << cameraId\n-\t\t\t  << std::endl;\n+\t\tEPR(\"Failed to acquire camera {}\", cameraId);\n \t\treturn;\n \t}\n \n@@ -55,15 +56,14 @@ CameraSession::CameraSession(CameraManager *cm,\n \tstd::unique_ptr<CameraConfiguration> config =\n \t\tcamera_->generateConfiguration(roles);\n \tif (!config || config->size() != roles.size()) {\n-\t\tstd::cerr << \"Failed to get default stream configuration\"\n-\t\t\t  << std::endl;\n+\t\tEPR(\"Failed to get default stream configuration\\n\");\n \t\treturn;\n \t}\n \n \t/* Apply configuration if explicitly requested. */\n \tif (StreamKeyValueParser::updateConfiguration(config.get(),\n \t\t\t\t\t\t      options_[OptStream])) {\n-\t\tstd::cerr << \"Failed to update configuration\" << std::endl;\n+\t\tEPR(\"Failed to update configuration\\n\");\n \t\treturn;\n \t}\n \n@@ -72,20 +72,17 @@ CameraSession::CameraSession(CameraManager *cm,\n #ifdef HAVE_KMS\n \tif (options_.isSet(OptDisplay)) {\n \t\tif (options_.isSet(OptFile)) {\n-\t\t\tstd::cerr << \"--display and --file options are mutually exclusive\"\n-\t\t\t\t  << std::endl;\n+\t\t\tEPR(\"--display and --file options are mutually exclusive\\n\");\n \t\t\treturn;\n \t\t}\n \n \t\tif (roles.size() != 1) {\n-\t\t\tstd::cerr << \"Display doesn't support multiple streams\"\n-\t\t\t\t  << std::endl;\n+\t\t\tEPR(\"Display doesn't support multiple streams\\n\");\n \t\t\treturn;\n \t\t}\n \n \t\tif (roles[0] != StreamRole::Viewfinder) {\n-\t\t\tstd::cerr << \"Display requires a viewfinder stream\"\n-\t\t\t\t  << std::endl;\n+\t\t\tEPR(\"Display requires a viewfinder stream\\n\");\n \t\t\treturn;\n \t\t}\n \t}\n@@ -97,15 +94,14 @@ CameraSession::CameraSession(CameraManager *cm,\n \n \tcase CameraConfiguration::Adjusted:\n \t\tif (strictFormats) {\n-\t\t\tstd::cout << \"Adjusting camera configuration disallowed by --strict-formats argument\"\n-\t\t\t\t  << std::endl;\n+\t\t\tPR(\"Adjusting camera configuration disallowed by --strict-formats argument\\n\");\n \t\t\treturn;\n \t\t}\n-\t\tstd::cout << \"Camera configuration adjusted\" << std::endl;\n+\t\tPR(\"Camera configuration adjusted\\n\");\n \t\tbreak;\n \n \tcase CameraConfiguration::Invalid:\n-\t\tstd::cout << \"Camera configuration invalid\" << std::endl;\n+\t\tPR(\"Camera configuration invalid\\n\");\n \t\treturn;\n \t}\n \n@@ -121,8 +117,7 @@ CameraSession::~CameraSession()\n void CameraSession::listControls() const\n {\n \tfor (const auto &[id, info] : camera_->controls()) {\n-\t\tstd::cout << \"Control: \" << id->name() << \": \"\n-\t\t\t  << info.toString() << std::endl;\n+\t\tPR(\"Control: {}: {}}n\", id->name(), info.toString());\n \t}\n }\n \n@@ -131,8 +126,7 @@ void CameraSession::listProperties() const\n \tfor (const auto &[key, value] : camera_->properties()) {\n \t\tconst ControlId *id = properties::properties.at(key);\n \n-\t\tstd::cout << \"Property: \" << id->name() << \" = \"\n-\t\t\t  << value.toString() << std::endl;\n+\t\tPR(\"Property: {} = {}\\n\", id->name(), value.toString());\n \t}\n }\n \n@@ -140,17 +134,15 @@ void CameraSession::infoConfiguration() const\n {\n \tunsigned int index = 0;\n \tfor (const StreamConfiguration &cfg : *config_) {\n-\t\tstd::cout << index << \": \" << cfg.toString() << std::endl;\n+\t\tPR(\"{}: {}\\n\", index, cfg.toString());\n \n \t\tconst StreamFormats &formats = cfg.formats();\n \t\tfor (PixelFormat pixelformat : formats.pixelformats()) {\n-\t\t\tstd::cout << \" * Pixelformat: \"\n-\t\t\t\t  << pixelformat << \" \"\n-\t\t\t\t  << formats.range(pixelformat).toString()\n-\t\t\t\t  << std::endl;\n+\t\t\tPR(\" * Pixelformat: {} {}\\n\", pixelformat,\n+\t\t\t   formats.range(pixelformat).toString());\n \n \t\t\tfor (const Size &size : formats.sizes(pixelformat))\n-\t\t\t\tstd::cout << \"  - \" << size << std::endl;\n+\t\t\t\tPR(\"  - {}\\n\", size);\n \t\t}\n \n \t\tindex++;\n@@ -168,7 +160,7 @@ int CameraSession::start()\n \n \tret = camera_->configure(config_.get());\n \tif (ret < 0) {\n-\t\tstd::cout << \"Failed to configure camera\" << std::endl;\n+\t\tPR(\"Failed to configure camera\\n\");\n \t\treturn ret;\n \t}\n \n@@ -197,8 +189,7 @@ int CameraSession::start()\n \tif (sink_) {\n \t\tret = sink_->configure(*config_);\n \t\tif (ret < 0) {\n-\t\t\tstd::cout << \"Failed to configure frame sink\"\n-\t\t\t\t  << std::endl;\n+\t\t\tPR(\"Failed to configure frame sink\\n\");\n \t\t\treturn ret;\n \t\t}\n \n@@ -214,12 +205,12 @@ void CameraSession::stop()\n {\n \tint ret = camera_->stop();\n \tif (ret)\n-\t\tstd::cout << \"Failed to stop capture\" << std::endl;\n+\t\tPR(\"Failed to stop capture\\n\");\n \n \tif (sink_) {\n \t\tret = sink_->stop();\n \t\tif (ret)\n-\t\t\tstd::cout << \"Failed to stop frame sink\" << std::endl;\n+\t\t\tPR(\"Failed to stop frame sink\\n\");\n \t}\n \n \tsink_.reset();\n@@ -238,7 +229,7 @@ int CameraSession::startCapture()\n \tfor (StreamConfiguration &cfg : *config_) {\n \t\tret = allocator_->allocate(cfg.stream());\n \t\tif (ret < 0) {\n-\t\t\tstd::cerr << \"Can't allocate buffers\" << std::endl;\n+\t\t\tEPR(\"Can't allocate buffers\\n\");\n \t\t\treturn -ENOMEM;\n \t\t}\n \n@@ -254,7 +245,7 @@ int CameraSession::startCapture()\n \tfor (unsigned int i = 0; i < nbuffers; i++) {\n \t\tstd::unique_ptr<Request> request = camera_->createRequest();\n \t\tif (!request) {\n-\t\t\tstd::cerr << \"Can't create request\" << std::endl;\n+\t\t\tEPR(\"Can't create request\\n\");\n \t\t\treturn -ENOMEM;\n \t\t}\n \n@@ -266,8 +257,7 @@ int CameraSession::startCapture()\n \n \t\t\tret = request->addBuffer(stream, buffer.get());\n \t\t\tif (ret < 0) {\n-\t\t\t\tstd::cerr << \"Can't set buffer for request\"\n-\t\t\t\t\t  << std::endl;\n+\t\t\t\tEPR(\"Can't set buffer for request\\n\");\n \t\t\t\treturn ret;\n \t\t\t}\n \n@@ -281,14 +271,14 @@ int CameraSession::startCapture()\n \tif (sink_) {\n \t\tret = sink_->start();\n \t\tif (ret) {\n-\t\t\tstd::cout << \"Failed to start frame sink\" << std::endl;\n+\t\t\tPR(\"Failed to start frame sink\\n\");\n \t\t\treturn ret;\n \t\t}\n \t}\n \n \tret = camera_->start();\n \tif (ret) {\n-\t\tstd::cout << \"Failed to start capture\" << std::endl;\n+\t\tPR(\"Failed to start capture\\n\");\n \t\tif (sink_)\n \t\t\tsink_->stop();\n \t\treturn ret;\n@@ -297,7 +287,7 @@ int CameraSession::startCapture()\n \tfor (std::unique_ptr<Request> &request : requests_) {\n \t\tret = queueRequest(request.get());\n \t\tif (ret < 0) {\n-\t\t\tstd::cerr << \"Can't queue request\" << std::endl;\n+\t\t\tEPR(\"Can't queue request\\n\");\n \t\t\tcamera_->stop();\n \t\t\tif (sink_)\n \t\t\t\tsink_->stop();\n@@ -306,13 +296,11 @@ int CameraSession::startCapture()\n \t}\n \n \tif (captureLimit_)\n-\t\tstd::cout << \"cam\" << cameraIndex_\n-\t\t\t  << \": Capture \" << captureLimit_ << \" frames\"\n-\t\t\t  << std::endl;\n+\t\tPR(\"cam{}: Capture {} frames\\n\", cameraIndex_,\n+\t\t   captureLimit_);\n \telse\n-\t\tstd::cout << \"cam\" << cameraIndex_\n-\t\t\t  << \": Capture until user interrupts by SIGINT\"\n-\t\t\t  << std::endl;\n+\t\tPR(\"cam{}: Capture until user interrupts by SIGINT\\n\",\n+\t\t   cameraIndex_);\n \n \treturn 0;\n }\n@@ -364,23 +352,23 @@ void CameraSession::processRequest(Request *request)\n \n \tbool requeue = true;\n \n-\tstd::stringstream info;\n-\tinfo << ts / 1000000000 << \".\"\n-\t     << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000\n-\t     << \" (\" << std::fixed << std::setprecision(2) << fps << \" fps)\";\n+\tauto sbuf = fmt::memory_buffer();\n+\tfmt::format_to(std::back_inserter(sbuf), \"{}.{:06} ({:.2f} fps)\",\n+\t\t       ts / 1000000000,\n+\t\t       ts / 1000 % 1000000,\n+\t\t       fps);\n \n \tfor (const auto &[stream, buffer] : buffers) {\n \t\tconst FrameMetadata &metadata = buffer->metadata();\n \n-\t\tinfo << \" \" << streamNames_[stream]\n-\t\t     << \" seq: \" << std::setw(6) << std::setfill('0') << metadata.sequence\n-\t\t     << \" bytesused: \";\n+\t\tfmt::format_to(std::back_inserter(sbuf), \" {} seq: {:06} bytesused: \",\n+\t\t\t       streamNames_[stream], metadata.sequence);\n \n \t\tunsigned int nplane = 0;\n \t\tfor (const FrameMetadata::Plane &plane : metadata.planes()) {\n-\t\t\tinfo << plane.bytesused;\n+\t\t\tfmt::format_to(std::back_inserter(sbuf), \"{}\", plane.bytesused);\n \t\t\tif (++nplane < metadata.planes().size())\n-\t\t\t\tinfo << \"/\";\n+\t\t\t\tfmt::format_to(std::back_inserter(sbuf), \"/\");\n \t\t}\n \t}\n \n@@ -389,14 +377,13 @@ void CameraSession::processRequest(Request *request)\n \t\t\trequeue = false;\n \t}\n \n-\tstd::cout << info.str() << std::endl;\n+\tPR(\"{}\\n\", fmt::to_string(sbuf));\n \n \tif (printMetadata_) {\n \t\tconst ControlList &requestMetadata = request->metadata();\n \t\tfor (const auto &[key, value] : requestMetadata) {\n \t\t\tconst ControlId *id = controls::controls.at(key);\n-\t\t\tstd::cout << \"\\t\" << id->name() << \" = \"\n-\t\t\t\t  << value.toString() << std::endl;\n+\t\t\tPR(\"\\t{} = {}\\n\", id->name(), value.toString());\n \t\t}\n \t}\n \ndiff --git a/src/cam/drm.cpp b/src/cam/drm.cpp\nindex 46e34eb5..84919ab3 100644\n--- a/src/cam/drm.cpp\n+++ b/src/cam/drm.cpp\n@@ -10,12 +10,12 @@\n #include <algorithm>\n #include <errno.h>\n #include <fcntl.h>\n-#include <iostream>\n #include <set>\n #include <string.h>\n #include <sys/ioctl.h>\n #include <sys/stat.h>\n #include <sys/types.h>\n+#include <fmt/core.h>\n \n #include <libcamera/framebuffer.h>\n #include <libcamera/geometry.h>\n@@ -25,6 +25,9 @@\n \n #include \"event_loop.h\"\n \n+#define PR(...) fmt::print(__VA_ARGS__)\n+#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n+\n namespace DRM {\n \n Object::Object(Device *dev, uint32_t id, Type type)\n@@ -178,9 +181,7 @@ Connector::Connector(Device *dev, const drmModeConnector *connector)\n {\n \tauto typeName = connectorTypeNames.find(connector->connector_type);\n \tif (typeName == connectorTypeNames.end()) {\n-\t\tstd::cerr\n-\t\t\t<< \"Invalid connector type \"\n-\t\t\t<< connector->connector_type << std::endl;\n+\t\tEPR(\"Invalid connector type {}}n\", connector->connector_type);\n \t\ttypeName = connectorTypeNames.find(DRM_MODE_CONNECTOR_Unknown);\n \t}\n \n@@ -213,9 +214,7 @@ Connector::Connector(Device *dev, const drmModeConnector *connector)\n \t\t\t\t\t\t    return e.id() == encoderId;\n \t\t\t\t\t    });\n \t\tif (encoder == encoders.end()) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Encoder \" << encoderId << \" not found\"\n-\t\t\t\t<< std::endl;\n+\t\t\tEPR(\"Encoder {} not found\\n\", encoderId);\n \t\t\tcontinue;\n \t\t}\n \n@@ -296,9 +295,7 @@ FrameBuffer::~FrameBuffer()\n \n \t\tif (ret == -1) {\n \t\t\tret = -errno;\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Failed to close GEM object: \"\n-\t\t\t\t<< strerror(-ret) << std::endl;\n+\t\t\tEPR(\"Failed to close GEM object: {}\\n\", strerror(-ret));\n \t\t}\n \t}\n \n@@ -408,9 +405,8 @@ int Device::init()\n \tfd_ = open(name, O_RDWR | O_CLOEXEC);\n \tif (fd_ < 0) {\n \t\tret = -errno;\n-\t\tstd::cerr\n-\t\t\t<< \"Failed to open DRM/KMS device \" << name << \": \"\n-\t\t\t<< strerror(-ret) << std::endl;\n+\t\tEPR(\"Failed to open DRM/KMS device {}: {}\\n\", name,\n+\t\t    strerror(-ret));\n \t\treturn ret;\n \t}\n \n@@ -421,9 +417,7 @@ int Device::init()\n \tret = drmSetClientCap(fd_, DRM_CLIENT_CAP_ATOMIC, 1);\n \tif (ret < 0) {\n \t\tret = -errno;\n-\t\tstd::cerr\n-\t\t\t<< \"Failed to enable atomic capability: \"\n-\t\t\t<< strerror(-ret) << std::endl;\n+\t\tEPR(\"Failed to enable atomic capability: {}\\n\", strerror(-ret));\n \t\treturn ret;\n \t}\n \n@@ -448,9 +442,7 @@ int Device::getResources()\n \t};\n \tif (!resources) {\n \t\tret = -errno;\n-\t\tstd::cerr\n-\t\t\t<< \"Failed to get DRM/KMS resources: \"\n-\t\t\t<< strerror(-ret) << std::endl;\n+\t\tEPR(\"Failed to get DRM/KMS resources: {}\\n\", strerror(-ret));\n \t\treturn ret;\n \t}\n \n@@ -458,9 +450,7 @@ int Device::getResources()\n \t\tdrmModeCrtc *crtc = drmModeGetCrtc(fd_, resources->crtcs[i]);\n \t\tif (!crtc) {\n \t\t\tret = -errno;\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Failed to get CRTC: \" << strerror(-ret)\n-\t\t\t\t<< std::endl;\n+\t\t\tEPR(\"Failed to get CRTC: {}\\n\", strerror(-ret));\n \t\t\treturn ret;\n \t\t}\n \n@@ -476,9 +466,7 @@ int Device::getResources()\n \t\t\tdrmModeGetEncoder(fd_, resources->encoders[i]);\n \t\tif (!encoder) {\n \t\t\tret = -errno;\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Failed to get encoder: \" << strerror(-ret)\n-\t\t\t\t<< std::endl;\n+\t\t\tEPR(\"Failed to get encoder: {}\\n\", strerror(-ret));\n \t\t\treturn ret;\n \t\t}\n \n@@ -494,9 +482,7 @@ int Device::getResources()\n \t\t\tdrmModeGetConnector(fd_, resources->connectors[i]);\n \t\tif (!connector) {\n \t\t\tret = -errno;\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Failed to get connector: \" << strerror(-ret)\n-\t\t\t\t<< std::endl;\n+\t\t\tEPR(\"Failed to get connector: {}\\n\", strerror(-ret));\n \t\t\treturn ret;\n \t\t}\n \n@@ -513,9 +499,7 @@ int Device::getResources()\n \t};\n \tif (!planes) {\n \t\tret = -errno;\n-\t\tstd::cerr\n-\t\t\t<< \"Failed to get DRM/KMS planes: \"\n-\t\t\t<< strerror(-ret) << std::endl;\n+\t\tEPR(\"Failed to get DRM/KMS planes: {}\\n\", strerror(-ret));\n \t\treturn ret;\n \t}\n \n@@ -524,9 +508,7 @@ int Device::getResources()\n \t\t\tdrmModeGetPlane(fd_, planes->planes[i]);\n \t\tif (!plane) {\n \t\t\tret = -errno;\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Failed to get plane: \" << strerror(-ret)\n-\t\t\t\t<< std::endl;\n+\t\t\tEPR(\"Failed to get plane: {}\\n\", strerror(-ret));\n \t\t\treturn ret;\n \t\t}\n \n@@ -556,9 +538,7 @@ int Device::getResources()\n \t\tdrmModePropertyRes *property = drmModeGetProperty(fd_, id);\n \t\tif (!property) {\n \t\t\tret = -errno;\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Failed to get property: \" << strerror(-ret)\n-\t\t\t\t<< std::endl;\n+\t\t\tEPR(\"Failed to get property: {}\\n\", strerror(-ret));\n \t\t\tcontinue;\n \t\t}\n \n@@ -573,9 +553,8 @@ int Device::getResources()\n \tfor (auto &object : objects_) {\n \t\tret = object.second->setup();\n \t\tif (ret < 0) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Failed to setup object \" << object.second->id()\n-\t\t\t\t<< \": \" << strerror(-ret) << std::endl;\n+\t\t\tEPR(\"Failed to setup object {}: {}\\n\",\n+\t\t\t    object.second->id(), strerror(-ret));\n \t\t\treturn ret;\n \t\t}\n \t}\n@@ -616,9 +595,8 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer(\n \t\t\tret = drmPrimeFDToHandle(fd_, plane.fd.get(), &handle);\n \t\t\tif (ret < 0) {\n \t\t\t\tret = -errno;\n-\t\t\t\tstd::cerr\n-\t\t\t\t\t<< \"Unable to import framebuffer dmabuf: \"\n-\t\t\t\t\t<< strerror(-ret) << std::endl;\n+\t\t\t\tEPR(\"Unable to import framebuffer dmabuf: {}\\n\",\n+\t\t\t\t    strerror(-ret));\n \t\t\t\treturn nullptr;\n \t\t\t}\n \n@@ -636,9 +614,7 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer(\n \t\t\t    strides.data(), offsets, &fb->id_, 0);\n \tif (ret < 0) {\n \t\tret = -errno;\n-\t\tstd::cerr\n-\t\t\t<< \"Failed to add framebuffer: \"\n-\t\t\t<< strerror(-ret) << std::endl;\n+\t\tEPR(\"Failed to add framebuffer: {}\\n\", strerror(-ret));\n \t\treturn nullptr;\n \t}\n \ndiff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp\nindex e25784c0..87aaf59a 100644\n--- a/src/cam/event_loop.cpp\n+++ b/src/cam/event_loop.cpp\n@@ -10,7 +10,10 @@\n #include <assert.h>\n #include <event2/event.h>\n #include <event2/thread.h>\n-#include <iostream>\n+#include <fmt/core.h>\n+\n+#define PR(...) fmt::print(__VA_ARGS__)\n+#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n \n EventLoop *EventLoop::instance_ = nullptr;\n \n@@ -71,13 +74,13 @@ void EventLoop::addEvent(int fd, EventType type,\n \tevent->event_ = event_new(base_, fd, events, &EventLoop::Event::dispatch,\n \t\t\t\t  event.get());\n \tif (!event->event_) {\n-\t\tstd::cerr << \"Failed to create event for fd \" << fd << std::endl;\n+\t\tEPR(\"Failed to create event for fd {}\\n\", fd);\n \t\treturn;\n \t}\n \n \tint ret = event_add(event->event_, nullptr);\n \tif (ret < 0) {\n-\t\tstd::cerr << \"Failed to add event for fd \" << fd << std::endl;\n+\t\tEPR(\"Failed to add event for fd {}\\n\", fd);\n \t\treturn;\n \t}\n \ndiff --git a/src/cam/file_sink.cpp b/src/cam/file_sink.cpp\nindex 45213d4a..86e2118c 100644\n--- a/src/cam/file_sink.cpp\n+++ b/src/cam/file_sink.cpp\n@@ -7,11 +7,12 @@\n \n #include <assert.h>\n #include <fcntl.h>\n-#include <iomanip>\n-#include <iostream>\n-#include <sstream>\n #include <string.h>\n #include <unistd.h>\n+#include <fmt/core.h>\n+\n+#define PR(...) fmt::print(__VA_ARGS__)\n+#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n \n #include <libcamera/camera.h>\n \n@@ -70,10 +71,10 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer)\n \n \tpos = filename.find_first_of('#');\n \tif (pos != std::string::npos) {\n-\t\tstd::stringstream ss;\n-\t\tss << streamNames_[stream] << \"-\" << std::setw(6)\n-\t\t   << std::setfill('0') << buffer->metadata().sequence;\n-\t\tfilename.replace(pos, 1, ss.str());\n+\t\tauto s = fmt::format(\"{}-{:06}\",\n+\t\t\t\t     streamNames_[stream],\n+\t\t\t\t     buffer->metadata().sequence);\n+\t\tfilename.replace(pos, 1, s);\n \t}\n \n \tfd = open(filename.c_str(), O_CREAT | O_WRONLY |\n@@ -81,8 +82,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer)\n \t\t  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);\n \tif (fd == -1) {\n \t\tret = -errno;\n-\t\tstd::cerr << \"failed to open file \" << filename << \": \"\n-\t\t\t  << strerror(-ret) << std::endl;\n+\t\tEPR(\"failed to open file {}: {}\\n\", filename, strerror(-ret));\n \t\treturn;\n \t}\n \n@@ -95,20 +95,17 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer)\n \t\tunsigned int length = std::min<unsigned int>(meta.bytesused, data.size());\n \n \t\tif (meta.bytesused > data.size())\n-\t\t\tstd::cerr << \"payload size \" << meta.bytesused\n-\t\t\t\t  << \" larger than plane size \" << data.size()\n-\t\t\t\t  << std::endl;\n+\t\t\tEPR(\"payload size {} larger than plane size {}\\n\",\n+\t\t\t    meta.bytesused, data.size());\n \n \t\tret = ::write(fd, data.data(), length);\n \t\tif (ret < 0) {\n \t\t\tret = -errno;\n-\t\t\tstd::cerr << \"write error: \" << strerror(-ret)\n-\t\t\t\t  << std::endl;\n+\t\t\tEPR(\"write error: {}\\n\", strerror(-ret));\n \t\t\tbreak;\n \t\t} else if (ret != (int)length) {\n-\t\t\tstd::cerr << \"write error: only \" << ret\n-\t\t\t\t  << \" bytes written instead of \"\n-\t\t\t\t  << length << std::endl;\n+\t\t\tEPR(\"write error: only {} bytes written instead of {}\\n\",\n+\t\t\t    ret, length);\n \t\t\tbreak;\n \t\t}\n \t}\ndiff --git a/src/cam/image.cpp b/src/cam/image.cpp\nindex fe2cc6da..73bcf915 100644\n--- a/src/cam/image.cpp\n+++ b/src/cam/image.cpp\n@@ -9,11 +9,14 @@\n \n #include <assert.h>\n #include <errno.h>\n-#include <iostream>\n #include <map>\n #include <string.h>\n #include <sys/mman.h>\n #include <unistd.h>\n+#include <fmt/core.h>\n+\n+#define PR(...) fmt::print(__VA_ARGS__)\n+#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n \n using namespace libcamera;\n \n@@ -49,10 +52,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const FrameBuffer *buffer, MapMode\n \n \t\tif (plane.offset > length ||\n \t\t    plane.offset + plane.length > length) {\n-\t\t\tstd::cerr << \"plane is out of buffer: buffer length=\"\n-\t\t\t\t  << length << \", plane offset=\" << plane.offset\n-\t\t\t\t  << \", plane length=\" << plane.length\n-\t\t\t\t  << std::endl;\n+\t\t\tEPR(\"plane is out of buffer: buffer length={}, plane offset={}, plane length={}\\n\",\n+\t\t\t    length, plane.offset, plane.length);\n \t\t\treturn nullptr;\n \t\t}\n \t\tsize_t &mapLength = mappedBuffers[fd].mapLength;\n@@ -68,8 +69,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const FrameBuffer *buffer, MapMode\n \t\t\t\t\t     MAP_SHARED, fd, 0);\n \t\t\tif (address == MAP_FAILED) {\n \t\t\t\tint error = -errno;\n-\t\t\t\tstd::cerr << \"Failed to mmap plane: \"\n-\t\t\t\t\t  << strerror(-error) << std::endl;\n+\t\t\t\tEPR(\"Failed to mmap plane: {}\\n\",\n+\t\t\t\t    strerror(-error));\n \t\t\t\treturn nullptr;\n \t\t\t}\n \ndiff --git a/src/cam/kms_sink.cpp b/src/cam/kms_sink.cpp\nindex 7add81a6..823b75e4 100644\n--- a/src/cam/kms_sink.cpp\n+++ b/src/cam/kms_sink.cpp\n@@ -10,10 +10,13 @@\n #include <array>\n #include <algorithm>\n #include <assert.h>\n-#include <iostream>\n #include <memory>\n #include <stdint.h>\n #include <string.h>\n+#include <fmt/core.h>\n+\n+#define PR(...) fmt::print(__VA_ARGS__)\n+#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n \n #include <libcamera/camera.h>\n #include <libcamera/formats.h>\n@@ -54,11 +57,9 @@ KMSSink::KMSSink(const std::string &connectorName)\n \n \tif (!connector_) {\n \t\tif (!connectorName.empty())\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Connector \" << connectorName << \" not found\"\n-\t\t\t\t<< std::endl;\n+\t\t\tEPR(\"Connector {} not found\\n\", connectorName);\n \t\telse\n-\t\t\tstd::cerr << \"No connected connector found\" << std::endl;\n+\t\t\tEPR(\"No connected connector found\\n\");\n \t\treturn;\n \t}\n \n@@ -119,7 +120,7 @@ int KMSSink::configure(const libcamera::CameraConfiguration &config)\n \t\t\t\t\t\t      mode.vdisplay == cfg.size.height;\n \t\t\t\t       });\n \tif (iter == modes.end()) {\n-\t\tstd::cerr << \"No mode matching \" << cfg.size << std::endl;\n+\t\tEPR(\"No mode matching {}\\n\", cfg.size);\n \t\treturn -EINVAL;\n \t}\n \n@@ -192,17 +193,12 @@ int KMSSink::configurePipeline(const libcamera::PixelFormat &format)\n {\n \tconst int ret = selectPipeline(format);\n \tif (ret) {\n-\t\tstd::cerr\n-\t\t\t<< \"Unable to find display pipeline for format \"\n-\t\t\t<< format << std::endl;\n-\n+\t\tEPR(\"Unable to find display pipeline for format {}\\n\", format);\n \t\treturn ret;\n \t}\n \n-\tstd::cout\n-\t\t<< \"Using KMS plane \" << plane_->id() << \", CRTC \" << crtc_->id()\n-\t\t<< \", connector \" << connector_->name()\n-\t\t<< \" (\" << connector_->id() << \")\" << std::endl;\n+\tPR(\"Using KMS plane {}, CRTC {}, connector {} ({})\\n\",\n+\t   plane_->id(), crtc_->id(), connector_->name(), connector_->id());\n \n \treturn 0;\n }\n@@ -228,9 +224,8 @@ int KMSSink::start()\n \n \tret = request->commit(DRM::AtomicRequest::FlagAllowModeset);\n \tif (ret < 0) {\n-\t\tstd::cerr\n-\t\t\t<< \"Failed to disable CRTCs and planes: \"\n-\t\t\t<< strerror(-ret) << std::endl;\n+\t\tEPR(\"Failed to disable CRTCs and planes: {}\\n\",\n+\t\t    strerror(-ret));\n \t\treturn ret;\n \t}\n \n@@ -250,9 +245,7 @@ int KMSSink::stop()\n \n \tint ret = request.commit(DRM::AtomicRequest::FlagAllowModeset);\n \tif (ret < 0) {\n-\t\tstd::cerr\n-\t\t\t<< \"Failed to stop display pipeline: \"\n-\t\t\t<< strerror(-ret) << std::endl;\n+\t\tEPR(\"Failed to stop display pipeline: {}\\n\", strerror(-ret));\n \t\treturn ret;\n \t}\n \n@@ -312,9 +305,8 @@ bool KMSSink::processRequest(libcamera::Request *camRequest)\n \tif (!queued_) {\n \t\tint ret = drmRequest->commit(flags);\n \t\tif (ret < 0) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Failed to commit atomic request: \"\n-\t\t\t\t<< strerror(-ret) << std::endl;\n+\t\t\tEPR(\"Failed to commit atomic request: {}\\n\",\n+\t\t\t    strerror(-ret));\n \t\t\t/* \\todo Implement error handling */\n \t\t}\n \ndiff --git a/src/cam/main.cpp b/src/cam/main.cpp\nindex c7f664b9..03615dc9 100644\n--- a/src/cam/main.cpp\n+++ b/src/cam/main.cpp\n@@ -6,10 +6,9 @@\n  */\n \n #include <atomic>\n-#include <iomanip>\n-#include <iostream>\n #include <signal.h>\n #include <string.h>\n+#include <fmt/core.h>\n \n #include <libcamera/libcamera.h>\n #include <libcamera/property_ids.h>\n@@ -78,8 +77,7 @@ int CamApp::init(int argc, char **argv)\n \n \tret = cm_->start();\n \tif (ret) {\n-\t\tstd::cout << \"Failed to start camera manager: \"\n-\t\t\t  << strerror(-ret) << std::endl;\n+\t\tfmt::print(\"Failed to start camera manager: {}\\n\", -ret);\n \t\treturn ret;\n \t}\n \n@@ -173,12 +171,12 @@ int CamApp::parseOptions(int argc, char *argv[])\n \n void CamApp::cameraAdded(std::shared_ptr<Camera> cam)\n {\n-\tstd::cout << \"Camera Added: \" << cam->id() << std::endl;\n+\tfmt::print(\"Camera Added: {}\\n\", cam->id());\n }\n \n void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)\n {\n-\tstd::cout << \"Camera Removed: \" << cam->id() << std::endl;\n+\tfmt::print(\"Camera Removed: {}\\n\", cam->id());\n }\n \n void CamApp::captureDone()\n@@ -193,11 +191,11 @@ int CamApp::run()\n \n \t/* 1. List all cameras. */\n \tif (options_.isSet(OptList)) {\n-\t\tstd::cout << \"Available cameras:\" << std::endl;\n+\t\tfmt::print(\"Available cameras:\\n\");\n \n \t\tunsigned int index = 1;\n \t\tfor (const std::shared_ptr<Camera> &cam : cm_->cameras()) {\n-\t\t\tstd::cout << index << \": \" << cameraName(cam.get()) << std::endl;\n+\t\t\tfmt::print(\"{}: {}\\n\", cameraName(cam.get()), index);\n \t\t\tindex++;\n \t\t}\n \t}\n@@ -215,12 +213,12 @@ int CamApp::run()\n \t\t\t\t\t\t\t\tindex,\n \t\t\t\t\t\t\t\tcamera.children());\n \t\t\tif (!session->isValid()) {\n-\t\t\t\tstd::cout << \"Failed to create camera session\" << std::endl;\n+\t\t\t\tfmt::print(\"Failed to create camera session\\n\");\n \t\t\t\treturn -EINVAL;\n \t\t\t}\n \n-\t\t\tstd::cout << \"Using camera \" << session->camera()->id()\n-\t\t\t\t  << \" as cam\" << index << std::endl;\n+\t\t\tfmt::print(\"Using camera{} as cam{}\\n\",\n+\t\t\t\t   session->camera()->id(), index);\n \n \t\t\tsession->captureDone.connect(this, &CamApp::captureDone);\n \n@@ -250,7 +248,7 @@ int CamApp::run()\n \n \t\tret = session->start();\n \t\tif (ret) {\n-\t\t\tstd::cout << \"Failed to start camera session\" << std::endl;\n+\t\t\tfmt::print(\"Failed to start camera session\\n\");\n \t\t\treturn ret;\n \t\t}\n \n@@ -259,8 +257,8 @@ int CamApp::run()\n \n \t/* 5. Enable hotplug monitoring. */\n \tif (options_.isSet(OptMonitor)) {\n-\t\tstd::cout << \"Monitoring new hotplug and unplug events\" << std::endl;\n-\t\tstd::cout << \"Press Ctrl-C to interrupt\" << std::endl;\n+\t\tfmt::print(\"Monitoring new hotplug and unplug events\\n\");\n+\t\tfmt::print(\"Press Ctrl-C to interrupt\\n\");\n \n \t\tcm_->cameraAdded.connect(this, &CamApp::cameraAdded);\n \t\tcm_->cameraRemoved.connect(this, &CamApp::cameraRemoved);\n@@ -323,7 +321,7 @@ std::string CamApp::cameraName(const Camera *camera)\n \n void signalHandler([[maybe_unused]] int signal)\n {\n-\tstd::cout << \"Exiting\" << std::endl;\n+\tfmt::print(\"Exiting\");\n \tCamApp::instance()->quit();\n }\n \ndiff --git a/src/cam/meson.build b/src/cam/meson.build\nindex 5bab8c9e..2b47383d 100644\n--- a/src/cam/meson.build\n+++ b/src/cam/meson.build\n@@ -7,6 +7,8 @@ if not libevent.found()\n     subdir_done()\n endif\n \n+libfmt_dep = dependency('fmt')\n+\n cam_enabled = true\n \n cam_sources = files([\n@@ -25,7 +27,7 @@ cam_cpp_args = []\n libdrm = dependency('libdrm', required : false)\n \n if libdrm.found()\n-    cam_cpp_args += [ '-DHAVE_KMS' ]\n+    cam_cpp_args += [ '-DHAVE_KMS', ]\n     cam_sources += files([\n         'drm.cpp',\n         'kms_sink.cpp'\n@@ -38,6 +40,7 @@ cam  = executable('cam', cam_sources,\n                       libcamera_public,\n                       libdrm,\n                       libevent,\n+                      libfmt_dep,\n                   ],\n                   cpp_args : cam_cpp_args,\n                   install : true)\ndiff --git a/src/cam/options.cpp b/src/cam/options.cpp\nindex 4f7e8691..c9979385 100644\n--- a/src/cam/options.cpp\n+++ b/src/cam/options.cpp\n@@ -7,9 +7,11 @@\n \n #include <assert.h>\n #include <getopt.h>\n-#include <iomanip>\n-#include <iostream>\n #include <string.h>\n+#include <fmt/core.h>\n+\n+#define PR(...) fmt::print(__VA_ARGS__)\n+#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n \n #include \"options.h\"\n \n@@ -390,26 +392,23 @@ KeyValueParser::Options KeyValueParser::parse(const char *arguments)\n \t\t\tcontinue;\n \n \t\tif (optionsMap_.find(key) == optionsMap_.end()) {\n-\t\t\tstd::cerr << \"Invalid option \" << key << std::endl;\n+\t\t\tEPR(\"Invalid option {}\\n\", key);\n \t\t\treturn options;\n \t\t}\n \n \t\tOptionArgument arg = optionsMap_[key].argument;\n \t\tif (value.empty() && arg == ArgumentRequired) {\n-\t\t\tstd::cerr << \"Option \" << key << \" requires an argument\"\n-\t\t\t\t  << std::endl;\n+\t\t\tEPR(\"Option {} requires an argument\\n\", key);\n \t\t\treturn options;\n \t\t} else if (!value.empty() && arg == ArgumentNone) {\n-\t\t\tstd::cerr << \"Option \" << key << \" takes no argument\"\n-\t\t\t\t  << std::endl;\n+\t\t\tEPR(\"Option {} takes no argument\\n\", key);\n \t\t\treturn options;\n \t\t}\n \n \t\tconst Option &option = optionsMap_[key];\n \t\tif (!options.parseValue(key, option, value.c_str())) {\n-\t\t\tstd::cerr << \"Failed to parse '\" << value << \"' as \"\n-\t\t\t\t  << option.typeName() << \" for option \" << key\n-\t\t\t\t  << std::endl;\n+\t\t\tEPR(\"Failed to parse '{}' as {} for option {}\\n\",\n+\t\t\t    value, option.typeName(), key);\n \t\t\treturn options;\n \t\t}\n \t}\n@@ -453,16 +452,16 @@ void KeyValueParser::usage(int indent)\n \t\t\t\targument += \"]\";\n \t\t}\n \n-\t\tstd::cerr << std::setw(indent) << argument;\n+\t\tEPR(\"{:{}}\", argument, indent);\n \n \t\tfor (const char *help = option.help, *end = help; end;) {\n \t\t\tend = strchr(help, '\\n');\n \t\t\tif (end) {\n-\t\t\t\tstd::cerr << std::string(help, end - help + 1);\n-\t\t\t\tstd::cerr << std::setw(indent) << \" \";\n+\t\t\t\tEPR(std::string(help, end - help + 1));\n+\t\t\t\tEPR(\"{:{}}\", \"\", indent);\n \t\t\t\thelp = end + 1;\n \t\t\t} else {\n-\t\t\t\tstd::cerr << help << std::endl;\n+\t\t\t\tEPR(\"{}\\n\", help);\n \t\t\t}\n \t\t}\n \t}\n@@ -929,10 +928,10 @@ OptionsParser::Options OptionsParser::parse(int argc, char **argv)\n \n \t\tif (c == '?' || c == ':') {\n \t\t\tif (c == '?')\n-\t\t\t\tstd::cerr << \"Invalid option \";\n+\t\t\t\tEPR(\"Invalid option \");\n \t\t\telse\n-\t\t\t\tstd::cerr << \"Missing argument for option \";\n-\t\t\tstd::cerr << argv[optind - 1] << std::endl;\n+\t\t\t\tEPR(\"Missing argument for option \");\n+\t\t\tEPR(\"{}\\n\", argv[optind - 1]);\n \n \t\t\tusage();\n \t\t\treturn options;\n@@ -946,8 +945,7 @@ OptionsParser::Options OptionsParser::parse(int argc, char **argv)\n \t}\n \n \tif (optind < argc) {\n-\t\tstd::cerr << \"Invalid non-option argument '\" << argv[optind]\n-\t\t\t  << \"'\" << std::endl;\n+\t\tEPR(\"Invalid non-option argument '{}'\\n\", argv[optind]);\n \t\tusage();\n \t\treturn options;\n \t}\n@@ -992,14 +990,9 @@ void OptionsParser::usage()\n \n \tindent = (indent + 7) / 8 * 8;\n \n-\tstd::cerr << \"Options:\" << std::endl;\n-\n-\tstd::ios_base::fmtflags f(std::cerr.flags());\n-\tstd::cerr << std::left;\n+\tEPR(\"Options:\\n\");\n \n \tusageOptions(options_, indent);\n-\n-\tstd::cerr.flags(f);\n }\n \n void OptionsParser::usageOptions(const std::list<Option> &options,\n@@ -1036,16 +1029,16 @@ void OptionsParser::usageOptions(const std::list<Option> &options,\n \t\tif (option.isArray)\n \t\t\targument += \" ...\";\n \n-\t\tstd::cerr << std::setw(indent) << argument;\n+\t\tEPR(\"{:{}}\", argument, indent);\n \n-\t\tfor (const char *help = option.help, *end = help; end; ) {\n+\t\tfor (const char *help = option.help, *end = help; end;) {\n \t\t\tend = strchr(help, '\\n');\n \t\t\tif (end) {\n-\t\t\t\tstd::cerr << std::string(help, end - help + 1);\n-\t\t\t\tstd::cerr << std::setw(indent) << \" \";\n+\t\t\t\tEPR(std::string(help, end - help + 1));\n+\t\t\t\tEPR(\"{:{}}\", \"\", indent);\n \t\t\t\thelp = end + 1;\n \t\t\t} else {\n-\t\t\t\tstd::cerr << help << std::endl;\n+\t\t\t\tEPR(\"{}\\n\", help);\n \t\t\t}\n \t\t}\n \n@@ -1060,8 +1053,8 @@ void OptionsParser::usageOptions(const std::list<Option> &options,\n \t\treturn;\n \n \tfor (const Option *option : parentOptions) {\n-\t\tstd::cerr << std::endl << \"Options valid in the context of \"\n-\t\t\t  << option->optionName() << \":\" << std::endl;\n+\t\tEPR(\"\\nOptions valid in the context of {}:\\n\",\n+\t\t    option->optionName());\n \t\tusageOptions(option->children, indent);\n \t}\n }\n@@ -1125,15 +1118,14 @@ bool OptionsParser::parseValue(const Option &option, const char *arg,\n \n \tstd::tie(options, error) = childOption(option.parent, options);\n \tif (error) {\n-\t\tstd::cerr << \"Option \" << option.optionName() << \" requires a \"\n-\t\t\t  << error->optionName() << \" context\" << std::endl;\n+\t\tEPR(\"Option {} requires a {} context\\n\",\n+\t\t    option.optionName(), error->optionName());\n \t\treturn false;\n \t}\n \n \tif (!options->parseValue(option.opt, option, arg)) {\n-\t\tstd::cerr << \"Can't parse \" << option.typeName()\n-\t\t\t  << \" argument for option \" << option.optionName()\n-\t\t\t  << std::endl;\n+\t\tEPR(\"Can't parse {} argument for option {}\\n\",\n+\t\t    option.typeName(), option.optionName());\n \t\treturn false;\n \t}\n \ndiff --git a/src/cam/stream_options.cpp b/src/cam/stream_options.cpp\nindex 150bd27c..666862eb 100644\n--- a/src/cam/stream_options.cpp\n+++ b/src/cam/stream_options.cpp\n@@ -6,7 +6,10 @@\n  */\n #include \"stream_options.h\"\n \n-#include <iostream>\n+#include <fmt/core.h>\n+\n+#define PR(...) fmt::print(__VA_ARGS__)\n+#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n \n using namespace libcamera;\n \n@@ -30,8 +33,7 @@ KeyValueParser::Options StreamKeyValueParser::parse(const char *arguments)\n \n \tif (options.valid() && options.isSet(\"role\") &&\n \t    !parseRole(&role, options)) {\n-\t\tstd::cerr << \"Unknown stream role \"\n-\t\t\t  << options[\"role\"].toString() << std::endl;\n+\t\tEPR(\"Unknown stream role {}\\n\", options[\"role\"].toString());\n \t\toptions.invalidate();\n \t}\n \n@@ -64,7 +66,7 @@ int StreamKeyValueParser::updateConfiguration(CameraConfiguration *config,\n \t\t\t\t\t      const OptionValue &values)\n {\n \tif (!config) {\n-\t\tstd::cerr << \"No configuration provided\" << std::endl;\n+\t\tEPR(\"No configuration provided\\n\");\n \t\treturn -EINVAL;\n \t}\n \n@@ -75,12 +77,8 @@ int StreamKeyValueParser::updateConfiguration(CameraConfiguration *config,\n \tconst std::vector<OptionValue> &streamParameters = values.toArray();\n \n \tif (config->size() != streamParameters.size()) {\n-\t\tstd::cerr\n-\t\t\t<< \"Number of streams in configuration \"\n-\t\t\t<< config->size()\n-\t\t\t<< \" does not match number of streams parsed \"\n-\t\t\t<< streamParameters.size()\n-\t\t\t<< std::endl;\n+\t\tEPR(\"Number of streams in configuration {} does not match number of streams parsed {}\\n\",\n+\t\t    config->size(), streamParameters.size());\n \t\treturn -EINVAL;\n \t}\n \n",
    "prefixes": [
        "libcamera-devel"
    ]
}