summaryrefslogtreecommitdiff
path: root/src/cam/capture.cpp
blob: 7b55fc6770225e2a1a6a03005a18714bcd6c978a (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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * capture.cpp - Cam capture
 */

#include <iomanip>
#include <iostream>
#include <limits.h>
#include <sstream>

#include "capture.h"
#include "main.h"

using namespace libcamera;

Capture::Capture(std::shared_ptr<Camera> camera, CameraConfiguration *config,
		 EventLoop *loop)
	: camera_(camera), config_(config), writer_(nullptr), last_(0), loop_(loop),
	  queueCount_(0), captureCount_(0), captureLimit_(0)
{
}

int Capture::run(const OptionsParser::Options &options)
{
	int ret;

	queueCount_ = 0;
	captureCount_ = 0;
	captureLimit_ = options[OptCapture].toInteger();

	if (!camera_) {
		std::cout << "Can't capture without a camera" << std::endl;
		return -ENODEV;
	}

	ret = camera_->configure(config_);
	if (ret < 0) {
		std::cout << "Failed to configure camera" << std::endl;
		return ret;
	}

	streamName_.clear();
	for (unsigned int index = 0; index < config_->size(); ++index) {
		StreamConfiguration &cfg = config_->at(index);
		streamName_[cfg.stream()] = "stream" + std::to_string(index);
	}

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

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


	FrameBufferAllocator *allocator = new FrameBufferAllocator(camera_);

	ret = capture(allocator);

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

	requests_.clear();

	delete allocator;

	return ret;
}

int Capture::capture(FrameBufferAllocator *allocator)
{
          description: Maximum allowed value (place any new values above here).

  # AeExposureMode needs further attention:
  # - Auto-generate max enum value.
  # - Better handling of custom types.
  - AeExposureMode:
      type: int32_t
= allocator->allocate(cfg.stream());
		if (ret < 0) {
			std::cerr << "Can't allocate buffers" << std::endl;
			return -ENOMEM;
		}

		unsigned int allocated = allocator->buffers(cfg.stream()).size();
		nbuffers = std::min(nbuffers, allocated);
	}

	/*
	 * TODO: make cam tool smarter to support still capture by for
	 * example pushing a button. For now run all streams all the time.
	 */

	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;
			return -ENOMEM;
		}

		for (StreamConfiguration &cfg : *config_) {
			Stream *stream = cfg.stream();
			const std::vector<std::unique_ptr<FrameBuffer>> &buffers =
				allocator->buffers(stream);
			const std::unique_ptr<FrameBuffer> &buffer = buffers[i];

			ret = request->addBuffer(stream, buffer.get());
			if (ret < 0) {
				std::cerr << "Can't set buffer for request"
					  << std::endl;
				return ret;
			}

			if (writer_)
				writer_->mapBuffer(buffer.get());
		}

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

	ret = camera_->start();
	if (ret) {
		std::cout << "Failed to start capture" << std::endl;
		return ret;
	}

	for (std::unique_ptr<Request> &request : requests_) {
		ret = queueRequest(request.get());
		if (ret < 0) {
			std::cerr << "Can't queue request" << std::endl;
			camera_->stop();
			return ret;
		}
	}

	if (captureLimit_)
		std::cout << "Capture " << captureLimit_ << " frames" << std::endl;
	else
		std::cout << "Capture until user interrupts by SIGINT" << std::endl;

	ret = loop_->exec();
	if (ret)
		std::cout << "Failed to run capture loop" << std::endl;

	ret = camera_->stop();
	if (ret)
		std::cout << "Failed to stop capture" << std::endl;

	return ret;
}

int Capture::queueRequest(Request *request)
{
	if (captureLimit_ && queueCount_ >= captureLimit_)
		return 0;

	queueCount_++;

	return camera_->queueRequest(request);
}

void Capture::requestComplete(Request *request)
{
	if (request->status, and shall be 0.0 unless the camera can't
        disable sharpening completely. The default value shall give a
        "reasonable" level of sharpening, suitable for most use cases.
        The maximum value may apply extremely high levels of sharpening,
        higher than anyone could reasonably want. Negative values are
        not allowed. Note also that sharpening is not applied to raw
        streams.

  - FocusFoM:
      type: int32_t
      description: |
        Reports a Figure of Merit (FoM) to indicate how in-focus the frame is.
        A larger FocusFoM value indicates a more in-focus frame. This control
        depends on the IPA to gather ISP statistics from the defined focus
        region, and combine them in a suitable way to generate a FocusFoM value.
        In this respect, it is not necessarily aimed at providing a way to
        implement a focus algorithm by the application, rather an indication of
        how in-focus a frame is.

  - ColourCorrectionMatrix:
      type: float
      description: |
        The 3x3 matrix that converts camera RGB to sRGB within the
        imaging pipeline. This should describe the matrix that is used
        after pixels have been white-balanced, but before any gamma
        transformation. The 3x3 matrix is stored in conventional reading
        order in an array of 9 floating point values.

      size: [3x3]
...
an> << "." << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000 << " (" << std::fixed << std::setprecision(2) << fps << " fps)"; for (auto it = buffers.begin(); it != buffers.end(); ++it) { const Stream *stream = it->first; FrameBuffer *buffer = it->second; const std::string &name = streamName_[stream]; const FrameMetadata &metadata = buffer->metadata(); info << " " << name << " seq: " << std::setw(6) << std::setfill('0') << metadata.sequence << " bytesused: "; unsigned int nplane = 0; for (const FrameMetadata::Plane &plane : metadata.planes) { info << plane.bytesused; if (++nplane < metadata.planes.size()) info << "/"; } if (writer_) writer_->write(buffer, name); } std::cout << info.str() << std::endl; captureCount_++; if (captureLimit_ && captureCount_ >= captureLimit_) { loop_->exit(0); return; } request->reuse(Request::ReuseBuffers); queueRequest(request); }