[libcamera-devel] [PATCH] cam: convert to libfmt

Tomi Valkeinen tomi.valkeinen at ideasonboard.com
Tue May 10 09:16:17 CEST 2022


This is just a conversation starter, not for merging. I really like
libfmt, and I really hate the C++ stream-based string formatting. libfmt
is the first library I add to any new C++ project I make.

You can find more information about libfmt from:

https://github.com/fmtlib/fmt
https://fmt.dev/latest/index.html

This patch is just a crude conversion with ugly macros to showcase what
the formatting code might look like.

libfmt pages suggest that libfmt would be faster and smaller (exe size)
than iostreams, but for the size it didn't seem to be true in cam's case
as the tests below show. However, simple prints did reduce the exe size,
but the few more complex ones increased the size.

Size tests with gcc 11.2.0-19ubuntu1

- Without libfmt

debug		3523400
debug lto	3269368
release		223056
release lto	172280

- With libfmt

debug		4424256
debug lto	4143840
release		303952
release lto	252640

Above shows that cam's size clearly increases with libfmt. However, the
increase really comes only from one case, the use of fmt::memory_buffer
and std::back_inserter. Converting that code to use fmt::format() and
naive string append gives:

release with string append	233680
release lto with string append	186936

Also, if I add another use of fmt::memory_buffer and std::back_inserter
to another file, I see much less increase in the size:

release lto with two uses of memory_buffer, back_inserter	256736

Signed-off-by: Tomi Valkeinen <tomi.valkeinen at ideasonboard.com>
---
 src/cam/camera_session.cpp | 105 ++++++++++++++++---------------------
 src/cam/drm.cpp            |  68 ++++++++----------------
 src/cam/event_loop.cpp     |   9 ++--
 src/cam/file_sink.cpp      |  31 +++++------
 src/cam/image.cpp          |  15 +++---
 src/cam/kms_sink.cpp       |  38 ++++++--------
 src/cam/main.cpp           |  28 +++++-----
 src/cam/meson.build        |   5 +-
 src/cam/options.cpp        |  66 ++++++++++-------------
 src/cam/stream_options.cpp |  18 +++----
 10 files changed, 165 insertions(+), 218 deletions(-)

diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp
index efffafbf..7843c3fd 100644
--- a/src/cam/camera_session.cpp
+++ b/src/cam/camera_session.cpp
@@ -5,10 +5,9 @@
  * camera_session.cpp - Camera capture session
  */
 
-#include <iomanip>
-#include <iostream>
 #include <limits.h>
-#include <sstream>
+#include <fmt/format.h>
+#include <fmt/ostream.h>
 
 #include <libcamera/control_ids.h>
 #include <libcamera/property_ids.h>
@@ -22,6 +21,9 @@
 #include "main.h"
 #include "stream_options.h"
 
