1// Copyright (C) 2016 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 <mediacapture/qgstreamercamera_p.h>
5
6#include <QtMultimedia/qcameradevice.h>
7#include <QtMultimedia/qmediacapturesession.h>
8#include <QtMultimedia/private/qcameradevice_p.h>
9#include <QtCore/qdebug.h>
10
11#include <common/qgst_debug_p.h>
12#include <qgstreamervideodevices_p.h>
13#include <qgstreamerintegration_p.h>
14
15#if QT_CONFIG(linux_v4l)
16#include <linux/videodev2.h>
17#include <private/qcore_unix_p.h>
18#endif
19
20
21QT_BEGIN_NAMESPACE
22
23QMaybe<QPlatformCamera *> QGstreamerCamera::create(QCamera *camera)
24{
25 static const auto error = qGstErrorMessageIfElementsNotAvailable(
26 arg: "videotestsrc", args: "capsfilter", args: "videoconvert", args: "videoscale", args: "identity");
27 if (error)
28 return *error;
29
30 return new QGstreamerCamera(camera);
31}
32
33QGstreamerCamera::QGstreamerCamera(QCamera *camera)
34 : QGstreamerCameraBase(camera),
35 gstCameraBin{
36 QGstBin::create(name: "camerabin"),
37 },
38 gstCamera{
39 QGstElement::createFromFactory(factory: "videotestsrc"),
40 },
41 gstCapsFilter{
42 QGstElement::createFromFactory(factory: "capsfilter", name: "videoCapsFilter"),
43 },
44 gstDecode{
45 QGstElement::createFromFactory(factory: "identity"),
46 },
47 gstVideoConvert{
48 QGstElement::createFromFactory(factory: "videoconvert", name: "videoConvert"),
49 },
50 gstVideoScale{
51 QGstElement::createFromFactory(factory: "videoscale", name: "videoScale"),
52 }
53{
54 gstCameraBin.add(ts: gstCamera, ts: gstCapsFilter, ts: gstDecode, ts: gstVideoConvert, ts: gstVideoScale);
55 qLinkGstElements(ts: gstCamera, ts: gstCapsFilter, ts: gstDecode, ts: gstVideoConvert, ts: gstVideoScale);
56 gstCameraBin.addGhostPad(child: gstVideoScale, name: "src");
57}
58
59QGstreamerCamera::~QGstreamerCamera()
60{
61 gstCameraBin.setStateSync(state: GST_STATE_NULL);
62}
63
64bool QGstreamerCamera::isActive() const
65{
66 return m_active;
67}
68
69void QGstreamerCamera::setActive(bool active)
70{
71 if (m_active == active)
72 return;
73 if (m_cameraDevice.isNull() && active)
74 return;
75
76 m_active = active;
77
78 emit activeChanged(active);
79}
80
81void QGstreamerCamera::setCamera(const QCameraDevice &camera)
82{
83 using namespace Qt::Literals;
84
85 if (m_cameraDevice == camera)
86 return;
87
88 m_cameraDevice = camera;
89
90 QGstElement gstNewCamera;
91 if (camera.isNull()) {
92 gstNewCamera = QGstElement::createFromFactory(factory: "videotestsrc");
93 } else {
94 auto *integration = static_cast<QGstreamerIntegration *>(QGstreamerIntegration::instance());
95 GstDevice *device = integration->videoDevice(id: camera.id());
96
97 if (!device) {
98 updateError(error: QCamera::Error::CameraError,
99 errorString: u"Failed to create GstDevice for camera: "_s
100 + QString::fromUtf8(ba: camera.id()));
101 return;
102 }
103
104 gstNewCamera = QGstElement::createFromDevice(device, name: "camerasrc");
105 QUniqueGstStructureHandle properties{
106 gst_device_get_properties(device),
107 };
108
109 if (properties) {
110 QGstStructureView propertiesView{ properties };
111 if (propertiesView.name() == "v4l2deviceprovider")
112 m_v4l2DevicePath = QString::fromUtf8(utf8: propertiesView["device.path"].toString());
113 }
114 }
115
116 QCameraFormat f = findBestCameraFormat(camera);
117 auto caps = QGstCaps::fromCameraFormat(format: f);
118 auto gstNewDecode = QGstElement::createFromFactory(
119 factory: f.pixelFormat() == QVideoFrameFormat::Format_Jpeg ? "jpegdec" : "identity");
120
121 m_currentCameraFormat = f;
122
123 updateCamera(f: [&] {
124 qUnlinkGstElements(ts: gstCamera, ts: gstCapsFilter, ts: gstDecode, ts: gstVideoConvert);
125 gstCameraBin.stopAndRemoveElements(ts&: gstCamera, ts&: gstDecode);
126
127 gstCapsFilter.set(property: "caps", c: caps);
128
129 gstCamera = std::move(gstNewCamera);
130 gstDecode = std::move(gstNewDecode);
131
132 gstCameraBin.add(ts: gstCamera, ts: gstDecode);
133 qLinkGstElements(ts: gstCamera, ts: gstCapsFilter, ts: gstDecode, ts: gstVideoConvert);
134
135 gstCameraBin.syncChildrenState();
136 });
137
138 updateCameraProperties();
139}
140
141bool QGstreamerCamera::setCameraFormat(const QCameraFormat &format)
142{
143 if (!format.isNull() && !m_cameraDevice.videoFormats().contains(t: format))
144 return false;
145
146 QCameraFormat f = format;
147 if (f.isNull())
148 f = findBestCameraFormat(camera: m_cameraDevice);
149
150 if (f == m_currentCameraFormat)
151 return true;
152
153 m_currentCameraFormat = f;
154
155 auto caps = QGstCaps::fromCameraFormat(format: f);
156
157 auto newGstDecode = QGstElement::createFromFactory(
158 factory: f.pixelFormat() == QVideoFrameFormat::Format_Jpeg ? "jpegdec" : "identity");
159
160 updateCamera(f: [&] {
161 qUnlinkGstElements(ts: gstCamera, ts: gstCapsFilter, ts: gstDecode, ts: gstVideoConvert);
162 gstCameraBin.stopAndRemoveElements(ts&: gstDecode);
163
164 gstCapsFilter.set(property: "caps", c: caps);
165
166 gstDecode = std::move(newGstDecode);
167
168 gstCameraBin.add(ts: gstDecode);
169 qLinkGstElements(ts: gstCamera, ts: gstCapsFilter, ts: gstDecode, ts: gstVideoConvert);
170 gstCameraBin.syncChildrenState();
171 });
172
173 return true;
174}
175
176void QGstreamerCamera::updateCameraProperties()
177{
178#if QT_CONFIG(linux_v4l)
179 if (isV4L2Camera()) {
180 initV4L2Controls();
181 return;
182 }
183#endif
184#if QT_CONFIG(gstreamer_photography)
185 if (auto *p = photography())
186 gst_photography_set_white_balance_mode(photo: p, wb_mode: GST_PHOTOGRAPHY_WB_MODE_AUTO);
187 QCamera::Features f = QCamera::Feature::ColorTemperature | QCamera::Feature::ExposureCompensation |
188 QCamera::Feature::IsoSensitivity | QCamera::Feature::ManualExposureTime;
189 supportedFeaturesChanged(f);
190#endif
191
192}
193
194#if QT_CONFIG(gstreamer_photography)
195GstPhotography *QGstreamerCamera::photography() const
196{
197 if (gstCamera && GST_IS_PHOTOGRAPHY(gstCamera.element()))
198 return GST_PHOTOGRAPHY(gstCamera.element());
199 return nullptr;
200}
201#endif
202
203void QGstreamerCamera::setFocusMode(QCamera::FocusMode mode)
204{
205 if (mode == focusMode())
206 return;
207
208#if QT_CONFIG(gstreamer_photography)
209 auto p = photography();
210 if (p) {
211 GstPhotographyFocusMode photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_CONTINUOUS_NORMAL;
212
213 switch (mode) {
214 case QCamera::FocusModeAutoNear:
215 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_MACRO;
216 break;
217 case QCamera::FocusModeAutoFar:
218 // not quite, but hey :)
219 Q_FALLTHROUGH();
220 case QCamera::FocusModeHyperfocal:
221 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_HYPERFOCAL;
222 break;
223 case QCamera::FocusModeInfinity:
224 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_INFINITY;
225 break;
226 case QCamera::FocusModeManual:
227 photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_MANUAL;
228 break;
229 default: // QCamera::FocusModeAuto:
230 break;
231 }
232
233 if (gst_photography_set_focus_mode(photo: p, mode: photographyMode))
234 focusModeChanged(mode);
235 }
236#endif
237}
238
239bool QGstreamerCamera::isFocusModeSupported(QCamera::FocusMode mode) const
240{
241#if QT_CONFIG(gstreamer_photography)
242 if (photography())
243 return true;
244#endif
245 return mode == QCamera::FocusModeAuto;
246}
247
248void QGstreamerCamera::setFlashMode(QCamera::FlashMode mode)
249{
250 Q_UNUSED(mode);
251
252#if QT_CONFIG(gstreamer_photography)
253 if (auto *p = photography()) {
254 GstPhotographyFlashMode flashMode;
255 gst_photography_get_flash_mode(photo: p, flash_mode: &flashMode);
256
257 switch (mode) {
258 case QCamera::FlashAuto:
259 flashMode = GST_PHOTOGRAPHY_FLASH_MODE_AUTO;
260 break;
261 case QCamera::FlashOff:
262 flashMode = GST_PHOTOGRAPHY_FLASH_MODE_OFF;
263 break;
264 case QCamera::FlashOn:
265 flashMode = GST_PHOTOGRAPHY_FLASH_MODE_ON;
266 break;
267 }
268
269 if (gst_photography_set_flash_mode(photo: p, flash_mode: flashMode))
270 flashModeChanged(mode);
271 }
272#endif
273}
274
275bool QGstreamerCamera::isFlashModeSupported(QCamera::FlashMode mode) const
276{
277#if QT_CONFIG(gstreamer_photography)
278 if (photography())
279 return true;
280#endif
281
282 return mode == QCamera::FlashAuto;
283}
284
285bool QGstreamerCamera::isFlashReady() const
286{
287#if QT_CONFIG(gstreamer_photography)
288 if (photography())
289 return true;
290#endif
291
292 return false;
293}
294
295void QGstreamerCamera::setExposureMode(QCamera::ExposureMode mode)
296{
297 Q_UNUSED(mode);
298#if QT_CONFIG(linux_v4l)
299 if (isV4L2Camera() && v4l2AutoExposureSupported && v4l2ManualExposureSupported) {
300 if (mode != QCamera::ExposureAuto && mode != QCamera::ExposureManual)
301 return;
302 int value = QCamera::ExposureAuto ? V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL;
303 setV4L2Parameter(V4L2_CID_EXPOSURE_AUTO, value);
304 exposureModeChanged(mode);
305 return;
306 }
307#endif
308
309#if QT_CONFIG(gstreamer_photography)
310 auto *p = photography();
311 if (!p)
312 return;
313
314 GstPhotographySceneMode sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_AUTO;
315
316 switch (mode) {
317 case QCamera::ExposureManual:
318 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_MANUAL;
319 break;
320 case QCamera::ExposurePortrait:
321 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_PORTRAIT;
322 break;
323 case QCamera::ExposureSports:
324 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SPORT;
325 break;
326 case QCamera::ExposureNight:
327 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_NIGHT;
328 break;
329 case QCamera::ExposureAuto:
330 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_AUTO;
331 break;
332 case QCamera::ExposureLandscape:
333 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_LANDSCAPE;
334 break;
335 case QCamera::ExposureSnow:
336 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SNOW;
337 break;
338 case QCamera::ExposureBeach:
339 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_BEACH;
340 break;
341 case QCamera::ExposureAction:
342 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_ACTION;
343 break;
344 case QCamera::ExposureNightPortrait:
345 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_NIGHT_PORTRAIT;
346 break;
347 case QCamera::ExposureTheatre:
348 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_THEATRE;
349 break;
350 case QCamera::ExposureSunset:
351 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SUNSET;
352 break;
353 case QCamera::ExposureSteadyPhoto:
354 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_STEADY_PHOTO;
355 break;
356 case QCamera::ExposureFireworks:
357 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_FIREWORKS;
358 break;
359 case QCamera::ExposureParty:
360 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_PARTY;
361 break;
362 case QCamera::ExposureCandlelight:
363 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_CANDLELIGHT;
364 break;
365 case QCamera::ExposureBarcode:
366 sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_BARCODE;
367 break;
368 default:
369 return;
370 }
371
372 if (gst_photography_set_scene_mode(photo: p, scene_mode: sceneMode))
373 exposureModeChanged(mode);
374#endif
375}
376
377bool QGstreamerCamera::isExposureModeSupported(QCamera::ExposureMode mode) const
378{
379 if (mode == QCamera::ExposureAuto)
380 return true;
381#if QT_CONFIG(linux_v4l)
382 if (isV4L2Camera() && v4l2ManualExposureSupported && v4l2AutoExposureSupported)
383 return mode == QCamera::ExposureManual;
384#endif
385#if QT_CONFIG(gstreamer_photography)
386 if (photography())
387 return true;
388#endif
389
390 return false;
391}
392
393void QGstreamerCamera::setExposureCompensation(float compensation)
394{
395 Q_UNUSED(compensation);
396#if QT_CONFIG(linux_v4l)
397 if (isV4L2Camera() && (v4l2MinExposureAdjustment != 0 || v4l2MaxExposureAdjustment != 0)) {
398 int value = qBound(min: v4l2MinExposureAdjustment, val: (int)(compensation*1000), max: v4l2MaxExposureAdjustment);
399 setV4L2Parameter(V4L2_CID_AUTO_EXPOSURE_BIAS, value);
400 exposureCompensationChanged(compensation: value/1000.);
401 return;
402 }
403#endif
404
405#if QT_CONFIG(gstreamer_photography)
406 if (auto *p = photography()) {
407 if (gst_photography_set_ev_compensation(photo: p, ev_comp: compensation))
408 exposureCompensationChanged(compensation);
409 }
410#endif
411}
412
413void QGstreamerCamera::setManualIsoSensitivity(int iso)
414{
415 Q_UNUSED(iso);
416#if QT_CONFIG(linux_v4l)
417 if (isV4L2Camera()) {
418 if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
419 return;
420 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY_AUTO, value: iso <= 0 ? V4L2_ISO_SENSITIVITY_AUTO : V4L2_ISO_SENSITIVITY_MANUAL);
421 if (iso > 0) {
422 iso = qBound(min: minIso(), val: iso, max: maxIso());
423 setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY, value: iso);
424 }
425 return;
426 }
427#endif
428#if QT_CONFIG(gstreamer_photography)
429 if (auto *p = photography()) {
430 if (gst_photography_set_iso_speed(photo: p, iso_speed: iso))
431 isoSensitivityChanged(iso);
432 }
433#endif
434}
435
436int QGstreamerCamera::isoSensitivity() const
437{
438#if QT_CONFIG(linux_v4l)
439 if (isV4L2Camera()) {
440 if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
441 return -1;
442 return getV4L2Parameter(V4L2_CID_ISO_SENSITIVITY);
443 }
444#endif
445#if QT_CONFIG(gstreamer_photography)
446 if (auto *p = photography()) {
447 guint speed = 0;
448 if (gst_photography_get_iso_speed(photo: p, iso_speed: &speed))
449 return speed;
450 }
451#endif
452 return 100;
453}
454
455void QGstreamerCamera::setManualExposureTime(float secs)
456{
457 Q_UNUSED(secs);
458#if QT_CONFIG(linux_v4l)
459 if (isV4L2Camera() && v4l2ManualExposureSupported && v4l2AutoExposureSupported) {
460 int exposure = qBound(min: v4l2MinExposure, val: qRound(d: secs*10000.), max: v4l2MaxExposure);
461 setV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE, value: exposure);
462 exposureTimeChanged(speed: exposure/10000.);
463 return;
464 }
465#endif
466
467#if QT_CONFIG(gstreamer_photography)
468 if (auto *p = photography()) {
469 if (gst_photography_set_exposure(photo: p, exposure: guint(secs*1000000)))
470 exposureTimeChanged(speed: secs);
471 }
472#endif
473}
474
475float QGstreamerCamera::exposureTime() const
476{
477#if QT_CONFIG(linux_v4l)
478 if (isV4L2Camera()) {
479 return getV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE)/10000.;
480 }
481#endif
482#if QT_CONFIG(gstreamer_photography)
483 if (auto *p = photography()) {
484 guint32 exposure = 0;
485 if (gst_photography_get_exposure(photo: p, exposure: &exposure))
486 return exposure/1000000.;
487 }
488#endif
489 return -1;
490}
491
492bool QGstreamerCamera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
493{
494 if (mode == QCamera::WhiteBalanceAuto)
495 return true;
496
497#if QT_CONFIG(linux_v4l)
498 if (isV4L2Camera()) {
499 if (v4l2AutoWhiteBalanceSupported && v4l2ColorTemperatureSupported)
500 return true;
501 }
502#endif
503#if QT_CONFIG(gstreamer_photography)
504 if (auto *p = photography()) {
505 Q_UNUSED(p);
506 switch (mode) {
507 case QCamera::WhiteBalanceAuto:
508 case QCamera::WhiteBalanceSunlight:
509 case QCamera::WhiteBalanceCloudy:
510 case QCamera::WhiteBalanceShade:
511 case QCamera::WhiteBalanceSunset:
512 case QCamera::WhiteBalanceTungsten:
513 case QCamera::WhiteBalanceFluorescent:
514 return true;
515 case QCamera::WhiteBalanceManual: {
516 GstPhotographyInterface *iface = GST_PHOTOGRAPHY_GET_INTERFACE(p);
517 if (iface->set_color_temperature && iface->get_color_temperature)
518 return true;
519 break;
520 }
521 default:
522 break;
523 }
524 }
525#endif
526
527 return mode == QCamera::WhiteBalanceAuto;
528}
529
530void QGstreamerCamera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode)
531{
532 Q_ASSERT(isWhiteBalanceModeSupported(mode));
533
534#if QT_CONFIG(linux_v4l)
535 if (isV4L2Camera()) {
536 int temperature = colorTemperatureForWhiteBalance(mode);
537 int t = setV4L2ColorTemperature(temperature);
538 if (t == 0)
539 mode = QCamera::WhiteBalanceAuto;
540 whiteBalanceModeChanged(mode);
541 return;
542 }
543#endif
544
545#if QT_CONFIG(gstreamer_photography)
546 if (auto *p = photography()) {
547 GstPhotographyWhiteBalanceMode gstMode = GST_PHOTOGRAPHY_WB_MODE_AUTO;
548 switch (mode) {
549 case QCamera::WhiteBalanceSunlight:
550 gstMode = GST_PHOTOGRAPHY_WB_MODE_DAYLIGHT;
551 break;
552 case QCamera::WhiteBalanceCloudy:
553 gstMode = GST_PHOTOGRAPHY_WB_MODE_CLOUDY;
554 break;
555 case QCamera::WhiteBalanceShade:
556 gstMode = GST_PHOTOGRAPHY_WB_MODE_SHADE;
557 break;
558 case QCamera::WhiteBalanceSunset:
559 gstMode = GST_PHOTOGRAPHY_WB_MODE_SUNSET;
560 break;
561 case QCamera::WhiteBalanceTungsten:
562 gstMode = GST_PHOTOGRAPHY_WB_MODE_TUNGSTEN;
563 break;
564 case QCamera::WhiteBalanceFluorescent:
565 gstMode = GST_PHOTOGRAPHY_WB_MODE_FLUORESCENT;
566 break;
567 case QCamera::WhiteBalanceAuto:
568 default:
569 break;
570 }
571 if (gst_photography_set_white_balance_mode(photo: p, wb_mode: gstMode)) {
572 whiteBalanceModeChanged(mode);
573 return;
574 }
575 }
576#endif
577}
578
579void QGstreamerCamera::setColorTemperature(int temperature)
580{
581 if (temperature == 0) {
582 setWhiteBalanceMode(QCamera::WhiteBalanceAuto);
583 return;
584 }
585
586 Q_ASSERT(isWhiteBalanceModeSupported(QCamera::WhiteBalanceManual));
587
588#if QT_CONFIG(linux_v4l)
589 if (isV4L2Camera()) {
590 int t = setV4L2ColorTemperature(temperature);
591 if (t)
592 colorTemperatureChanged(temperature: t);
593 return;
594 }
595#endif
596
597#if QT_CONFIG(gstreamer_photography)
598 if (auto *p = photography()) {
599 GstPhotographyInterface *iface = GST_PHOTOGRAPHY_GET_INTERFACE(p);
600 Q_ASSERT(iface->set_color_temperature);
601 iface->set_color_temperature(p, temperature);
602 return;
603 }
604#endif
605}
606
607#if QT_CONFIG(linux_v4l)
608bool QGstreamerCamera::isV4L2Camera() const
609{
610 return !m_v4l2DevicePath.isEmpty();
611}
612
613void QGstreamerCamera::initV4L2Controls()
614{
615 v4l2AutoWhiteBalanceSupported = false;
616 v4l2ColorTemperatureSupported = false;
617 QCamera::Features features{};
618
619 Q_ASSERT(!m_v4l2DevicePath.isEmpty());
620
621
622 withV4L2DeviceFileDescriptor(f: [&](int fd) {
623 struct v4l2_queryctrl queryControl = {};
624 queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
625
626 if (::ioctl(fd: fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
627 v4l2AutoWhiteBalanceSupported = true;
628 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, value: true);
629 }
630
631 queryControl = {};
632 queryControl.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE;
633 if (::ioctl(fd: fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
634 v4l2MinColorTemp = queryControl.minimum;
635 v4l2MaxColorTemp = queryControl.maximum;
636 v4l2ColorTemperatureSupported = true;
637 features |= QCamera::Feature::ColorTemperature;
638 }
639
640 queryControl = {};
641 queryControl.id = V4L2_CID_EXPOSURE_AUTO;
642 if (::ioctl(fd: fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
643 v4l2AutoExposureSupported = true;
644 }
645
646 queryControl = {};
647 queryControl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
648 if (::ioctl(fd: fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
649 v4l2ManualExposureSupported = true;
650 v4l2MinExposure = queryControl.minimum;
651 v4l2MaxExposure = queryControl.maximum;
652 features |= QCamera::Feature::ManualExposureTime;
653 }
654
655 queryControl = {};
656 queryControl.id = V4L2_CID_AUTO_EXPOSURE_BIAS;
657 if (::ioctl(fd: fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
658 v4l2MinExposureAdjustment = queryControl.minimum;
659 v4l2MaxExposureAdjustment = queryControl.maximum;
660 features |= QCamera::Feature::ExposureCompensation;
661 }
662
663 queryControl = {};
664 queryControl.id = V4L2_CID_ISO_SENSITIVITY_AUTO;
665 if (::ioctl(fd: fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
666 queryControl.id = V4L2_CID_ISO_SENSITIVITY;
667 if (::ioctl(fd: fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
668 features |= QCamera::Feature::IsoSensitivity;
669 minIsoChanged(iso: queryControl.minimum);
670 maxIsoChanged(iso: queryControl.minimum);
671 }
672 }
673 });
674
675 supportedFeaturesChanged(features);
676}
677
678int QGstreamerCamera::setV4L2ColorTemperature(int temperature)
679{
680 if (v4l2AutoWhiteBalanceSupported) {
681 setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, value: temperature == 0 ? true : false);
682 } else if (temperature == 0) {
683 temperature = 5600;
684 }
685
686 if (temperature != 0 && v4l2ColorTemperatureSupported) {
687 temperature = qBound(min: v4l2MinColorTemp, val: temperature, max: v4l2MaxColorTemp);
688 if (!setV4L2Parameter(V4L2_CID_WHITE_BALANCE_TEMPERATURE, value: qBound(min: v4l2MinColorTemp, val: temperature, max: v4l2MaxColorTemp)))
689 temperature = 0;
690 } else {
691 temperature = 0;
692 }
693
694 return temperature;
695}
696
697bool QGstreamerCamera::setV4L2Parameter(quint32 id, qint32 value)
698{
699 return withV4L2DeviceFileDescriptor(f: [&](int fd) {
700 v4l2_control control{ .id: id, .value: value };
701 if (::ioctl(fd: fd, VIDIOC_S_CTRL, &control) != 0) {
702 qWarning() << "Unable to set the V4L2 Parameter" << Qt::hex << id << "to" << value
703 << qt_error_string(errno);
704 return false;
705 }
706 return true;
707 });
708}
709
710int QGstreamerCamera::getV4L2Parameter(quint32 id) const
711{
712 return withV4L2DeviceFileDescriptor(f: [&](int fd) {
713 v4l2_control control{ .id: id, .value: 0 };
714 if (::ioctl(fd: fd, VIDIOC_G_CTRL, &control) != 0) {
715 qWarning() << "Unable to get the V4L2 Parameter" << Qt::hex << id
716 << qt_error_string(errno);
717 return 0;
718 }
719 return control.value;
720 });
721}
722
723#endif // QT_CONFIG(linux_v4l)
724
725QGstreamerCustomCamera::QGstreamerCustomCamera(QCamera *camera)
726 : QGstreamerCameraBase{
727 camera,
728 },
729 m_userProvidedGstElement{
730 false,
731 }
732{
733}
734
735QGstreamerCustomCamera::QGstreamerCustomCamera(QCamera *camera, QGstElement element)
736 : QGstreamerCameraBase{
737 camera,
738 },
739 gstCamera{
740 std::move(element),
741 },
742 m_userProvidedGstElement{
743 true,
744 }
745{
746}
747
748void QGstreamerCustomCamera::setCamera(const QCameraDevice &device)
749{
750 if (m_userProvidedGstElement)
751 return;
752
753 gstCamera = QGstBin::createFromPipelineDescription(pipelineDescription: device.id(), /*name=*/nullptr,
754 /* ghostUnlinkedPads=*/true);
755}
756
757bool QGstreamerCustomCamera::isActive() const
758{
759 return m_active;
760}
761
762void QGstreamerCustomCamera::setActive(bool active)
763{
764 if (m_active == active)
765 return;
766
767 m_active = active;
768
769 emit activeChanged(active);
770}
771
772QT_END_NAMESPACE
773

source code of qtmultimedia/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp