summaryrefslogtreecommitdiff
path: root/src/apps/qcam/viewfinder_qt.h
blob: eb3a99882d1996f26350fb31bad4cc77fd71e6a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * viewfinder_qt.h - qcam - QPainter-based ViewFinder
 */

#pragma once

#include <QIcon>
#include <QImage>
#include <QList>
#include <QMutex>
#include <QSize>
#include <QWidget>

#include <libcamera/formats.h>
#include <libcamera/framebuffer.h>
#include <libcamera/pixel_format.h>

#include "format_converter.h"
#include "viewfinder.h"

class ViewFinderQt : public QWidget, public ViewFinder
{
	Q_OBJECT

public:
	ViewFinderQt(QWidget *parent);
	~ViewFinderQt();

	const QList<libcamera::PixelFormat> &nativeFormats() const override;

	int setFormat(const libcamera::PixelFormat &format, const QSize &size,
		      const libcamera::ColorSpace &colorSpace,
		      unsigned int stride) override;
	void render(libcamera::FrameBuffer *buffer, Image *image) override;
	void stop() override;

	QImage getCurrentImage() override;

Q_SIGNALS:
	void renderComplete(libcamera::FrameBuffer *buffer);

protected:
	void paintEvent(QPaintEvent *) override;
	QSize sizeHint() const override;

private:
	FormatConverter converter_;

	libcamera::PixelFormat format_;
	QSize size_;

	/* Camera stopped icon */
	QSize vfSize_;
	QIcon icon_;
	QPixmap pixmap_;

	/* Buffer and render image */
	libcamera::FrameBuffer *buffer_;
	QImage image_;
	QMutex mutex_; /* Prevent concurrent access to image_ */
};
/a> 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * main.cpp - cam - The libcamera swiss army knife
 */

#include <iomanip>
#include <iostream>
#include <map>
#include <signal.h>
#include <string.h>

#include <libcamera/libcamera.h>

#include "buffer_writer.h"
#include "event_loop.h"
#include "options.h"

using namespace libcamera;

OptionsParser::Options options;
std::shared_ptr<Camera> camera;
EventLoop *loop;
BufferWriter *writer;

enum {
	OptCamera = 'c',
	OptCapture = 'C',
	OptFile = 'F',
	OptFormat = 'f',
	OptHelp = 'h',
	OptList = 'l',
};

void signalHandler(int signal)
{
	std::cout << "Exiting" << std::endl;
	loop->exit();
}

static int parseOptions(int argc, char *argv[])
{
	KeyValueParser formatKeyValue;
	formatKeyValue.addOption("width", OptionInteger, "Width in pixels",
				 ArgumentRequired);
	formatKeyValue.addOption("height", OptionInteger, "Height in pixels",
				 ArgumentRequired);
	formatKeyValue.addOption("pixelformat", OptionInteger, "Pixel format",
				 ArgumentRequired);

	OptionsParser parser;
	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(OptFile, OptionString,
			 "Write captured frames to disk\n"
			 "The first '#' character in the file name is expanded to the frame sequence number.\n"
			 "The default file name is 'frame-#.bin'.",
			 "file", ArgumentOptional, "filename");
	parser.addOption(OptFormat, &formatKeyValue,
			 "Set format of the camera's first stream", "format");
	parser.addOption(OptHelp, OptionNone, "Display this help message",
			 "help");
	parser.addOption(OptList, OptionNone, "List all cameras", "list");

	options = parser.parse(argc, argv);
	if (!options.valid())
		return -EINVAL;

	if (argc == 1 || options.isSet(OptHelp)) {
		parser.usage();
		return 1;
	}

	return 0;
}

static bool configureStreams(Camera *camera, std::vector<Stream *> &streams)
{
	KeyValueParser::Options format = options[OptFormat];
	Stream *id = streams.front();

	std::map<Stream *, StreamConfiguration> config =
		camera->streamConfiguration(streams);

	if (options.isSet(OptFormat)) {
		if (format.isSet("width"))
			config[id].width = format["width"];

		if (format.isSet("height"))
			config[id].height = format["height"];

		/* TODO: Translate 4CC string to ID. */
		if (format.isSet("pixelformat"))
			config[id].pixelFormat = format["pixelformat"];
	}

	if (camera->configureStreams(config))
		return false;

	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;

	if (writer)
		writer->write(buffer);

	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;

	ret = parseOptions(argc, argv);
	if (ret < 0)
		return EXIT_FAILURE;

	CameraManager *cm = CameraManager::instance();

	ret = cm->start();
	if (ret) {
		std::cout << "Failed to start camera manager: "
			  << strerror(-ret) << std::endl;
		return EXIT_FAILURE;
	}

	loop = new EventLoop(cm->eventDispatcher());

	struct sigaction sa = {};
	sa.sa_handler = &signalHandler;
	sigaction(SIGINT, &sa, nullptr);

	if (options.isSet(OptList)) {
		std::cout << "Available cameras:" << std::endl;
		for (const std::shared_ptr<Camera> &cam : cm->cameras())
			std::cout << "- " << cam->name() << std::endl;
	}

	if (options.isSet(OptCamera)) {
		camera = cm->get(options[OptCamera]);
		if (!camera) {
			std::cout << "Camera " << options[OptCamera]
				  << " not found" << std::endl;
			goto out;
		}

		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;
			goto out;
		}

		std::cout << "Using camera " << camera->name() << std::endl;
	}

	if (options.isSet(OptCapture)) {
		if (!camera) {
			std::cout << "Can't capture without a camera"
				  << std::endl;
			ret = EXIT_FAILURE;
			goto out;
		}

		if (options.isSet(OptFile)) {
			if (!options[OptFile].toString().empty())
				writer = new BufferWriter(options[OptFile]);
			else
				writer = new BufferWriter();
		}

		capture();

		if (options.isSet(OptFile)) {
			delete writer;
			writer = nullptr;
		}
	}

	if (camera) {
		camera->release();
		camera.reset();
	}
out:
	delete loop;

	cm->stop();

	return ret;
}