diff options
author | Lars Knoll <lars.knoll@qt.io> | 2021-05-20 15:59:41 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2021-05-21 12:28:01 +0000 |
commit | bf0f87dc4f4480efe51b8ba5b5706f8c1ce7a4fb (patch) | |
tree | 51e28a0602ac5983cc7fe16cd0a71b61a602b832 | |
parent | 9d2b2a97450095de4a13fd1c3b8d06a4a012bc61 (diff) |
Improve V4L2 support
Check for exposure times and adjustments and implement support
for setting those if the camera supports them.
Check for those in the camerabackend test.
Change-Id: I860b2e7b94ef27e64a62e800cb6c05bfa6134250
Reviewed-by: André de la Rocha <andre.rocha@qt.io>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
3 files changed, 114 insertions, 32 deletions
diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera.cpp index 2ac1da4b4..d1d848e4c 100644 --- a/src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera.cpp +++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera.cpp @@ -66,7 +66,14 @@ QGstreamerCamera::QGstreamerCamera(QCamera *camera) gstCameraBin.addGhostPad(gstVideoScale, "src"); } -QGstreamerCamera::~QGstreamerCamera() = default; +QGstreamerCamera::~QGstreamerCamera() +{ +#if QT_CONFIG(linux_v4l) + if (v4l2FileDescriptor >= 0) + qt_safe_close(v4l2FileDescriptor); + v4l2FileDescriptor = -1; +#endif +} bool QGstreamerCamera::isActive() const { @@ -289,6 +296,17 @@ bool QGstreamerCamera::isFlashReady() const void QGstreamerCamera::setExposureMode(QCamera::ExposureMode mode) { +#if QT_CONFIG(linux_v4l) + if (isV4L2Camera() && v4l2AutoExposureSupported && v4l2ManualExposureSupported) { + if (mode != QCamera::ExposureAuto && mode != QCamera::ExposureManual) + return; + int value = QCamera::ExposureAuto ? V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL; + setV4L2Parameter(V4L2_CID_EXPOSURE_AUTO, value); + exposureModeChanged(mode); + return; + } +#endif + auto *p = photography(); if (!p) return; @@ -357,14 +375,29 @@ void QGstreamerCamera::setExposureMode(QCamera::ExposureMode mode) bool QGstreamerCamera::isExposureModeSupported(QCamera::ExposureMode mode) const { + if (mode == QCamera::ExposureAuto) + return true; +#if QT_CONFIG(linux_v4l) + if (isV4L2Camera() && v4l2ManualExposureSupported && v4l2AutoExposureSupported) + return mode == QCamera::ExposureManual; +#endif if (photography()) return true; - return mode == QCamera::ExposureAuto; + return false; } void QGstreamerCamera::setExposureCompensation(float compensation) { +#if QT_CONFIG(linux_v4l) + if (isV4L2Camera() && (v4l2MinExposureAdjustment != 0 || v4l2MaxExposureAdjustment != 0)) { + int value = qBound(v4l2MinExposureAdjustment, (int)(compensation*1000), v4l2MaxExposureAdjustment); + setV4L2Parameter(V4L2_CID_AUTO_EXPOSURE_BIAS, value); + exposureCompensationChanged(value/1000.); + return; + } +#endif + if (auto *p = photography()) { if (gst_photography_set_ev_compensation(p, compensation)) exposureCompensationChanged(compensation); @@ -391,6 +424,15 @@ int QGstreamerCamera::isoSensitivity() const void QGstreamerCamera::setManualShutterSpeed(float secs) { +#if QT_CONFIG(linux_v4l) + if (isV4L2Camera() && v4l2ManualExposureSupported && v4l2AutoExposureSupported) { + int exposure = qBound(v4l2MinExposure, qRound(secs*10000.), v4l2MaxExposure); + setV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE, exposure); + shutterSpeedChanged(exposure/10000.); + return; + } +#endif + if (auto *p = photography()) { if (gst_photography_set_exposure(p, guint(secs*1000000))) shutterSpeedChanged(secs); @@ -526,8 +568,8 @@ void QGstreamerCamera::initV4L2Controls() const QString deviceName = v4l2Device(); Q_ASSERT(!deviceName.isEmpty()); - const int fd = qt_safe_open(deviceName.toLocal8Bit().constData(), O_RDONLY); - if (fd == -1) { + v4l2FileDescriptor = qt_safe_open(deviceName.toLocal8Bit().constData(), O_RDONLY); + if (v4l2FileDescriptor == -1) { qWarning() << "Unable to open the camera" << deviceName << "for read to query the parameter info:" << qt_error_string(errno); return; @@ -537,24 +579,42 @@ void QGstreamerCamera::initV4L2Controls() ::memset(&queryControl, 0, sizeof(queryControl)); queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE; - if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) { + if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) { v4l2AutoWhiteBalanceSupported = true; struct v4l2_control control; control.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE; control.value = true; - ::ioctl(fd, VIDIOC_S_CTRL, &control); + ::ioctl(v4l2FileDescriptor, VIDIOC_S_CTRL, &control); } ::memset(&queryControl, 0, sizeof(queryControl)); queryControl.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE; - if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) { + if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) { v4l2MinColorTemp = queryControl.minimum; v4l2MaxColorTemp = queryControl.maximum; v4l2ColorTemperatureSupported = true; } - qt_safe_close(fd); + ::memset(&queryControl, 0, sizeof(queryControl)); + queryControl.id = V4L2_CID_EXPOSURE_AUTO; + if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) { + v4l2AutoExposureSupported = true; + } + + ::memset(&queryControl, 0, sizeof(queryControl)); + queryControl.id = V4L2_CID_EXPOSURE_ABSOLUTE; + if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) { + v4l2ManualExposureSupported = true; + v4l2MinExposure = queryControl.minimum; + v4l2MaxExposure = queryControl.maximum; + } + ::memset(&queryControl, 0, sizeof(queryControl)); + queryControl.id = V4L2_CID_AUTO_EXPOSURE_BIAS; + if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) { + v4l2MinExposureAdjustment = queryControl.minimum; + v4l2MaxExposureAdjustment = queryControl.maximum; + } } int QGstreamerCamera::setV4L2ColorTemperature(int temperature) @@ -562,38 +622,36 @@ int QGstreamerCamera::setV4L2ColorTemperature(int temperature) struct v4l2_control control; ::memset(&control, 0, sizeof(control)); - const int fd = qt_safe_open(v4l2Device().toLocal8Bit().constData(), O_RDONLY); - if (fd == -1) { - qWarning() << "Unable to open the camera" << v4l2Device() - << "for read to get the parameter value:" << qt_error_string(errno); - return 0; - } - if (v4l2AutoWhiteBalanceSupported) { - control.id = V4L2_CID_AUTO_WHITE_BALANCE; - control.value = temperature == 0 ? true : false; - if (::ioctl(fd, VIDIOC_S_CTRL, &control) != 0) { - qWarning() << "Unable to set the V4L2 AUTO_WHITE_BALANCE property" << qt_error_string(errno); - } + setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, temperature == 0 ? true : false); } else if (temperature == 0) { temperature = 5600; } if (temperature != 0 && v4l2ColorTemperatureSupported) { temperature = qBound(v4l2MinColorTemp, temperature, v4l2MaxColorTemp); - control.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE; - control.value = qBound(v4l2MinColorTemp, temperature, v4l2MaxColorTemp); - if (::ioctl(fd, VIDIOC_S_CTRL, &control) != 0) { - qWarning() << "Unable to set the V4L2 AUTO_WHITE_BALANCE property" << qt_error_string(errno); + if (!setV4L2Parameter(V4L2_CID_WHITE_BALANCE_TEMPERATURE, qBound(v4l2MinColorTemp, temperature, v4l2MaxColorTemp))) temperature = 0; - } } else { temperature = 0; } - qt_safe_close(fd); return temperature; } + +bool QGstreamerCamera::setV4L2Parameter(quint32 id, qint32 value) +{ + struct v4l2_control control; + ::memset(&control, 0, sizeof(control)); + control.id = id; + control.value = value; + if (::ioctl(v4l2FileDescriptor, VIDIOC_S_CTRL, &control) != 0) { + qWarning() << "Unable to set the V4L2 Parameter" << Qt::hex << id << "to" << value << qt_error_string(errno); + return false; + } + return true; +} + #endif #endif diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera_p.h index 54064e399..82a7c2c75 100644 --- a/src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera_p.h +++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera_p.h @@ -108,11 +108,19 @@ private: #if QT_CONFIG(linux_v4l) void initV4L2Controls(); int setV4L2ColorTemperature(int temperature); + bool setV4L2Parameter(quint32 id, qint32 value); bool v4l2AutoWhiteBalanceSupported = false; bool v4l2ColorTemperatureSupported = false; + bool v4l2AutoExposureSupported = false; + bool v4l2ManualExposureSupported = false; qint32 v4l2MinColorTemp = 5600; // Daylight... qint32 v4l2MaxColorTemp = 5600; + qint32 v4l2MinExposure = 0; + qint32 v4l2MaxExposure = 0; + qint32 v4l2MinExposureAdjustment = 0; + qint32 v4l2MaxExposureAdjustment = 0; + int v4l2FileDescriptor = -1; #endif QGstreamerMediaCapture *m_session = nullptr; diff --git a/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp b/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp index 4c5f6c251..5692a3845 100644 --- a/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp +++ b/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp @@ -360,7 +360,7 @@ void tst_QCameraBackend::testExposureCompensation() QSignalSpy exposureCompensationSignal(&camera, SIGNAL(exposureCompensationChanged(qreal))); //it should be possible to set exposure parameters in Unloaded state - QCOMPARE(camera.exposureCompensation()+1.0, 1.0); + QCOMPARE(camera.exposureCompensation(), 0.); camera.setExposureCompensation(1.0); QCOMPARE(camera.exposureCompensation(), 1.0); QTRY_COMPARE(exposureCompensationSignal.count(), 1); @@ -394,11 +394,13 @@ void tst_QCameraBackend::testExposureMode() QCOMPARE(camera.exposureMode(), QCamera::ExposureAuto); // Night - camera.setExposureMode(QCamera::ExposureNight); - QCOMPARE(camera.exposureMode(), QCamera::ExposureNight); - camera.start(); - QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); - QCOMPARE(camera.exposureMode(), QCamera::ExposureNight); + if (camera.isExposureModeSupported(QCamera::ExposureNight)) { + camera.setExposureMode(QCamera::ExposureNight); + QCOMPARE(camera.exposureMode(), QCamera::ExposureNight); + camera.start(); + QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); + QCOMPARE(camera.exposureMode(), QCamera::ExposureNight); + } camera.stop(); QTRY_COMPARE(camera.status(), QCamera::InactiveStatus); @@ -409,6 +411,20 @@ void tst_QCameraBackend::testExposureMode() camera.start(); QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); QCOMPARE(camera.exposureMode(), QCamera::ExposureAuto); + + // Manual + if (camera.isExposureModeSupported(QCamera::ExposureManual)) { + camera.setExposureMode(QCamera::ExposureManual); + QCOMPARE(camera.exposureMode(), QCamera::ExposureManual); + camera.start(); + QTRY_COMPARE(camera.status(), QCamera::ActiveStatus); + QCOMPARE(camera.exposureMode(), QCamera::ExposureManual); + + camera.setManualShutterSpeed(.02); // ~20ms should be supported by most cameras + QVERIFY(camera.manualShutterSpeed() > .01 && camera.manualShutterSpeed() < .04); + } + + camera.setExposureMode(QCamera::ExposureAuto); } void tst_QCameraBackend::testVideoRecording_data() |