Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qavfcamerabase.mm
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
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 "avfcameradebug_p.h"
5#include "qavfcamerabase_p.h"
7#include <private/qcameradevice_p.h>
8#include "qavfhelpers_p.h"
9#include <private/qplatformmediaintegration_p.h>
10#include <QtCore/qset.h>
11
13
14namespace {
15
16// All these methods to work with exposure/ISO/SS in custom mode do not support macOS.
17
18#ifdef Q_OS_IOS
19
20// Misc. helpers to check values/ranges:
21
22bool qt_check_exposure_duration(AVCaptureDevice *captureDevice, CMTime duration)
23{
24 Q_ASSERT(captureDevice);
25
26 AVCaptureDeviceFormat *activeFormat = captureDevice.activeFormat;
27 if (!activeFormat) {
28 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to obtain capture device format";
29 return false;
30 }
31
32 return CMTimeCompare(duration, activeFormat.minExposureDuration) != -1
33 && CMTimeCompare(activeFormat.maxExposureDuration, duration) != -1;
34}
35
36bool qt_check_ISO_value(AVCaptureDevice *captureDevice, int newISO)
37{
38 Q_ASSERT(captureDevice);
39
40 AVCaptureDeviceFormat *activeFormat = captureDevice.activeFormat;
41 if (!activeFormat) {
42 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to obtain capture device format";
43 return false;
44 }
45
46 return !(newISO < activeFormat.minISO || newISO > activeFormat.maxISO);
47}
48
49bool qt_exposure_duration_equal(AVCaptureDevice *captureDevice, qreal qDuration)
50{
51 Q_ASSERT(captureDevice);
52 const CMTime avDuration = CMTimeMakeWithSeconds(qDuration, captureDevice.exposureDuration.timescale);
53 return !CMTimeCompare(avDuration, captureDevice.exposureDuration);
54}
55
56bool qt_iso_equal(AVCaptureDevice *captureDevice, int iso)
57{
58 Q_ASSERT(captureDevice);
59 return qFuzzyCompare(float(iso), captureDevice.ISO);
60}
61
62bool qt_exposure_bias_equal(AVCaptureDevice *captureDevice, qreal bias)
63{
64 Q_ASSERT(captureDevice);
65 return qFuzzyCompare(bias, qreal(captureDevice.exposureTargetBias));
66}
67
68// Converters:
69
70bool qt_convert_exposure_mode(AVCaptureDevice *captureDevice, QCamera::ExposureMode mode,
71 AVCaptureExposureMode &avMode)
72{
73 // Test if mode supported and convert.
74 Q_ASSERT(captureDevice);
75
77 if ([captureDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
78 avMode = AVCaptureExposureModeContinuousAutoExposure;
79 return true;
80 }
81 }
82
84 if ([captureDevice isExposureModeSupported:AVCaptureExposureModeCustom]) {
85 avMode = AVCaptureExposureModeCustom;
86 return true;
87 }
88 }
89
90 return false;
91}
92
93#endif // defined(Q_OS_IOS)
94
95} // Unnamed namespace.
96
97
99 : QPlatformVideoDevices(integration)
100{
101 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
102 m_deviceConnectedObserver = [notificationCenter addObserverForName:AVCaptureDeviceWasConnectedNotification
103 object:nil
104 queue:[NSOperationQueue mainQueue]
105 usingBlock:^(NSNotification *) {
106 this->updateCameraDevices();
107 }];
108
109 m_deviceDisconnectedObserver = [notificationCenter addObserverForName:AVCaptureDeviceWasDisconnectedNotification
110 object:nil
111 queue:[NSOperationQueue mainQueue]
112 usingBlock:^(NSNotification *) {
113 this->updateCameraDevices();
114 }];
115 updateCameraDevices();
116}
117
119{
120 NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
121 [notificationCenter removeObserver:(id)m_deviceConnectedObserver];
122 [notificationCenter removeObserver:(id)m_deviceDisconnectedObserver];
123}
124
126{
127 return m_cameraDevices;
128}
129
130void QAVFVideoDevices::updateCameraDevices()
131{
132#ifdef Q_OS_IOS
133 // Cameras can't change dynamically on iOS. Update only once.
134 if (!m_cameraDevices.isEmpty())
135 return;
136#endif
137
138 QList<QCameraDevice> cameras;
139
140 AVCaptureDevice *defaultDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
141 NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
142
143 for (AVCaptureDevice *device in videoDevices) {
144 auto info = std::make_unique<QCameraDevicePrivate>();
145 if (defaultDevice && [defaultDevice.uniqueID isEqualToString:device.uniqueID])
146 info->isDefault = true;
147 info->id = QByteArray([[device uniqueID] UTF8String]);
148 info->description = QString::fromNSString([device localizedName]);
149
150 qCDebug(qLcCamera) << "Handling camera info" << info->description
151 << (info->isDefault ? "(default)" : "");
152
153 QSet<QSize> photoResolutions;
154 QList<QCameraFormat> videoFormats;
155
156 for (AVCaptureDeviceFormat *format in device.formats) {
157 if (![format.mediaType isEqualToString:AVMediaTypeVideo])
158 continue;
159
160 auto dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
161 QSize resolution(dimensions.width, dimensions.height);
162 photoResolutions.insert(resolution);
163
164 float maxFrameRate = 0;
165 float minFrameRate = 1.e6;
166
167 auto encoding = CMVideoFormatDescriptionGetCodecType(format.formatDescription);
168 auto pixelFormat = QAVFHelpers::fromCVPixelFormat(encoding);
169 // Ignore pixel formats we can't handle
170 if (pixelFormat == QVideoFrameFormat::Format_Invalid) {
171 qCDebug(qLcCamera) << "ignore camera CV format" << encoding
172 << "as no matching video format found";
173 continue;
174 }
175
176 for (AVFrameRateRange *frameRateRange in format.videoSupportedFrameRateRanges) {
177 if (frameRateRange.minFrameRate < minFrameRate)
178 minFrameRate = frameRateRange.minFrameRate;
179 if (frameRateRange.maxFrameRate > maxFrameRate)
180 maxFrameRate = frameRateRange.maxFrameRate;
181 }
182
183#ifdef Q_OS_IOS
184 // From Apple's docs (iOS):
185 // By default, AVCaptureStillImageOutput emits images with the same dimensions as
186 // its source AVCaptureDevice instance’s activeFormat.formatDescription. However,
187 // if you set this property to YES, the receiver emits still images at the capture
188 // device’s highResolutionStillImageDimensions value.
190 if (!hrRes.isNull() && hrRes.isValid())
191 photoResolutions.insert(hrRes);
192#endif
193
194 qCDebug(qLcCamera) << "Add camera format. pixelFormat:" << pixelFormat
195 << "cvPixelFormat" << encoding << "resolution:" << resolution
196 << "frameRate: [" << minFrameRate << maxFrameRate << "]";
197
198 auto *f = new QCameraFormatPrivate{
199 QSharedData(),
200 pixelFormat,
201 resolution,
202 minFrameRate,
203 maxFrameRate
204 };
205 videoFormats << f->create();
206 }
207 if (videoFormats.isEmpty()) {
208 // skip broken cameras without valid formats
209 qCWarning(qLcCamera())
210 << "Skip camera" << info->description << "without supported formats";
211 continue;
212 }
213 info->videoFormats = videoFormats;
214 info->photoResolutions = photoResolutions.values();
215
216 cameras.append(info.release()->create());
217 }
218
219 if (cameras != m_cameraDevices) {
220 m_cameraDevices = cameras;
222 }
223}
224
225
228{
230}
231
233{
234}
235
237{
238 return m_active;
239}
240
242{
243 if (m_active == active)
244 return;
245 if (m_cameraDevice.isNull() && active)
246 return;
247
248 m_active = active;
249
250 if (active)
253}
254
256{
257 if (m_cameraDevice == camera)
258 return;
260 setCameraFormat({});
261}
262
264{
265 if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format))
266 return false;
267
269
270 return true;
271}
272
273AVCaptureDevice *QAVFCameraBase::device() const
274{
275 AVCaptureDevice *device = nullptr;
276 QByteArray deviceId = m_cameraDevice.id();
277 if (!deviceId.isEmpty()) {
278 device = [AVCaptureDevice deviceWithUniqueID:
279 [NSString stringWithUTF8String:
280 deviceId.constData()]];
281 }
282 return device;
283}
284
285#ifdef Q_OS_IOS
286namespace
287{
288
289bool qt_focus_mode_supported(QCamera::FocusMode mode)
290{
291 // Check if QCamera::FocusMode has counterpart in AVFoundation.
292
293 // AVFoundation has 'Manual', 'Auto' and 'Continuous',
294 // where 'Manual' is actually 'Locked' + writable property 'lensPosition'.
297}
298
299AVCaptureFocusMode avf_focus_mode(QCamera::FocusMode requestedMode)
300{
301 switch (requestedMode) {
305 return AVCaptureFocusModeLocked;
306 default:
307 return AVCaptureFocusModeContinuousAutoFocus;
308 }
309
310}
311
312}
313#endif
314
316{
317#ifdef Q_OS_IOS
318 if (focusMode() == mode)
319 return;
320
321 AVCaptureDevice *captureDevice = device();
322 if (!captureDevice) {
323 if (qt_focus_mode_supported(mode)) {
325 } else {
326 qCDebug(qLcCamera) << Q_FUNC_INFO
327 << "focus mode not supported";
328 }
329 return;
330 }
331
333 const AVFConfigurationLock lock(captureDevice);
334 if (!lock) {
335 qCDebug(qLcCamera) << Q_FUNC_INFO
336 << "failed to lock for configuration";
337 return;
338 }
339
340 captureDevice.focusMode = avf_focus_mode(mode);
341 } else {
342 qCDebug(qLcCamera) << Q_FUNC_INFO << "focus mode not supported";
343 return;
344 }
345
347#else
348 Q_UNUSED(mode);
349#endif
350}
351
353{
354#ifdef Q_OS_IOS
355 AVCaptureDevice *captureDevice = device();
356 if (captureDevice) {
357 AVCaptureFocusMode avMode = avf_focus_mode(mode);
358 switch (mode) {
363 return [captureDevice isFocusModeSupported:avMode];
367 return captureDevice.autoFocusRangeRestrictionSupported
368 && [captureDevice isFocusModeSupported:avMode];
369 }
370 }
371#endif
372 return mode == QCamera::FocusModeAuto; // stupid builtin webcam doesn't do any focus handling, but hey it's usually focused :)
373}
374
376{
377 if (customFocusPoint() == point)
378 return;
379
380 if (!QRectF(0.f, 0.f, 1.f, 1.f).contains(point)) {
381 // ### release custom focus point, tell the camera to focus where it wants...
382 qCDebug(qLcCamera) << Q_FUNC_INFO << "invalid focus point (out of range)";
383 return;
384 }
385
386 AVCaptureDevice *captureDevice = device();
387 if (!captureDevice)
388 return;
389
390 if ([captureDevice isFocusPointOfInterestSupported]) {
391 const AVFConfigurationLock lock(captureDevice);
392 if (!lock) {
393 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
394 return;
395 }
396
397 const CGPoint focusPOI = CGPointMake(point.x(), point.y());
398 [captureDevice setFocusPointOfInterest:focusPOI];
400 [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
401
403 }
404}
405
407{
408#ifdef Q_OS_IOS
409 AVCaptureDevice *captureDevice = device();
410 if (!captureDevice)
411 return;
412
413 if (captureDevice.lockingFocusWithCustomLensPositionSupported) {
414 qCDebug(qLcCamera) << Q_FUNC_INFO << "Setting custom focus distance not supported\n";
415 return;
416 }
417
418 {
419 AVFConfigurationLock lock(captureDevice);
420 if (!lock) {
421 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
422 return;
423 }
424 [captureDevice setFocusModeLockedWithLensPosition:d completionHandler:nil];
425 }
427#else
428 Q_UNUSED(d);
429#endif
430}
431
433{
434 AVCaptureDevice *captureDevice = device();
435 if (!captureDevice) {
436 qCDebug(qLcCamera) << Q_FUNC_INFO << "capture device is nil in 'active' state";
437 return;
438 }
439
440 const AVFConfigurationLock lock(captureDevice);
441 if (!lock) {
442 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
443 return;
444 }
445
446 if ([captureDevice isFocusPointOfInterestSupported]) {
447 auto point = customFocusPoint();
448 const CGPoint focusPOI = CGPointMake(point.x(), point.y());
449 [captureDevice setFocusPointOfInterest:focusPOI];
450 }
451
452#ifdef Q_OS_IOS
454 const AVCaptureFocusMode avMode = avf_focus_mode(focusMode());
455 if (captureDevice.focusMode != avMode) {
456 if ([captureDevice isFocusModeSupported:avMode]) {
457 [captureDevice setFocusMode:avMode];
458 } else {
459 qCDebug(qLcCamera) << Q_FUNC_INFO << "focus mode not supported";
460 }
461 }
462 }
463
464 if (!captureDevice.activeFormat) {
465 qCDebug(qLcCamera) << Q_FUNC_INFO << "camera state is active, but active format is nil";
466 return;
467 }
468
469 minimumZoomFactorChanged(captureDevice.minAvailableVideoZoomFactor);
470 maximumZoomFactorChanged(captureDevice.activeFormat.videoMaxZoomFactor);
471
472 captureDevice.videoZoomFactor = zoomFactor();
473
474 CMTime newDuration = AVCaptureExposureDurationCurrent;
475 bool setCustomMode = false;
476
478 if (exposureTime > 0
479 && !qt_exposure_duration_equal(captureDevice, exposureTime)) {
480 newDuration = CMTimeMakeWithSeconds(exposureTime, captureDevice.exposureDuration.timescale);
481 if (!qt_check_exposure_duration(captureDevice, newDuration)) {
482 qCDebug(qLcCamera) << Q_FUNC_INFO << "requested exposure duration is out of range";
483 return;
484 }
485 setCustomMode = true;
486 }
487
488 float newISO = AVCaptureISOCurrent;
489 int iso = manualIsoSensitivity();
490 if (iso > 0 && !qt_iso_equal(captureDevice, iso)) {
491 newISO = iso;
492 if (!qt_check_ISO_value(captureDevice, newISO)) {
493 qCDebug(qLcCamera) << Q_FUNC_INFO << "requested ISO value is out of range";
494 return;
495 }
496 setCustomMode = true;
497 }
498
499 float bias = exposureCompensation();
500 if (bias != 0 && !qt_exposure_bias_equal(captureDevice, bias)) {
501 // TODO: mixed fpns.
502 if (bias < captureDevice.minExposureTargetBias || bias > captureDevice.maxExposureTargetBias) {
503 qCDebug(qLcCamera) << Q_FUNC_INFO << "exposure compensation value is"
504 << "out of range";
505 return;
506 }
507 [captureDevice setExposureTargetBias:bias completionHandler:nil];
508 }
509
510 // Setting shutter speed (exposure duration) or ISO values
511 // also reset exposure mode into Custom. With this settings
512 // we ignore any attempts to set exposure mode.
513
514 if (setCustomMode) {
515 [captureDevice setExposureModeCustomWithDuration:newDuration
516 ISO:newISO
517 completionHandler:nil];
518 return;
519 }
520
522 AVCaptureExposureMode avMode = AVCaptureExposureModeContinuousAutoExposure;
523 if (!qt_convert_exposure_mode(captureDevice, qtMode, avMode)) {
524 qCDebug(qLcCamera) << Q_FUNC_INFO << "requested exposure mode is not supported";
525 return;
526 }
527
528 captureDevice.exposureMode = avMode;
529#endif
530
531 isFlashSupported = isFlashAutoSupported = false;
532 isTorchSupported = isTorchAutoSupported = false;
533
534 if (captureDevice.hasFlash) {
535 if ([captureDevice isFlashModeSupported:AVCaptureFlashModeOn])
536 isFlashSupported = true;
537 if ([captureDevice isFlashModeSupported:AVCaptureFlashModeAuto])
538 isFlashAutoSupported = true;
539 }
540
541 if (captureDevice.hasTorch) {
542 if ([captureDevice isTorchModeSupported:AVCaptureTorchModeOn])
543 isTorchSupported = true;
544 if ([captureDevice isTorchModeSupported:AVCaptureTorchModeAuto])
545 isTorchAutoSupported = true;
546 }
547
549 flashReadyChanged(isFlashSupported);
550}
551
553{
554 QCamera::Features features;
555 AVCaptureDevice *captureDevice = device();
556
557#ifdef Q_OS_IOS
560
561 if (captureDevice && [captureDevice isLockingFocusWithCustomLensPositionSupported])
563#endif
564
565 if (captureDevice && [captureDevice isFocusPointOfInterestSupported])
567
568 supportedFeaturesChanged(features);
569}
570
571void QAVFCameraBase::zoomTo(float factor, float rate)
572{
573 Q_UNUSED(factor);
574 Q_UNUSED(rate);
575
576#ifdef Q_OS_IOS
577 if (zoomFactor() == factor)
578 return;
579
580 AVCaptureDevice *captureDevice = device();
581 if (!captureDevice || !captureDevice.activeFormat)
582 return;
583
584 factor = qBound(captureDevice.minAvailableVideoZoomFactor, factor,
585 captureDevice.activeFormat.videoMaxZoomFactor);
586
587 const AVFConfigurationLock lock(captureDevice);
588 if (!lock) {
589 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
590 return;
591 }
592
593 if (rate <= 0)
594 captureDevice.videoZoomFactor = factor;
595 else
596 [captureDevice rampToVideoZoomFactor:factor withRate:rate];
597#endif
598}
599
601{
602 if (flashMode() == mode)
603 return;
604
606 qCDebug(qLcCamera) << Q_FUNC_INFO << "unsupported mode" << mode;
607 return;
608 }
609
611
612 if (!isActive())
613 return;
614
616}
617
619{
620 if (mode == QCamera::FlashOff)
621 return true;
622 else if (mode == QCamera::FlashOn)
623 return isFlashSupported;
624 else //if (mode == QCamera::FlashAuto)
625 return isFlashAutoSupported;
626}
627
629{
630 if (!isActive())
631 return false;
632
633 AVCaptureDevice *captureDevice = device();
634 if (!captureDevice)
635 return false;
636
637 if (!captureDevice.hasFlash)
638 return false;
639
641 return false;
642
643 // AVCaptureDevice's docs:
644 // "The flash may become unavailable if, for example,
645 // the device overheats and needs to cool off."
646 return [captureDevice isFlashAvailable];
647}
648
650{
651 if (torchMode() == mode)
652 return;
653
655 qCDebug(qLcCamera) << Q_FUNC_INFO << "unsupported torch mode" << mode;
656 return;
657 }
658
660
661 if (!isActive())
662 return;
663
665}
666
668{
669 if (mode == QCamera::TorchOff)
670 return true;
671 else if (mode == QCamera::TorchOn)
672 return isTorchSupported;
673 else //if (mode == QCamera::TorchAuto)
674 return isTorchAutoSupported;
675}
676
678{
679#ifdef Q_OS_IOS
680 if (qtMode != QCamera::ExposureAuto && qtMode != QCamera::ExposureManual) {
681 qCDebug(qLcCamera) << Q_FUNC_INFO << "exposure mode not supported";
682 return;
683 }
684
685 AVCaptureDevice *captureDevice = device();
686 if (!captureDevice) {
687 exposureModeChanged(qtMode);
688 return;
689 }
690
691 AVCaptureExposureMode avMode = AVCaptureExposureModeContinuousAutoExposure;
692 if (!qt_convert_exposure_mode(captureDevice, qtMode, avMode)) {
693 qCDebug(qLcCamera) << Q_FUNC_INFO << "exposure mode not supported";
694 return;
695 }
696
697 const AVFConfigurationLock lock(captureDevice);
698 if (!lock) {
699 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock a capture device"
700 << "for configuration";
701 return;
702 }
703
704 [captureDevice setExposureMode:avMode];
705 exposureModeChanged(qtMode);
706#else
707 Q_UNUSED(qtMode);
708#endif
709}
710
712{
714 return true;
716 return false;
717
718 if (@available(macOS 10.15, *)) {
719 AVCaptureDevice *captureDevice = device();
720 return captureDevice && [captureDevice isExposureModeSupported:AVCaptureExposureModeCustom];
721 }
722
723 return false;
724}
725
727{
729
730 AVCaptureDevice *captureDevice = device();
731 if (!captureDevice) {
732 qCDebug(qLcCamera) << Q_FUNC_INFO << "no capture device found";
733 return;
734 }
735
736 const AVFConfigurationLock lock(captureDevice);
737
738 if (captureDevice.hasFlash) {
739 const auto mode = flashMode();
740
741 auto setAvFlashModeSafe = [&captureDevice](AVCaptureFlashMode avFlashMode) {
742 // Note, in some cases captureDevice.hasFlash == false even though
743 // no there're no supported flash modes.
744 if ([captureDevice isFlashModeSupported:avFlashMode])
745 captureDevice.flashMode = avFlashMode;
746 else
747 qCDebug(qLcCamera) << "Attempt to setup unsupported flash mode " << avFlashMode;
748 };
749
750 if (mode == QCamera::FlashOff) {
751 setAvFlashModeSafe(AVCaptureFlashModeOff);
752 } else {
753 if ([captureDevice isFlashAvailable]) {
754 if (mode == QCamera::FlashOn)
755 setAvFlashModeSafe(AVCaptureFlashModeOn);
756 else if (mode == QCamera::FlashAuto)
757 setAvFlashModeSafe(AVCaptureFlashModeAuto);
758 } else {
759 qCDebug(qLcCamera) << Q_FUNC_INFO << "flash is not available at the moment";
760 }
761 }
762 }
763
764 if (captureDevice.hasTorch) {
765 const auto mode = torchMode();
766
767 auto setAvTorchModeSafe = [&captureDevice](AVCaptureTorchMode avTorchMode) {
768 if ([captureDevice isTorchModeSupported:avTorchMode])
769 captureDevice.torchMode = avTorchMode;
770 else
771 qCDebug(qLcCamera) << "Attempt to setup unsupported torch mode " << avTorchMode;
772 };
773
774 if (mode == QCamera::TorchOff) {
775 setAvTorchModeSafe(AVCaptureTorchModeOff);
776 } else {
777 if ([captureDevice isTorchAvailable]) {
778 if (mode == QCamera::TorchOn)
779 setAvTorchModeSafe(AVCaptureTorchModeOn);
780 else if (mode == QCamera::TorchAuto)
781 setAvTorchModeSafe(AVCaptureTorchModeAuto);
782 } else {
783 qCDebug(qLcCamera) << Q_FUNC_INFO << "torch is not available at the moment";
784 }
785 }
786 }
787}
788
789
791{
792#ifdef Q_OS_IOS
793 AVCaptureDevice *captureDevice = device();
794 if (!captureDevice) {
796 return;
797 }
798
799 bias = qBound(captureDevice.minExposureTargetBias, bias, captureDevice.maxExposureTargetBias);
800
801 const AVFConfigurationLock lock(captureDevice);
802 if (!lock) {
803 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
804 return;
805 }
806
807 [captureDevice setExposureTargetBias:bias completionHandler:nil];
809#else
810 Q_UNUSED(bias);
811#endif
812}
813
815{
816#ifdef Q_OS_IOS
817 if (value < 0) {
819 return;
820 }
821
822 AVCaptureDevice *captureDevice = device();
823 if (!captureDevice) {
825 return;
826 }
827
828 const CMTime newDuration = CMTimeMakeWithSeconds(value, captureDevice.exposureDuration.timescale);
829 if (!qt_check_exposure_duration(captureDevice, newDuration)) {
830 qCDebug(qLcCamera) << Q_FUNC_INFO << "shutter speed value is out of range";
831 return;
832 }
833
834 const AVFConfigurationLock lock(captureDevice);
835 if (!lock) {
836 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
837 return;
838 }
839
840 // Setting the shutter speed (exposure duration in Apple's terms,
841 // since there is no shutter actually) will also reset
842 // exposure mode into custom mode.
843 [captureDevice setExposureModeCustomWithDuration:newDuration
844 ISO:AVCaptureISOCurrent
845 completionHandler:nil];
846
848
849#else
851#endif
852}
853
855{
856#ifdef Q_OS_IOS
857 AVCaptureDevice *captureDevice = device();
858 if (!captureDevice)
859 return -1.;
860 auto duration = captureDevice.exposureDuration;
861 return CMTimeGetSeconds(duration);
862#else
863 return -1;
864#endif
865}
866
867#ifdef Q_OS_IOS
868namespace {
869
870void avf_convert_white_balance_mode(QCamera::WhiteBalanceMode qtMode,
871 AVCaptureWhiteBalanceMode &avMode)
872{
873 if (qtMode == QCamera::WhiteBalanceAuto)
874 avMode = AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance;
875 else
876 avMode = AVCaptureWhiteBalanceModeLocked;
877}
878
879bool avf_set_white_balance_mode(AVCaptureDevice *captureDevice,
880 AVCaptureWhiteBalanceMode avMode)
881{
882 Q_ASSERT(captureDevice);
883
884 const bool lock = [captureDevice lockForConfiguration:nil];
885 if (!lock) {
886 qDebug() << "Failed to lock a capture device for configuration\n";
887 return false;
888 }
889
890 captureDevice.whiteBalanceMode = avMode;
891 [captureDevice unlockForConfiguration];
892 return true;
893}
894
895bool avf_convert_temp_and_tint_to_wb_gains(AVCaptureDevice *captureDevice,
896 float temp, float tint, AVCaptureWhiteBalanceGains &wbGains)
897{
898 Q_ASSERT(captureDevice);
899
900 AVCaptureWhiteBalanceTemperatureAndTintValues wbTTValues = {
901 .temperature = temp,
902 .tint = tint
903 };
904 wbGains = [captureDevice deviceWhiteBalanceGainsForTemperatureAndTintValues:wbTTValues];
905
906 if (wbGains.redGain >= 1.0 && wbGains.redGain <= captureDevice.maxWhiteBalanceGain
907 && wbGains.greenGain >= 1.0 && wbGains.greenGain <= captureDevice.maxWhiteBalanceGain
908 && wbGains.blueGain >= 1.0 && wbGains.blueGain <= captureDevice.maxWhiteBalanceGain)
909 return true;
910
911 return false;
912}
913
914bool avf_set_white_balance_gains(AVCaptureDevice *captureDevice,
915 AVCaptureWhiteBalanceGains wbGains)
916{
917 const bool lock = [captureDevice lockForConfiguration:nil];
918 if (!lock) {
919 qDebug() << "Failed to lock a capture device for configuration\n";
920 return false;
921 }
922
923 [captureDevice setWhiteBalanceModeLockedWithDeviceWhiteBalanceGains:wbGains
924 completionHandler:nil];
925 [captureDevice unlockForConfiguration];
926 return true;
927}
928
929}
930
932{
934 return true;
935 AVCaptureDevice *captureDevice = device();
936 if (!captureDevice)
937 return false;
938 return [captureDevice isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked];
939}
940
942{
944 return;
945
946 AVCaptureDevice *captureDevice = device();
947 Q_ASSERT(captureDevice);
948
949 const AVFConfigurationLock lock(captureDevice);
950 if (!lock) {
951 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock a capture device"
952 << "for configuration";
953 return;
954 }
955
956 AVCaptureWhiteBalanceMode avMode;
957 avf_convert_white_balance_mode(mode, avMode);
958 avf_set_white_balance_mode(captureDevice, avMode);
959
962 return;
963 }
964
965 const int colorTemp = colorTemperatureForWhiteBalance(mode);
966 AVCaptureWhiteBalanceGains wbGains;
967 if (avf_convert_temp_and_tint_to_wb_gains(captureDevice, colorTemp, 0., wbGains)
968 && avf_set_white_balance_gains(captureDevice, wbGains))
970}
971
972void QAVFCameraBase::setColorTemperature(int colorTemp)
973{
974 if (colorTemp == 0) {
975 colorTemperatureChanged(colorTemp);
976 return;
977 }
978
979 AVCaptureDevice *captureDevice = device();
980 if (!captureDevice || ![captureDevice isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked])
981 return;
982
983 const AVFConfigurationLock lock(captureDevice);
984 if (!lock) {
985 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock a capture device"
986 << "for configuration";
987 return;
988 }
989
990 AVCaptureWhiteBalanceGains wbGains;
991 if (avf_convert_temp_and_tint_to_wb_gains(captureDevice, colorTemp, 0., wbGains)
992 && avf_set_white_balance_gains(captureDevice, wbGains))
993 colorTemperatureChanged(colorTemp);
994}
995#endif
996
998{
999#ifdef Q_OS_IOS
1000 if (value < 0) {
1002 return;
1003 }
1004
1005 AVCaptureDevice *captureDevice = device();
1006 if (!captureDevice) {
1008 return;
1009 }
1010
1011 if (!qt_check_ISO_value(captureDevice, value)) {
1012 qCDebug(qLcCamera) << Q_FUNC_INFO << "ISO value is out of range";
1013 return;
1014 }
1015
1016 const AVFConfigurationLock lock(captureDevice);
1017 if (!lock) {
1018 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock a capture device"
1019 << "for configuration";
1020 return;
1021 }
1022
1023 // Setting the ISO will also reset
1024 // exposure mode to the custom mode.
1025 [captureDevice setExposureModeCustomWithDuration:AVCaptureExposureDurationCurrent
1026 ISO:value
1027 completionHandler:nil];
1028
1030#else
1031 Q_UNUSED(value);
1032#endif
1033}
1034
1036{
1037 return manualIsoSensitivity();
1038}
1039
1040
1041#include "moc_qavfcamerabase_p.cpp"
QSize qt_device_format_high_resolution(AVCaptureDeviceFormat *format)
IOBluetoothDevice * device
virtual int isoSensitivity() const override
void setFocusDistance(float d) override
bool isTorchModeSupported(QCamera::TorchMode mode) const override
void setCamera(const QCameraDevice &camera) override
QCameraDevice m_cameraDevice
bool isFlashModeSupported(QCamera::FlashMode mode) const override
void setManualIsoSensitivity(int value) override
void setExposureMode(QCamera::ExposureMode) override
bool setCameraFormat(const QCameraFormat &format) override
void setActive(bool activce) override
void updateCameraConfiguration()
void setFocusMode(QCamera::FocusMode mode) override
void setCustomFocusPoint(const QPointF &point) override
void setFlashMode(QCamera::FlashMode mode) override
void setManualExposureTime(float value) override
bool isActive() const override
QAVFCameraBase(QCamera *camera)
virtual float exposureTime() const override
void setTorchMode(QCamera::TorchMode mode) override
bool isFlashReady() const override
void zoomTo(float factor, float rate) override
bool isExposureModeSupported(QCamera::ExposureMode mode) const override
AVCaptureDevice * device() const
void updateCameraProperties()
void setExposureCompensation(float bias) override
bool isFocusModeSupported(QCamera::FocusMode mode) const override
QList< QCameraDevice > videoDevices() const override
QAVFVideoDevices(QPlatformMediaIntegration *integration)
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
The QCameraDevice class provides general information about camera devices.
bool isNull() const
Returns true if this QCameraDevice is null or invalid.
QList< QCameraFormat > videoFormats
\qmlproperty CameraFormat QtMultimedia::cameraDevice::videoFormats
QByteArray id
\qmlproperty string QtMultimedia::cameraDevice::id
The QCameraFormat class describes a video format supported by a camera device. \inmodule QtMultimedia...
bool isNull() const noexcept
Returns true if this is a default constructed QCameraFormat.
The QCamera class provides interface for system camera devices.
Definition qcamera.h:28
WhiteBalanceMode
\value WhiteBalanceAuto Auto white balance mode.
Definition qcamera.h:112
@ WhiteBalanceManual
Definition qcamera.h:114
@ WhiteBalanceAuto
Definition qcamera.h:113
TorchMode
\value TorchOff Torch is Off.
Definition qcamera.h:84
@ TorchOn
Definition qcamera.h:86
@ TorchAuto
Definition qcamera.h:87
@ TorchOff
Definition qcamera.h:85
FocusMode
\value FocusModeAuto Continuous auto focus mode.
Definition qcamera.h:67
@ FocusModeAutoNear
Definition qcamera.h:69
@ FocusModeInfinity
Definition qcamera.h:72
@ FocusModeAutoFar
Definition qcamera.h:70
@ FocusModeAuto
Definition qcamera.h:68
@ FocusModeManual
Definition qcamera.h:73
@ FocusModeHyperfocal
Definition qcamera.h:71
FlashMode
\value FlashOff Flash is Off.
Definition qcamera.h:77
@ FlashAuto
Definition qcamera.h:80
@ FlashOn
Definition qcamera.h:79
@ FlashOff
Definition qcamera.h:78
ExposureMode
\value ExposureAuto Automatic mode.
Definition qcamera.h:91
@ ExposureManual
Definition qcamera.h:93
@ ExposureAuto
Definition qcamera.h:92
Definition qlist.h:74
bool isEmpty() const noexcept
Definition qlist.h:390
void append(parameter_type t)
Definition qlist.h:441
QCameraFormat findBestCameraFormat(const QCameraDevice &camera) const
void isoSensitivityChanged(int iso)
void torchModeChanged(QCamera::TorchMode mode)
void flashReadyChanged(bool)
void focusModeChanged(QCamera::FocusMode mode)
void exposureCompensationChanged(float compensation)
virtual void setWhiteBalanceMode(QCamera::WhiteBalanceMode)
virtual void setColorTemperature(int)
void whiteBalanceModeChanged(QCamera::WhiteBalanceMode mode)
float zoomFactor() const
QPointF customFocusPoint() const
QCamera::FocusMode focusMode() const
QCamera::FlashMode flashMode() const
void focusDistanceChanged(float d)
int manualIsoSensitivity() const
QCamera::ExposureMode exposureMode() const
static int colorTemperatureForWhiteBalance(QCamera::WhiteBalanceMode mode)
void maximumZoomFactorChanged(float)
void customFocusPointChanged(const QPointF &point)
void flashModeChanged(QCamera::FlashMode mode)
void colorTemperatureChanged(int temperature)
float manualExposureTime() const
float exposureCompensation() const
void exposureModeChanged(QCamera::ExposureMode mode)
void supportedFeaturesChanged(QCamera::Features)
void minimumZoomFactorChanged(float factor)
virtual bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
QCamera::TorchMode torchMode() const
QCameraFormat m_cameraFormat
void exposureTimeChanged(float speed)
void activeChanged(bool)
\inmodule QtCore\reentrant
Definition qpoint.h:214
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:333
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:338
\inmodule QtCore\reentrant
Definition qrect.h:483
Definition qset.h:18
QList< T > values() const
Definition qset.h:297
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtCore
Definition qshareddata.h:19
\inmodule QtCore
Definition qsize.h:25
QCamera * camera
Definition camera.cpp:19
QVideoFrameFormat::PixelFormat fromCVPixelFormat(unsigned avPixelFormat)
#define Q_FALLTHROUGH()
#define Q_FUNC_INFO
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
#define qDebug
[1]
Definition qlogging.h:160
#define qCWarning(category,...)
#define qCDebug(category,...)
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLenum mode
GLenum GLuint id
[7]
GLuint object
[3]
GLfloat GLfloat f
GLint GLsizei GLsizei GLenum format
GLfloat bias
GLuint GLenum * rate
GLuint in
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_EMIT
#define emit
#define Q_UNUSED(x)
double qreal
Definition qtypes.h:92
QFileInfo info(fileName)
[8]
QReadWriteLock lock
[0]
QQueue< int > queue
[0]
bool contains(const AT &t) const noexcept
Definition qlist.h:44