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 | |
21 | QT_BEGIN_NAMESPACE |
22 | |
23 | QMaybe<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 | |
33 | QGstreamerCamera::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 | |
59 | QGstreamerCamera::~QGstreamerCamera() |
60 | { |
61 | gstCameraBin.setStateSync(state: GST_STATE_NULL); |
62 | } |
63 | |
64 | bool QGstreamerCamera::isActive() const |
65 | { |
66 | return m_active; |
67 | } |
68 | |
69 | void 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 | |
81 | void 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 | |
141 | bool 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 | |
176 | void 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) |
195 | GstPhotography *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 | |
203 | void 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 | |
239 | bool 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 | |
248 | void 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 | |
275 | bool 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 | |
285 | bool QGstreamerCamera::isFlashReady() const |
286 | { |
287 | #if QT_CONFIG(gstreamer_photography) |
288 | if (photography()) |
289 | return true; |
290 | #endif |
291 | |
292 | return false; |
293 | } |
294 | |
295 | void 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 | |
377 | bool 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 | |
393 | void 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 | |
413 | void 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 | |
436 | int 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 | |
455 | void 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 | |
475 | float 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 | |
492 | bool 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 | |
530 | void 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 | |
579 | void 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) |
608 | bool QGstreamerCamera::isV4L2Camera() const |
609 | { |
610 | return !m_v4l2DevicePath.isEmpty(); |
611 | } |
612 | |
613 | void 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 | |
678 | int 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 | |
697 | bool 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 | |
710 | int 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 | |
725 | QGstreamerCustomCamera::QGstreamerCustomCamera(QCamera *camera) |
726 | : QGstreamerCameraBase{ |
727 | camera, |
728 | }, |
729 | m_userProvidedGstElement{ |
730 | false, |
731 | } |
732 | { |
733 | } |
734 | |
735 | QGstreamerCustomCamera::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 | |
748 | void 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 | |
757 | bool QGstreamerCustomCamera::isActive() const |
758 | { |
759 | return m_active; |
760 | } |
761 | |
762 | void QGstreamerCustomCamera::setActive(bool active) |
763 | { |
764 | if (m_active == active) |
765 | return; |
766 | |
767 | m_active = active; |
768 | |
769 | emit activeChanged(active); |
770 | } |
771 | |
772 | QT_END_NAMESPACE |
773 | |