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>
14#include <qsocketnotifier.h>
15#include <qloggingcategory.h>
26 { QVideoFrameFormat::Format_YUV420P, V4L2_PIX_FMT_YUV420 },
27 { QVideoFrameFormat::Format_YUV422P, V4L2_PIX_FMT_YUV422P },
28 { QVideoFrameFormat::Format_YUYV, V4L2_PIX_FMT_YUYV },
29 { QVideoFrameFormat::Format_UYVY, V4L2_PIX_FMT_UYVY },
30 { QVideoFrameFormat::Format_XBGR8888, V4L2_PIX_FMT_XBGR32 },
31 { QVideoFrameFormat::Format_XRGB8888, V4L2_PIX_FMT_XRGB32 },
32 { QVideoFrameFormat::Format_ABGR8888, V4L2_PIX_FMT_ABGR32 },
33 { QVideoFrameFormat::Format_ARGB8888, V4L2_PIX_FMT_ARGB32 },
34 { QVideoFrameFormat::Format_BGRX8888, V4L2_PIX_FMT_BGR32 },
35 { QVideoFrameFormat::Format_RGBX8888, V4L2_PIX_FMT_RGB32 },
36 { QVideoFrameFormat::Format_BGRA8888, V4L2_PIX_FMT_BGRA32 },
37 { QVideoFrameFormat::Format_RGBA8888, V4L2_PIX_FMT_RGBA32 },
38 { QVideoFrameFormat::Format_Y8, V4L2_PIX_FMT_GREY },
39 { QVideoFrameFormat::Format_Y16, V4L2_PIX_FMT_Y16 },
40 { QVideoFrameFormat::Format_NV12, V4L2_PIX_FMT_NV12 },
41 { QVideoFrameFormat::Format_NV21, V4L2_PIX_FMT_NV21 },
42 { QVideoFrameFormat::Format_Jpeg, V4L2_PIX_FMT_MJPEG },
43 { QVideoFrameFormat::Format_Jpeg, V4L2_PIX_FMT_JPEG },
44 { QVideoFrameFormat::Format_Invalid, 0 },
50 while (f->v4l2Format) {
51 if (f->v4l2Format == v4l2Format)
55 return QVideoFrameFormat::Format_Invalid;
61 while (f->v4l2Format) {
70 : QPlatformCamera(camera)
87 if (m_active == active)
89 if (m_cameraDevice.isNull() && active)
92 if (m_cameraFormat.isNull())
93 resolveCameraFormat({});
101 emit newVideoFrame({});
103 emit activeChanged(active);
108 if (m_cameraDevice == camera)
114 m_cameraDevice = camera;
115 resolveCameraFormat({});
125 if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format))
128 if (!resolveCameraFormat(format))
146 fmt = findBestCameraFormat(m_cameraDevice);
148 if (fmt == m_cameraFormat)
151 m_cameraFormat = fmt;
157 if (mode == focusMode())
160 bool focusDist = supportedFeatures() & QCamera::Feature::FocusDistance;
161 if (!focusDist && !m_v4l2Info.rangedFocus)
166 case QCamera::FocusModeAuto:
167 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 1);
168 if (m_v4l2Info.rangedFocus)
169 setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, V4L2_AUTO_FOCUS_RANGE_AUTO);
171 case QCamera::FocusModeAutoNear:
172 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 1);
173 if (m_v4l2Info.rangedFocus)
174 setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, V4L2_AUTO_FOCUS_RANGE_MACRO);
176 setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, m_v4l2Info.minFocus);
178 case QCamera::FocusModeAutoFar:
179 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 1);
180 if (m_v4l2Info.rangedFocus)
181 setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, V4L2_AUTO_FOCUS_RANGE_INFINITY);
183 case QCamera::FocusModeInfinity:
184 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 0);
185 setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, m_v4l2Info.maxFocus);
187 case QCamera::FocusModeManual:
188 setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 0);
192 focusModeChanged(mode);
197 int distance = m_v4l2Info.minFocus +
int((m_v4l2Info.maxFocus - m_v4l2Info.minFocus) * d);
198 setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, distance);
199 focusDistanceChanged(d);
204 if (m_v4l2Info.maxZoom == m_v4l2Info.minZoom)
206 factor = qBound(1., factor, 2.);
207 int zoom = m_v4l2Info.minZoom + (factor - 1.) * (m_v4l2Info.maxZoom - m_v4l2Info.minZoom);
208 setV4L2Parameter(V4L2_CID_ZOOM_ABSOLUTE, zoom);
209 zoomFactorChanged(factor);
214 if (supportedFeatures() & QCamera::Feature::FocusDistance &&
215 (mode == QCamera::FocusModeManual || mode == QCamera::FocusModeAutoNear || mode == QCamera::FocusModeInfinity))
218 return mode == QCamera::FocusModeAuto;
223 if (!m_v4l2Info.flashSupported || mode == QCamera::FlashOn)
225 setV4L2Parameter(V4L2_CID_FLASH_LED_MODE, mode == QCamera::FlashAuto ? V4L2_FLASH_LED_MODE_FLASH : V4L2_FLASH_LED_MODE_NONE);
226 flashModeChanged(mode);
231 if (m_v4l2Info.flashSupported && mode == QCamera::FlashAuto)
233 return mode == QCamera::FlashOff;
238 struct v4l2_queryctrl queryControl;
239 ::memset(&queryControl, 0,
sizeof(queryControl));
240 queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
242 return m_v4l2FileDescriptor && m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl);
247 if (!m_v4l2Info.torchSupported || mode == QCamera::TorchOn)
249 setV4L2Parameter(V4L2_CID_FLASH_LED_MODE, mode == QCamera::TorchOn ? V4L2_FLASH_LED_MODE_TORCH : V4L2_FLASH_LED_MODE_NONE);
250 torchModeChanged(mode);
255 if (mode == QCamera::TorchOn)
256 return m_v4l2Info.torchSupported;
257 return mode == QCamera::TorchOff;
262 if (m_v4l2Info.autoExposureSupported && m_v4l2Info.manualExposureSupported) {
263 if (mode != QCamera::ExposureAuto && mode != QCamera::ExposureManual)
265 int value = QCamera::ExposureAuto ? V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL;
266 setV4L2Parameter(V4L2_CID_EXPOSURE_AUTO, value);
267 exposureModeChanged(mode);
274 if (mode == QCamera::ExposureAuto)
276 if (m_v4l2Info.manualExposureSupported && m_v4l2Info.autoExposureSupported)
277 return mode == QCamera::ExposureManual;
283 if ((m_v4l2Info.minExposureAdjustment != 0 || m_v4l2Info.maxExposureAdjustment != 0)) {
284 int value = qBound(m_v4l2Info.minExposureAdjustment, (
int)(compensation * 1000),
285 m_v4l2Info.maxExposureAdjustment);
286 setV4L2Parameter(V4L2_CID_AUTO_EXPOSURE_BIAS, value);
287 exposureCompensationChanged(value/1000.);
294 if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
296 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY_AUTO, iso <= 0 ? V4L2_ISO_SENSITIVITY_AUTO : V4L2_ISO_SENSITIVITY_MANUAL);
298 iso = qBound(minIso(), iso, maxIso());
299 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY, iso);
305 if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
307 return getV4L2Parameter(V4L2_CID_ISO_SENSITIVITY);
312 if (m_v4l2Info.manualExposureSupported && m_v4l2Info.autoExposureSupported) {
314 qBound(m_v4l2Info.minExposure, qRound(secs * 10000.), m_v4l2Info.maxExposure);
315 setV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE, exposure);
316 exposureTimeChanged(exposure/10000.);
323 return getV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE)/10000.;
328 if (m_v4l2Info.autoWhiteBalanceSupported && m_v4l2Info.colorTemperatureSupported)
331 return mode == QCamera::WhiteBalanceAuto;
336 Q_ASSERT(isWhiteBalanceModeSupported(mode));
338 int temperature = colorTemperatureForWhiteBalance(mode);
339 int t = setV4L2ColorTemperature(temperature);
341 mode = QCamera::WhiteBalanceAuto;
342 whiteBalanceModeChanged(mode);
347 if (temperature == 0) {
348 setWhiteBalanceMode(QCamera::WhiteBalanceAuto);
352 Q_ASSERT(isWhiteBalanceModeSupported(QCamera::WhiteBalanceManual));
354 int t = setV4L2ColorTemperature(temperature);
356 colorTemperatureChanged(t);
361 Q_ASSERT(m_memoryTransfer);
363 auto buffer = m_memoryTransfer->dequeueBuffer();
365 qCWarning(qLcV4L2Camera) <<
"Cannot take buffer";
367 if (errno == ENODEV) {
376 auto videoBuffer = std::make_unique<QMemoryVideoBuffer>(buffer->data, m_bytesPerLine);
377 QVideoFrame frame = QVideoFramePrivate::createFrame(std::move(videoBuffer), frameFormat());
379 auto &v4l2Buffer = buffer->v4l2Buffer;
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);
388 emit newVideoFrame(frame);
390 if (!m_memoryTransfer->enqueueBuffer(v4l2Buffer.index))
391 qCWarning(qLcV4L2Camera) <<
"Cannot add buffer";
397 updateError(QCamera::CameraError, QLatin1String(
"Camera is in use"));
403 QCamera::Features features;
405 const QByteArray deviceName = m_cameraDevice.id();
406 Q_ASSERT(!deviceName.isEmpty());
410 const int descriptor = qt_safe_open(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(QCamera::CameraError, QLatin1String(
"Cannot open camera"));
419 m_v4l2FileDescriptor = std::make_shared<QV4L2FileDescriptor>(descriptor);
421 qCDebug(qLcV4L2Camera) <<
"FD=" << descriptor;
423 struct v4l2_queryctrl queryControl;
424 ::memset(&queryControl, 0,
sizeof(queryControl));
425 queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
427 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
428 m_v4l2Info.autoWhiteBalanceSupported =
true;
429 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE,
true);
432 ::memset(&queryControl, 0,
sizeof(queryControl));
433 queryControl.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE;
434 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
435 m_v4l2Info.minColorTemp = queryControl.minimum;
436 m_v4l2Info.maxColorTemp = queryControl.maximum;
437 m_v4l2Info.colorTemperatureSupported =
true;
438 features |= QCamera::Feature::ColorTemperature;
441 ::memset(&queryControl, 0,
sizeof(queryControl));
442 queryControl.id = V4L2_CID_EXPOSURE_AUTO;
443 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
444 m_v4l2Info.autoExposureSupported =
true;
447 ::memset(&queryControl, 0,
sizeof(queryControl));
448 queryControl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
449 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
450 m_v4l2Info.manualExposureSupported =
true;
451 m_v4l2Info.minExposure = queryControl.minimum;
452 m_v4l2Info.maxExposure = queryControl.maximum;
453 features |= QCamera::Feature::ManualExposureTime;
456 ::memset(&queryControl, 0,
sizeof(queryControl));
457 queryControl.id = V4L2_CID_AUTO_EXPOSURE_BIAS;
458 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
459 m_v4l2Info.minExposureAdjustment = queryControl.minimum;
460 m_v4l2Info.maxExposureAdjustment = queryControl.maximum;
461 features |= QCamera::Feature::ExposureCompensation;
464 ::memset(&queryControl, 0,
sizeof(queryControl));
465 queryControl.id = V4L2_CID_ISO_SENSITIVITY_AUTO;
466 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
467 queryControl.id = V4L2_CID_ISO_SENSITIVITY;
468 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
469 features |= QCamera::Feature::IsoSensitivity;
470 minIsoChanged(queryControl.minimum);
471 maxIsoChanged(queryControl.minimum);
475 ::memset(&queryControl, 0,
sizeof(queryControl));
476 queryControl.id = V4L2_CID_FOCUS_ABSOLUTE;
477 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
478 m_v4l2Info.minExposureAdjustment = queryControl.minimum;
479 m_v4l2Info.maxExposureAdjustment = queryControl.maximum;
480 features |= QCamera::Feature::FocusDistance;
483 ::memset(&queryControl, 0,
sizeof(queryControl));
484 queryControl.id = V4L2_CID_AUTO_FOCUS_RANGE;
485 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
486 m_v4l2Info.rangedFocus =
true;
489 ::memset(&queryControl, 0,
sizeof(queryControl));
490 queryControl.id = V4L2_CID_FLASH_LED_MODE;
491 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &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;
498 ::memset(&queryControl, 0,
sizeof(queryControl));
499 queryControl.id = V4L2_CID_ZOOM_ABSOLUTE;
500 if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
501 m_v4l2Info.minZoom = queryControl.minimum;
502 m_v4l2Info.maxZoom = queryControl.maximum;
506 minimumZoomFactorChanged(1);
507 maximumZoomFactorChanged(m_v4l2Info.minZoom != m_v4l2Info.maxZoom ? 2 : 1);
509 supportedFeaturesChanged(features);
514 Q_ASSERT(!m_memoryTransfer);
517 m_cameraBusy =
false;
518 m_v4l2FileDescriptor =
nullptr;
521int QV4L2Camera::setV4L2ColorTemperature(
int temperature)
523 struct v4l2_control control;
524 ::memset(&control, 0,
sizeof(control));
526 if (m_v4l2Info.autoWhiteBalanceSupported) {
527 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, temperature == 0 ?
true :
false);
528 }
else if (temperature == 0) {
532 if (temperature != 0 && m_v4l2Info.colorTemperatureSupported) {
533 temperature = qBound(m_v4l2Info.minColorTemp, temperature, m_v4l2Info.maxColorTemp);
534 if (!setV4L2Parameter(
535 V4L2_CID_WHITE_BALANCE_TEMPERATURE,
536 qBound(m_v4l2Info.minColorTemp, temperature, m_v4l2Info.maxColorTemp)))
545bool QV4L2Camera::setV4L2Parameter(quint32 id, qint32 value)
547 v4l2_control control{ id, value };
548 if (!m_v4l2FileDescriptor->call(VIDIOC_S_CTRL, &control)) {
549 qWarning() <<
"Unable to set the V4L2 Parameter" << Qt::hex << id <<
"to" << value << qt_error_string(errno);
557 struct v4l2_control control{id, 0};
558 if (!m_v4l2FileDescriptor->call(VIDIOC_G_CTRL, &control)) {
559 qWarning() <<
"Unable to get the V4L2 Parameter" << Qt::hex << id << qt_error_string(errno);
562 return control.value;
567 if (m_v4l2Info.formatInitialized || !m_v4l2FileDescriptor)
570 Q_ASSERT(!m_cameraFormat.isNull());
571 qCDebug(qLcV4L2Camera) <<
"XXXXX" <<
this << m_cameraDevice.id() << m_cameraFormat.pixelFormat()
572 << m_cameraFormat.resolution();
574 v4l2_format fmt = {};
575 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
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(m_cameraFormat.pixelFormat());
581 fmt.fmt.pix.field = V4L2_FIELD_ANY;
583 qCDebug(qLcV4L2Camera) <<
"setting camera format to" << size << fmt.fmt.pix.pixelformat;
585 if (!m_v4l2FileDescriptor->call(VIDIOC_S_FMT, &fmt)) {
586 if (errno == EBUSY) {
590 qWarning() <<
"Couldn't set video format on v4l2 camera" << strerror(errno);
593 m_v4l2Info.formatInitialized =
true;
594 m_cameraBusy =
false;
596 m_bytesPerLine = fmt.fmt.pix.bytesperline;
597 m_imageSize = std::max(fmt.fmt.pix.sizeimage, m_bytesPerLine * fmt.fmt.pix.height);
599 switch (v4l2_colorspace(fmt.fmt.pix.colorspace)) {
601 case V4L2_COLORSPACE_DCI_P3:
602 m_colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
604 case V4L2_COLORSPACE_REC709:
605 m_colorSpace = QVideoFrameFormat::ColorSpace_BT709;
607 case V4L2_COLORSPACE_JPEG:
608 m_colorSpace = QVideoFrameFormat::ColorSpace_AdobeRgb;
610 case V4L2_COLORSPACE_SRGB:
612 m_colorSpace = QVideoFrameFormat::ColorSpace_BT601;
614 case V4L2_COLORSPACE_BT2020:
615 m_colorSpace = QVideoFrameFormat::ColorSpace_BT2020;
619 v4l2_streamparm streamParam = {};
620 streamParam.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
622 streamParam.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
623 auto [num, den] = qRealToFraction(1./m_cameraFormat.maxFrameRate());
624 streamParam.parm.capture.timeperframe = { (uint)num, (uint)den };
625 m_v4l2FileDescriptor->call(VIDIOC_S_PARM, &streamParam);
627 m_frameDuration = 1000000 * streamParam.parm.capture.timeperframe.numerator
628 / streamParam.parm.capture.timeperframe.denominator;
636 Q_ASSERT(!m_memoryTransfer);
638 m_memoryTransfer = makeUserPtrMemoryTransfer(m_v4l2FileDescriptor, m_imageSize);
640 if (m_memoryTransfer)
643 if (errno == EBUSY) {
648 qCDebug(qLcV4L2Camera) <<
"Cannot init V4L2_MEMORY_USERPTR; trying V4L2_MEMORY_MMAP";
650 m_memoryTransfer = makeMMapMemoryTransfer(m_v4l2FileDescriptor);
652 if (!m_memoryTransfer) {
653 qCWarning(qLcV4L2Camera) <<
"Cannot init v4l2 memory transfer," << qt_error_string(errno);
654 updateError(QCamera::CameraError, QLatin1String(
"Cannot init V4L2 memory transfer"));
660 if (!m_memoryTransfer || !m_v4l2FileDescriptor)
663 m_notifier =
nullptr;
665 if (!m_v4l2FileDescriptor->stopStream()) {
668 qWarning() <<
"failed to stop capture";
671 m_memoryTransfer =
nullptr;
672 m_cameraBusy =
false;
677 if (!m_v4l2FileDescriptor)
680 setV4L2CameraFormat();
681 initV4L2MemoryTransfer();
683 if (m_cameraBusy || !m_memoryTransfer)
686 if (!m_v4l2FileDescriptor->startStream()) {
687 qWarning() <<
"Couldn't start v4l2 camera stream";
692 std::make_unique<QSocketNotifier>(m_v4l2FileDescriptor->get(), QSocketNotifier::Read);
693 connect(m_notifier.get(), &QSocketNotifier::activated,
this, &QV4L2Camera::readFrame);
695 m_firstFrameTime = { -1, -1 };
700 auto result = QPlatformCamera::frameFormat();
701 result.setColorSpace(m_colorSpace);
707#include "moc_qv4l2camera_p.cpp"
void zoomTo(float, float=-1.) override
void setManualIsoSensitivity(int) override
void setColorTemperature(int) override
bool isActive() const override
void setFocusMode(QCamera::FocusMode) override
QVideoFrameFormat frameFormat() const 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
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
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)
uint32_t v4l2FormatForPixelFormat(QVideoFrameFormat::PixelFormat format)
QVideoFrameFormat::PixelFormat fmt
QVideoFrameFormat::PixelFormat formatForV4L2Format(uint32_t v4l2Format)