summaryrefslogtreecommitdiffstats
path: root/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/multimedia/darwin/camera/qavfcamerabase.mm')
-rw-r--r--src/plugins/multimedia/darwin/camera/qavfcamerabase.mm219
1 files changed, 112 insertions, 107 deletions
diff --git a/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm b/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm
index db7f4f8d3..9d99de0b9 100644
--- a/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm
+++ b/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfcameradebug_p.h"
#include "qavfcamerabase_p.h"
@@ -43,6 +7,8 @@
#include <private/qcameradevice_p.h>
#include "qavfhelpers_p.h"
#include <private/qplatformmediaintegration_p.h>
+#include <QtCore/qset.h>
+#include <QtCore/qsystemdetection.h>
QT_USE_NAMESPACE
@@ -54,29 +20,6 @@ namespace {
// Misc. helpers to check values/ranges:
-bool qt_check_ISO_conversion(float isoValue)
-{
- if (isoValue >= std::numeric_limits<int>::max())
- return false;
- if (isoValue <= std::numeric_limits<int>::min())
- return false;
- return true;
-}
-
-bool qt_check_ISO_range(AVCaptureDeviceFormat *format)
-{
- // Qt is using int for ISO, AVFoundation - float. It looks like the ISO range
- // at the moment can be represented by int (it's max - min > 100, etc.).
- Q_ASSERT(format);
- if (format.maxISO - format.minISO < 1.) {
- // ISO is in some strange units?
- return false;
- }
-
- return qt_check_ISO_conversion(format.minISO)
- && qt_check_ISO_conversion(format.maxISO);
-}
-
bool qt_check_exposure_duration(AVCaptureDevice *captureDevice, CMTime duration)
{
Q_ASSERT(captureDevice);
@@ -195,17 +138,64 @@ void QAVFVideoDevices::updateCameraDevices()
QList<QCameraDevice> cameras;
- AVCaptureDevice *defaultDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
- NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
+ // List of all capture device types that we want to discover. Seems that this is the
+ // only way to discover all types. This filter is mandatory and has no "unspecified"
+ // option like AVCaptureDevicePosition(Unspecified) has. Order of the list is important
+ // because discovered devices will be in the same order and we want the first one found
+ // to be our default device.
+ NSArray *discoveryDevices = @[
+#ifdef Q_OS_IOS
+ AVCaptureDeviceTypeBuiltInTripleCamera, // We always prefer triple camera.
+ AVCaptureDeviceTypeBuiltInDualCamera, // If triple is not available, we prefer
+ // dual with wide + tele lens.
+ AVCaptureDeviceTypeBuiltInDualWideCamera, // Dual with wide and ultrawide is still
+ // better than single.
+#endif
+ AVCaptureDeviceTypeBuiltInWideAngleCamera, // This is the most common single camera type.
+ // We prefer that over tele and ultra-wide.
+#ifdef Q_OS_IOS
+ AVCaptureDeviceTypeBuiltInTelephotoCamera, // Cannot imagine how, but if only tele and
+ // ultrawide are available, we prefer tele.
+ AVCaptureDeviceTypeBuiltInUltraWideCamera,
+#endif
+ ];
+
+#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_14_0, __IPHONE_17_0, __TVOS_NA, __WATCHOS_NA)
+ if (@available(macOS 14, iOS 17, *)) {
+ discoveryDevices = [discoveryDevices arrayByAddingObjectsFromArray: @[
+ AVCaptureDeviceTypeExternal,
+ AVCaptureDeviceTypeContinuityCamera
+ ]];
+ } else
+#endif
+ {
+#ifdef Q_OS_MACOS
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_DEPRECATED
+ discoveryDevices = [discoveryDevices arrayByAddingObjectsFromArray: @[
+ AVCaptureDeviceTypeExternalUnknown
+ ]];
+ QT_WARNING_POP
+#endif
+ }
+ // Create discovery session to discover all possible camera types of the system.
+ // Both "hard" and "soft" types.
+ AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession
+ discoverySessionWithDeviceTypes:discoveryDevices
+ mediaType:AVMediaTypeVideo
+ position:AVCaptureDevicePositionUnspecified];
+ NSArray<AVCaptureDevice *> *videoDevices = discoverySession.devices;
for (AVCaptureDevice *device in videoDevices) {
-
- QCameraDevicePrivate *info = new QCameraDevicePrivate;
- if (defaultDevice && [defaultDevice.uniqueID isEqualToString:device.uniqueID])
+ auto info = std::make_unique<QCameraDevicePrivate>();
+ if ([videoDevices[0].uniqueID isEqualToString:device.uniqueID])
info->isDefault = true;
info->id = QByteArray([[device uniqueID] UTF8String]);
info->description = QString::fromNSString([device localizedName]);
+ qCDebug(qLcCamera) << "Handling camera info" << info->description
+ << (info->isDefault ? "(default)" : "");
+
QSet<QSize> photoResolutions;
QList<QCameraFormat> videoFormats;
@@ -222,9 +212,13 @@ void QAVFVideoDevices::updateCameraDevices()
auto encoding = CMVideoFormatDescriptionGetCodecType(format.formatDescription);
auto pixelFormat = QAVFHelpers::fromCVPixelFormat(encoding);
+ auto colorRange = QAVFHelpers::colorRangeForCVPixelFormat(encoding);
// Ignore pixel formats we can't handle
- if (pixelFormat == QVideoFrameFormat::Format_Invalid)
+ if (pixelFormat == QVideoFrameFormat::Format_Invalid) {
+ qCDebug(qLcCamera) << "ignore camera CV format" << encoding
+ << "as no matching video format found";
continue;
+ }
for (AVFrameRateRange *frameRateRange in format.videoSupportedFrameRateRanges) {
if (frameRateRange.minFrameRate < minFrameRate)
@@ -244,29 +238,30 @@ void QAVFVideoDevices::updateCameraDevices()
photoResolutions.insert(hrRes);
#endif
- auto *f = new QCameraFormatPrivate{
- QSharedData(),
- pixelFormat,
- resolution,
- minFrameRate,
- maxFrameRate
- };
+ qCDebug(qLcCamera) << "Add camera format. pixelFormat:" << pixelFormat
+ << "colorRange:" << colorRange << "cvPixelFormat" << encoding
+ << "resolution:" << resolution << "frameRate: [" << minFrameRate
+ << maxFrameRate << "]";
+
+ auto *f = new QCameraFormatPrivate{ QSharedData(), pixelFormat, resolution,
+ minFrameRate, maxFrameRate, colorRange };
videoFormats << f->create();
}
if (videoFormats.isEmpty()) {
// skip broken cameras without valid formats
- delete info;
+ qCWarning(qLcCamera())
+ << "Skip camera" << info->description << "without supported formats";
continue;
}
info->videoFormats = videoFormats;
info->photoResolutions = photoResolutions.values();
- cameras.append(info->create());
+ cameras.append(info.release()->create());
}
if (cameras != m_cameraDevices) {
m_cameraDevices = cameras;
- videoInputsChanged();
+ emit videoInputsChanged();
}
}
@@ -515,7 +510,7 @@ void QAVFCameraBase::updateCameraConfiguration()
}
minimumZoomFactorChanged(captureDevice.minAvailableVideoZoomFactor);
- maximumZoomFactorChanged(captureDevice.maxAvailableVideoZoomFactor);
+ maximumZoomFactorChanged(captureDevice.activeFormat.videoMaxZoomFactor);
captureDevice.videoZoomFactor = zoomFactor();
@@ -629,7 +624,8 @@ void QAVFCameraBase::zoomTo(float factor, float rate)
if (!captureDevice || !captureDevice.activeFormat)
return;
- factor = qBound(captureDevice.minAvailableVideoZoomFactor, factor, captureDevice.maxAvailableVideoZoomFactor);
+ factor = qBound(captureDevice.minAvailableVideoZoomFactor, factor,
+ captureDevice.activeFormat.videoMaxZoomFactor);
const AVFConfigurationLock lock(captureDevice);
if (!lock) {
@@ -637,10 +633,10 @@ void QAVFCameraBase::zoomTo(float factor, float rate)
return;
}
- if (rate < 0)
+ if (rate <= 0)
captureDevice.videoZoomFactor = factor;
- else
- [AVCaptureDevice rampToVideoZoomFactor:factor withRate:rate];
+ else
+ [captureDevice rampToVideoZoomFactor:factor withRate:rate];
#endif
}
@@ -687,14 +683,10 @@ bool QAVFCameraBase::isFlashReady() const
if (!isFlashModeSupported(flashMode()))
return false;
-#ifdef Q_OS_IOS
// AVCaptureDevice's docs:
// "The flash may become unavailable if, for example,
// the device overheats and needs to cool off."
return [captureDevice isFlashAvailable];
-#endif
-
- return true;
}
void QAVFCameraBase::setTorchMode(QCamera::TorchMode mode)
@@ -784,42 +776,55 @@ void QAVFCameraBase::applyFlashSettings()
return;
}
-
const AVFConfigurationLock lock(captureDevice);
if (captureDevice.hasFlash) {
- auto mode = flashMode();
+ const auto mode = flashMode();
+
+ auto setAvFlashModeSafe = [&captureDevice](AVCaptureFlashMode avFlashMode) {
+ // Note, in some cases captureDevice.hasFlash == false even though
+ // no there're no supported flash modes.
+ if ([captureDevice isFlashModeSupported:avFlashMode])
+ captureDevice.flashMode = avFlashMode;
+ else
+ qCDebug(qLcCamera) << "Attempt to setup unsupported flash mode " << avFlashMode;
+ };
+
if (mode == QCamera::FlashOff) {
- captureDevice.flashMode = AVCaptureFlashModeOff;
+ setAvFlashModeSafe(AVCaptureFlashModeOff);
} else {
-#ifdef Q_OS_IOS
- if (![captureDevice isFlashAvailable]) {
+ if ([captureDevice isFlashAvailable]) {
+ if (mode == QCamera::FlashOn)
+ setAvFlashModeSafe(AVCaptureFlashModeOn);
+ else if (mode == QCamera::FlashAuto)
+ setAvFlashModeSafe(AVCaptureFlashModeAuto);
+ } else {
qCDebug(qLcCamera) << Q_FUNC_INFO << "flash is not available at the moment";
- return;
}
-#endif
- if (mode == QCamera::FlashOn)
- captureDevice.flashMode = AVCaptureFlashModeOn;
- else if (mode == QCamera::FlashAuto)
- captureDevice.flashMode = AVCaptureFlashModeAuto;
}
}
if (captureDevice.hasTorch) {
- auto mode = torchMode();
+ const auto mode = torchMode();
+
+ auto setAvTorchModeSafe = [&captureDevice](AVCaptureTorchMode avTorchMode) {
+ if ([captureDevice isTorchModeSupported:avTorchMode])
+ captureDevice.torchMode = avTorchMode;
+ else
+ qCDebug(qLcCamera) << "Attempt to setup unsupported torch mode " << avTorchMode;
+ };
+
if (mode == QCamera::TorchOff) {
- captureDevice.torchMode = AVCaptureTorchModeOff;
+ setAvTorchModeSafe(AVCaptureTorchModeOff);
} else {
-#ifdef Q_OS_IOS
- if (![captureDevice isTorchAvailable]) {
+ if ([captureDevice isTorchAvailable]) {
+ if (mode == QCamera::TorchOn)
+ setAvTorchModeSafe(AVCaptureTorchModeOn);
+ else if (mode == QCamera::TorchAuto)
+ setAvTorchModeSafe(AVCaptureTorchModeAuto);
+ } else {
qCDebug(qLcCamera) << Q_FUNC_INFO << "torch is not available at the moment";
- return;
}
-#endif
- if (mode == QCamera::TorchOn)
- captureDevice.torchMode = AVCaptureTorchModeOn;
- else if (mode == QCamera::TorchAuto)
- captureDevice.torchMode = AVCaptureTorchModeAuto;
}
}
}