summaryrefslogtreecommitdiff
path: root/src/lc-compliance/simple_capture.cpp
blob: 64e862a08e3a8221f24677a5a14f9cbea44aa936 (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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * v4l2_camera.h - V4L2 compatibility camera
 */

#pragma once

#include <deque>
#include <utility>

#include <libcamera/base/mutex.h>
#include <libcamera/base/semaphore.h>
#include <libcamera/base/shared_fd.h>

#include <libcamera/camera.h>
#include <libcamera/framebuffer.h>
#include <libcamera/framebuffer_allocator.h>

class V4L2Camera
{
public:
	struct Buffer {
		Buffer(unsigned int index, const libcamera::FrameMetadata &data)
			: index_(index), data_(data)
		{
		}

		unsigned int index_;
		libcamera::FrameMetadata data_;
	};

	V4L2Camera(std::shared_ptr<libcamera::Camera> camera);
	~V4L2Camera();

	int open(libcamera::StreamConfiguration *streamConfig);
	void close();
	void bind(int efd);
	void unbind();

	std::vector<Buffer> completedBuffers();

	int configure(libcamera::StreamConfiguration *streamConfigOut,
		      const libcamera::Size &size,
		      const libcamera::PixelFormat &pixelformat,
		      unsigned int bufferCount);
	int validateConfiguration(const libcamera::PixelFormat &pixelformat,
				  const libcamera::Size &size,
				  libcamera::StreamConfiguration *str/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2020, Google Inc.
 *
 * simple_capture.cpp - Simple capture helper
 */

#include "simple_capture.h"

using namespace libcamera;

SimpleCapture::SimpleCapture(std::shared_ptr<Camera> camera)
	: loop_(nullptr), camera_(camera),
	  allocator_(std::make_unique<FrameBufferAllocator>(camera))
{
}

SimpleCapture::~SimpleCapture()
{
}

Results::Result SimpleCapture::configure(StreamRole role)
{
	config_ = camera_->generateConfiguration({ role });

	if (!config_)
		return { Results::Skip, "Role not supported by camera" };

	if (config_->validate() != CameraConfiguration::Valid) {
		config_.reset();
		return { Results::Fail, "Configuration not valid" };
	}

	if (camera_->configure(config_.get())) {
		config_.reset();
		return { Results::Fail, "Failed to configure camera" };
	}

	return { Results::Pass, "Configure camera" };
}

Results::Result SimpleCapture::start()
{
	Stream *stream = config_->at(0).stream();
	if (allocator_->allocate(stream) < 0)
		return { Results::Fail, "Failed to allocate buffers" };

	if (camera_->start())
		return { Results::Fail, "Failed to start camera" };

	camera_->requestCompleted.connect(this, &SimpleCapture::requestComplete);

	return { Results::Pass, "Started camera" };
}

void SimpleCapture::stop()
{
	Stream *stream = config_->at(0).stream();

	camera_->stop();

	camera_->requestCompleted.disconnect(this, &SimpleCapture::requestComplete);

	allocator_->free(stream);
}

/* SimpleCaptureBalanced */

SimpleCaptureBalanced::SimpleCaptureBalanced(std::shared_ptr<Camera> camera)
	: SimpleCapture(camera)
{
}

Results::Result SimpleCaptureBalanced::capture(unsigned int numRequests)
{
	Results::Result ret = start();
	if (ret.first != Results::Pass)
		return ret;

	Stream *stream = config_->at(0).stream();
	const std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator_->buffers(stream);

	/* No point in testing less requests then the camera depth. */
	if (buffers.size() > numRequests) {
		/* Cache buffers.size() before we destroy it in stop() */
		int buffers_size = buffers.size();
		stop();

		return { Results::Skip, "Camera needs " + std::to_string(buffers_size)
			+ " requests, can't test only " + std::to_string(numRequests) };
	}

	queueCount_ = 0;
	captureCount_ = 0;
	captureLimit_ = numRequests;

	/* Queue the recommended number of reqeuests. */
	std::vector<std::unique_ptr<libcamera::Request>> requests;
	for (const std::unique_ptr<FrameBuffer> &buffer : buffers) {
		std::unique_ptr<Request> request = camera_->createRequest();
		if (!request) {
			stop();
			return { Results::Fail, "Can't create request" };
		}

		if (request->addBuffer(stream, buffer.get())) {
			stop();
			return { Results::Fail, "Can't set buffer for request" };
		}

		if (queueRequest(request.get()) < 0) {
			stop();
			return { Results::Fail, "Failed to queue request" };
		}

		requests.push_back(std::move(request));
	}

	/* Run capture session. */
	loop_ = new EventLoop();
	loop_->exec();
	stop();
	delete loop_;

	if (captureCount_ != captureLimit_)
		return { Results::Fail, "Got " + std::to_string(captureCount_) +
			" request, wanted " + std::to_string(captureLimit_) };

	return { Results::Pass, "Balanced capture of " +
		std::to_string(numRequests) + " requests" };
}

int SimpleCaptureBalanced::queueRequest(Request *request)
{
	queueCount_++;
	if (queueCount_ > captureLimit_)
		return 0;

	return camera_->queueRequest(request);
}

void SimpleCaptureBalanced::requestComplete(Request *request)
{
	captureCount_++;
	if (captureCount_ >= captureLimit_) {
		loop_->exit(0);
		return;
	}

	request->reuse(Request::ReuseBuffers);
	if (queueRequest(request))
		loop_->exit(-EINVAL);
}

/* SimpleCaptureUnbalanced */

SimpleCaptureUnbalanced::SimpleCaptureUnbalanced(std::shared_ptr<Camera> camera)
	: SimpleCapture(camera)
{
}

Results::Result SimpleCaptureUnbalanced::capture(unsigned int numRequests)
{
	Results::Result ret = start();
	if (ret.first != Results::Pass)
		return ret;

	Stream *stream = config_->at(0).stream();
	const std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator_->buffers(stream);

	captureCount_ = 0;
	captureLimit_ = numRequests;

	/* Queue the recommended number of reqeuests. */
	std::vector<std::unique_ptr<libcamera::Request>> requests;
	for (const std::unique_ptr<FrameBuffer> &buffer : buffers) {
		std::unique_ptr<Request> request = camera_->createRequest();
		if (!request) {
			stop();
			return { Results::Fail, "Can't create request" };
		}

		if (request->addBuffer(stream, buffer.get())) {
			stop();
			return { Results::Fail, "Can't set buffer for request" };
		}

		if (camera_->queueRequest(request.get()) < 0) {
			stop();
			return { Results::Fail, "Failed to queue request" };
		}

		requests.push_back(std::move(request));
	}

	/* Run capture session. */
	loop_ = new EventLoop();
	int status = loop_->exec();
	stop();
	delete loop_;

	return { status ? Results::Fail : Results::Pass, "Unbalanced capture of " + std::to_string(numRequests) + " requests" };
}

void SimpleCaptureUnbalanced::requestComplete(Request *request)
{
	captureCount_++;
	if (captureCount_ >= captureLimit_) {
		loop_->exit(0);
		return;
	}

	request->reuse(Request::ReuseBuffers);
	if (camera_->queueRequest(request))
		loop_->exit(-EINVAL);
}