Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4l2camera.cpp
Go to the documentation of this file.
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
5
6#include <QtMultimedia/private/qcameradevice_p.h>
7#include <QtMultimedia/private/qmultimediautils_p.h>
8#include <QtMultimedia/private/qv4l2filedescriptor_p.h>
9#include <QtMultimedia/private/qv4l2memorytransfer_p.h>
10#include <QtMultimedia/private/qv4l2formats_p.h>
11#include <QtMultimedia/private/qvideoframe_p.h>
12#include <QtCore/qloggingcategory.h>
13#include <QtCore/qsocketnotifier.h>
14#include <QtCore/private/qcore_unix_p.h>
15
16#include <sys/time.h>
17
18#include <chrono>
19
21
22namespace QtV4L2 {
23
24using namespace Qt::Literals;
25
26Q_STATIC_LOGGING_CATEGORY(qLcV4L2Camera, "qt.multimedia.v4l2.camera");
27
28static std::chrono::microseconds toMicroseconds(const timeval &tv)
29{
30 return std::chrono::seconds{ tv.tv_sec } + std::chrono::microseconds{ tv.tv_usec };
31}
32
34
40
41bool QV4L2Camera::isActive() const
42{
43 return m_active;
44}
45
47{
48 if (m_active == active)
49 return;
51 return;
52
55
57 if (m_active)
59 else
61
63
65}
66
68{
70 return;
71
74
77
79
80 if (m_active)
82}
83
85{
87 return false;
88
90 return true;
91
92 if (m_active) {
95
98 }
99
100 return true;
101}
102
104{
105 auto fmt = format;
106 if (fmt.isNull())
108
109 if (fmt == m_cameraFormat)
110 return false;
111
113 // The device must be reconfigured with the new format before the next stream start.
115 return true;
116}
117
119{
120 if (mode == focusMode())
121 return;
122
125 return;
126
127 switch (mode) {
128 default:
130 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 1);
132 setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, V4L2_AUTO_FOCUS_RANGE_AUTO);
133 break;
135 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 1);
137 setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, V4L2_AUTO_FOCUS_RANGE_MACRO);
138 else if (focusDist)
139 setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, m_v4l2Info.minFocus);
140 break;
142 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 1);
144 setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, V4L2_AUTO_FOCUS_RANGE_INFINITY);
145 break;
147 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 0);
148 setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, m_v4l2Info.maxFocus);
149 break;
151 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 0);
153 break;
154 }
156}
157
159{
161 setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, distance);
163}
164
165void QV4L2Camera::zoomTo(float factor, float)
166{
168 return;
169 factor = qBound(1., factor, 2.);
171 setV4L2Parameter(V4L2_CID_ZOOM_ABSOLUTE, zoom);
173}
174
184
194
196{
198 return true;
199 return mode == QCamera::FlashOff;
200}
201
203{
204 return getV4L2Parameter(V4L2_CID_FLASH_READY) != 0;
205}
206
216
218{
219 if (mode == QCamera::TorchOn)
221 return mode == QCamera::TorchOff;
222}
223
235
244
255
257{
259 return;
260 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY_AUTO,
262 if (iso > 0) {
263 iso = qBound(minIso(), iso, maxIso());
264 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY, iso);
265 }
266}
267
269{
271 return -1;
272 return getV4L2Parameter(V4L2_CID_ISO_SENSITIVITY);
273}
274
276{
278 int exposure =
280 setV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE, exposure);
282 return;
283 }
284}
285
287{
288 return getV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE) / 10000.;
289}
290
298
309
323
325{
327
329 if (!dequeued) {
330 qCWarning(qLcV4L2Camera) << "Cannot take buffer";
331
332 if (errno == ENODEV) {
333 // camera got removed while being active
335 closeV4L2Fd();
336 }
337
338 return;
339 }
340
343
345 if (!m_firstFrameTime)
347 const auto frameTime = timestamp - *m_firstFrameTime;
350
352}
353
355{
356 m_cameraBusy = true;
357 updateError(QCamera::CameraError, QLatin1String("Camera is in use"));
358}
359
361{
362 m_v4l2Info = {};
364
367
368 closeV4L2Fd();
369
371 if (descriptor == -1) {
372 qCWarning(qLcV4L2Camera) << "Unable to open the camera" << deviceName
373 << "for read to query the parameter info:"
375 updateError(QCamera::CameraError, QLatin1String("Cannot open camera"));
376 return;
377 }
378
381
382 qCDebug(qLcV4L2Camera) << "FD=" << descriptor;
383
385 ::memset(&queryControl, 0, sizeof(queryControl));
386 queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
387
388 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
390 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, true);
391 }
392
393 ::memset(&queryControl, 0, sizeof(queryControl));
394 queryControl.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE;
395 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
400 }
401
402 ::memset(&queryControl, 0, sizeof(queryControl));
403 queryControl.id = V4L2_CID_EXPOSURE_AUTO;
404 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
406 }
407
408 ::memset(&queryControl, 0, sizeof(queryControl));
409 queryControl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
410 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
415 }
416
417 ::memset(&queryControl, 0, sizeof(queryControl));
418 queryControl.id = V4L2_CID_AUTO_EXPOSURE_BIAS;
419 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
423 }
424
425 ::memset(&queryControl, 0, sizeof(queryControl));
426 queryControl.id = V4L2_CID_ISO_SENSITIVITY_AUTO;
427 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
428 queryControl.id = V4L2_CID_ISO_SENSITIVITY;
429 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
433 }
434 }
435
436 ::memset(&queryControl, 0, sizeof(queryControl));
437 queryControl.id = V4L2_CID_FOCUS_ABSOLUTE;
438 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
442 }
443
444 ::memset(&queryControl, 0, sizeof(queryControl));
445 queryControl.id = V4L2_CID_AUTO_FOCUS_RANGE;
446 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
447 m_v4l2Info.rangedFocus = true;
448 }
449
450 ::memset(&queryControl, 0, sizeof(queryControl));
451 queryControl.id = V4L2_CID_FLASH_LED_MODE;
452 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
457 }
458
459 ::memset(&queryControl, 0, sizeof(queryControl));
460 queryControl.id = V4L2_CID_ZOOM_ABSOLUTE;
461 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
464 }
465 // zoom factors are in arbitrary units, so we simply normalize them to go from 1 to 2
466 // if they are different
469
471}
472
474{
476
477 m_v4l2Info = {};
478 m_pixFormat = {};
479 m_cameraBusy = false;
480 m_v4l2FileDescriptor = nullptr;
481}
482
484{
486 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, temperature == 0 ? true : false);
487 } else if (temperature == 0) {
488 temperature = 5600;
489 }
490
493 if (!setV4L2Parameter(
494 V4L2_CID_WHITE_BALANCE_TEMPERATURE,
496 temperature = 0;
497 } else {
498 temperature = 0;
499 }
500
501 return temperature;
502}
503
505{
507 if (!m_v4l2FileDescriptor->call(VIDIOC_S_CTRL, &control)) {
508 qWarning() << "Unable to set the V4L2 Parameter" << Qt::hex << id << "to" << value
510 return false;
511 }
512 return true;
513}
514
516{
517 struct v4l2_control control{ id, 0 };
518 if (!m_v4l2FileDescriptor->call(VIDIOC_G_CTRL, &control)) {
519 qWarning() << "Unable to get the V4L2 Parameter" << Qt::hex << id << qt_error_string(errno);
520 return 0;
521 }
522 return control.value;
523}
524
526{
528 return {};
529
531
535
536 v4l2_format fmt = {};
538
540 if (!v4l2PixelFormat) {
541 qWarning() << "Unsupported pixel format" << m_cameraFormat.pixelFormat();
542 return q23::unexpected{ u"Unsupported V4L2 pixel format"_s };
543 }
544
550
551 qCDebug(qLcV4L2Camera) << "setting camera format to" << size << fmt.fmt.pix.pixelformat;
552
553 auto setFormatResult = m_v4l2FileDescriptor->call(VIDIOC_S_FMT, &fmt);
554 if (!setFormatResult) {
556 m_cameraBusy = true;
557 return q23::unexpected{ u"Camera is in use"_s };
558 }
559 qWarning() << "Couldn't set video format on v4l2 camera" << qt_error_string(errno);
560 return q23::unexpected{ u"Cannot set V4L2 camera format"_s };
561 }
562
563 // The driver may have adjusted the requested format, so keep what it negotiated.
565
568
570 || (m_pixFormat.bytesperline == 0 && !isCompressed)) {
571 qWarning() << "Driver negotiated an unusable camera format" << m_pixFormat.pixelformat;
572 return q23::unexpected{ u"Unsupported V4L2 camera format"_s };
573 }
574
576 m_cameraBusy = false;
577
580
581 streamParam.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
585 m_v4l2FileDescriptor->call(VIDIOC_S_PARM, &streamParam);
586
587 using namespace std::chrono;
589 return {};
590}
591
593{
594 if (m_cameraBusy)
595 return;
596
598
599 // QT_V4L2_CAMERA_MEMORY=dmabuf|userptr|mmap selects one strategy, with no fallback.
600 const QByteArray forcedStrategy = qgetenv("QT_V4L2_CAMERA_MEMORY");
601 const auto isAllowed = [&](QByteArrayView strategy) {
603 };
604
605#if QT_CONFIG(linux_dmabuf)
606 if (isAllowed("dmabuf")) {
608
610 return;
611 }
612#endif
613
614 if (isAllowed("userptr")) {
616
618 return;
619
620 if (errno == EBUSY) {
622 return;
623 }
624 }
625
626 if (isAllowed("mmap")) {
627 qCDebug(qLcV4L2Camera) << "Trying V4L2_MEMORY_MMAP";
628
630 }
631
632 if (!m_memoryTransfer) {
633 qCWarning(qLcV4L2Camera) << "Cannot init v4l2 memory transfer," << qt_error_string(errno);
634 updateError(QCamera::CameraError, QLatin1String("Cannot init V4L2 memory transfer"));
635 }
636}
637
639{
641 return;
642
643 m_notifier = nullptr;
644
646 // TODO: handle the case carefully to avoid possible memory corruption
647 if (errno != ENODEV)
648 qWarning() << "failed to stop capture";
649 }
650
651 m_memoryTransfer = nullptr;
652 m_cameraBusy = false;
653}
654
656{
658 return;
659
661 if (!setFormatResult) {
663 return;
664 }
666
668 return;
669
671 qWarning() << "Couldn't start v4l2 camera stream";
672 return;
673 }
674
675 m_notifier =
678 this->readFrame();
679 });
680
682}
683
696
697} // namespace QtV4L2
698
699QT_END_NAMESPACE
700
701#include "moc_qv4l2camera_p.cpp"
Combined button and popup list for selecting options.
static std::chrono::microseconds toMicroseconds(const timeval &tv)
Q_STATIC_LOGGING_CATEGORY(qLcV4L2Camera, "qt.multimedia.v4l2.camera")