Qt 6.x
The Qt SDK
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
4#include "qv4l2camera_p.h"
5
6#include <qdir.h>
7#include <qmutex.h>
8#include <qendian.h>
9#include <private/qcameradevice_p.h>
10#include <private/qabstractvideobuffer_p.h>
11#include <private/qvideotexturehelper_p.h>
12#include <private/qmultimediautils_p.h>
13#include <private/qplatformmediadevices_p.h>
14
15#include <sys/types.h>
16#include <sys/stat.h>
17#include <sys/ioctl.h>
18#include <unistd.h>
19#include <fcntl.h>
20#include <private/qcore_unix_p.h>
21#include <sys/mman.h>
22
23#include <linux/videodev2.h>
24
25#include <qloggingcategory.h>
26
28
29static Q_LOGGING_CATEGORY(qLV4L2Camera, "qt.multimedia.ffmpeg.v4l2camera");
30
32 auto areCamerasDataEqual = [](const QCameraDevice& a, const QCameraDevice& b) {
36 };
37
38 return std::equal(a.cbegin(), a.cend(), b.cbegin(), b.cend(), areCamerasDataEqual);
39}
40
42 : QPlatformVideoDevices(integration)
43{
44 m_deviceWatcher.addPath(QLatin1String("/dev"));
46 doCheckCameras();
47}
48
50{
51 return m_cameras;
52}
53
55{
56 if (doCheckCameras())
58}
59
60static const struct {
62 uint32_t v4l2Format;
63} formatMap[] = {
64 // ### How do we handle V4L2_PIX_FMT_H264 and V4L2_PIX_FMT_MPEG4?
65 { QVideoFrameFormat::Format_YUV420P, V4L2_PIX_FMT_YUV420 },
66 { QVideoFrameFormat::Format_YUV422P, V4L2_PIX_FMT_YUV422P },
67 { QVideoFrameFormat::Format_YUYV, V4L2_PIX_FMT_YUYV },
68 { QVideoFrameFormat::Format_UYVY, V4L2_PIX_FMT_UYVY },
69 { QVideoFrameFormat::Format_XBGR8888, V4L2_PIX_FMT_XBGR32 },
70 { QVideoFrameFormat::Format_XRGB8888, V4L2_PIX_FMT_XRGB32 },
71 { QVideoFrameFormat::Format_ABGR8888, V4L2_PIX_FMT_ABGR32 },
72 { QVideoFrameFormat::Format_ARGB8888, V4L2_PIX_FMT_ARGB32 },
73 { QVideoFrameFormat::Format_BGRX8888, V4L2_PIX_FMT_BGR32 },
74 { QVideoFrameFormat::Format_RGBX8888, V4L2_PIX_FMT_RGB32 },
75 { QVideoFrameFormat::Format_BGRA8888, V4L2_PIX_FMT_BGRA32 },
76 { QVideoFrameFormat::Format_RGBA8888, V4L2_PIX_FMT_RGBA32 },
77 { QVideoFrameFormat::Format_Y8, V4L2_PIX_FMT_GREY },
78 { QVideoFrameFormat::Format_Y16, V4L2_PIX_FMT_Y16 },
79 { QVideoFrameFormat::Format_NV12, V4L2_PIX_FMT_NV12 },
80 { QVideoFrameFormat::Format_NV21, V4L2_PIX_FMT_NV21 },
81 { QVideoFrameFormat::Format_Jpeg, V4L2_PIX_FMT_MJPEG },
82 { QVideoFrameFormat::Format_Jpeg, V4L2_PIX_FMT_JPEG },
84};
85
87{
88 auto *f = formatMap;
89 while (f->v4l2Format) {
90 if (f->v4l2Format == v4l2Format)
91 return f->fmt;
92 ++f;
93 }
95}
96
98{
99 auto *f = formatMap;
100 while (f->v4l2Format) {
101 if (f->fmt == format)
102 return f->v4l2Format;
103 ++f;
104 }
105 return 0;
106}
107
108
109bool QV4L2CameraDevices::doCheckCameras()
110{
111 QList<QCameraDevice> newCameras;
112
113 QDir dir(QLatin1String("/dev"));
114 const auto devices = dir.entryList(QDir::System);
115
116 bool first = true;
117
118 for (auto device : devices) {
119// qCDebug(qLV4L2Camera) << "device:" << device;
120 if (!device.startsWith(QLatin1String("video")))
121 continue;
122
124 const int fd = open(file.constData(), O_RDONLY);
125 if (fd < 0)
126 continue;
127
128 auto fileCloseGuard = qScopeGuard([fd](){ close(fd); });
129
130 v4l2_fmtdesc formatDesc = {};
131
132 struct v4l2_capability cap;
133 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
134 continue;
135
136 if (cap.device_caps & V4L2_CAP_META_CAPTURE)
137 continue;
138 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
139 continue;
140 if (!(cap.capabilities & V4L2_CAP_STREAMING))
141 continue;
142
143 auto camera = std::make_unique<QCameraDevicePrivate>();
144
145 camera->id = file;
146 camera->description = QString::fromUtf8((const char *)cap.card);
147// qCDebug(qLV4L2Camera) << "found camera" << camera->id << camera->description;
148
149 formatDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
150
151 while (!ioctl(fd, VIDIOC_ENUM_FMT, &formatDesc)) {
152 auto pixelFmt = formatForV4L2Format(formatDesc.pixelformat);
153 qCDebug(qLV4L2Camera) << " " << pixelFmt;
154
155 if (pixelFmt == QVideoFrameFormat::Format_Invalid) {
156 ++formatDesc.index;
157 continue;
158 }
159
160// qCDebug(qLV4L2Camera) << "frame sizes:";
161 v4l2_frmsizeenum frameSize = {};
162 frameSize.pixel_format = formatDesc.pixelformat;
163
164 while (!ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frameSize)) {
165 ++frameSize.index;
166 if (frameSize.type != V4L2_FRMSIZE_TYPE_DISCRETE)
167 continue;
168
169 QSize resolution(frameSize.discrete.width, frameSize.discrete.height);
170 float min = 1e10;
171 float max = 0;
172
173 v4l2_frmivalenum frameInterval = {};
174 frameInterval.pixel_format = formatDesc.pixelformat;
175 frameInterval.width = frameSize.discrete.width;
176 frameInterval.height = frameSize.discrete.height;
177
178 while (!ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frameInterval)) {
179 ++frameInterval.index;
180 if (frameInterval.type != V4L2_FRMIVAL_TYPE_DISCRETE)
181 continue;
182 float rate = float(frameInterval.discrete.denominator)/float(frameInterval.discrete.numerator);
183 if (rate > max)
184 max = rate;
185 if (rate < min)
186 min = rate;
187 }
188
189// qCDebug(qLV4L2Camera) << " " << resolution << min << max;
190
191 if (min <= max) {
192 auto fmt = std::make_unique<QCameraFormatPrivate>();
193 fmt->pixelFormat = pixelFmt;
194 fmt->resolution = resolution;
195 fmt->minFrameRate = min;
196 fmt->maxFrameRate = max;
197 camera->videoFormats.append(fmt.release()->create());
198 camera->photoResolutions.append(resolution);
199 }
200 }
201
202 ++formatDesc.index;
203 }
204
205 // first camera is default
206 camera->isDefault = std::exchange(first, false);
207
208 newCameras.append(camera.release()->create());
209 }
210
211 if (areCamerasEqual(m_cameras, newCameras))
212 return false;
213
214 m_cameras = std::move(newCameras);
215 return true;
216}
217
219{
220public:
223 , index(index)
224 , d(d)
225 {}
227 {
228 d->release(index);
229 }
230
231 QVideoFrame::MapMode mapMode() const override { return m_mode; }
233 m_mode = mode;
234 return d->v4l2FileDescriptor >= 0 ? data : MapData{};
235 }
236 void unmap() override {
238 }
239
242 int index = 0;
244};
245
247{
248 QMutexLocker locker(&mutex);
250 unmapBuffers();
251}
252
253
254
256{
257 QMutexLocker locker(&mutex);
258 if (v4l2FileDescriptor < 0 || index >= mappedBuffers.size())
259 return;
260
261 struct v4l2_buffer buf = {};
262
263 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
264 buf.memory = V4L2_MEMORY_MMAP;
265 buf.index = index;
266
267 if (ioctl(v4l2FileDescriptor, VIDIOC_QBUF, &buf) < 0)
268 qWarning() << "Couldn't release V4L2 buffer" << errno << strerror(errno) << index;
269}
270
272{
273 for (const auto &b : std::as_const(mappedBuffers))
274 munmap(b.data, b.size);
275 mappedBuffers.clear();
276}
277
280{
281}
282
284{
285 setActive(false);
286 stopCapturing();
287 closeV4L2Fd();
288}
289
291{
292 return m_active;
293}
294
295void QV4L2Camera::setActive(bool active)
296{
297 if (m_active == active)
298 return;
299 if (m_cameraDevice.isNull() && active)
300 return;
301
304
305 m_active = active;
306 if (m_active) {
307 setV4L2CameraFormat();
308 initMMap();
309 startCapturing();
310 } else {
311 stopCapturing();
312 }
314
315 emit activeChanged(active);
316}
317
319{
320 if (m_cameraDevice == camera)
321 return;
322 if (m_active)
323 stopCapturing();
324
325 closeV4L2Fd();
326
327 m_cameraDevice = camera;
329
330 initV4L2Controls();
331
332 if (m_active) {
333 setV4L2CameraFormat();
334 initMMap();
335 startCapturing();
336 }
337}
338
340{
341 if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format))
342 return false;
343
345 return true;
346
347 if (m_active) {
348 stopCapturing();
349 closeV4L2Fd();
350 initV4L2Controls();
351 setV4L2CameraFormat();
352 initMMap();
353 startCapturing();
354 }
355
356 return true;
357}
358
360{
361 auto fmt = format;
362 if (fmt.isNull())
363 fmt = findBestCameraFormat(m_cameraDevice);
364
365 if (fmt == m_cameraFormat)
366 return false;
367
369 return true;
370}
371
373{
374 if (mode == focusMode())
375 return;
376
378 if (!focusDist && !v4l2RangedFocus)
379 return;
380
381 switch (mode) {
382 default:
384 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 1);
385 if (v4l2RangedFocus)
386 setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, V4L2_AUTO_FOCUS_RANGE_AUTO);
387 break;
389 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 1);
390 if (v4l2RangedFocus)
391 setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, V4L2_AUTO_FOCUS_RANGE_MACRO);
392 else if (focusDist)
393 setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, v4l2MinFocus);
394 break;
396 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 1);
397 if (v4l2RangedFocus)
398 setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, V4L2_AUTO_FOCUS_RANGE_INFINITY);
399 break;
401 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 0);
402 setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, v4l2MaxFocus);
403 break;
405 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 0);
407 break;
408 }
410}
411
413{
414 int distance = v4l2MinFocus + int((v4l2MaxFocus - v4l2MinFocus)*d);
415 setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, distance);
417}
418
419void QV4L2Camera::zoomTo(float factor, float)
420{
421 if (v4l2MaxZoom == v4l2MinZoom)
422 return;
423 factor = qBound(1., factor, 2.);
424 int zoom = v4l2MinZoom + (factor - 1.)*(v4l2MaxZoom - v4l2MinZoom);
425 setV4L2Parameter(V4L2_CID_ZOOM_ABSOLUTE, zoom);
426 zoomFactorChanged(factor);
427}
428
430{
433 return true;
434
436}
437
439{
440 if (!v4l2FlashSupported || mode == QCamera::FlashOn)
441 return;
442 setV4L2Parameter(V4L2_CID_FLASH_LED_MODE, mode == QCamera::FlashAuto ? V4L2_FLASH_LED_MODE_FLASH : V4L2_FLASH_LED_MODE_NONE);
444}
445
447{
448 if (v4l2FlashSupported && mode == QCamera::FlashAuto)
449 return true;
450 return mode == QCamera::FlashOff;
451}
452
454{
455 struct v4l2_queryctrl queryControl;
456 ::memset(&queryControl, 0, sizeof(queryControl));
457 queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
458
459 if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0)
460 return true;
461
462 return false;
463}
464
466{
467 if (!v4l2TorchSupported || mode == QCamera::TorchOn)
468 return;
469 setV4L2Parameter(V4L2_CID_FLASH_LED_MODE, mode == QCamera::TorchOn ? V4L2_FLASH_LED_MODE_TORCH : V4L2_FLASH_LED_MODE_NONE);
471}
472
474{
475 if (mode == QCamera::TorchOn)
476 return v4l2TorchSupported;
477 return mode == QCamera::TorchOff;
478}
479
481{
482 if (v4l2AutoExposureSupported && v4l2ManualExposureSupported) {
484 return;
485 int value = QCamera::ExposureAuto ? V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL;
486 setV4L2Parameter(V4L2_CID_EXPOSURE_AUTO, value);
488 return;
489 }
490}
491
493{
495 return true;
496 if (v4l2ManualExposureSupported && v4l2AutoExposureSupported)
498 return false;
499}
500
502{
503 if ((v4l2MinExposureAdjustment != 0 || v4l2MaxExposureAdjustment != 0)) {
504 int value = qBound(v4l2MinExposureAdjustment, (int)(compensation*1000), v4l2MaxExposureAdjustment);
505 setV4L2Parameter(V4L2_CID_AUTO_EXPOSURE_BIAS, value);
507 return;
508 }
509}
510
512{
514 return;
515 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY_AUTO, iso <= 0 ? V4L2_ISO_SENSITIVITY_AUTO : V4L2_ISO_SENSITIVITY_MANUAL);
516 if (iso > 0) {
517 iso = qBound(minIso(), iso, maxIso());
518 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY, iso);
519 }
520 return;
521}
522
524{
526 return -1;
527 return getV4L2Parameter(V4L2_CID_ISO_SENSITIVITY);
528}
529
531{
532 if (v4l2ManualExposureSupported && v4l2AutoExposureSupported) {
533 int exposure = qBound(v4l2MinExposure, qRound(secs*10000.), v4l2MaxExposure);
534 setV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE, exposure);
535 exposureTimeChanged(exposure/10000.);
536 return;
537 }
538}
539
541{
542 return getV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE)/10000.;
543}
544
546{
547 if (v4l2AutoWhiteBalanceSupported && v4l2ColorTemperatureSupported)
548 return true;
549
551}
552
554{
556
557 int temperature = colorTemperatureForWhiteBalance(mode);
558 int t = setV4L2ColorTemperature(temperature);
559 if (t == 0)
562}
563
565{
566 if (temperature == 0) {
568 return;
569 }
570
572
573 int t = setV4L2ColorTemperature(temperature);
574 if (t)
576}
577
578void QV4L2Camera::readFrame()
579{
580 if (!d)
581 return;
582
583 v4l2_buffer buf = {};
584 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
585 buf.memory = V4L2_MEMORY_MMAP;
586
587 if (ioctl(d->v4l2FileDescriptor, VIDIOC_DQBUF, &buf) < 0) {
588 if (errno == ENODEV) {
589 // camera got removed while being active
590 stopCapturing();
591 closeV4L2Fd();
592 return;
593 }
594 if (errno != EAGAIN)
595 qWarning() << "error calling VIDIOC_DQBUF" << errno << strerror(errno);
596 }
597
598 Q_ASSERT(qsizetype(buf.index) < d->mappedBuffers.size());
599 int i = buf.index;
600
601// auto textureDesc = QVideoTextureHelper::textureDescription(m_format.pixelFormat());
602
604 buffer->data.nPlanes = 1;
605 buffer->data.bytesPerLine[0] = bytesPerLine;
606 buffer->data.data[0] = (uchar *)d->mappedBuffers.at(i).data;
607 buffer->data.size[0] = d->mappedBuffers.at(i).size;
609 fmt.setColorSpace(colorSpace);
610// qCDebug(qLV4L2Camera) << "got a frame" << d->mappedBuffers.at(i).data << d->mappedBuffers.at(i).size << fmt << i;
612
613 if (firstFrameTime.tv_sec == -1)
614 firstFrameTime = buf.timestamp;
615 qint64 secs = buf.timestamp.tv_sec - firstFrameTime.tv_sec;
616 qint64 usecs = buf.timestamp.tv_usec - firstFrameTime.tv_usec;
617 frame.setStartTime(secs*1000000 + usecs);
618 frame.setEndTime(frame.startTime() + frameDuration);
619
621}
622
623void QV4L2Camera::setCameraBusy()
624{
625 cameraBusy = true;
626 error(QCamera::CameraError, tr("Camera is in use."));
627}
628
629void QV4L2Camera::initV4L2Controls()
630{
631 v4l2AutoWhiteBalanceSupported = false;
632 v4l2ColorTemperatureSupported = false;
633 v4l2RangedFocus = false;
634 v4l2FlashSupported = false;
635 v4l2TorchSupported = false;
636 QCamera::Features features;
637
638
639 const QByteArray deviceName = m_cameraDevice.id();
641
642 closeV4L2Fd();
643 Q_ASSERT(!d);
644
645 d = new QV4L2CameraBuffers;
646
647 d->v4l2FileDescriptor = qt_safe_open(deviceName.constData(), O_RDWR);
648 if (d->v4l2FileDescriptor == -1) {
649 qWarning() << "Unable to open the camera" << deviceName
650 << "for read to query the parameter info:" << qt_error_string(errno);
651 return;
652 }
653 qCDebug(qLV4L2Camera) << "FD=" << d->v4l2FileDescriptor;
654
655 struct v4l2_queryctrl queryControl;
656 ::memset(&queryControl, 0, sizeof(queryControl));
657 queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
658
659 if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
660 v4l2AutoWhiteBalanceSupported = true;
661 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, true);
662 }
663
664 ::memset(&queryControl, 0, sizeof(queryControl));
665 queryControl.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE;
666 if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
667 v4l2MinColorTemp = queryControl.minimum;
668 v4l2MaxColorTemp = queryControl.maximum;
669 v4l2ColorTemperatureSupported = true;
671 }
672
673 ::memset(&queryControl, 0, sizeof(queryControl));
674 queryControl.id = V4L2_CID_EXPOSURE_AUTO;
675 if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
676 v4l2AutoExposureSupported = true;
677 }
678
679 ::memset(&queryControl, 0, sizeof(queryControl));
680 queryControl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
681 if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
682 v4l2ManualExposureSupported = true;
683 v4l2MinExposure = queryControl.minimum;
684 v4l2MaxExposure = queryControl.maximum;
686 }
687
688 ::memset(&queryControl, 0, sizeof(queryControl));
689 queryControl.id = V4L2_CID_AUTO_EXPOSURE_BIAS;
690 if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
691 v4l2MinExposureAdjustment = queryControl.minimum;
692 v4l2MaxExposureAdjustment = queryControl.maximum;
694 }
695
696 ::memset(&queryControl, 0, sizeof(queryControl));
697 queryControl.id = V4L2_CID_ISO_SENSITIVITY_AUTO;
698 if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
699 queryControl.id = V4L2_CID_ISO_SENSITIVITY;
700 if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
702 minIsoChanged(queryControl.minimum);
703 maxIsoChanged(queryControl.minimum);
704 }
705 }
706
707 ::memset(&queryControl, 0, sizeof(queryControl));
708 queryControl.id = V4L2_CID_FOCUS_ABSOLUTE;
709 if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
710 v4l2MinExposureAdjustment = queryControl.minimum;
711 v4l2MaxExposureAdjustment = queryControl.maximum;
713 }
714
715 ::memset(&queryControl, 0, sizeof(queryControl));
716 queryControl.id = V4L2_CID_AUTO_FOCUS_RANGE;
717 if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
718 v4l2RangedFocus = true;
719 }
720
721 ::memset(&queryControl, 0, sizeof(queryControl));
722 queryControl.id = V4L2_CID_FLASH_LED_MODE;
723 if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
724 v4l2FlashSupported = queryControl.minimum <= V4L2_FLASH_LED_MODE_FLASH && queryControl.maximum >= V4L2_FLASH_LED_MODE_FLASH;
725 v4l2TorchSupported = queryControl.minimum <= V4L2_FLASH_LED_MODE_TORCH && queryControl.maximum >= V4L2_FLASH_LED_MODE_TORCH;
726 }
727
728 v4l2MinZoom = 0;
729 v4l2MaxZoom = 0;
730 ::memset(&queryControl, 0, sizeof(queryControl));
731 queryControl.id = V4L2_CID_ZOOM_ABSOLUTE;
732 if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
733 v4l2MinZoom = queryControl.minimum;
734 v4l2MaxZoom = queryControl.maximum;
735 }
736 // zoom factors are in arbitrary units, so we simply normalize them to go from 1 to 2
737 // if they are different
739 maximumZoomFactorChanged(v4l2MinZoom != v4l2MaxZoom ? 2 : 1);
740
741 supportedFeaturesChanged(features);
742}
743
744void QV4L2Camera::closeV4L2Fd()
745{
746 if (d && d->v4l2FileDescriptor >= 0) {
747 QMutexLocker locker(&d->mutex);
748 d->unmapBuffers();
749 qt_safe_close(d->v4l2FileDescriptor);
750 d->v4l2FileDescriptor = -1;
751 }
752 d = nullptr;
753}
754
755int QV4L2Camera::setV4L2ColorTemperature(int temperature)
756{
757 struct v4l2_control control;
758 ::memset(&control, 0, sizeof(control));
759
760 if (v4l2AutoWhiteBalanceSupported) {
761 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, temperature == 0 ? true : false);
762 } else if (temperature == 0) {
763 temperature = 5600;
764 }
765
766 if (temperature != 0 && v4l2ColorTemperatureSupported) {
767 temperature = qBound(v4l2MinColorTemp, temperature, v4l2MaxColorTemp);
768 if (!setV4L2Parameter(V4L2_CID_WHITE_BALANCE_TEMPERATURE, qBound(v4l2MinColorTemp, temperature, v4l2MaxColorTemp)))
769 temperature = 0;
770 } else {
771 temperature = 0;
772 }
773
774 return temperature;
775}
776
777bool QV4L2Camera::setV4L2Parameter(quint32 id, qint32 value)
778{
779 struct v4l2_control control{id, value};
780 if (::ioctl(d->v4l2FileDescriptor, VIDIOC_S_CTRL, &control) != 0) {
781 qWarning() << "Unable to set the V4L2 Parameter" << Qt::hex << id << "to" << value << qt_error_string(errno);
782 return false;
783 }
784 return true;
785}
786
787int QV4L2Camera::getV4L2Parameter(quint32 id) const
788{
789 struct v4l2_control control{id, 0};
790 if (::ioctl(d->v4l2FileDescriptor, VIDIOC_G_CTRL, &control) != 0) {
791 qWarning() << "Unable to get the V4L2 Parameter" << Qt::hex << id << qt_error_string(errno);
792 return 0;
793 }
794 return control.value;
795}
796
797void QV4L2Camera::setV4L2CameraFormat()
798{
800 qCDebug(qLV4L2Camera) << "XXXXX" << this << m_cameraDevice.id() << m_cameraFormat.pixelFormat() << m_cameraFormat.resolution();
801
802 v4l2_format fmt = {};
803 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
804
806 fmt.fmt.pix.width = size.width();
807 fmt.fmt.pix.height = size.height();
809 fmt.fmt.pix.field = V4L2_FIELD_ANY;
810
811 qCDebug(qLV4L2Camera) << "setting camera format to" << size;
812
813 if (ioctl(d->v4l2FileDescriptor, VIDIOC_S_FMT, &fmt) < 0) {
814 if (errno == EBUSY) {
815 setCameraBusy();
816 return;
817 }
818 qWarning() << "Couldn't set video format on v4l2 camera" << strerror(errno);
819 }
820
821 bytesPerLine = fmt.fmt.pix.bytesperline;
822
823 switch (v4l2_colorspace(fmt.fmt.pix.colorspace)) {
824 default:
825 case V4L2_COLORSPACE_DCI_P3:
827 break;
828 case V4L2_COLORSPACE_REC709:
830 break;
831 case V4L2_COLORSPACE_JPEG:
833 break;
834 case V4L2_COLORSPACE_SRGB:
835 // ##### is this correct???
837 break;
838 case V4L2_COLORSPACE_BT2020:
840 break;
841 }
842
843 v4l2_streamparm streamParam = {};
844 streamParam.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
845
846 streamParam.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
848 streamParam.parm.capture.timeperframe = { (uint)num, (uint)den };
849 ioctl(d->v4l2FileDescriptor, VIDIOC_S_PARM, &streamParam);
850
851 frameDuration = 1000000*streamParam.parm.capture.timeperframe.numerator
852 /streamParam.parm.capture.timeperframe.denominator;
853}
854
855void QV4L2Camera::initMMap()
856{
857 if (cameraBusy)
858 return;
859
860 v4l2_requestbuffers req = {};
861 req.count = 4;
862 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
863 req.memory = V4L2_MEMORY_MMAP;
864
865 if (ioctl(d->v4l2FileDescriptor, VIDIOC_REQBUFS, &req) < 0) {
866 if (errno == EBUSY)
867 setCameraBusy();
868 qWarning() << "requesting mmap'ed buffers failed" << strerror(errno);
869 return;
870 }
871
872 if (req.count < 2) {
873 qWarning() << "Can't map 2 or more buffers";
874 return;
875 }
876
877 for (uint32_t n = 0; n < req.count; ++n) {
878 v4l2_buffer buf = {};
879 buf.index = n;
880 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
881 buf.memory = V4L2_MEMORY_MMAP;
882
883 if (ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYBUF, &buf) != 0) {
884 qWarning() << "Can't map buffer" << n;
885 return;
886 }
887
889 buffer.size = buf.length;
890 buffer.data = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,
891 d->v4l2FileDescriptor, buf.m.offset);
892
893 if (buffer.data == MAP_FAILED) {
894 qWarning() << "mmap failed" << n << buf.length << buf.m.offset;
895 return;
896 }
897
898 d->mappedBuffers.append(buffer);
899 }
900
901}
902
903void QV4L2Camera::stopCapturing()
904{
905 if (!d)
906 return;
907
908 delete notifier;
909 notifier = nullptr;
910
911 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
912
913 if (ioctl(d->v4l2FileDescriptor, VIDIOC_STREAMOFF, &type) < 0) {
914 if (errno != ENODEV)
915 qWarning() << "failed to stop capture";
916 }
917 cameraBusy = false;
918}
919
920void QV4L2Camera::startCapturing()
921{
922 if (cameraBusy)
923 return;
924
925 // #### better to use the user data method instead of mmap???
926 qsizetype i;
927
928 for (i = 0; i < d->mappedBuffers.size(); ++i) {
929 v4l2_buffer buf = {};
930 buf.index = i;
931 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
932 buf.memory = V4L2_MEMORY_MMAP;
933
934 if (ioctl(d->v4l2FileDescriptor, VIDIOC_QBUF, &buf) < 0) {
935 qWarning() << "failed to set up mapped buffer";
936 return;
937 }
938 }
939 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
940 if (ioctl(d->v4l2FileDescriptor, VIDIOC_STREAMON, &type) < 0)
941 qWarning() << "failed to start capture";
942
944 connect(notifier, &QSocketNotifier::activated, this, &QV4L2Camera::readFrame);
945
946 firstFrameTime = { -1, -1 };
947}
948
bool m_active
IOBluetoothDevice * device
The QAbstractVideoBuffer class is an abstraction for video data. \inmodule QtMultimedia.
\inmodule QtCore
Definition qbytearray.h:57
static const QCameraDevicePrivate * handle(const QCameraDevice &device)
The QCameraDevice class provides general information about camera devices.
bool isNull() const
Returns true if this QCameraDevice is null or invalid.
QList< QCameraFormat > videoFormats
\qmlproperty CameraFormat QtMultimedia::cameraDevice::videoFormats
QByteArray id
\qmlproperty string QtMultimedia::cameraDevice::id
The QCameraFormat class describes a video format supported by a camera device. \inmodule QtMultimedia...
QSize resolution
\qmlproperty size QtMultimedia::cameraFormat::resolution
bool isNull() const noexcept
Returns true if this is a default constructed QCameraFormat.
QVideoFrameFormat::PixelFormat pixelFormat
\qmlproperty enumeration QtMultimedia::cameraFormat::pixelFormat
float maxFrameRate
\qmlproperty real QtMultimedia::cameraFormat::maxFrameRate
The QCamera class provides interface for system camera devices.
Definition qcamera.h:28
WhiteBalanceMode
\value WhiteBalanceAuto Auto white balance mode.
Definition qcamera.h:112
@ WhiteBalanceManual
Definition qcamera.h:114
@ WhiteBalanceAuto
Definition qcamera.h:113
TorchMode
\value TorchOff Torch is Off.
Definition qcamera.h:84
@ TorchOn
Definition qcamera.h:86
@ TorchOff
Definition qcamera.h:85
FocusMode
\value FocusModeAuto Continuous auto focus mode.
Definition qcamera.h:67
@ FocusModeAutoNear
Definition qcamera.h:69
@ FocusModeInfinity
Definition qcamera.h:72
@ FocusModeAutoFar
Definition qcamera.h:70
@ FocusModeAuto
Definition qcamera.h:68
@ FocusModeManual
Definition qcamera.h:73
FlashMode
\value FlashOff Flash is Off.
Definition qcamera.h:77
@ FlashAuto
Definition qcamera.h:80
@ FlashOn
Definition qcamera.h:79
@ FlashOff
Definition qcamera.h:78
ExposureMode
\value ExposureAuto Automatic mode.
Definition qcamera.h:91
@ ExposureManual
Definition qcamera.h:93
@ ExposureAuto
Definition qcamera.h:92
@ CameraError
Definition qcamera.h:63
\inmodule QtCore
Definition qdir.h:19
@ System
Definition qdir.h:35
void directoryChanged(const QString &path, QPrivateSignal)
This signal is emitted when the directory at a specified path is modified (e.g., when a file is added...
bool addPath(const QString &file)
Adds path to the file system watcher if path exists.
static QByteArray encodeName(const QString &fileName)
Converts fileName to an 8-bit encoding that you can use in native APIs.
Definition qfile.h:158
Definition qlist.h:74
void append(parameter_type t)
Definition qlist.h:441
\inmodule QtCore
Definition qmutex.h:317
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
QCameraFormat findBestCameraFormat(const QCameraDevice &camera) const
void maxIsoChanged(int iso)
QCamera::Features supportedFeatures() const
void torchModeChanged(QCamera::TorchMode mode)
void focusModeChanged(QCamera::FocusMode mode)
void exposureCompensationChanged(float compensation)
float focusDistance() const
void whiteBalanceModeChanged(QCamera::WhiteBalanceMode mode)
QCamera::FocusMode focusMode() const
void focusDistanceChanged(float d)
static int colorTemperatureForWhiteBalance(QCamera::WhiteBalanceMode mode)
void maximumZoomFactorChanged(float)
void flashModeChanged(QCamera::FlashMode mode)
void zoomFactorChanged(float zoom)
void colorTemperatureChanged(int temperature)
void minIsoChanged(int iso)
void exposureModeChanged(QCamera::ExposureMode mode)
void supportedFeaturesChanged(QCamera::Features)
void minimumZoomFactorChanged(float factor)
void error(int error, const QString &errorString)
QCameraFormat m_cameraFormat
void exposureTimeChanged(float speed)
void newVideoFrame(const QVideoFrame &)
void activeChanged(bool)
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
\inmodule QtCore
void activated(QSocketDescriptor socket, QSocketNotifier::Type activationEvent, QPrivateSignal)
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1101
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QList< QCameraDevice > videoDevices() const override
QV4L2CameraDevices(QPlatformMediaIntegration *integration)
void zoomTo(float, float=-1.) override
void setManualIsoSensitivity(int) override
void setColorTemperature(int) override
bool isActive() const override
void setFocusMode(QCamera::FocusMode) override
bool setCameraFormat(const QCameraFormat &format) override
void setTorchMode(QCamera::TorchMode) override
void setFocusDistance(float) override
bool isFocusModeSupported(QCamera::FocusMode mode) const override
void setExposureMode(QCamera::ExposureMode) override
void setActive(bool active) override
bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const override
bool isFlashReady() const override
bool resolveCameraFormat(const QCameraFormat &format)
int isoSensitivity() const override
QV4L2Camera(QCamera *parent)
void setExposureCompensation(float) override
void setFlashMode(QCamera::FlashMode) override
bool isFlashModeSupported(QCamera::FlashMode mode) const override
bool isTorchModeSupported(QCamera::TorchMode mode) const override
void setWhiteBalanceMode(QCamera::WhiteBalanceMode) override
bool isExposureModeSupported(QCamera::ExposureMode mode) const override
void setCamera(const QCameraDevice &camera) override
float exposureTime() const override
void setManualExposureTime(float) override
QV4L2VideoBuffer(QV4L2CameraBuffers *d, int index)
QVideoFrame::MapMode mapMode() const override
void unmap() override
Releases the memory mapped by the map() function.
QVideoFrame::MapMode m_mode
MapData map(QVideoFrame::MapMode mode) override
Independently maps the planes of a video buffer to memory.
QExplicitlySharedDataPointer< QV4L2CameraBuffers > d
The QVideoFrameFormat class specifies the stream format of a video presentation surface.
PixelFormat
Enumerates video data types.
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:26
MapMode
Enumerates how a video buffer's data is mapped to system memory.
Definition qvideoframe.h:36
QCamera * camera
Definition camera.cpp:19
Combined button and popup list for selecting options.
QString deviceName()
constexpr const T & min(const T &a, const T &b)
Definition qnumeric.h:366
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
static int qt_safe_open(const char *pathname, int flags, mode_t mode=0777)
static int qt_safe_close(int fd)
EGLDeviceEXT * devices
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:281
Q_DECL_COLD_FUNCTION Q_CORE_EXPORT QString qt_error_string(int errorCode=-1)
#define qWarning
Definition qlogging.h:162
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
QT_BEGIN_NAMESPACE Fraction qRealToFraction(qreal value)
GLboolean GLboolean GLboolean b
GLenum mode
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLenum GLuint id
[7]
GLfloat GLfloat f
GLsizei GLsizei GLfloat distance
GLenum GLuint buffer
GLenum type
GLenum GLuint GLenum GLsizei const GLchar * buf
GLuint64 GLenum GLint fd
GLint first
GLfloat n
GLint GLsizei GLsizei GLenum format
GLuint GLenum * rate
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint num
GLenum cap
static constexpr QSize frameSize(const T &frame)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define MAP_FAILED
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define tr(X)
#define emit
unsigned int quint32
Definition qtypes.h:45
unsigned char uchar
Definition qtypes.h:27
int qint32
Definition qtypes.h:44
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
static QVideoFrameFormat::PixelFormat formatForV4L2Format(uint32_t v4l2Format)
static const struct @727 formatMap[]
static uint32_t v4l2FormatForPixelFormat(QVideoFrameFormat::PixelFormat format)
QVideoFrameFormat::PixelFormat fmt
uint32_t v4l2Format
static bool areCamerasEqual(QList< QCameraDevice > a, QList< QCameraDevice > b)
QFile file
[0]
file open(QIODevice::ReadOnly)
QObject::connect nullptr
QString dir
[11]
QFrame frame
[0]
bool contains(const AT &t) const noexcept
Definition qlist.h:44
void release(int index)
QList< MappedBuffer > mappedBuffers