11#include <QtMultimedia/private/qcameradevice_p.h>
12#include <QtMultimedia/private/qmultimedia_ranges_p.h>
13#include <QtMultimedia/qcameradevice.h>
15#include <QtCore/qscopeguard.h>
16#include <QtCore/private/qsystemerror_p.h>
18#include <libcamera/camera.h>
19#include <libcamera/control_ids.h>
20#include <libcamera/controls.h>
21#include <libcamera/framebuffer.h>
22#include <libcamera/framebuffer_allocator.h>
23#include <libcamera/geometry.h>
24#include <libcamera/pixel_format.h>
25#include <libcamera/request.h>
26#include <libcamera/stream.h>
34using namespace Qt::StringLiterals;
42auto makeCameraError(
const QString &errorString)
44 return q23::unexpected(std::tuple{
53 : QPlatformCamera(parent), m_instance(std::move(instance))
66 case QVideoFrameFormat::Format_NV12:
67 case QVideoFrameFormat::Format_YUV420P:
69 case QVideoFrameFormat::Format_YUYV:
70 case QVideoFrameFormat::Format_UYVY:
72 case QVideoFrameFormat::Format_Jpeg:
81 if (m_active == active)
83 if (m_cameraDevice.isNull() && active)
86 if (m_cameraFormat.isNull())
87 m_cameraFormat = findBestCameraFormat(m_cameraDevice);
91 startCapturingReportingErrors();
95 Q_EMIT newVideoFrame({});
96 Q_EMIT activeChanged(active);
99void QLibCameraCamera::
setCamera(
const QCameraDevice &camera)
101 if (m_cameraDevice == camera)
104 const bool wasActive = m_active;
107 m_cameraDevice = camera;
111 if (!camera.isNull() && m_instance)
112 m_camera = m_instance->get(QString::fromUtf8(camera.id()));
115 supportedFeaturesChanged(QLibCameraControls::detectSupportedFeatures(m_camera->controls()));
117 supportedFeaturesChanged({});
127 if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format))
130 QCameraFormat newFormat = format.isNull() ? findBestCameraFormat(m_cameraDevice) : format;
131 if (newFormat == m_cameraFormat)
134 m_cameraFormat = newFormat;
138 startCapturingReportingErrors();
143QLibCameraCamera::StartCapturingResult QLibCameraCamera::startCapturing()
146 namespace ranges = QtMultimediaPrivate::ranges;
148 if (!m_camera || m_cameraFormat.isNull())
151 auto abortGuard = qScopeGuard([
this] {
155 const int acquireResult = m_camera->acquire();
156 switch (acquireResult) {
160 return makeCameraError(u"Camera is in use"_s);
162 return makeCameraError(u"Camera has been disconnected"_s);
164 return makeCameraError(u"Failed to acquire camera: "_s
165 + QSystemError::stdString(-acquireResult));
168 m_streamState = StreamState::Acquired;
170 m_config = m_camera->generateConfiguration({ StreamRole::VideoRecording });
171 if (!m_config || m_config->empty())
172 return makeCameraError(u"Camera does not support video capture"_s);
174 StreamConfiguration &streamConfig = m_config->at(0);
177 const std::vector<PixelFormat> pixelFormats = streamConfig.formats().pixelformats();
178 const auto pixelFormatIt = ranges::find_if(pixelFormats, [&](
const PixelFormat &pf) {
179 return pixelFormatFromLibcamera(pf) == m_cameraFormat.pixelFormat();
181 if (pixelFormatIt != pixelFormats.end())
182 streamConfig.pixelFormat = *pixelFormatIt;
184 streamConfig.size = toSize(m_cameraFormat.resolution());
185 streamConfig.bufferCount = 4;
187 switch (m_config->validate()) {
188 case CameraConfiguration::Valid:
189 case CameraConfiguration::Adjusted:
191 case CameraConfiguration::Invalid:
192 return makeCameraError(u"Invalid camera configuration"_s);
195 const int configureResult = m_camera->configure(m_config.get());
196 switch (configureResult) {
200 return makeCameraError(u"Camera has been disconnected"_s);
202 return makeCameraError(u"Camera is not in a state where it can be configured"_s);
204 return makeCameraError(u"Invalid camera configuration"_s);
206 return makeCameraError(u"Failed to configure camera: "_s
207 + QSystemError::stdString(-configureResult));
212 const StreamConfiguration &adjusted = m_config->at(0);
213 m_stride =
int(adjusted.stride);
214 std::tie(m_colorSpace, m_colorRange) = colorSpaceFromLibcamera(adjusted.colorSpace);
215 if (
const std::optional adjustedFormat = pixelFormatFromLibcamera(adjusted.pixelFormat))
216 m_framePixelFormat = *adjustedFormat;
218 Stream *stream = adjusted.stream();
220 m_allocator = std::make_unique<FrameBufferAllocator>(m_camera);
221 if (
const int allocated = m_allocator->allocate(stream); allocated < 0) {
224 return makeCameraError(u"Camera is not in a state where buffers can be allocated"_s);
226 return makeCameraError(u"Camera stream is not part of the active configuration"_s);
228 return makeCameraError(u"Capture buffers are already allocated"_s);
230 return makeCameraError(u"Failed to allocate capture buffers: "_s
231 + QSystemError::stdString(-allocated));
235 m_stream = std::make_unique<QLibCameraCameraStream>(m_camera, stream, m_stride, frameFormat(),
236 m_cameraFormat.maxFrameRate());
238 connect(m_stream.get(), &QLibCameraCameraStream::newVideoFrame,
this,
239 &QLibCameraCamera::newVideoFrame);
241 m_camera->requestCompleted.connect(
this, [stream = m_stream.get()](Request *request) {
243 stream->handleRequestCompleted(request);
247 for (
const std::unique_ptr<FrameBuffer> &buffer : m_allocator->buffers(stream)) {
248 std::unique_ptr<Request> request = m_camera->createRequest();
250 return makeCameraError(u"Failed to create capture request"_s);
252 const int addBufferResult = request->addBuffer(stream, buffer.get());
253 switch (addBufferResult) {
257 return makeCameraError(u"Capture request already has a buffer for the stream"_s);
259 return makeCameraError(u"Frame buffer does not reference a valid stream"_s);
261 return makeCameraError(u"Failed to add buffer to capture request: "_s
262 + QSystemError::stdString(-addBufferResult));
264 m_requests.push_back(std::move(request));
267 using namespace std::chrono;
268 ControlList startControls(m_camera->controls());
269 if (m_cameraFormat.maxFrameRate() > 0) {
270 const auto frameDuration =
271 duration_cast<microseconds>(duration<
double>(1.0 / m_cameraFormat.maxFrameRate()));
272 startControls.set(libcamera::controls::FrameDurationLimits,
273 { frameDuration.count(), frameDuration.count() });
276 const int startResult = m_camera->start(&startControls);
277 switch (startResult) {
281 return makeCameraError(u"Camera has been disconnected"_s);
283 return makeCameraError(u"Camera is not in a state where it can be started"_s);
285 return makeCameraError(u"Failed to start camera: "_s
286 + QSystemError::stdString(-startResult));
288 m_streamState = StreamState::Running;
290 for (
const std::unique_ptr<Request> &request : m_requests) {
291 const int queueResult = m_camera->queueRequest(request.get());
292 switch (queueResult) {
296 return makeCameraError(u"Camera has been disconnected"_s);
298 return makeCameraError(u"Camera is not running so requests can't be queued"_s);
300 return makeCameraError(u"Capture request does not belong to this camera"_s);
302 return makeCameraError(u"Capture request is invalid"_s);
304 return makeCameraError(u"No buffer memory available to queue capture request"_s);
306 return makeCameraError(u"Failed to queue capture request: "_s
307 + QSystemError::stdString(-queueResult));
311 abortGuard.dismiss();
315void QLibCameraCamera::startCapturingReportingErrors()
317 if (StartCapturingResult result = startCapturing(); !result)
318 updateError(
std::get<0>(result.error()),
std::get<1>(result.error()));
321void QLibCameraCamera::stopCapturing()
324 m_camera->requestCompleted.disconnect(
this);
325 if (m_streamState == StreamState::Running) {
326 const int stopResult = m_camera->stop();
327 switch (stopResult) {
332 qCDebug(lcLibCamera) <<
"Camera disconnected while stopping capture";
335 qCWarning(lcLibCamera)
336 <<
"Failed to stop camera:" << QSystemError::stdString(-stopResult);
342 Stream *stream = m_stream ? m_stream->stream() :
nullptr;
346 if (m_allocator && stream) {
347 const int freeResult = m_allocator->free(stream);
348 switch (freeResult) {
354 qCWarning(lcLibCamera)
355 <<
"Failed to free capture buffers:" << QSystemError::stdString(-freeResult);
362 if (m_camera && m_streamState != StreamState::Idle) {
363 const int releaseResult = m_camera->release();
364 switch (releaseResult) {
368 qCWarning(lcLibCamera) <<
"Camera still running while releasing it";
371 qCWarning(lcLibCamera)
372 <<
"Failed to release camera:" << QSystemError::stdString(-releaseResult);
376 m_streamState = StreamState::Idle;
381 if (mode != QCamera::ExposureAuto && mode != QCamera::ExposureManual)
383 withPendingControls([&](ControlList &pending) {
386 exposureModeChanged(mode);
391 return mode == QCamera::ExposureAuto || mode == QCamera::ExposureManual;
396 withPendingControls([&](ControlList &pending) {
399 exposureCompensationChanged(compensation);
404 withPendingControls([&](ControlList &pending) {
407 isoSensitivityChanged(iso);
412 withPendingControls([&](ControlList &pending) {
415 exposureTimeChanged(seconds);
420 withPendingControls([&](ControlList &pending) {
423 if (mode != QCamera::WhiteBalanceAuto)
425 whiteBalanceModeChanged(mode);
430 withPendingControls([&](ControlList &pending) {
433 colorTemperatureChanged(temperature);
436void QLibCameraCamera::
zoomTo(
float newZoomFactor,
float )
440 newZoomFactor =
std::clamp(newZoomFactor, minZoomFactor(), maxZoomFactor());
441 withPendingControls([&](ControlList &pending) {
444 zoomFactorChanged(newZoomFactor);
449 QVideoFrameFormat result = QPlatformCamera::frameFormat();
450 result.setColorSpace(m_colorSpace);
451 result.setColorRange(m_colorRange);
459#include "moc_qlibcamera_camera_p.cpp"
void setExposureCompensation(float compensation) override
void setManualIsoSensitivity(int iso) override
void setActive(bool active) override
void setCamera(const QCameraDevice &) override
void setManualExposureTime(float seconds) override
bool setCameraFormat(const QCameraFormat &) override
void zoomTo(float newZoomFactor, float rate=-1.) override
~QLibCameraCamera() override
void setExposureMode(QCamera::ExposureMode) override
QVideoFrameFormat frameFormat() const override
int cameraPixelFormatScore(QVideoFrameFormat::PixelFormat, QVideoFrameFormat::ColorRange) const override
void setColorTemperature(int temperature) override
void setWhiteBalanceMode(QCamera::WhiteBalanceMode) override
bool isExposureModeSupported(QCamera::ExposureMode mode) const override
void setExposureCompensation(ControlList &pending, float stops)
void setAnalogueGainForIso(ControlList &pending, int iso)
void setAwbEnable(ControlList &pending, bool enable)
void setZoomFactor(ControlList &pending, const ControlInfoMap &controls, float zoomFactor)
void setColorTemperature(ControlList &pending, int kelvin)
void setManualExposureTime(ControlList &pending, float seconds)
void setAeEnable(ControlList &pending, bool enable)