Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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#include <QtCore/qsystemdetection.h>
12
14
15namespace {
16
17// All these methods to work with exposure/ISO/SS in custom mode do not support macOS.
18
19#ifdef Q_OS_IOS
20
21// Misc. helpers to check values/ranges:
22
23bool qt_check_exposure_duration(AVCaptureDevice *captureDevice, CMTime duration)
24{
25 Q_ASSERT(captureDevice);
26
27 AVCaptureDeviceFormat *activeFormat = captureDevice.activeFormat;
28 if (!activeFormat) {
29 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to obtain capture device format";
30 return false;
31 }
32
33 return CMTimeCompare(duration, activeFormat.minExposureDuration) != -1
34 && CMTimeCompare(activeFormat.maxExposureDuration, duration) != -1;
35}
36
37bool qt_check_ISO_value(AVCaptureDevice *captureDevice, int newISO)
38{
39 Q_ASSERT(captureDevice);
40
41 AVCaptureDeviceFormat *activeFormat = captureDevice.activeFormat;
42 if (!activeFormat) {
43 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to obtain capture device format";
44 return false;
45 }
46
47 return !(newISO < activeFormat.minISO || newISO > activeFormat.maxISO);
48}
49
50bool qt_exposure_duration_equal(AVCaptureDevice *captureDevice, qreal qDuration)
51{
52 Q_ASSERT(captureDevice);
53 const CMTime avDuration = CMTimeMakeWithSeconds(qDuration, captureDevice.exposureDuration.timescale);
54 return !CMTimeCompare(avDuration, captureDevice.exposureDuration);
55}
56
57bool qt_iso_equal(AVCaptureDevice *captureDevice, int iso)
58{
59 Q_ASSERT(captureDevice);
60 return qFuzzyCompare(float(iso), captureDevice.ISO);
61}
62
63bool qt_exposure_bias_equal(AVCaptureDevice *captureDevice, qreal bias)
64{
65 Q_ASSERT(captureDevice);
66 return qFuzzyCompare(bias, qreal(captureDevice.exposureTargetBias));
67}
68
69// Converters:
70
71bool qt_convert_exposure_mode(AVCaptureDevice *captureDevice, QCamera::ExposureMode mode,
72 AVCaptureExposureMode &avMode)
73{
74 // Test if mode supported and convert.
75 Q_ASSERT(captureDevice);
76
78 if ([captureDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
79 avMode = AVCaptureExposureModeContinuousAutoExposure;
80 return true;
81 }
82 }
83
85 if ([captureDevice isExposureModeSupported:AVCaptureExposureModeCustom]) {
86 avMode = AVCaptureExposureModeCustom;
87 return true;
88 }
89 }
90
91 return false;
92}
93
94#endif // defined(Q_OS_IOS)
95
96} // Unnamed namespace.
97
98
100 : QPlatformVideoDevices(integration)
101{
102 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
103 m_deviceConnectedObserver = [notificationCenter addObserverForName:AVCaptureDeviceWasConnectedNotification
104 object:nil
105 queue:[NSOperationQueue mainQueue]
106 usingBlock:^(NSNotification *) {
107 this->updateCameraDevices();
108 }];
109
110 m_deviceDisconnectedObserver = [notificationCenter addObserverForName:AVCaptureDeviceWasDisconnectedNotification
111 object:nil
112 queue:[NSOperationQueue mainQueue]
113 usingBlock:^(NSNotification *) {
114 this->updateCameraDevices();
115 }];
116 updateCameraDevices();
117}
118
120{
121 NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
122 [notificationCenter removeObserver:(id)m_deviceConnectedObserver];
123 [notificationCenter removeObserver:(id)m_deviceDisconnectedObserver];
124}
125
126QList<QCameraDevice> QAVFVideoDevices::videoDevices() const
127{
128 return m_cameraDevices;
129}
130
131void QAVFVideoDevices::updateCameraDevices()
132{
133#ifdef Q_OS_IOS
134 // Cameras can't change dynamically on iOS. Update only once.
135 if (!m_cameraDevices.isEmpty())
136 return;
137#endif
138
139 QList<QCameraDevice> cameras;
140
141 // List of all capture device types that we want to discover. Seems that this is the
142 // only way to discover all types. This filter is mandatory and has no "unspecified"
143 // option like AVCaptureDevicePosition(Unspecified) has. Order of the list is important
144 // because discovered devices will be in the same order and we want the first one found
145 // to be our default device.
146 NSArray *discoveryDevices = @[
147#ifdef Q_OS_IOS
148 AVCaptureDeviceTypeBuiltInTripleCamera, // We always prefer triple camera.
149 AVCaptureDeviceTypeBuiltInDualCamera, // If triple is not available, we prefer
150 // dual with wide + tele lens.
151 AVCaptureDeviceTypeBuiltInDualWideCamera, // Dual with wide and ultrawide is still
152 // better than single.
153#endif
154 AVCaptureDeviceTypeBuiltInWideAngleCamera, // This is the most common single camera type.
155 // We prefer that over tele and ultra-wide.
156#ifdef Q_OS_IOS
157 AVCaptureDeviceTypeBuiltInTelephotoCamera, // Cannot imagine how, but if only tele and
158 // ultrawide are available, we prefer tele.
159 AVCaptureDeviceTypeBuiltInUltraWideCamera,
160#endif
161 ];
162
163#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_14_0, __IPHONE_17_0, __TVOS_NA, __WATCHOS_NA)
164 if (@available(macOS 14, iOS 17, *)) {
165 discoveryDevices = [discoveryDevices arrayByAddingObjectsFromArray: @[
166 AVCaptureDeviceTypeExternal,
167 AVCaptureDeviceTypeContinuityCamera
168 ]];
169 } else
170#endif
171 {
172#ifdef Q_OS_MACOS
175 discoveryDevices = [discoveryDevices arrayByAddingObjectsFromArray: @[
176 AVCaptureDeviceTypeExternalUnknown
177 ]];
179#endif
180 }
181 // Create discovery session to discover all possible camera types of the system.
182 // Both "hard" and "soft" types.
183 AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession
184 discoverySessionWithDeviceTypes:discoveryDevices
185 mediaType:AVMediaTypeVideo
186 position:AVCaptureDevicePositionUnspecified];
187 NSArray<AVCaptureDevice *> *videoDevices = discoverySession.devices;
188
189 for (AVCaptureDevice *device in videoDevices) {
190 auto info = std::make_unique<QCameraDevicePrivate>();
191 if ([videoDevices[0].uniqueID isEqualToString:device.uniqueID])
192 info->isDefault = true;
193 info->id = QByteArray([[device uniqueID] UTF8String]);
194 info->description = QString::fromNSString([device localizedName]);
195
196 qCDebug(qLcCamera) << "Handling camera info" << info->description
197 << (info->isDefault ? "(default)" : "");
198
199 QSet<QSize> photoResolutions;
200 QList<QCameraFormat> videoFormats;
201
202 for (AVCaptureDeviceFormat *format in device.formats) {
203 if (![format.mediaType isEqualToString:AVMediaTypeVideo])
204 continue;
205
206 auto dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
207 QSize resolution(dimensions.width, dimensions.height);
208 photoResolutions.insert(resolution);
209
210 float maxFrameRate = 0;
211 float minFrameRate = 1.e6;
212
213 auto encoding = CMVideoFormatDescriptionGetCodecType(format.formatDescription);
214 auto pixelFormat = QAVFHelpers::fromCVPixelFormat(encoding);
215 auto colorRange = QAVFHelpers::colorRangeForCVPixelFormat(encoding);
216 // Ignore pixel formats we can't handle
217 if (pixelFormat == QVideoFrameFormat::Format_Invalid) {
218 qCDebug(qLcCamera) << "ignore camera CV format" << encoding
219 << "as no matching video format found";
220 continue;
221 }
222
223 for (AVFrameRateRange *frameRateRange in format.videoSupportedFrameRateRanges) {
224 if (frameRateRange.minFrameRate < minFrameRate)
225 minFrameRate = frameRateRange.minFrameRate;
226 if (frameRateRange.maxFrameRate > maxFrameRate)
227 maxFrameRate = frameRateRange.maxFrameRate;
228 }
229
230#ifdef Q_OS_IOS
231 // From Apple's docs (iOS):
232 // By default, AVCaptureStillImageOutput emits images with the same dimensions as
233 // its source AVCaptureDevice instance’s activeFormat.formatDescription. However,
234 // if you set this property to YES, the receiver emits still images at the capture
235 // device’s highResolutionStillImageDimensions value.
237 if (!hrRes.isNull() && hrRes.isValid())
238 photoResolutions.insert(hrRes);
239#endif
240
241 qCDebug(qLcCamera) << "Add camera format. pixelFormat:" << pixelFormat
242 << "colorRange:" << colorRange << "cvPixelFormat" << encoding
243 << "resolution:" << resolution << "frameRate: [" << minFrameRate
244 << maxFrameRate << "]";
245
246 auto *f = new QCameraFormatPrivate{ QSharedData(), pixelFormat, resolution,
247 minFrameRate, maxFrameRate, colorRange };
248 videoFormats << f->create();
249 }
250 if (videoFormats.isEmpty()) {
251 // skip broken cameras without valid formats
252 qCWarning(qLcCamera())
253 << "Skip camera" << info->description << "without supported formats";
254 continue;
255 }
256 info->videoFormats = videoFormats;
257 info->photoResolutions = photoResolutions.values();
258
259 cameras.append(info.release()->create());
260 }
261
262 if (cameras != m_cameraDevices) {
263 m_cameraDevices = cameras;
265 }
266}
267
268
274
278
280{
281 return m_active;
282}
283
285{
286 if (m_active == active)
287 return;
288 if (m_cameraDevice.isNull() && active)
289 return;
290
291 m_active = active;
292
293 if (active)
296}
297
299{
300 if (m_cameraDevice == camera)
301 return;
303 setCameraFormat({});
304}
305
307{
308 if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format))
309 return false;
310
312
313 return true;
314}
315
316AVCaptureDevice *QAVFCameraBase::device() const
317{
318 AVCaptureDevice *device = nullptr;
319 QByteArray deviceId = m_cameraDevice.id();
320 if (!deviceId.isEmpty()) {
321 device = [AVCaptureDevice deviceWithUniqueID:
322 [NSString stringWithUTF8String:
323 deviceId.constData()]];
324 }
325 return device;
326}
327
328#ifdef Q_OS_IOS
329namespace
330{
331
332bool qt_focus_mode_supported(QCamera::FocusMode mode)
333{
334 // Check if QCamera::FocusMode has counterpart in AVFoundation.
335
336 // AVFoundation has 'Manual', 'Auto' and 'Continuous',
337 // where 'Manual' is actually 'Locked' + writable property 'lensPosition'.
340}
341
342AVCaptureFocusMode avf_focus_mode(QCamera::FocusMode requestedMode)
343{
344 switch (requestedMode) {
348 return AVCaptureFocusModeLocked;
349 default:
350 return AVCaptureFocusModeContinuousAutoFocus;
351 }
352
353}
354
355}
356#endif
357
359{
360#ifdef Q_OS_IOS
361 if (focusMode() == mode)
362 return;
363
364 AVCaptureDevice *captureDevice = device();
365 if (!captureDevice) {
366 if (qt_focus_mode_supported(mode)) {
368 } else {
369 qCDebug(qLcCamera) << Q_FUNC_INFO
370 << "focus mode not supported";
371 }
372 return;
373 }
374
376 const AVFConfigurationLock lock(captureDevice);
377 if (!lock) {
378 qCDebug(qLcCamera) << Q_FUNC_INFO
379 << "failed to lock for configuration";
380 return;
381 }
382
383 captureDevice.focusMode = avf_focus_mode(mode);
384 } else {
385 qCDebug(qLcCamera) << Q_FUNC_INFO << "focus mode not supported";
386 return;
387 }
388
390#else
391 Q_UNUSED(mode);
392#endif
393}
394
396{
397#ifdef Q_OS_IOS
398 AVCaptureDevice *captureDevice = device();
399 if (captureDevice) {
400 AVCaptureFocusMode avMode = avf_focus_mode(mode);
401 switch (mode) {
406 return [captureDevice isFocusModeSupported:avMode];
410 return captureDevice.autoFocusRangeRestrictionSupported
411 && [captureDevice isFocusModeSupported:avMode];
412 }
413 }
414#endif
415 return mode == QCamera::FocusModeAuto; // stupid builtin webcam doesn't do any focus handling, but hey it's usually focused :)
416}
417
419{
420 if (customFocusPoint() == point)
421 return;
422
423 if (!QRectF(0.f, 0.f, 1.f, 1.f).contains(point)) {
424 // ### release custom focus point, tell the camera to focus where it wants...
425 qCDebug(qLcCamera) << Q_FUNC_INFO << "invalid focus point (out of range)";
426 return;
427 }
428
429 AVCaptureDevice *captureDevice = device();
430 if (!captureDevice)
431 return;
432
433 if ([captureDevice isFocusPointOfInterestSupported]) {
434 const AVFConfigurationLock lock(captureDevice);
435 if (!lock) {
436 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
437 return;
438 }
439
440 const CGPoint focusPOI = CGPointMake(point.x(), point.y());
441 [captureDevice setFocusPointOfInterest:focusPOI];
443 [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
444
446 }
447}
448
450{
451#ifdef Q_OS_IOS
452 AVCaptureDevice *captureDevice = device();
453 if (!captureDevice)
454 return;
455
456 if (captureDevice.lockingFocusWithCustomLensPositionSupported) {
457 qCDebug(qLcCamera) << Q_FUNC_INFO << "Setting custom focus distance not supported\n";
458 return;
459 }
460
461 {
462 AVFConfigurationLock lock(captureDevice);
463 if (!lock) {
464 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
465 return;
466 }
467 [captureDevice setFocusModeLockedWithLensPosition:d completionHandler:nil];
468 }
470#else
471 Q_UNUSED(d);
472#endif
473}
474
476{
477 AVCaptureDevice *captureDevice = device();
478 if (!captureDevice) {
479 qCDebug(qLcCamera) << Q_FUNC_INFO << "capture device is nil in 'active' state";
480 return;
481 }
482
483 const AVFConfigurationLock lock(captureDevice);
484 if (!lock) {
485 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
486 return;
487 }
488
489 if ([captureDevice isFocusPointOfInterestSupported]) {
490 auto point = customFocusPoint();
491 const CGPoint focusPOI = CGPointMake(point.x(), point.y());
492 [captureDevice setFocusPointOfInterest:focusPOI];
493 }
494
495#ifdef Q_OS_IOS
497 const AVCaptureFocusMode avMode = avf_focus_mode(focusMode());
498 if (captureDevice.focusMode != avMode) {
499 if ([captureDevice isFocusModeSupported:avMode]) {
500 [captureDevice setFocusMode:avMode];
501 } else {
502 qCDebug(qLcCamera) << Q_FUNC_INFO << "focus mode not supported";
503 }
504 }
505 }
506
507 if (!captureDevice.activeFormat) {
508 qCDebug(qLcCamera) << Q_FUNC_INFO << "camera state is active, but active format is nil";
509 return;
510 }
511
512 minimumZoomFactorChanged(captureDevice.minAvailableVideoZoomFactor);
513 maximumZoomFactorChanged(captureDevice.activeFormat.videoMaxZoomFactor);
514
515 captureDevice.videoZoomFactor = zoomFactor();
516
517 CMTime newDuration = AVCaptureExposureDurationCurrent;
518 bool setCustomMode = false;
519
521 if (exposureTime > 0
522 && !qt_exposure_duration_equal(captureDevice, exposureTime)) {
523 newDuration = CMTimeMakeWithSeconds(exposureTime, captureDevice.exposureDuration.timescale);
524 if (!qt_check_exposure_duration(captureDevice, newDuration)) {
525 qCDebug(qLcCamera) << Q_FUNC_INFO << "requested exposure duration is out of range";
526 return;
527 }
528 setCustomMode = true;
529 }
530
531 float newISO = AVCaptureISOCurrent;
532 int iso = manualIsoSensitivity();
533 if (iso > 0 && !qt_iso_equal(captureDevice, iso)) {
534 newISO = iso;
535 if (!qt_check_ISO_value(captureDevice, newISO)) {
536 qCDebug(qLcCamera) << Q_FUNC_INFO << "requested ISO value is out of range";
537 return;
538 }
539 setCustomMode = true;
540 }
541
542 float bias = exposureCompensation();
543 if (bias != 0 && !qt_exposure_bias_equal(captureDevice, bias)) {
544 // TODO: mixed fpns.
545 if (bias < captureDevice.minExposureTargetBias || bias > captureDevice.maxExposureTargetBias) {
546 qCDebug(qLcCamera) << Q_FUNC_INFO << "exposure compensation value is"
547 << "out of range";
548 return;
549 }
550 [captureDevice setExposureTargetBias:bias completionHandler:nil];
551 }
552
553 // Setting shutter speed (exposure duration) or ISO values
554 // also reset exposure mode into Custom. With this settings
555 // we ignore any attempts to set exposure mode.
556
557 if (setCustomMode) {
558 [captureDevice setExposureModeCustomWithDuration:newDuration
559 ISO:newISO
560 completionHandler:nil];
561 return;
562 }
563
565 AVCaptureExposureMode avMode = AVCaptureExposureModeContinuousAutoExposure;
566 if (!qt_convert_exposure_mode(captureDevice, qtMode, avMode)) {
567 qCDebug(qLcCamera) << Q_FUNC_INFO << "requested exposure mode is not supported";
568 return;
569 }
570
571 captureDevice.exposureMode = avMode;
572#endif
573
574 isFlashSupported = isFlashAutoSupported = false;
575 isTorchSupported = isTorchAutoSupported = false;
576
577 if (captureDevice.hasFlash) {
578 if ([captureDevice isFlashModeSupported:AVCaptureFlashModeOn])
579 isFlashSupported = true;
580 if ([captureDevice isFlashModeSupported:AVCaptureFlashModeAuto])
581 isFlashAutoSupported = true;
582 }
583
584 if (captureDevice.hasTorch) {
585 if ([captureDevice isTorchModeSupported:AVCaptureTorchModeOn])
586 isTorchSupported = true;
587 if ([captureDevice isTorchModeSupported:AVCaptureTorchModeAuto])
588 isTorchAutoSupported = true;
589 }
590
592 flashReadyChanged(isFlashSupported);
593}
594
596{
597 QCamera::Features features;
598 AVCaptureDevice *captureDevice = device();
599
600#ifdef Q_OS_IOS
603
604 if (captureDevice && [captureDevice isLockingFocusWithCustomLensPositionSupported])
606#endif
607
608 if (captureDevice && [captureDevice isFocusPointOfInterestSupported])
610
611 supportedFeaturesChanged(features);
612}
613
614void QAVFCameraBase::zoomTo(float factor, float rate)
615{
616 Q_UNUSED(factor);
617 Q_UNUSED(rate);
618
619#ifdef Q_OS_IOS
620 if (zoomFactor() == factor)
621 return;
622
623 AVCaptureDevice *captureDevice = device();
624 if (!captureDevice || !captureDevice.activeFormat)
625 return;
626
627 factor = qBound(captureDevice.minAvailableVideoZoomFactor, factor,
628 captureDevice.activeFormat.videoMaxZoomFactor);
629
630 const AVFConfigurationLock lock(captureDevice);
631 if (!lock) {
632 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
633 return;
634 }
635
636 if (rate <= 0)
637 captureDevice.videoZoomFactor = factor;
638 else
639 [captureDevice rampToVideoZoomFactor:factor withRate:rate];
640#endif
641}
642
644{
645 if (flashMode() == mode)
646 return;
647
649 qCDebug(qLcCamera) << Q_FUNC_INFO << "unsupported mode" << mode;
650 return;
651 }
652
654
655 if (!isActive())
656 return;
657
659}
660
662{
663 if (mode == QCamera::FlashOff)
664 return true;
665 else if (mode == QCamera::FlashOn)
666 return isFlashSupported;
667 else //if (mode == QCamera::FlashAuto)
668 return isFlashAutoSupported;
669}
670
672{
673 if (!isActive())
674 return false;
675
676 AVCaptureDevice *captureDevice = device();
677 if (!captureDevice)
678 return false;
679
680 if (!captureDevice.hasFlash)
681 return false;
682
684 return false;
685
686 // AVCaptureDevice's docs:
687 // "The flash may become unavailable if, for example,
688 // the device overheats and needs to cool off."
689 return [captureDevice isFlashAvailable];
690}
691
693{
694 if (torchMode() == mode)
695 return;
696
698 qCDebug(qLcCamera) << Q_FUNC_INFO << "unsupported torch mode" << mode;
699 return;
700 }
701
703
704 if (!isActive())
705 return;
706
708}
709
711{
712 if (mode == QCamera::TorchOff)
713 return true;
714 else if (mode == QCamera::TorchOn)
715 return isTorchSupported;
716 else //if (mode == QCamera::TorchAuto)
717 return isTorchAutoSupported;
718}
719
721{
722#ifdef Q_OS_IOS
723 if (qtMode != QCamera::ExposureAuto && qtMode != QCamera::ExposureManual) {
724 qCDebug(qLcCamera) << Q_FUNC_INFO << "exposure mode not supported";
725 return;
726 }
727
728 AVCaptureDevice *captureDevice = device();
729 if (!captureDevice) {
730 exposureModeChanged(qtMode);
731 return;
732 }
733
734 AVCaptureExposureMode avMode = AVCaptureExposureModeContinuousAutoExposure;
735 if (!qt_convert_exposure_mode(captureDevice, qtMode, avMode)) {
736 qCDebug(qLcCamera) << Q_FUNC_INFO << "exposure mode not supported";
737 return;
738 }
739
740 const AVFConfigurationLock lock(captureDevice);
741 if (!lock) {
742 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock a capture device"
743 << "for configuration";
744 return;
745 }
746
747 [captureDevice setExposureMode:avMode];
748 exposureModeChanged(qtMode);
749#else
750 Q_UNUSED(qtMode);
751#endif
752}
753
755{
757 return true;
759 return false;
760
761 if (@available(macOS 10.15, *)) {
762 AVCaptureDevice *captureDevice = device();
763 return captureDevice && [captureDevice isExposureModeSupported:AVCaptureExposureModeCustom];
764 }
765
766 return false;
767}
768
770{
772
773 AVCaptureDevice *captureDevice = device();
774 if (!captureDevice) {
775 qCDebug(qLcCamera) << Q_FUNC_INFO << "no capture device found";
776 return;
777 }
778
779 const AVFConfigurationLock lock(captureDevice);
780
781 if (captureDevice.hasFlash) {
782 const auto mode = flashMode();
783
784 auto setAvFlashModeSafe = [&captureDevice](AVCaptureFlashMode avFlashMode) {
785 // Note, in some cases captureDevice.hasFlash == false even though
786 // no there're no supported flash modes.
787 if ([captureDevice isFlashModeSupported:avFlashMode])
788 captureDevice.flashMode = avFlashMode;
789 else
790 qCDebug(qLcCamera) << "Attempt to setup unsupported flash mode " << avFlashMode;
791 };
792
793 if (mode == QCamera::FlashOff) {
794 setAvFlashModeSafe(AVCaptureFlashModeOff);
795 } else {
796 if ([captureDevice isFlashAvailable]) {
797 if (mode == QCamera::FlashOn)
798 setAvFlashModeSafe(AVCaptureFlashModeOn);
799 else if (mode == QCamera::FlashAuto)
800 setAvFlashModeSafe(AVCaptureFlashModeAuto);
801 } else {
802 qCDebug(qLcCamera) << Q_FUNC_INFO << "flash is not available at the moment";
803 }
804 }
805 }
806
807 if (captureDevice.hasTorch) {
808 const auto mode = torchMode();
809
810 auto setAvTorchModeSafe = [&captureDevice](AVCaptureTorchMode avTorchMode) {
811 if ([captureDevice isTorchModeSupported:avTorchMode])
812 captureDevice.torchMode = avTorchMode;
813 else
814 qCDebug(qLcCamera) << "Attempt to setup unsupported torch mode " << avTorchMode;
815 };
816
817 if (mode == QCamera::TorchOff) {
818 setAvTorchModeSafe(AVCaptureTorchModeOff);
819 } else {
820 if ([captureDevice isTorchAvailable]) {
821 if (mode == QCamera::TorchOn)
822 setAvTorchModeSafe(AVCaptureTorchModeOn);
823 else if (mode == QCamera::TorchAuto)
824 setAvTorchModeSafe(AVCaptureTorchModeAuto);
825 } else {
826 qCDebug(qLcCamera) << Q_FUNC_INFO << "torch is not available at the moment";
827 }
828 }
829 }
830}
831
832
834{
835#ifdef Q_OS_IOS
836 AVCaptureDevice *captureDevice = device();
837 if (!captureDevice) {
839 return;
840 }
841
842 bias = qBound(captureDevice.minExposureTargetBias, bias, captureDevice.maxExposureTargetBias);
843
844 const AVFConfigurationLock lock(captureDevice);
845 if (!lock) {
846 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
847 return;
848 }
849
850 [captureDevice setExposureTargetBias:bias completionHandler:nil];
852#else
853 Q_UNUSED(bias);
854#endif
855}
856
858{
859#ifdef Q_OS_IOS
860 if (value < 0) {
862 return;
863 }
864
865 AVCaptureDevice *captureDevice = device();
866 if (!captureDevice) {
868 return;
869 }
870
871 const CMTime newDuration = CMTimeMakeWithSeconds(value, captureDevice.exposureDuration.timescale);
872 if (!qt_check_exposure_duration(captureDevice, newDuration)) {
873 qCDebug(qLcCamera) << Q_FUNC_INFO << "shutter speed value is out of range";
874 return;
875 }
876
877 const AVFConfigurationLock lock(captureDevice);
878 if (!lock) {
879 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
880 return;
881 }
882
883 // Setting the shutter speed (exposure duration in Apple's terms,
884 // since there is no shutter actually) will also reset
885 // exposure mode into custom mode.
886 [captureDevice setExposureModeCustomWithDuration:newDuration
887 ISO:AVCaptureISOCurrent
888 completionHandler:nil];
889
891
892#else
894#endif
895}
896
898{
899#ifdef Q_OS_IOS
900 AVCaptureDevice *captureDevice = device();
901 if (!captureDevice)
902 return -1.;
903 auto duration = captureDevice.exposureDuration;
904 return CMTimeGetSeconds(duration);
905#else
906 return -1;
907#endif
908}
909
910#ifdef Q_OS_IOS
911namespace {
912
913void avf_convert_white_balance_mode(QCamera::WhiteBalanceMode qtMode,
914 AVCaptureWhiteBalanceMode &avMode)
915{
916 if (qtMode == QCamera::WhiteBalanceAuto)
917 avMode = AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance;
918 else
919 avMode = AVCaptureWhiteBalanceModeLocked;
920}
921
922bool avf_set_white_balance_mode(AVCaptureDevice *captureDevice,
923 AVCaptureWhiteBalanceMode avMode)
924{
925 Q_ASSERT(captureDevice);
926
927 const bool lock = [captureDevice lockForConfiguration:nil];
928 if (!lock) {
929 qDebug() << "Failed to lock a capture device for configuration\n";
930 return false;
931 }
932
933 captureDevice.whiteBalanceMode = avMode;
934 [captureDevice unlockForConfiguration];
935 return true;
936}
937
938bool avf_convert_temp_and_tint_to_wb_gains(AVCaptureDevice *captureDevice,
939 float temp, float tint, AVCaptureWhiteBalanceGains &wbGains)
940{
941 Q_ASSERT(captureDevice);
942
943 AVCaptureWhiteBalanceTemperatureAndTintValues wbTTValues = {
944 .temperature = temp,
945 .tint = tint
946 };
947 wbGains = [captureDevice deviceWhiteBalanceGainsForTemperatureAndTintValues:wbTTValues];
948
949 if (wbGains.redGain >= 1.0 && wbGains.redGain <= captureDevice.maxWhiteBalanceGain
950 && wbGains.greenGain >= 1.0 && wbGains.greenGain <= captureDevice.maxWhiteBalanceGain
951 && wbGains.blueGain >= 1.0 && wbGains.blueGain <= captureDevice.maxWhiteBalanceGain)
952 return true;
953
954 return false;
955}
956
957bool avf_set_white_balance_gains(AVCaptureDevice *captureDevice,
958 AVCaptureWhiteBalanceGains wbGains)
959{
960 const bool lock = [captureDevice lockForConfiguration:nil];
961 if (!lock) {
962 qDebug() << "Failed to lock a capture device for configuration\n";
963 return false;
964 }
965
966 [captureDevice setWhiteBalanceModeLockedWithDeviceWhiteBalanceGains:wbGains
967 completionHandler:nil];
968 [captureDevice unlockForConfiguration];
969 return true;
970}
971
972}
973
975{
977 return true;
978 AVCaptureDevice *captureDevice = device();
979 if (!captureDevice)
980 return false;
981 return [captureDevice isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked];
982}
983
985{
987 return;
988
989 AVCaptureDevice *captureDevice = device();
990 Q_ASSERT(captureDevice);
991
992 const AVFConfigurationLock lock(captureDevice);
993 if (!lock) {
994 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock a capture device"
995 << "for configuration";
996 return;
997 }
998
999 AVCaptureWhiteBalanceMode avMode;
1000 avf_convert_white_balance_mode(mode, avMode);
1001 avf_set_white_balance_mode(captureDevice, avMode);
1002
1005 return;
1006 }
1007
1008 const int colorTemp = colorTemperatureForWhiteBalance(mode);
1009 AVCaptureWhiteBalanceGains wbGains;
1010 if (avf_convert_temp_and_tint_to_wb_gains(captureDevice, colorTemp, 0., wbGains)
1011 && avf_set_white_balance_gains(captureDevice, wbGains))
1013}
1014
1015void QAVFCameraBase::setColorTemperature(int colorTemp)
1016{
1017 if (colorTemp == 0) {
1018 colorTemperatureChanged(colorTemp);
1019 return;
1020 }
1021
1022 AVCaptureDevice *captureDevice = device();
1023 if (!captureDevice || ![captureDevice isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked])
1024 return;
1025
1026 const AVFConfigurationLock lock(captureDevice);
1027 if (!lock) {
1028 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock a capture device"
1029 << "for configuration";
1030 return;
1031 }
1032
1033 AVCaptureWhiteBalanceGains wbGains;
1034 if (avf_convert_temp_and_tint_to_wb_gains(captureDevice, colorTemp, 0., wbGains)
1035 && avf_set_white_balance_gains(captureDevice, wbGains))
1036 colorTemperatureChanged(colorTemp);
1037}
1038#endif
1039
1041{
1042#ifdef Q_OS_IOS
1043 if (value < 0) {
1045 return;
1046 }
1047
1048 AVCaptureDevice *captureDevice = device();
1049 if (!captureDevice) {
1051 return;
1052 }
1053
1054 if (!qt_check_ISO_value(captureDevice, value)) {
1055 qCDebug(qLcCamera) << Q_FUNC_INFO << "ISO value is out of range";
1056 return;
1057 }
1058
1059 const AVFConfigurationLock lock(captureDevice);
1060 if (!lock) {
1061 qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock a capture device"
1062 << "for configuration";
1063 return;
1064 }
1065
1066 // Setting the ISO will also reset
1067 // exposure mode to the custom mode.
1068 [captureDevice setExposureModeCustomWithDuration:AVCaptureExposureDurationCurrent
1069 ISO:value
1070 completionHandler:nil];
1071
1073#else
1074 Q_UNUSED(value);
1075#endif
1076}
1077
1079{
1080 return manualIsoSensitivity();
1081}
1082
1083
1084#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:124
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
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
bool isEmpty() const noexcept
Definition qlist.h:401
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:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:348
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtCore
Definition qshareddata.h:19
\inmodule QtCore
Definition qsize.h:25
QCamera * camera
Definition camera.cpp:19
QVideoFrameFormat::ColorRange colorRangeForCVPixelFormat(CvPixelFormat cvPixelFormat)
QVideoFrameFormat::PixelFormat fromCVPixelFormat(CvPixelFormat cvPixelFormat)
#define Q_FALLTHROUGH()
#define QT_WARNING_POP
#define QT_WARNING_DISABLE_DEPRECATED
#define Q_FUNC_INFO
#define QT_WARNING_PUSH
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
#define qDebug
[1]
Definition qlogging.h:164
#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
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_EMIT
#define emit
#define Q_UNUSED(x)
double qreal
Definition qtypes.h:187
QReadWriteLock lock
[0]
QQueue< int > queue
[0]
QHostInfo info
[0]
bool contains(const AT &t) const noexcept
Definition qlist.h:45