summaryrefslogtreecommitdiff
path: root/src/android/camera_device.h
AgeCommit message (Expand)Author
2020-10-21android: camera_device: use member style on Camera3RequestDescriptorKieran Bingham
2020-10-14android: camera_device: Queue request using WorkerJacopo Mondi
2020-10-07android: camera_device: Return Camera as shared_ptrJacopo Mondi
2020-10-07android: camera_device: Move processing to CameraStreamJacopo Mondi
2020-10-07android: camera_stream: Construct with Android streamJacopo Mondi
2020-10-07android: camera_stream: Break out CameraStreamJacopo Mondi
2020-09-18android: camera_device: Make CameraStream a classJacopo Mondi
2020-09-18android: camera_device: Set Encoder at constructionJacopo Mondi
2020-09-18android: camera_device: Rework CameraStream handlingJacopo Mondi
2020-09-18android: camera_device: Generate RAW resolutionsJacopo Mondi
2020-09-18android: camera_device: Break out size calculationJacopo Mondi
2020-08-23android: camera_device: Add a getter to get libcamera::Camera pointerUmang Jain
2020-08-23android: camera_device: Make CameraDevice a shared objectUmang Jain
2020-08-06android: camera_device: Support MJPEG stream constructionKieran Bingham
2020-08-05android: camera_device: Break-out request templateJacopo Mondi
2020-08-05andrdid: camera_device: Store const templatesJacopo Mondi
2020-07-24android: camera_device: Use HAL_PIXEL_FORMAT_* defines for formatsNiklas Söderlund
2020-07-07android: camera_device: Add buffers for each stream to RequestsKieran Bingham
2020-07-06android: camera_device: Maintain a vector of CameraStreamKieran Bingham
2020-07-06android: camera_device: Simplify FrameBuffer construction from a buffer_handle_tKieran Bingham
2020-07-06android: camera_device: Provide a toPixelFormat helperKieran Bingham
2020-06-08android: camera_device: Calculate metadata sizeJacopo Mondi
2020-06-08android: camera_device: Initialize stream configurationJacopo Mondi
2020-06-04android: hal_manager: Do not hardcode propertiesJacopo Mondi
2020-06-04android: camera_device: Provide log prefixJacopo Mondi
2020-05-16libcamera: Move internal headers to include/libcamera/internal/Laurent Pinchart
2020-02-13android: Remove internal threadLaurent Pinchart
2019-11-19libcamera: camera: Remove explicit stream to buffer map in requestCompleted s...Niklas Söderlund
2019-10-30android: Replace ThreadRPC with blocking method callJacopo Mondi
2019-09-05android: camera_device: Fix handling of request templateJacopo Mondi
2019-09-05android: camera_device: Use the new CameraMetadata helper classLaurent Pinchart
2019-08-19android: Pass Camera shared pointer to CameraProxy by const referenceLaurent Pinchart
2019-08-19android: Simplify thread RPC with Object::invokeMethod()Laurent Pinchart
2019-08-12android: hal: Add Camera3 HALJacopo Mondi
4' href='#n264'>264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
/* 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 <signal.h>
#include <string.h>

#include <libcamera/libcamera.h>
#include <libcamera/property_ids.h>

#include "capture.h"
#include "event_loop.h"
#include "main.h"
#include "options.h"

using namespace libcamera;

class CamApp
{
public:
	CamApp();
	~CamApp();

	static CamApp *instance();

	int init(int argc, char **argv);
	void cleanup();

	int exec();
	void quit();

private:
	int parseOptions(int argc, char *argv[]);
	int prepareConfig();
	int listProperties();
	int infoConfiguration();
	int run();

	static CamApp *app_;
	OptionsParser::Options options_;
	CameraManager *cm_;
	std::shared_ptr<Camera> camera_;
	std::unique_ptr<libcamera::CameraConfiguration> config_;
	EventLoop *loop_;
};

CamApp *CamApp::app_ = nullptr;

CamApp::CamApp()
	: cm_(nullptr), camera_(nullptr), config_(nullptr), loop_(nullptr)
{
	CamApp::app_ = this;
}

CamApp::~CamApp()
{
	delete cm_;
}

CamApp *CamApp::instance()
{
	return CamApp::app_;
}

int CamApp::init(int argc, char **argv)
{
	int ret;

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

	cm_ = new CameraManager();

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

	if (options_.isSet(OptCamera)) {
		const std::string &cameraName = options_[OptCamera];
		char *endptr;
		unsigned long index = strtoul(cameraName.c_str(), &endptr, 10);
		if (*endptr == '\0' && index > 0 && index <= cm_->cameras().size())
			camera_ = cm_->cameras()[index - 1];
		else
			camera_ = cm_->get(cameraName);

		if (!camera_) {
			std::cout << "Camera "
				  << std::string(options_[OptCamera])
				  << " not found" << std::endl;
			cm_->stop();
			return -ENODEV;
		}

		if (camera_->acquire()) {
			std::cout << "Failed to acquire camera" << std::endl;
			camera_.reset();
			cm_->stop();
			return -EINVAL;
		}

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

		ret = prepareConfig();
		if (ret)
			return ret;
	}

	loop_ = new EventLoop(cm_->eventDispatcher());

	return 0;
}

void CamApp::cleanup()
{
	delete loop_;
	loop_ = nullptr;

	if (camera_) {
		camera_->release();
		camera_.reset();
	}

	config_.reset();

	cm_->stop();
}

int CamApp::exec()
{
	int ret;

	ret = run();
	cleanup();

	return ret;
}

void CamApp::quit()
{
	if (loop_)
		loop_->exit();
}

int CamApp::parseOptions(int argc, char *argv[])
{
	KeyValueParser streamKeyValue;
	streamKeyValue.addOption("role", OptionString,
				 "Role for the stream (viewfinder, video, still)",
				 ArgumentRequired);
	streamKeyValue.addOption("width", OptionInteger, "Width in pixels",
				 ArgumentRequired);
	streamKeyValue.addOption("height", OptionInteger, "Height in pixels",
				 ArgumentRequired);
	streamKeyValue.addOption("pixelformat", OptionInteger, "Pixel format",
				 ArgumentRequired);

	OptionsParser parser;
	parser.addOption(OptCamera, OptionString,
			 "Specify which camera to operate on, by name or by index", "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 stream name and frame sequence number.\n"
			 "The default file name is 'frame-#.bin'.",
			 "file", ArgumentOptional, "filename");
	parser.addOption(OptStream, &streamKeyValue,
			 "Set configuration of a camera stream", "stream", true);
	parser.addOption(OptHelp, OptionNone, "Display this help message",
			 "help");
	parser.addOption(OptInfo, OptionNone,
			 "Display information about stream(s)", "info");
	parser.addOption(OptList, OptionNone, "List all cameras", "list");
	parser.addOption(OptProps, OptionNone, "List cameras properties",
			 "list-properties");

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

	if (options_.empty() || options_.isSet(OptHelp)) {
		parser.usage();
		return options_.empty() ? -EINVAL : -EINTR;
	}

	return 0;
}

int CamApp::prepareConfig()
{
	StreamRoles roles;

	if (options_.isSet(OptStream)) {
		const std::vector<OptionValue> &streamOptions =
			options_[OptStream].toArray();

		/* Use roles and get a default configuration. */
		for (auto const &value : streamOptions) {
			KeyValueParser::Options opt = value.toKeyValues();

			if (!opt.isSet("role")) {
				roles.push_back(StreamRole::VideoRecording);
			} else if (opt["role"].toString() == "viewfinder") {
				roles.push_back(StreamRole::Viewfinder);
			} else if (opt["role"].toString() == "video") {
				roles.push_back(StreamRole::VideoRecording);
			} else if (opt["role"].toString() == "still") {
				roles.push_back(StreamRole::StillCapture);
			} else {
				std::cerr << "Unknown stream role "
					  << opt["role"].toString() << std::endl;
				return -EINVAL;
			}
		}
	} else {
		/* If no configuration is provided assume a single video stream. */
		roles.push_back(StreamRole::VideoRecording);
	}

	config_ = camera_->generateConfiguration(roles);
	if (!config_ || config_->size() != roles.size()) {
		std::cerr << "Failed to get default stream configuration"
			  << std::endl;
		return -EINVAL;
	}

	/* Apply configuration if explicitly requested. */
	if (options_.isSet(OptStream)) {
		const std::vector<OptionValue> &streamOptions =
			options_[OptStream].toArray();

		unsigned int i = 0;
		for (auto const &value : streamOptions) {
			KeyValueParser::Options opt = value.toKeyValues();
			StreamConfiguration &cfg = config_->at(i++);

			if (opt.isSet("width"))
				cfg.size.width = opt["width"];

			if (opt.isSet("height"))
				cfg.size.height = opt["height"];

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

	switch (config_->validate()) {
	case CameraConfiguration::Valid:
		break;
	case CameraConfiguration::Adjusted:
		std::cout << "Camera configuration adjusted" << std::endl;
		break;
	case CameraConfiguration::Invalid:
		std::cout << "Camera configuration invalid" << std::endl;
		config_.reset();
		return -EINVAL;
	}

	return 0;
}

int CamApp::listProperties()
{
	if (!camera_) {
		std::cout << "Cannot list properties without a camera"
			  << std::endl;
		return -EINVAL;
	}

	for (const auto &prop : camera_->properties()) {
		const ControlId *id = properties::properties.at(prop.first);
		const ControlValue &value = prop.second;

		std::cout << "Property: " << id->name() << " = " << value.toString();
	}

	return 0;
}

int CamApp::infoConfiguration()
{
	if (!config_) {
		std::cout << "Cannot print stream information without a camera"
			  << std::endl;
		return -EINVAL;
	}

	unsigned int index = 0;
	for (const StreamConfiguration &cfg : *config_) {
		std::cout << index << ": " << cfg.toString() << std::endl;

		const StreamFormats &formats = cfg.formats();
		for (unsigned int pixelformat : formats.pixelformats()) {
			std::cout << " * Pixelformat: 0x" << std::hex
				  << std::setw(8) << pixelformat << " "
				  << formats.range(pixelformat).toString()
				  << std::endl;

			for (const Size &size : formats.sizes(pixelformat))
				std::cout << "  - " << size.toString()
					  << std::endl;
		}

		index++;
	}

	return 0;
}

int CamApp::run()
{
	int ret;

	if (options_.isSet(OptList)) {
		std::cout << "Available cameras:" << std::endl;

		unsigned int index = 1;
		for (const std::shared_ptr<Camera> &cam : cm_->cameras()) {
			std::cout << index << ": " << cam->name() << std::endl;
			index++;
		}
	}

	if (options_.isSet(OptProps)) {
		ret = listProperties();
		if (ret)
			return ret;
	}

	if (options_.isSet(OptInfo)) {
		ret = infoConfiguration();
		if (ret)
			return ret;
	}

	if (options_.isSet(OptCapture)) {
		Capture capture(camera_, config_.get());
		return capture.run(loop_, options_);
	}

	return 0;
}

void signalHandler(int signal)
{
	std::cout << "Exiting" << std::endl;
	CamApp::instance()->quit();
}

int main(int argc, char **argv)
{
	CamApp app;
	int ret;

	ret = app.init(argc, argv);
	if (ret)
		return ret == -EINTR ? 0 : EXIT_FAILURE;

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

	if (app.exec())
		return EXIT_FAILURE;

	return 0;
}