diff options
Diffstat (limited to 'src/plugins/multimedia/darwin/camera/qavfcamerabase.mm')
-rw-r--r-- | src/plugins/multimedia/darwin/camera/qavfcamerabase.mm | 219 |
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; } } } |