summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2021-05-20 15:59:41 +0200
committerLars Knoll <lars.knoll@qt.io>2021-05-21 12:28:01 +0000
commitbf0f87dc4f4480efe51b8ba5b5706f8c1ce7a4fb (patch)
tree51e28a0602ac5983cc7fe16cd0a71b61a602b832
parent9d2b2a97450095de4a13fd1c3b8d06a4a012bc61 (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>
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera.cpp110
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera_p.h8
-rw-r--r--tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp28
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()