[libcamera-devel,25/27] cam: Add capture operation

Message ID 20190206060818.13907-26-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • Capture frames throught requests
Related show

Commit Message

Laurent Pinchart Feb. 6, 2019, 6:08 a.m. UTC
From: Niklas Söderlund <niklas.soderlund@ragnatech.se>

Add an option to capture frames from a camera and keep it running until
the user terminates by sending SIGINT.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 src/cam/main.cpp | 141 +++++++++++++++++++++++++++++++++++++----------
 1 file changed, 111 insertions(+), 30 deletions(-)

Patch

diff --git a/src/cam/main.cpp b/src/cam/main.cpp
index 06f9e6e16e87..ec5040e19935 100644
--- a/src/cam/main.cpp
+++ b/src/cam/main.cpp
@@ -5,6 +5,7 @@ 
  * main.cpp - cam - The libcamera swiss army knife
  */
 
+#include <iomanip>
 #include <iostream>
 #include <map>
 #include <signal.h>
@@ -18,16 +19,17 @@ 
 using namespace libcamera;
 
 OptionsParser::Options options;
+std::shared_ptr<Camera> camera;
+EventLoop *loop;
 
 enum {
 	OptCamera = 'c',
+	OptCapture = 'C',
 	OptFormat = 'f',
 	OptHelp = 'h',
 	OptList = 'l',
 };
 
-EventLoop *loop;
-
 void signalHandler(int signal)
 {
 	std::cout << "Exiting" << std::endl;
@@ -48,6 +50,8 @@  static int parseOptions(int argc, char *argv[])
 	parser.addOption(OptCamera, OptionString,
 			 "Specify which camera to operate on", "camera",
 			 ArgumentRequired, "camera");
+	parser.addOption(OptCapture, OptionNone,
+			 "Capture until interrupted by user", "capture");
 	parser.addOption(OptFormat, &formatKeyValue,
 			 "Set format of the camera's first stream", "format");
 	parser.addOption(OptHelp, OptionNone, "Display this help message",
@@ -66,30 +70,25 @@  static int parseOptions(int argc, char *argv[])
 	return 0;
 }
 
-bool configureStreams(Camera *camera, std::vector<Stream *> &streams)
+static bool configureStreams(Camera *camera, std::vector<Stream *> &streams)
 {
 	KeyValueParser::Options format = options[OptFormat];
-
-	if (streams.size() != 1) {
-		std::cout << "Camera has " << streams.size()
-			  << " streams, I only know how to work with 1"
-			  << std::endl;
-		return false;
-	}
 	Stream *id = streams.front();
 
 	std::map<Stream *, StreamConfiguration> config =
 		camera->streamConfiguration(streams);
 
-	if (format.isSet("width"))
-		config[id].width = format["width"];
+	if (options.isSet(OptFormat)) {
+		if (format.isSet("width"))
+			config[id].width = format["width"];
 
-	if (format.isSet("height"))
-		config[id].height = format["height"];
+		if (format.isSet("height"))
+			config[id].height = format["height"];
 
-	/* TODO: Translate 4CC string to ID. */
-	if (format.isSet("pixelformat"))
-		config[id].pixelFormat = format["pixelformat"];
+		/* TODO: Translate 4CC string to ID. */
+		if (format.isSet("pixelformat"))
+			config[id].pixelFormat = format["pixelformat"];
+	}
 
 	if (camera->configureStreams(config))
 		return false;
@@ -97,6 +96,89 @@  bool configureStreams(Camera *camera, std::vector<Stream *> &streams)
 	return true;
 }
 
+static void requestComplete(Request *request, const std::map<Stream *, Buffer *> &buffers)
+{
+	static uint64_t last = 0;
+
+	Buffer *buffer = buffers.begin()->second;
+
+	double fps = buffer->timestamp() - last;
+	fps = last && fps ? 1000000000.0 / fps : 0.0;
+	last = buffer->timestamp();
+
+	std::cout << "seq: " << std::setw(6) << std::setfill('0') << buffer->sequence()
+		  << " buf: " << buffer->index()
+		  << " bytesused: " << buffer->bytesused()
+		  << " timestamp: " << buffer->timestamp()
+		  << " fps: " << std::fixed << std::setprecision(2) << fps
+		  << std::endl;
+
+	request = camera->createRequest();
+	if (!request) {
+		std::cerr << "Can't create request" << std::endl;
+		return;
+	}
+
+	request->setBuffers(buffers);
+	camera->queueRequest(request);
+}
+
+static int capture()
+{
+	int ret;
+
+	std::vector<Stream *> streams = camera->streams();
+
+	ret = configureStreams(camera.get(), streams);
+	if (ret < 0) {
+		std::cout << "Failed to configure camera" << std::endl;
+		return ret;
+	}
+
+	Stream *stream = streams.front();
+
+	ret = camera->allocateBuffers();
+	if (ret) {
+		std::cerr << "Failed to allocate buffers"
+			  << std::endl;
+		return ret;
+	}
+
+	camera->requestCompleted.connect(requestComplete);
+
+	BufferPool &pool = stream->bufferPool();
+
+	for (Buffer &buffer : pool.buffers()) {
+		Request *request = camera->createRequest();
+		if (!request) {
+			std::cerr << "Can't create request" << std::endl;
+			return -ENOMEM;
+		}
+
+		std::map<Stream *, Buffer *> map;
+		map[stream] = &buffer;
+		ret = request->setBuffers(map);
+		if (ret < 0) {
+			std::cerr << "Can't set buffers for request" << std::endl;
+			return ret;
+		}
+
+		ret = camera->queueRequest(request);
+		if (ret < 0) {
+			std::cerr << "Can't queue request" << std::endl;
+			return ret;
+		}
+	}
+
+	std::cout << "Capture until user interrupts by SIGINT" << std::endl;
+	camera->start();
+
+	ret = loop->exec();
+
+	camera->stop();
+	return ret;
+}
+
 int main(int argc, char **argv)
 {
 	int ret;
@@ -106,8 +188,6 @@  int main(int argc, char **argv)
 		return EXIT_FAILURE;
 
 	CameraManager *cm = CameraManager::instance();
-	std::shared_ptr<Camera> camera;
-	std::vector<Stream *> streams;
 
 	ret = cm->start();
 	if (ret) {
@@ -136,7 +216,13 @@  int main(int argc, char **argv)
 			goto out;
 		}
 
-		streams = camera->streams();
+		const std::vector<Stream *> &streams = camera->streams();
+		if (streams.size() != 1) {
+			std::cout << "Camera has " << streams.size()
+				  << " streams, only 1 is supported"
+				  << std::endl;
+			goto out;
+		}
 
 		if (camera->acquire()) {
 			std::cout << "Failed to acquire camera" << std::endl;
@@ -146,22 +232,17 @@  int main(int argc, char **argv)
 		std::cout << "Using camera " << camera->name() << std::endl;
 	}
 
-	if (options.isSet(OptFormat)) {
+	if (options.isSet(OptCapture)) {
 		if (!camera) {
-			std::cout << "Can't configure stream, no camera selected"
+			std::cout << "Can't capture without a camera"
 				  << std::endl;
-			goto out_camera;
+			ret = EXIT_FAILURE;
+			goto out;
 		}
 
-		if (!configureStreams(camera.get(), streams)) {
-			std::cout << "Failed to configure camera" << std::endl;
-			goto out_camera;
-		}
+		capture();
 	}
 
-	ret = loop->exec();
-
-out_camera:
 	if (camera) {
 		camera->release();
 		camera.reset();