+#define PR(...) fmt::print(__VA_ARGS__)
+#define EPR(...) fmt::print(stderr, __VA_ARGS__)
+
 using namespace libcamera;
 
 CameraSession::CameraSession(CameraManager *cm,
@@ -40,13 +42,12 @@ CameraSession::CameraSession(CameraManager *cm,
 		camera_ = cm->get(cameraId);
 
 	if (!camera_) {
-		std::cerr << "Camera " << cameraId << " not found" << std::endl;
+		EPR("Camera {} not found\n", cameraId);
 		return;
 	}
 
 	if (camera_->acquire()) {
-		std::cerr << "Failed to acquire camera " << cameraId
-			  << std::endl;
+		EPR("Failed to acquire camera {}", cameraId);
 		return;
 	}
 
@@ -55,15 +56,14 @@ CameraSession::CameraSession(CameraManager *cm,
 	std::unique_ptr<CameraConfiguration> config =
 		camera_->generateConfiguration(roles);
 	if (!config || config->size() != roles.size()) {
-		std::cerr << "Failed to get default stream configuration"
-			  << std::endl;
+		EPR("Failed to get default stream configuration\n");
 		return;
 	}
 
 	/* Apply configuration if explicitly requested. */
 	if (StreamKeyValueParser::updateConfiguration(config.get(),
 						      options_[OptStream])) {
-		std::cerr << "Failed to update configuration" << std::endl;
+		EPR("Failed to update configuration\n");
 		return;
 	}
 
@@ -72,20 +72,17 @@ CameraSession::CameraSession(CameraManager *cm,
 #ifdef HAVE_KMS
 	if (options_.isSet(OptDisplay)) {
 		if (options_.isSet(OptFile)) {
-			std::cerr << "--display and --file options are mutually exclusive"
-				  << std::endl;
+			EPR("--display and --file options are mutually exclusive\n");
 			return;
 		}
 
 		if (roles.size() != 1) {
-			std::cerr << "Display doesn't support multiple streams"
-				  << std::endl;
+			EPR("Display doesn't support multiple streams\n");
 			return;
 		}
 
 		if (roles[0] != StreamRole::Viewfinder) {
-			std::cerr << "Display requires a viewfinder stream"
-				  << std::endl;
+			EPR("Display requires a viewfinder stream\n");
 			return;
 		}
 	}
@@ -97,15 +94,14 @@ CameraSession::CameraSession(CameraManager *cm,
 
 	case CameraConfiguration::Adjusted:
 		if (strictFormats) {
-			std::cout << "Adjusting camera configuration disallowed by --strict-formats argument"
-				  << std::endl;
+			PR("Adjusting camera configuration disallowed by --strict-formats argument\n");
 			return;
 		}
-		std::cout << "Camera configuration adjusted" << std::endl;
+		PR("Camera configuration adjusted\n");
 		break;
 
 	case CameraConfiguration::Invalid:
-		std::cout << "Camera configuration invalid" << std::endl;
+		PR("Camera configuration invalid\n");
 		return;
 	}
 
@@ -121,8 +117,7 @@ CameraSession::~CameraSession()
 void CameraSession::listControls() const
 {
 	for (const auto &[id, info] : camera_->controls()) {
-		std::cout << "Control: " << id->name() << ": "
-			  << info.toString() << std::endl;
+		PR("Control: {}: {}}n", id->name(), info.toString());
 	}
 }
 
@@ -131,8 +126,7 @@ void CameraSession::listProperties() const
 	for (const auto &[key, value] : camera_->properties()) {
 		const ControlId *id = properties::properties.at(key);
 
-		std::cout << "Property: " << id->name() << " = "
-			  << value.toString() << std::endl;
+		PR("Property: {} = {}\n", id->name(), value.toString());
 	}
 }
 
@@ -140,17 +134,15 @@ void CameraSession::infoConfiguration() const
 {
 	unsigned int index = 0;
 	for (const StreamConfiguration &cfg : *config_) {
-		std::cout << index << ": " << cfg.toString() << std::endl;
+		PR("{}: {}\n", index, cfg.toString());
 
 		const StreamFormats &formats = cfg.formats();
 		for (PixelFormat pixelformat : formats.pixelformats()) {
-			std::cout << " * Pixelformat: "
-				  << pixelformat << " "
-				  << formats.range(pixelformat).toString()
-				  << std::endl;
+			PR(" * Pixelformat: {} {}\n", pixelformat,
+			   formats.range(pixelformat).toString());
 
 			for (const Size &size : formats.sizes(pixelformat))
-				std::cout << "  - " << size << std::endl;
+				PR("  - {}\n", size);
 		}
 
 		index++;
@@ -168,7 +160,7 @@ int CameraSession::start()
 
 	ret = camera_->configure(config_.get());
 	if (ret < 0) {
-		std::cout << "Failed to configure camera" << std::endl;
+		PR("Failed to configure camera\n");
 		return ret;
 	}
 
@@ -197,8 +189,7 @@ int CameraSession::start()
 	if (sink_) {
 		ret = sink_->configure(*config_);
 		if (ret < 0) {
-			std::cout << "Failed to configure frame sink"
-				  << std::endl;
+			PR("Failed to configure frame sink\n");
 			return ret;
 		}
 
@@ -214,12 +205,12 @@ void CameraSession::stop()
 {
 	int ret = camera_->stop();
 	if (ret)
-		std::cout << "Failed to stop capture" << std::endl;
+		PR("Failed to stop capture\n");
 
 	if (sink_) {
 		ret = sink_->stop();
 		if (ret)
-			std::cout << "Failed to stop frame sink" << std::endl;
+			PR("Failed to stop frame sink\n");
 	}
 
 	sink_.reset();
@@ -238,7 +229,7 @@ int CameraSession::startCapture()
 	for (StreamConfiguration &cfg : *config_) {
 		ret = allocator_->allocate(cfg.stream());
 		if (ret < 0) {
-			std::cerr << "Can't allocate buffers" << std::endl;
+			EPR("Can't allocate buffers\n");
 			return -ENOMEM;
 		}
 
@@ -254,7 +245,7 @@ int CameraSession::startCapture()
 	for (unsigned int i = 0; i < nbuffers; i++) {
 		std::unique_ptr<Request> request = camera_->createRequest();
 		if (!request) {
-			std::cerr << "Can't create request" << std::endl;
+			EPR("Can't create request\n");
 			return -ENOMEM;
 		}
 
@@ -266,8 +257,7 @@ int CameraSession::startCapture()
 
 			ret = request->addBuffer(stream, buffer.get());
 			if (ret < 0) {
-				std::cerr << "Can't set buffer for request"
-					  << std::endl;
+				EPR("Can't set buffer for request\n");
 				return ret;
 			}
 
@@ -281,14 +271,14 @@ int CameraSession::startCapture()
 	if (sink_) {
 		ret = sink_->start();
 		if (ret) {
-			std::cout << "Failed to start frame sink" << std::endl;
+			PR("Failed to start frame sink\n");
 			return ret;
 		}
 	}
 
 	ret = camera_->start();
 	if (ret) {
-		std::cout << "Failed to start capture" << std::endl;
+		PR("Failed to start capture\n");
 		if (sink_)
 			sink_->stop();
 		return ret;
@@ -297,7 +287,7 @@ int CameraSession::startCapture()
 	for (std::unique_ptr<Request> &request : requests_) {
 		ret = queueRequest(request.get());
 		if (ret < 0) {
-			std::cerr << "Can't queue request" << std::endl;
+			EPR("Can't queue request\n");
 			camera_->stop();
 			if (sink_)
 				sink_->stop();
@@ -306,13 +296,11 @@ int CameraSession::startCapture()
 	}
 
 	if (captureLimit_)
-		std::cout << "cam" << cameraIndex_
-			  << ": Capture " << captureLimit_ << " frames"
-			  << std::endl;
+		PR("cam{}: Capture {} frames\n", cameraIndex_,
+		   captureLimit_);
 	else
-		std::cout << "cam" << cameraIndex_
-			  << ": Capture until user interrupts by SIGINT"
-			  << std::endl;
+		PR("cam{}: Capture until user interrupts by SIGINT\n",
+		   cameraIndex_);
 
 	return 0;
 }
@@ -364,23 +352,23 @@ void CameraSession::processRequest(Request *request)
 
 	bool requeue = true;
 
-	std::stringstream info;
-	info << ts / 1000000000 << "."
-	     << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000
-	     << " (" << std::fixed << std::setprecision(2) << fps << " fps)";
+	auto sbuf = fmt::memory_buffer();
+	fmt::format_to(std::back_inserter(sbuf), "{}.{:06} ({:.2f} fps)",
+		       ts / 1000000000,
+		       ts / 1000 % 1000000,
+		       fps);
 
 	for (const auto &[stream, buffer] : buffers) {
 		const FrameMetadata &metadata = buffer->metadata();
 
-		info << " " << streamNames_[stream]
-		     << " seq: " << std::setw(6) << std::setfill('0') << metadata.sequence
-		     << " bytesused: ";
+		fmt::format_to(std::back_inserter(sbuf), " {} seq: {:06} bytesused: ",
+			       streamNames_[stream], metadata.sequence);
 
 		unsigned int nplane = 0;
 		for (const FrameMetadata::Plane &plane : metadata.planes()) {
-			info << plane.bytesused;
+			fmt::format_to(std::back_inserter(sbuf), "{}", plane.bytesused);
 			if (++nplane < metadata.planes().size())
-				info << "/";
+				fmt::format_to(std::back_inserter(sbuf), "/");
 		}
 	}
 
@@ -389,14 +377,13 @@ void CameraSession::processRequest(Request *request)
 			requeue = false;
 	}
 
-	std::cout << info.str() << std::endl;
+	PR("{}\n", fmt::to_string(sbuf));
 
 	if (printMetadata_) {
 		const ControlList &requestMetadata = request->metadata();
 		for (const auto &[key, value] : requestMetadata) {
 			const ControlId *id = controls::controls.at(key);
-			std::cout << "\t" << id->name() << " = "
-				  << value.toString() << std::endl;
+			PR("\t{} = {}\n", id->name(), value.toString());
 		}
 	}
 
diff --git a/src/cam/drm.cpp b/src/cam/drm.cpp
index 46e34eb5..84919ab3 100644
--- a/src/cam/drm.cpp
+++ b/src/cam/drm.cpp
@@ -10,12 +10,12 @@
 #include <algorithm>
 #include <errno.h>
 #include <fcntl.h>
-#include <iostream>
 #include <set>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <fmt/core.h>
 
 #include <libcamera/framebuffer.h>
 #include <libcamera/geometry.h>
@@ -25,6 +25,9 @@
 
 #include "event_loop.h"
 
+#define PR(...) fmt::print(__VA_ARGS__)
+#define EPR(...) fmt::print(stderr, __VA_ARGS__)
+
 namespace DRM {
 
 Object::Object(Device *dev, uint32_t id, Type type)
@@ -178,9 +181,7 @@ Connector::Connector(Device *dev, const drmModeConnector *connector)
 {
 	auto typeName = connectorTypeNames.find(connector->connector_type);
 	if (typeName == connectorTypeNames.end()) {
-		std::cerr
-			<< "Invalid connector type "
-			<< connector->connector_type << std::endl;
+		EPR("Invalid connector type {}}n", connector->connector_type);
 		typeName = connectorTypeNames.find(DRM_MODE_CONNECTOR_Unknown);
 	}
 
@@ -213,9 +214,7 @@ Connector::Connector(Device *dev, const drmModeConnector *connector)
 						    return e.id() == encoderId;
 					    });
 		if (encoder == encoders.end()) {
-			std::cerr
-				<< "Encoder " << encoderId << " not found"
-				<< std::endl;
+			EPR("Encoder {} not found\n", encoderId);
 			continue;
 		}
 
@@ -296,9 +295,7 @@ FrameBuffer::~FrameBuffer()
 
 		if (ret == -1) {
 			ret = -errno;
-			std::cerr
-				<< "Failed to close GEM object: "
-				<< strerror(-ret) << std::endl;
+			EPR("Failed to close GEM object: {}\n", strerror(-ret));
 		}
 	}
 
@@ -408,9 +405,8 @@ int Device::init()
 	fd_ = open(name, O_RDWR | O_CLOEXEC);
 	if (fd_ < 0) {
 		ret = -errno;
-		std::cerr
-			<< "Failed to open DRM/KMS device " << name << ": "
-			<< strerror(-ret) << std::endl;
+		EPR("Failed to open DRM/KMS device {}: {}\n", name,
+		    strerror(-ret));
 		return ret;
 	}
 
@@ -421,9 +417,7 @@ int Device::init()
 	ret = drmSetClientCap(fd_, DRM_CLIENT_CAP_ATOMIC, 1);
 	if (ret < 0) {
 		ret = -errno;
-		std::cerr
-			<< "Failed to enable atomic capability: "
-			<< strerror(-ret) << std::endl;
+		EPR("Failed to enable atomic capability: {}\n", strerror(-ret));
 		return ret;
 	}
 
@@ -448,9 +442,7 @@ int Device::getResources()
 	};
 	if (!resources) {
 		ret = -errno;
-		std::cerr
-			<< "Failed to get DRM/KMS resources: "
-			<< strerror(-ret) << std::endl;
+		EPR("Failed to get DRM/KMS resources: {}\n", strerror(-ret));
 		return ret;
 	}
 
@@ -458,9 +450,7 @@ int Device::getResources()
 		drmModeCrtc *crtc = drmModeGetCrtc(fd_, resources->crtcs[i]);
 		if (!crtc) {
 			ret = -errno;
-			std::cerr
-				<< "Failed to get CRTC: " << strerror(-ret)
-				<< std::endl;
+			EPR("Failed to get CRTC: {}\n", strerror(-ret));
 			return ret;
 		}
 
@@ -476,9 +466,7 @@ int Device::getResources()
 			drmModeGetEncoder(fd_, resources->encoders[i]);
 		if (!encoder) {
 			ret = -errno;
-			std::cerr
-				<< "Failed to get encoder: " << strerror(-ret)
-				<< std::endl;
+			EPR("Failed to get encoder: {}\n", strerror(-ret));
 			return ret;
 		}
 
@@ -494,9 +482,7 @@ int Device::getResources()
 			drmModeGetConnector(fd_, resources->connectors[i]);
 		if (!connector) {
 			ret = -errno;
-			std::cerr
-				<< "Failed to get connector: " << strerror(-ret)
-				<< std::endl;
+			EPR("Failed to get connector: {}\n", strerror(-ret));
 			return ret;
 		}
 
@@ -513,9 +499,7 @@ int Device::getResources()
 	};
 	if (!planes) {
 		ret = -errno;
-		std::cerr
-			<< "Failed to get DRM/KMS planes: "
-			<< strerror(-ret) << std::endl;
+		EPR("Failed to get DRM/KMS planes: {}\n", strerror(-ret));
 		return ret;
 	}
 
@@ -524,9 +508,7 @@ int Device::getResources()
 			drmModeGetPlane(fd_, planes->planes[i]);
 		if (!plane) {
 			ret = -errno;
-			std::cerr
-				<< "Failed to get plane: " << strerror(-ret)
-				<< std::endl;
+			EPR("Failed to get plane: {}\n", strerror(-ret));
 			return ret;
 		}
 
@@ -556,9 +538,7 @@ int Device::getResources()
 		drmModePropertyRes *property = drmModeGetProperty(fd_, id);
 		if (!property) {
 			ret = -errno;
-			std::cerr
-				<< "Failed to get property: " << strerror(-ret)
-				<< std::endl;
+			EPR("Failed to get property: {}\n", strerror(-ret));
 			continue;
 		}
 
@@ -573,9 +553,8 @@ int Device::getResources()
 	for (auto &object : objects_) {
 		ret = object.second->setup();
 		if (ret < 0) {
-			std::cerr
-				<< "Failed to setup object " << object.second->id()
-				<< ": " << strerror(-ret) << std::endl;
+			EPR("Failed to setup object {}: {}\n",
+			    object.second->id(), strerror(-ret));
 			return ret;
 		}
 	}
@@ -616,9 +595,8 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer(
 			ret = drmPrimeFDToHandle(fd_, plane.fd.get(), &handle);
 			if (ret < 0) {
 				ret = -errno;
-				std::cerr
-					<< "Unable to import framebuffer dmabuf: "
-					<< strerror(-ret) << std::endl;
+				EPR("Unable to import framebuffer dmabuf: {}\n",
+				    strerror(-ret));
 				return nullptr;
 			}
 
@@ -636,9 +614,7 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer(
 			    strides.data(), offsets, &fb->id_, 0);
 	if (ret < 0) {
 		ret = -errno;
-		std::cerr
-			<< "Failed to add framebuffer: "
-			<< strerror(-ret) << std::endl;
+		EPR("Failed to add framebuffer: {}\n", strerror(-ret));
 		return nullptr;
 	}
 
diff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp
index e25784c0..87aaf59a 100644
--- a/src/cam/event_loop.cpp
+++ b/src/cam/event_loop.cpp
@@ -10,7 +10,10 @@
 #include <assert.h>
 #include <event2/event.h>
 #include <event2/thread.h>
-#include <iostream>
+#include <fmt/core.h>
+
+#define PR(...) fmt::print(__VA_ARGS__)
+#define EPR(...) fmt::print(stderr, __VA_ARGS__)
 
 EventLoop *EventLoop::instance_ = nullptr;
 
@@ -71,13 +74,13 @@ void EventLoop::addEvent(int fd, EventType type,
 	event->event_ = event_new(base_, fd, events, &EventLoop::Event::dispatch,
 				  event.get());
 	if (!event->event_) {
-		std::cerr << "Failed to create event for fd " << fd << std::endl;
+		EPR("Failed to create event for fd {}\n", fd);
 		return;
 	}
 
 	int ret = event_add(event->event_, nullptr);
 	if (ret < 0) {
-		std::cerr << "Failed to add event for fd " << fd << std::endl;
+		EPR("Failed to add event for fd {}\n", fd);
 		return;
 	}
 
diff --git a/src/cam/file_sink.cpp b/src/cam/file_sink.cpp
index 45213d4a..86e2118c 100644
--- a/src/cam/file_sink.cpp
+++ b/src/cam/file_sink.cpp
@@ -7,11 +7,12 @@
 
 #include <assert.h>
 #include <fcntl.h>
-#include <iomanip>
-#include <iostream>
-#include <sstream>
 #include <string.h>
 #include <unistd.h>
+#include <fmt/core.h>
+
+#define PR(...) fmt::print(__VA_ARGS__)
+#define EPR(...) fmt::print(stderr, __VA_ARGS__)
 
 #include <libcamera/camera.h>
 
@@ -70,10 +71,10 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer)
 
 	pos = filename.find_first_of('#');
 	if (pos != std::string::npos) {
-		std::stringstream ss;
-		ss << streamNames_[stream] << "-" << std::setw(6)
-		   << std::setfill('0') << buffer->metadata().sequence;
-		filename.replace(pos, 1, ss.str());
+		auto s = fmt::format("{}-{:06}",
+				     streamNames_[stream],
+				     buffer->metadata().sequence);
+		filename.replace(pos, 1, s);
 	}
 
 	fd = open(filename.c_str(), O_CREAT | O_WRONLY |
@@ -81,8 +82,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer)
 		  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
 	if (fd == -1) {
 		ret = -errno;
-		std::cerr << "failed to open file " << filename << ": "
-			  << strerror(-ret) << std::endl;
+		EPR("failed to open file {}: {}\n", filename, strerror(-ret));
 		return;
 	}
 
@@ -95,20 +95,17 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer)
 		unsigned int length = std::min<unsigned int>(meta.bytesused, data.size());
 
 		if (meta.bytesused > data.size())
-			std::cerr << "payload size " << meta.bytesused
-				  << " larger than plane size " << data.size()
-				  << std::endl;
+			EPR("payload size {} larger than plane size {}\n",
+			    meta.bytesused, data.size());
 
 		ret = ::write(fd, data.data(), length);
 		if (ret < 0) {
 			ret = -errno;
-			std::cerr << "write error: " << strerror(-ret)
-				  << std::endl;
+			EPR("write error: {}\n", strerror(-ret));
 			break;
 		} else if (ret != (int)length) {
-			std::cerr << "write error: only " << ret
-				  << " bytes written instead of "
-				  << length << std::endl;
+			EPR("write error: only {} bytes written instead of {}\n",
+			    ret, length);
 			break;
 		}
 	}
diff --git a/src/cam/image.cpp b/src/cam/image.cpp
index fe2cc6da..73bcf915 100644
--- a/src/cam/image.cpp
+++ b/src/cam/image.cpp
@@ -9,11 +9,14 @@
 
 #include <assert.h>
 #include <errno.h>
-#include <iostream>
 #include <map>
 #include <string.h>
 #include <sys/mman.h>
 #include <unistd.h>
+#include <fmt/core.h>
+
+#define PR(...) fmt::print(__VA_ARGS__)
+#define EPR(...) fmt::print(stderr, __VA_ARGS__)
 
 using namespace libcamera;
 
@@ -49,10 +52,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const FrameBuffer *buffer, MapMode
 
 		if (plane.offset > length ||
 		    plane.offset + plane.length > length) {
-			std::cerr << "plane is out of buffer: buffer length="
-				  << length << ", plane offset=" << plane.offset
-				  << ", plane length=" << plane.length
-				  << std::endl;
+			EPR("plane is out of buffer: buffer length={}, plane offset={}, plane length={}\n",
+			    length, plane.offset, plane.length);
 			return nullptr;
 		}
 		size_t &mapLength = mappedBuffers[fd].mapLength;
@@ -68,8 +69,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const FrameBuffer *buffer, MapMode
 					     MAP_SHARED, fd, 0);
 			if (address == MAP_FAILED) {
 				int error = -errno;
-				std::cerr << "Failed to mmap plane: "
-					  << strerror(-error) << std::endl;
+				EPR("Failed to mmap plane: {}\n",
+				    strerror(-error));
 				return nullptr;
 			}
 
diff --git a/src/cam/kms_sink.cpp b/src/cam/kms_sink.cpp
index 7add81a6..823b75e4 100644
--- a/src/cam/kms_sink.cpp
+++ b/src/cam/kms_sink.cpp
@@ -10,10 +10,13 @@
 #include <array>
 #include <algorithm>
 #include <assert.h>
-#include <iostream>
 #include <memory>
 #include <stdint.h>
 #include <string.h>
+#include <fmt/core.h>
+
+#define PR(...) fmt::print(__VA_ARGS__)
+#define EPR(...) fmt::print(stderr, __VA_ARGS__)
 
 #include <libcamera/camera.h>
 #include <libcamera/formats.h>
@@ -54,11 +57,9 @@ KMSSink::KMSSink(const std::string &connectorName)
 
 	if (!connector_) {
 		if (!connectorName.empty())
-			std::cerr
-				<< "Connector " << connectorName << " not found"
-				<< std::endl;
+			EPR("Connector {} not found\n", connectorName);
 		else
-			std::cerr << "No connected connector found" << std::endl;
+			EPR("No connected connector found\n");
 		return;
 	}
 
@@ -119,7 +120,7 @@ int KMSSink::configure(const libcamera::CameraConfiguration &config)
 						      mode.vdisplay == cfg.size.height;
 				       });
 	if (iter == modes.end()) {
-		std::cerr << "No mode matching " << cfg.size << std::endl;
+		EPR("No mode matching {}\n", cfg.size);
 		return -EINVAL;
 	}
 
@@ -192,17 +193,12 @@ int KMSSink::configurePipeline(const libcamera::PixelFormat &format)
 {
 	const int ret = selectPipeline(format);
 	if (ret) {
-		std::cerr
-			<< "Unable to find display pipeline for format "
-			<< format << std::endl;
-
+		EPR("Unable to find display pipeline for format {}\n", format);
 		return ret;
 	}
 
-	std::cout
-		<< "Using KMS plane " << plane_->id() << ", CRTC " << crtc_->id()
-		<< ", connector " << connector_->name()
-		<< " (" << connector_->id() << ")" << std::endl;
+	PR("Using KMS plane {}, CRTC {}, connector {} ({})\n",
+	   plane_->id(), crtc_->id(), connector_->name(), connector_->id());
 
 	return 0;
 }
@@ -228,9 +224,8 @@ int KMSSink::start()
 
 	ret = request->commit(DRM::AtomicRequest::FlagAllowModeset);
 	if (ret < 0) {
-		std::cerr
-			<< "Failed to disable CRTCs and planes: "
-			<< strerror(-ret) << std::endl;
+		EPR("Failed to disable CRTCs and planes: {}\n",
+		    strerror(-ret));
 		return ret;
 	}
 
@@ -250,9 +245,7 @@ int KMSSink::stop()
 
 	int ret = request.commit(DRM::AtomicRequest::FlagAllowModeset);
 	if (ret < 0) {
-		std::cerr
-			<< "Failed to stop display pipeline: "
-			<< strerror(-ret) << std::endl;
+		EPR("Failed to stop display pipeline: {}\n", strerror(-ret));
 		return ret;
 	}
 
@@ -312,9 +305,8 @@ bool KMSSink::processRequest(libcamera::Request *camRequest)
 	if (!queued_) {
 		int ret = drmRequest->commit(flags);
 		if (ret < 0) {
-			std::cerr
-				<< "Failed to commit atomic request: "
-				<< strerror(-ret) << std::endl;
+			EPR("Failed to commit atomic request: {}\n",
+			    strerror(-ret));
 			/* \todo Implement error handling */
 		}
 
diff --git a/src/cam/main.cpp b/src/cam/main.cpp
index c7f664b9..03615dc9 100644
--- a/src/cam/main.cpp
+++ b/src/cam/main.cpp
@@ -6,10 +6,9 @@
  */
 
 #include <atomic>
-#include <iomanip>
-#include <iostream>
 #include <signal.h>
 #include <string.h>
+#include <fmt/core.h>
 
 #include <libcamera/libcamera.h>
 #include <libcamera/property_ids.h>
@@ -78,8 +77,7 @@ int CamApp::init(int argc, char **argv)
 
 	ret = cm_->start();
 	if (ret) {
-		std::cout << "Failed to start camera manager: "
-			  << strerror(-ret) << std::endl;
+		fmt::print("Failed to start camera manager: {}\n", -ret);
 		return ret;
 	}
 
@@ -173,12 +171,12 @@ int CamApp::parseOptions(int argc, char *argv[])
 
 void CamApp::cameraAdded(std::shared_ptr<Camera> cam)
 {
-	std::cout << "Camera Added: " << cam->id() << std::endl;
+	fmt::print("Camera Added: {}\n", cam->id());
 }
 
 void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)
 {
-	std::cout << "Camera Removed: " << cam->id() << std::endl;
+	fmt::print("Camera Removed: {}\n", cam->id());
 }
 
 void CamApp::captureDone()
@@ -193,11 +191,11 @@ int CamApp::run()
 
 	/* 1. List all cameras. */
 	if (options_.isSet(OptList)) {
-		std::cout << "Available cameras:" << std::endl;
+		fmt::print("Available cameras:\n");
 
 		unsigned int index = 1;
 		for (const std::shared_ptr<Camera> &cam : cm_->cameras()) {
-			std::cout << index << ": " << cameraName(cam.get()) << std::endl;
+			fmt::print("{}: {}\n", cameraName(cam.get()), index);
 			index++;
 		}
 	}
@@ -215,12 +213,12 @@ int CamApp::run()
 								index,
 								camera.children());
 			if (!session->isValid()) {
-				std::cout << "Failed to create camera session" << std::endl;
+				fmt::print("Failed to create camera session\n");
 				return -EINVAL;
 			}
 
-			std::cout << "Using camera " << session->camera()->id()
-				  << " as cam" << index << std::endl;
+			fmt::print("Using camera{} as cam{}\n",
+				   session->camera()->id(), index);
 
 			session->captureDone.connect(this, &CamApp::captureDone);
 
@@ -250,7 +248,7 @@ int CamApp::run()
 
 		ret = session->start();
 		if (ret) {
-			std::cout << "Failed to start camera session" << std::endl;
+			fmt::print("Failed to start camera session\n");
 			return ret;
 		}
 
@@ -259,8 +257,8 @@ int CamApp::run()
 
 	/* 5. Enable hotplug monitoring. */
 	if (options_.isSet(OptMonitor)) {
-		std::cout << "Monitoring new hotplug and unplug events" << std::endl;
-		std::cout << "Press Ctrl-C to interrupt" << std::endl;
+		fmt::print("Monitoring new hotplug and unplug events\n");
+		fmt::print("Press Ctrl-C to interrupt\n");
 
 		cm_->cameraAdded.connect(this, &CamApp::cameraAdded);
 		cm_->cameraRemoved.connect(this, &CamApp::cameraRemoved);
@@ -323,7 +321,7 @@ std::string CamApp::cameraName(const Camera *camera)
 
 void signalHandler([[maybe_unused]] int signal)
 {
-	std::cout << "Exiting" << std::endl;
+	fmt::print("Exiting");
 	CamApp::instance()->quit();
 }
 
diff --git a/src/cam/meson.build b/src/cam/meson.build
index 5bab8c9e..2b47383d 100644
--- a/src/cam/meson.build
+++ b/src/cam/meson.build
@@ -7,6 +7,8 @@ if not libevent.found()
     subdir_done()
 endif
 
+libfmt_dep = dependency('fmt')
+
 cam_enabled = true
 
 cam_sources = files([
@@ -25,7 +27,7 @@ cam_cpp_args = []
 libdrm = dependency('libdrm', required : false)
 
 if libdrm.found()
-    cam_cpp_args += [ '-DHAVE_KMS' ]
+    cam_cpp_args += [ '-DHAVE_KMS', ]
     cam_sources += files([
         'drm.cpp',
         'kms_sink.cpp'
@@ -38,6 +40,7 @@ cam  = executable('cam', cam_sources,
                       libcamera_public,
                       libdrm,
                       libevent,
+                      libfmt_dep,
                   ],
                   cpp_args : cam_cpp_args,
                   install : true)
diff --git a/src/cam/options.cpp b/src/cam/options.cpp
index 4f7e8691..c9979385 100644
--- a/src/cam/options.cpp
+++ b/src/cam/options.cpp
@@ -7,9 +7,11 @@
 
 #include <assert.h>
 #include <getopt.h>
-#include <iomanip>
-#include <iostream>
 #include <string.h>
+#include <fmt/core.h>
+
+#define PR(...) fmt::print(__VA_ARGS__)
+#define EPR(...) fmt::print(stderr, __VA_ARGS__)
 
 #include "options.h"
 
@@ -390,26 +392,23 @@ KeyValueParser::Options KeyValueParser::parse(const char *arguments)
 			continue;
 
 		if (optionsMap_.find(key) == optionsMap_.end()) {
-			std::cerr << "Invalid option " << key << std::endl;
+			EPR("Invalid option {}\n", key);
 			return options;
 		}
 
 		OptionArgument arg = optionsMap_[key].argument;
 		if (value.empty() && arg == ArgumentRequired) {
-			std::cerr << "Option " << key << " requires an argument"
-				  << std::endl;
+			EPR("Option {} requires an argument\n", key);
 			return options;
 		} else if (!value.empty() && arg == ArgumentNone) {
-			std::cerr << "Option " << key << " takes no argument"
-				  << std::endl;
+			EPR("Option {} takes no argument\n", key);
 			return options;
 		}
 
 		const Option &option = optionsMap_[key];
 		if (!options.parseValue(key, option, value.c_str())) {
-			std::cerr << "Failed to parse '" << value << "' as "
-				  << option.typeName() << " for option " << key
-				  << std::endl;
+			EPR("Failed to parse '{}' as {} for option {}\n",
+			    value, option.typeName(), key);
 			return options;
 		}
 	}
@@ -453,16 +452,16 @@ void KeyValueParser::usage(int indent)
 				argument += "]";
 		}
 
-		std::cerr << std::setw(indent) << argument;
+		EPR("{:{}}", argument, indent);
 
 		for (const char *help = option.help, *end = help; end;) {
 			end = strchr(help, '\n');
 			if (end) {
-				std::cerr << std::string(help, end - help + 1);
-				std::cerr << std::setw(indent) << " ";
+				EPR(std::string(help, end - help + 1));
+				EPR("{:{}}", "", indent);
 				help = end + 1;
 			} else {
-				std::cerr << help << std::endl;
+				EPR("{}\n", help);
 			}
 		}
 	}
@@ -929,10 +928,10 @@ OptionsParser::Options OptionsParser::parse(int argc, char **argv)
 
 		if (c == '?' || c == ':') {
 			if (c == '?')
-				std::cerr << "Invalid option ";
+				EPR("Invalid option ");
 			else
-				std::cerr << "Missing argument for option ";
-			std::cerr << argv[optind - 1] << std::endl;
+				EPR("Missing argument for option ");
+			EPR("{}\n", argv[optind - 1]);
 
 			usage();
 			return options;
@@ -946,8 +945,7 @@ OptionsParser::Options OptionsParser::parse(int argc, char **argv)
 	}
 
 	if (optind < argc) {
-		std::cerr << "Invalid non-option argument '" << argv[optind]
-			  << "'" << std::endl;
+		EPR("Invalid non-option argument '{}'\n", argv[optind]);
 		usage();
 		return options;
 	}
@@ -992,14 +990,9 @@ void OptionsParser::usage()
 
 	indent = (indent + 7) / 8 * 8;
 
-	std::cerr << "Options:" << std::endl;
-
-	std::ios_base::fmtflags f(std::cerr.flags());
-	std::cerr << std::left;
+	EPR("Options:\n");
 
 	usageOptions(options_, indent);
-
-	std::cerr.flags(f);
 }
 
 void OptionsParser::usageOptions(const std::list<Option> &options,
@@ -1036,16 +1029,16 @@ void OptionsParser::usageOptions(const std::list<Option> &options,
 		if (option.isArray)
 			argument += " ...";
 
-		std::cerr << std::setw(indent) << argument;
+		EPR("{:{}}", argument, indent);
 
-		for (const char *help = option.help, *end = help; end; ) {
+		for (const char *help = option.help, *end = help; end;) {
 			end = strchr(help, '\n');
 			if (end) {
-				std::cerr << std::string(help, end - help + 1);
-				std::cerr << std::setw(indent) << " ";
+				EPR(std::string(help, end - help + 1));
+				EPR("{:{}}", "", indent);
 				help = end + 1;
 			} else {
-				std::cerr << help << std::endl;
+				EPR("{}\n", help);
 			}
 		}
 
@@ -1060,8 +1053,8 @@ void OptionsParser::usageOptions(const std::list<Option> &options,
 		return;
 
 	for (const Option *option : parentOptions) {
-		std::cerr << std::endl << "Options valid in the context of "
-			  << option->optionName() << ":" << std::endl;
+		EPR("\nOptions valid in the context of {}:\n",
+		    option->optionName());
 		usageOptions(option->children, indent);
 	}
 }
@@ -1125,15 +1118,14 @@ bool OptionsParser::parseValue(const Option &option, const char *arg,
 
 	std::tie(options, error) = childOption(option.parent, options);
 	if (error) {
-		std::cerr << "Option " << option.optionName() << " requires a "
-			  << error->optionName() << " context" << std::endl;
+		EPR("Option {} requires a {} context\n",
+		    option.optionName(), error->optionName());
 		return false;
 	}
 
 	if (!options->parseValue(option.opt, option, arg)) {
-		std::cerr << "Can't parse " << option.typeName()
-			  << " argument for option " << option.optionName()
-			  << std::endl;
+		EPR("Can't parse {} argument for option {}\n",
+		    option.typeName(), option.optionName());
 		return false;
 	}
 
diff --git a/src/cam/stream_options.cpp b/src/cam/stream_options.cpp
index 150bd27c..666862eb 100644
--- a/src/cam/stream_options.cpp
+++ b/src/cam/stream_options.cpp
@@ -6,7 +6,10 @@
  */
 #include "stream_options.h"
 
-#include <iostream>
+#include <fmt/core.h>
+
+#define PR(...) fmt::print(__VA_ARGS__)
+#define EPR(...) fmt::print(stderr, __VA_ARGS__)
 
 using namespace libcamera;
 
@@ -30,8 +33,7 @@ KeyValueParser::Options StreamKeyValueParser::parse(const char *arguments)
 
 	if (options.valid() && options.isSet("role") &&
 	    !parseRole(&role, options)) {
-		std::cerr << "Unknown stream role "
-			  << options["role"].toString() << std::endl;
+		EPR("Unknown stream role {}\n", options["role"].toString());
 		options.invalidate();
 	}
 
@@ -64,7 +66,7 @@ int StreamKeyValueParser::updateConfiguration(CameraConfiguration *config,
 					      const OptionValue &values)
 {
 	if (!config) {
-		std::cerr << "No configuration provided" << std::endl;
+		EPR("No configuration provided\n");
 		return -EINVAL;
 	}
 
@@ -75,12 +77,8 @@ int StreamKeyValueParser::updateConfiguration(CameraConfiguration *config,
 	const std::vector<OptionValue> &streamParameters = values.toArray();
 
 	if (config->size() != streamParameters.size()) {
-		std::cerr
-			<< "Number of streams in configuration "
-			<< config->size()
-			<< " does not match number of streams parsed "
-			<< streamParameters.size()
-			<< std::endl;
+		EPR("Number of streams in configuration {} does not match number of streams parsed {}\n",
+		    config->size(), streamParameters.size());
 		return -EINVAL;
 	}
 
-- 
2.34.1



More information about the libcamera-devel mailing list