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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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