1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qv4l2camera_p.h"
5#include "qv4l2filedescriptor_p.h"
6#include "qv4l2memorytransfer_p.h"
7
8#include <private/qcameradevice_p.h>
9#include <private/qmultimediautils_p.h>
10#include <private/qmemoryvideobuffer_p.h>
11#include <private/qvideoframe_p.h>
12#include <private/qcore_unix_p.h>
13
14#include <qsocketnotifier.h>
15#include <qloggingcategory.h>
16
17QT_BEGIN_NAMESPACE
18
19static Q_LOGGING_CATEGORY(qLcV4L2Camera, "qt.multimedia.ffmpeg.v4l2camera");
20
21static const struct {
22 QVideoFrameFormat::PixelFormat fmt;
23 uint32_t v4l2Format;
24} formatMap[] = {
25 // ### How do we handle V4L2_PIX_FMT_H264 and V4L2_PIX_FMT_MPEG4?
26 { .fmt: QVideoFrameFormat::Format_YUV420P, V4L2_PIX_FMT_YUV420 },
27 { .fmt: QVideoFrameFormat::Format_YUV422P, V4L2_PIX_FMT_YUV422P },
28 { .fmt: QVideoFrameFormat::Format_YUYV, V4L2_PIX_FMT_YUYV },
29 { .fmt: QVideoFrameFormat::Format_UYVY, V4L2_PIX_FMT_UYVY },
30 { .fmt: QVideoFrameFormat::Format_XBGR8888, V4L2_PIX_FMT_XBGR32 },
31 { .fmt: QVideoFrameFormat::Format_XRGB8888, V4L2_PIX_FMT_XRGB32 },
32 { .fmt: QVideoFrameFormat::Format_ABGR8888, V4L2_PIX_FMT_ABGR32 },
33 { .fmt: QVideoFrameFormat::Format_ARGB8888, V4L2_PIX_FMT_ARGB32 },
34 { .fmt: QVideoFrameFormat::Format_BGRX8888, V4L2_PIX_FMT_BGR32 },
35 { .fmt: QVideoFrameFormat::Format_RGBX8888, V4L2_PIX_FMT_RGB32 },
36 { .fmt: QVideoFrameFormat::Format_BGRA8888, V4L2_PIX_FMT_BGRA32 },
37 { .fmt: QVideoFrameFormat::Format_RGBA8888, V4L2_PIX_FMT_RGBA32 },
38 { .fmt: QVideoFrameFormat::Format_Y8, V4L2_PIX_FMT_GREY },
39 { .fmt: QVideoFrameFormat::Format_Y16, V4L2_PIX_FMT_Y16 },
40 { .fmt: QVideoFrameFormat::Format_NV12, V4L2_PIX_FMT_NV12 },
41 { .fmt: QVideoFrameFormat::Format_NV21, V4L2_PIX_FMT_NV21 },
42 { .fmt: QVideoFrameFormat::Format_Jpeg, V4L2_PIX_FMT_MJPEG },
43 { .fmt: QVideoFrameFormat::Format_Jpeg, V4L2_PIX_FMT_JPEG },
44 { .fmt: QVideoFrameFormat::Format_Invalid, .v4l2Format: 0 },
45};
46
47QVideoFrameFormat::PixelFormat formatForV4L2Format(uint32_t v4l2Format)
48{
49 auto *f = formatMap;
50 while (f->v4l2Format) {
51 if (f->v4l2Format == v4l2Format)
52 return f->fmt;
53 ++f;
54 }
55 return QVideoFrameFormat::Format_Invalid;
56}
57
58uint32_t v4l2FormatForPixelFormat(QVideoFrameFormat::PixelFormat format)
59{
60 auto *f = formatMap;
61 while (f->v4l2Format) {
62 if (f->fmt == format)
63 return f->v4l2Format;
64 ++f;
65 }
66 return 0;
67}
68
69QV4L2Camera::QV4L2Camera(QCamera *camera)
70 : QPlatformCamera(camera)
71{
72}
73
74QV4L2Camera::~QV4L2Camera()
75{
76 stopCapturing();
77 closeV4L2Fd();
78}
79
80bool QV4L2Camera::isActive() const
81{
82 return m_active;
83}
84
85void QV4L2Camera::setActive(bool active)
86{
87 if (m_active == active)
88 return;
89 if (m_cameraDevice.isNull() && active)
90 return;
91
92 if (m_cameraFormat.isNull())
93 resolveCameraFormat(format: {});
94
95 m_active = active;
96 if (m_active)
97 startCapturing();
98 else
99 stopCapturing();
100
101 emit newVideoFrame({});
102
103 emit activeChanged(active);
104}
105
106void QV4L2Camera::setCamera(const QCameraDevice &camera)
107{
108 if (m_cameraDevice == camera)
109 return;
110
111 stopCapturing();
112 closeV4L2Fd();
113
114 m_cameraDevice = camera;
115 resolveCameraFormat(format: {});
116
117 initV4L2Controls();
118
119 if (m_active)
120 startCapturing();
121}
122
123bool QV4L2Camera::setCameraFormat(const QCameraFormat &format)
124{
125 if (!format.isNull() && !m_cameraDevice.videoFormats().contains(t: format))
126 return false;
127
128 if (!resolveCameraFormat(format))
129 return true;
130
131 if (m_active) {
132 stopCapturing();
133 closeV4L2Fd();
134
135 initV4L2Controls();
136 startCapturing();
137 }
138
139 return true;
140}
141
142bool QV4L2Camera::resolveCameraFormat(const QCameraFormat &format)
143{
144 auto fmt = format;
145 if (fmt.isNull())
146 fmt = findBestCameraFormat(camera: m_cameraDevice);
147
148 if (fmt == m_cameraFormat)
149 return false;
150
151 m_cameraFormat = fmt;
152 return true;
153}
154
155void QV4L2Camera::setFocusMode(QCamera::FocusMode mode)
156{
157 if (mode == focusMode())
158 return;
159
160 bool focusDist = supportedFeatures() & QCamera::Feature::FocusDistance;
161 if (!focusDist && !m_v4l2Info.rangedFocus)
162 return;
163
164 switch (mode) {
165 default:
166 case QCamera::FocusModeAuto:
167 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, value: 1);
168 if (m_v4l2Info.rangedFocus)
169 setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, value: V4L2_AUTO_FOCUS_RANGE_AUTO);
170 break;
171 case QCamera::FocusModeAutoNear:
172 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, value: 1);
173 if (m_v4l2Info.rangedFocus)
174 setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, value: V4L2_AUTO_FOCUS_RANGE_MACRO);
175 else if (focusDist)
176 setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, value: m_v4l2Info.minFocus);
177 break;
178 case QCamera::FocusModeAutoFar:
179 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, value: 1);
180 if (m_v4l2Info.rangedFocus)
181 setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, value: V4L2_AUTO_FOCUS_RANGE_INFINITY);
182 break;
183 case QCamera::FocusModeInfinity:
184 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, value: 0);
185 setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, value: m_v4l2Info.maxFocus);
186 break;
187 case QCamera::FocusModeManual:
188 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, value: 0);
189 setFocusDistance(focusDistance());
190 break;
191 }
192 focusModeChanged(mode);
193}
194
195void QV4L2Camera::setFocusDistance(float d)
196{
197 int distance = m_v4l2Info.minFocus + int((m_v4l2Info.maxFocus - m_v4l2Info.minFocus) * d);
198 setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, value: distance);
199 focusDistanceChanged(d);
200}
201
202void QV4L2Camera::zoomTo(float factor, float)
203{
204 if (m_v4l2Info.maxZoom == m_v4l2Info.minZoom)
205 return;
206 factor = qBound(min: 1., val: factor, max: 2.);
207 int zoom = m_v4l2Info.minZoom + (factor - 1.) * (m_v4l2Info.maxZoom - m_v4l2Info.minZoom);
208 setV4L2Parameter(V4L2_CID_ZOOM_ABSOLUTE, value: zoom);
209 zoomFactorChanged(zoom: factor);
210}
211
212bool QV4L2Camera::isFocusModeSupported(QCamera::FocusMode mode) const
213{
214 if (supportedFeatures() & QCamera::Feature::FocusDistance &&
215 (mode == QCamera::FocusModeManual || mode == QCamera::FocusModeAutoNear || mode == QCamera::FocusModeInfinity))
216 return true;
217
218 return mode == QCamera::FocusModeAuto;
219}
220
221void QV4L2Camera::setFlashMode(QCamera::FlashMode mode)
222{
223 if (!m_v4l2Info.flashSupported || mode == QCamera::FlashOn)
224 return;
225 setV4L2Parameter(V4L2_CID_FLASH_LED_MODE, value: mode == QCamera::FlashAuto ? V4L2_FLASH_LED_MODE_FLASH : V4L2_FLASH_LED_MODE_NONE);
226 flashModeChanged(mode);
227}
228
229bool QV4L2Camera::isFlashModeSupported(QCamera::FlashMode mode) const
230{
231 if (m_v4l2Info.flashSupported && mode == QCamera::FlashAuto)
232 return true;
233 return mode == QCamera::FlashOff;
234}
235
236bool QV4L2Camera::isFlashReady() const
237{
238 struct v4l2_queryctrl queryControl;
239 ::memset(s: &queryControl, c: 0, n: sizeof(queryControl));
240 queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
241
242 return m_v4l2FileDescriptor && m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, arg: &queryControl);
243}
244
245void QV4L2Camera::setTorchMode(QCamera::TorchMode mode)
246{
247 if (!m_v4l2Info.torchSupported || mode == QCamera::TorchOn)
248 return;
249 setV4L2Parameter(V4L2_CID_FLASH_LED_MODE, value: mode == QCamera::TorchOn ? V4L2_FLASH_LED_MODE_TORCH : V4L2_FLASH_LED_MODE_NONE);
250 torchModeChanged(mode);
251}
252
253bool QV4L2Camera::isTorchModeSupported(QCamera::TorchMode mode) const
254{
255 if (mode == QCamera::TorchOn)
256 return m_v4l2Info.torchSupported;
257 return mode == QCamera::TorchOff;
258}
259
260void QV4L2Camera::setExposureMode(QCamera::ExposureMode mode)
261{
262 if (m_v4l2Info.autoExposureSupported && m_v4l2Info.manualExposureSupported) {
263 if (mode != QCamera::ExposureAuto && mode != QCamera::ExposureManual)
264 return;
265 int value = QCamera::ExposureAuto ? V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL;
266 setV4L2Parameter(V4L2_CID_EXPOSURE_AUTO, value);
267 exposureModeChanged(mode);
268 return;
269 }
270}
271
272bool QV4L2Camera::isExposureModeSupported(QCamera::ExposureMode mode) const
273{
274 if (mode == QCamera::ExposureAuto)
275 return true;
276 if (m_v4l2Info.manualExposureSupported && m_v4l2Info.autoExposureSupported)
277 return mode == QCamera::ExposureManual;
278 return false;
279}
280
281void QV4L2Camera::setExposureCompensation(float compensation)
282{
283 if ((m_v4l2Info.minExposureAdjustment != 0 || m_v4l2Info.maxExposureAdjustment != 0)) {
284 int value = qBound(min: m_v4l2Info.minExposureAdjustment, val: (int)(compensation * 1000),
285 max: m_v4l2Info.maxExposureAdjustment);
286 setV4L2Parameter(V4L2_CID_AUTO_EXPOSURE_BIAS, value);
287 exposureCompensationChanged(compensation: value/1000.);
288 return;
289 }
290}
291
292void QV4L2Camera::setManualIsoSensitivity(int iso)
293{
294 if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
295 return;
296 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY_AUTO, value: iso <= 0 ? V4L2_ISO_SENSITIVITY_AUTO : V4L2_ISO_SENSITIVITY_MANUAL);
297 if (iso > 0) {
298 iso = qBound(min: minIso(), val: iso, max: maxIso());
299 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY, value: iso);
300 }
301}
302
303int QV4L2Camera::isoSensitivity() const
304{
305 if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
306 return -1;
307 return getV4L2Parameter(V4L2_CID_ISO_SENSITIVITY);
308}
309
310void QV4L2Camera::setManualExposureTime(float secs)
311{
312 if (m_v4l2Info.manualExposureSupported && m_v4l2Info.autoExposureSupported) {
313 int exposure =
314 qBound(min: m_v4l2Info.minExposure, val: qRound(d: secs * 10000.), max: m_v4l2Info.maxExposure);
315 setV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE, value: exposure);
316 exposureTimeChanged(speed: exposure/10000.);
317 return;
318 }
319}
320
321float QV4L2Camera::exposureTime() const
322{
323 return getV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE)/10000.;
324}
325
326bool QV4L2Camera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
327{
328 if (m_v4l2Info.autoWhiteBalanceSupported && m_v4l2Info.colorTemperatureSupported)
329 return true;
330
331 return mode == QCamera::WhiteBalanceAuto;
332}
333
334void QV4L2Camera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode)
335{
336 Q_ASSERT(isWhiteBalanceModeSupported(mode));
337
338 int temperature = colorTemperatureForWhiteBalance(mode);
339 int t = setV4L2ColorTemperature(temperature);
340 if (t == 0)
341 mode = QCamera::WhiteBalanceAuto;
342 whiteBalanceModeChanged(mode);
343}
344
345void QV4L2Camera::setColorTemperature(int temperature)
346{
347 if (temperature == 0) {
348 setWhiteBalanceMode(QCamera::WhiteBalanceAuto);
349 return;
350 }
351
352 Q_ASSERT(isWhiteBalanceModeSupported(QCamera::WhiteBalanceManual));
353
354 int t = setV4L2ColorTemperature(temperature);
355 if (t)
356 colorTemperatureChanged(temperature: t);
357}
358
359void QV4L2Camera::readFrame()
360{
361 Q_ASSERT(m_memoryTransfer);
362
363 auto buffer = m_memoryTransfer->dequeueBuffer();
364 if (!buffer) {
365 qCWarning(qLcV4L2Camera) << "Cannot take buffer";
366
367 if (errno == ENODEV) {
368 // camera got removed while being active
369 stopCapturing();
370 closeV4L2Fd();
371 }
372
373 return;
374 }
375
376 auto videoBuffer = std::make_unique<QMemoryVideoBuffer>(args&: buffer->data, args&: m_bytesPerLine);
377 QVideoFrame frame = QVideoFramePrivate::createFrame(buffer: std::move(videoBuffer), format: frameFormat());
378
379 auto &v4l2Buffer = buffer->v4l2Buffer;
380
381 if (m_firstFrameTime.tv_sec == -1)
382 m_firstFrameTime = v4l2Buffer.timestamp;
383 qint64 secs = v4l2Buffer.timestamp.tv_sec - m_firstFrameTime.tv_sec;
384 qint64 usecs = v4l2Buffer.timestamp.tv_usec - m_firstFrameTime.tv_usec;
385 frame.setStartTime(secs*1000000 + usecs);
386 frame.setEndTime(frame.startTime() + m_frameDuration);
387
388 emit newVideoFrame(frame);
389
390 if (!m_memoryTransfer->enqueueBuffer(index: v4l2Buffer.index))
391 qCWarning(qLcV4L2Camera) << "Cannot add buffer";
392}
393
394void QV4L2Camera::setCameraBusy()
395{
396 m_cameraBusy = true;
397 updateError(error: QCamera::CameraError, errorString: QLatin1String("Camera is in use"));
398}
399
400void QV4L2Camera::initV4L2Controls()
401{
402 m_v4l2Info = {};
403 QCamera::Features features;
404
405 const QByteArray deviceName = m_cameraDevice.id();
406 Q_ASSERT(!deviceName.isEmpty());
407
408 closeV4L2Fd();
409
410 const int descriptor = qt_safe_open(pathname: deviceName.constData(), O_RDWR);
411 if (descriptor == -1) {
412 qCWarning(qLcV4L2Camera) << "Unable to open the camera" << deviceName
413 << "for read to query the parameter info:"
414 << qt_error_string(errno);
415 updateError(error: QCamera::CameraError, errorString: QLatin1String("Cannot open camera"));
416 return;
417 }
418
419 m_v4l2FileDescriptor = std::make_shared<QV4L2FileDescriptor>(args: descriptor);
420
421 qCDebug(qLcV4L2Camera) << "FD=" << descriptor;
422
423 struct v4l2_queryctrl queryControl;
424 ::memset(s: &queryControl, c: 0, n: sizeof(queryControl));
425 queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
426
427 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, arg: &queryControl)) {
428 m_v4l2Info.autoWhiteBalanceSupported = true;
429 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, value: true);
430 }
431
432 ::memset(s: &queryControl, c: 0, n: sizeof(queryControl));
433 queryControl.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE;
434 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, arg: &queryControl)) {
435 m_v4l2Info.minColorTemp = queryControl.minimum;
436 m_v4l2Info.maxColorTemp = queryControl.maximum;
437 m_v4l2Info.colorTemperatureSupported = true;
438 features |= QCamera::Feature::ColorTemperature;
439 }
440
441 ::memset(s: &queryControl, c: 0, n: sizeof(queryControl));
442 queryControl.id = V4L2_CID_EXPOSURE_AUTO;
443 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, arg: &queryControl)) {
444 m_v4l2Info.autoExposureSupported = true;
445 }
446
447 ::memset(s: &queryControl, c: 0, n: sizeof(queryControl));
448 queryControl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
449 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, arg: &queryControl)) {
450 m_v4l2Info.manualExposureSupported = true;
451 m_v4l2Info.minExposure = queryControl.minimum;
452 m_v4l2Info.maxExposure = queryControl.maximum;
453 features |= QCamera::Feature::ManualExposureTime;
454 }
455
456 ::memset(s: &queryControl, c: 0, n: sizeof(queryControl));
457 queryControl.id = V4L2_CID_AUTO_EXPOSURE_BIAS;
458 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, arg: &queryControl)) {
459 m_v4l2Info.minExposureAdjustment = queryControl.minimum;
460 m_v4l2Info.maxExposureAdjustment = queryControl.maximum;
461 features |= QCamera::Feature::ExposureCompensation;
462 }
463
464 ::memset(s: &queryControl, c: 0, n: sizeof(queryControl));
465 queryControl.id = V4L2_CID_ISO_SENSITIVITY_AUTO;
466 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, arg: &queryControl)) {
467 queryControl.id = V4L2_CID_ISO_SENSITIVITY;
468 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, arg: &queryControl)) {
469 features |= QCamera::Feature::IsoSensitivity;
470 minIsoChanged(iso: queryControl.minimum);
471 maxIsoChanged(iso: queryControl.minimum);
472 }
473 }
474
475 ::memset(s: &queryControl, c: 0, n: sizeof(queryControl));
476 queryControl.id = V4L2_CID_FOCUS_ABSOLUTE;
477 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, arg: &queryControl)) {
478 m_v4l2Info.minExposureAdjustment = queryControl.minimum;
479 m_v4l2Info.maxExposureAdjustment = queryControl.maximum;
480 features |= QCamera::Feature::FocusDistance;
481 }
482
483 ::memset(s: &queryControl, c: 0, n: sizeof(queryControl));
484 queryControl.id = V4L2_CID_AUTO_FOCUS_RANGE;
485 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, arg: &queryControl)) {
486 m_v4l2Info.rangedFocus = true;
487 }
488
489 ::memset(s: &queryControl, c: 0, n: sizeof(queryControl));
490 queryControl.id = V4L2_CID_FLASH_LED_MODE;
491 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, arg: &queryControl)) {
492 m_v4l2Info.flashSupported = queryControl.minimum <= V4L2_FLASH_LED_MODE_FLASH
493 && queryControl.maximum >= V4L2_FLASH_LED_MODE_FLASH;
494 m_v4l2Info.torchSupported = queryControl.minimum <= V4L2_FLASH_LED_MODE_TORCH
495 && queryControl.maximum >= V4L2_FLASH_LED_MODE_TORCH;
496 }
497
498 ::memset(s: &queryControl, c: 0, n: sizeof(queryControl));
499 queryControl.id = V4L2_CID_ZOOM_ABSOLUTE;
500 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, arg: &queryControl)) {
501 m_v4l2Info.minZoom = queryControl.minimum;
502 m_v4l2Info.maxZoom = queryControl.maximum;
503 }
504 // zoom factors are in arbitrary units, so we simply normalize them to go from 1 to 2
505 // if they are different
506 minimumZoomFactorChanged(factor: 1);
507 maximumZoomFactorChanged(m_v4l2Info.minZoom != m_v4l2Info.maxZoom ? 2 : 1);
508
509 supportedFeaturesChanged(features);
510}
511
512void QV4L2Camera::closeV4L2Fd()
513{
514 Q_ASSERT(!m_memoryTransfer);
515
516 m_v4l2Info = {};
517 m_cameraBusy = false;
518 m_v4l2FileDescriptor = nullptr;
519}
520
521int QV4L2Camera::setV4L2ColorTemperature(int temperature)
522{
523 struct v4l2_control control;
524 ::memset(s: &control, c: 0, n: sizeof(control));
525
526 if (m_v4l2Info.autoWhiteBalanceSupported) {
527 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, value: temperature == 0 ? true : false);
528 } else if (temperature == 0) {
529 temperature = 5600;
530 }
531
532 if (temperature != 0 && m_v4l2Info.colorTemperatureSupported) {
533 temperature = qBound(min: m_v4l2Info.minColorTemp, val: temperature, max: m_v4l2Info.maxColorTemp);
534 if (!setV4L2Parameter(
535 V4L2_CID_WHITE_BALANCE_TEMPERATURE,
536 value: qBound(min: m_v4l2Info.minColorTemp, val: temperature, max: m_v4l2Info.maxColorTemp)))
537 temperature = 0;
538 } else {
539 temperature = 0;
540 }
541
542 return temperature;
543}
544
545bool QV4L2Camera::setV4L2Parameter(quint32 id, qint32 value)
546{
547 v4l2_control control{ .id: id, .value: value };
548 if (!m_v4l2FileDescriptor->call(VIDIOC_S_CTRL, arg: &control)) {
549 qWarning() << "Unable to set the V4L2 Parameter" << Qt::hex << id << "to" << value << qt_error_string(errno);
550 return false;
551 }
552 return true;
553}
554
555int QV4L2Camera::getV4L2Parameter(quint32 id) const
556{
557 struct v4l2_control control{.id: id, .value: 0};
558 if (!m_v4l2FileDescriptor->call(VIDIOC_G_CTRL, arg: &control)) {
559 qWarning() << "Unable to get the V4L2 Parameter" << Qt::hex << id << qt_error_string(errno);
560 return 0;
561 }
562 return control.value;
563}
564
565void QV4L2Camera::setV4L2CameraFormat()
566{
567 if (m_v4l2Info.formatInitialized || !m_v4l2FileDescriptor)
568 return;
569
570 Q_ASSERT(!m_cameraFormat.isNull());
571 qCDebug(qLcV4L2Camera) << "XXXXX" << this << m_cameraDevice.id() << m_cameraFormat.pixelFormat()
572 << m_cameraFormat.resolution();
573
574 v4l2_format fmt = {};
575 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
576
577 auto size = m_cameraFormat.resolution();
578 fmt.fmt.pix.width = size.width();
579 fmt.fmt.pix.height = size.height();
580 fmt.fmt.pix.pixelformat = v4l2FormatForPixelFormat(format: m_cameraFormat.pixelFormat());
581 fmt.fmt.pix.field = V4L2_FIELD_ANY;
582
583 qCDebug(qLcV4L2Camera) << "setting camera format to" << size << fmt.fmt.pix.pixelformat;
584
585 if (!m_v4l2FileDescriptor->call(VIDIOC_S_FMT, arg: &fmt)) {
586 if (errno == EBUSY) {
587 setCameraBusy();
588 return;
589 }
590 qWarning() << "Couldn't set video format on v4l2 camera" << strerror(errno);
591 }
592
593 m_v4l2Info.formatInitialized = true;
594 m_cameraBusy = false;
595
596 m_bytesPerLine = fmt.fmt.pix.bytesperline;
597 m_imageSize = std::max(a: fmt.fmt.pix.sizeimage, b: m_bytesPerLine * fmt.fmt.pix.height);
598
599 switch (v4l2_colorspace(fmt.fmt.pix.colorspace)) {
600 default:
601 case V4L2_COLORSPACE_DCI_P3:
602 m_colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
603 break;
604 case V4L2_COLORSPACE_REC709:
605 m_colorSpace = QVideoFrameFormat::ColorSpace_BT709;
606 break;
607 case V4L2_COLORSPACE_JPEG:
608 m_colorSpace = QVideoFrameFormat::ColorSpace_AdobeRgb;
609 break;
610 case V4L2_COLORSPACE_SRGB:
611 // ##### is this correct???
612 m_colorSpace = QVideoFrameFormat::ColorSpace_BT601;
613 break;
614 case V4L2_COLORSPACE_BT2020:
615 m_colorSpace = QVideoFrameFormat::ColorSpace_BT2020;
616 break;
617 }
618
619 v4l2_streamparm streamParam = {};
620 streamParam.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
621
622 streamParam.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
623 auto [num, den] = qRealToFraction(value: 1./m_cameraFormat.maxFrameRate());
624 streamParam.parm.capture.timeperframe = { .numerator: (uint)num, .denominator: (uint)den };
625 m_v4l2FileDescriptor->call(VIDIOC_S_PARM, arg: &streamParam);
626
627 m_frameDuration = 1000000 * streamParam.parm.capture.timeperframe.numerator
628 / streamParam.parm.capture.timeperframe.denominator;
629}
630
631void QV4L2Camera::initV4L2MemoryTransfer()
632{
633 if (m_cameraBusy)
634 return;
635
636 Q_ASSERT(!m_memoryTransfer);
637
638 m_memoryTransfer = makeUserPtrMemoryTransfer(fileDescriptor: m_v4l2FileDescriptor, imageSize: m_imageSize);
639
640 if (m_memoryTransfer)
641 return;
642
643 if (errno == EBUSY) {
644 setCameraBusy();
645 return;
646 }
647
648 qCDebug(qLcV4L2Camera) << "Cannot init V4L2_MEMORY_USERPTR; trying V4L2_MEMORY_MMAP";
649
650 m_memoryTransfer = makeMMapMemoryTransfer(fileDescriptor: m_v4l2FileDescriptor);
651
652 if (!m_memoryTransfer) {
653 qCWarning(qLcV4L2Camera) << "Cannot init v4l2 memory transfer," << qt_error_string(errno);
654 updateError(error: QCamera::CameraError, errorString: QLatin1String("Cannot init V4L2 memory transfer"));
655 }
656}
657
658void QV4L2Camera::stopCapturing()
659{
660 if (!m_memoryTransfer || !m_v4l2FileDescriptor)
661 return;
662
663 m_notifier = nullptr;
664
665 if (!m_v4l2FileDescriptor->stopStream()) {
666 // TODO: handle the case carefully to avoid possible memory corruption
667 if (errno != ENODEV)
668 qWarning() << "failed to stop capture";
669 }
670
671 m_memoryTransfer = nullptr;
672 m_cameraBusy = false;
673}
674
675void QV4L2Camera::startCapturing()
676{
677 if (!m_v4l2FileDescriptor)
678 return;
679
680 setV4L2CameraFormat();
681 initV4L2MemoryTransfer();
682
683 if (m_cameraBusy || !m_memoryTransfer)
684 return;
685
686 if (!m_v4l2FileDescriptor->startStream()) {
687 qWarning() << "Couldn't start v4l2 camera stream";
688 return;
689 }
690
691 m_notifier =
692 std::make_unique<QSocketNotifier>(args: m_v4l2FileDescriptor->get(), args: QSocketNotifier::Read);
693 connect(sender: m_notifier.get(), signal: &QSocketNotifier::activated, context: this, slot: &QV4L2Camera::readFrame);
694
695 m_firstFrameTime = { .tv_sec: -1, .tv_usec: -1 };
696}
697
698QVideoFrameFormat QV4L2Camera::frameFormat() const
699{
700 auto result = QPlatformCamera::frameFormat();
701 result.setColorSpace(m_colorSpace);
702 return result;
703}
704
705QT_END_NAMESPACE
706
707#include "moc_qv4l2camera_p.cpp"
708

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtmultimedia/src/plugins/multimedia/ffmpeg/qv4l2camera.cpp