diff options
Diffstat (limited to 'src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm')
-rw-r--r-- | src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm | 661 |
1 files changed, 0 insertions, 661 deletions
diff --git a/src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm b/src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm deleted file mode 100644 index 62387604f..000000000 --- a/src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm +++ /dev/null @@ -1,661 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** 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$ -** -****************************************************************************/ - -#include "avfcameraexposurecontrol.h" -#include "avfcamerautility.h" -#include "avfcamerasession.h" -#include "avfcameraservice.h" -#include "avfcameradebug.h" - -#include <QtCore/qvariant.h> -#include <QtCore/qpointer.h> -#include <QtCore/qdebug.h> -#include <QtCore/qpair.h> - -#include <AVFoundation/AVFoundation.h> - -#include <limits> - -QT_BEGIN_NAMESPACE - -namespace { - -// All these methods to work with exposure/ISO/SS in custom mode do not support macOS. - -#ifdef Q_OS_IOS - -// 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); - - AVCaptureDeviceFormat *activeFormat = captureDevice.activeFormat; - if (!activeFormat) { - qDebugCamera() << Q_FUNC_INFO << "failed to obtain capture device format"; - return false; - } - - return CMTimeCompare(duration, activeFormat.minExposureDuration) != -1 - && CMTimeCompare(activeFormat.maxExposureDuration, duration) != -1; -} - -bool qt_check_ISO_value(AVCaptureDevice *captureDevice, int newISO) -{ - Q_ASSERT(captureDevice); - - AVCaptureDeviceFormat *activeFormat = captureDevice.activeFormat; - if (!activeFormat) { - qDebugCamera() << Q_FUNC_INFO << "failed to obtain capture device format"; - return false; - } - - return !(newISO < activeFormat.minISO || newISO > activeFormat.maxISO); -} - -bool qt_exposure_duration_equal(AVCaptureDevice *captureDevice, qreal qDuration) -{ - Q_ASSERT(captureDevice); - const CMTime avDuration = CMTimeMakeWithSeconds(qDuration, captureDevice.exposureDuration.timescale); - return !CMTimeCompare(avDuration, captureDevice.exposureDuration); -} - -bool qt_iso_equal(AVCaptureDevice *captureDevice, int iso) -{ - Q_ASSERT(captureDevice); - return qFuzzyCompare(float(iso), captureDevice.ISO); -} - -bool qt_exposure_bias_equal(AVCaptureDevice *captureDevice, qreal bias) -{ - Q_ASSERT(captureDevice); - return qFuzzyCompare(bias, qreal(captureDevice.exposureTargetBias)); -} - -// Converters: - -bool qt_convert_exposure_mode(AVCaptureDevice *captureDevice, QCameraExposure::ExposureMode mode, - AVCaptureExposureMode &avMode) -{ - // Test if mode supported and convert. - Q_ASSERT(captureDevice); - - if (mode == QCameraExposure::ExposureAuto) { - if ([captureDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) { - avMode = AVCaptureExposureModeContinuousAutoExposure; - return true; - } - } - - if (mode == QCameraExposure::ExposureManual) { - if ([captureDevice isExposureModeSupported:AVCaptureExposureModeCustom]) { - avMode = AVCaptureExposureModeCustom; - return true; - } - } - - return false; -} - -// We set ISO/exposure duration with completion handlers, completion handlers try -// to avoid dangling pointers (thus QPointer for QObjects) and not to create -// a reference loop (in case we have ARC). - -void qt_set_exposure_bias(QPointer<AVFCameraService> service, QPointer<AVFCameraExposureControl> control, - AVCaptureDevice *captureDevice, float bias) -{ - Q_ASSERT(captureDevice); - - __block AVCaptureDevice *device = captureDevice; //For ARC. - - void (^completionHandler)(CMTime syncTime) = ^(CMTime) { - // Test that service control is still alive and that - // capture device is our device, if yes - emit actual value changed. - if (service) { - if (control) { - if (service->session() && service->session()->videoCaptureDevice() == device) - Q_EMIT control->actualValueChanged(int(QCameraExposureControl::ExposureCompensation)); - } - } - device = nil; - }; - - [captureDevice setExposureTargetBias:bias completionHandler:completionHandler]; -} - -void qt_set_duration_iso(QPointer<AVFCameraService> service, QPointer<AVFCameraExposureControl> control, - AVCaptureDevice *captureDevice, CMTime duration, float iso) -{ - Q_ASSERT(captureDevice); - - __block AVCaptureDevice *device = captureDevice; //For ARC. - const bool setDuration = CMTimeCompare(duration, AVCaptureExposureDurationCurrent); - const bool setISO = !qFuzzyCompare(iso, AVCaptureISOCurrent); - - void (^completionHandler)(CMTime syncTime) = ^(CMTime) { - // Test that service control is still alive and that - // capture device is our device, if yes - emit actual value changed. - if (service) { - if (control) { - if (service->session() && service->session()->videoCaptureDevice() == device) { - if (setDuration) - Q_EMIT control->actualValueChanged(int(QCameraExposureControl::ShutterSpeed)); - if (setISO) - Q_EMIT control->actualValueChanged(int(QCameraExposureControl::ISO)); - } - } - } - device = nil; - }; - - [captureDevice setExposureModeCustomWithDuration:duration - ISO:iso - completionHandler:completionHandler]; -} - -#endif // defined(Q_OS_IOS) - -} // Unnamed namespace. - -AVFCameraExposureControl::AVFCameraExposureControl(AVFCameraService *service) - : m_service(service), - m_session(nullptr) -{ - Q_ASSERT(service); - m_session = m_service->session(); - Q_ASSERT(m_session); - - connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(cameraStateChanged())); -} - -bool AVFCameraExposureControl::isParameterSupported(ExposureParameter parameter) const -{ -#ifdef Q_OS_IOS - AVCaptureDevice *captureDevice = m_session->videoCaptureDevice(); - if (!captureDevice) - return false; - - // These are the parameters we have an API to support: - return parameter == QCameraExposureControl::ISO - || parameter == QCameraExposureControl::ShutterSpeed - || parameter == QCameraExposureControl::ExposureCompensation - || parameter == QCameraExposureControl::ExposureMode; -#else - Q_UNUSED(parameter); - return false; -#endif -} - -QVariantList AVFCameraExposureControl::supportedParameterRange(ExposureParameter parameter, - bool *continuous) const -{ - QVariantList parameterRange; -#ifdef Q_OS_IOS - - AVCaptureDevice *captureDevice = m_session->videoCaptureDevice(); - if (!captureDevice || !isParameterSupported(parameter)) { - qDebugCamera() << Q_FUNC_INFO << "parameter not supported"; - return parameterRange; - } - - if (continuous) - *continuous = false; - - AVCaptureDeviceFormat *activeFormat = captureDevice.activeFormat; - - if (parameter == QCameraExposureControl::ISO) { - if (!activeFormat) { - qDebugCamera() << Q_FUNC_INFO << "failed to obtain capture device format"; - return parameterRange; - } - - if (!qt_check_ISO_range(activeFormat)) { - qDebugCamera() << Q_FUNC_INFO << "ISO range can not be represented as int"; - return parameterRange; - } - - parameterRange << QVariant(int(activeFormat.minISO)); - parameterRange << QVariant(int(activeFormat.maxISO)); - if (continuous) - *continuous = true; - } else if (parameter == QCameraExposureControl::ExposureCompensation) { - parameterRange << captureDevice.minExposureTargetBias; - parameterRange << captureDevice.maxExposureTargetBias; - if (continuous) - *continuous = true; - } else if (parameter == QCameraExposureControl::ShutterSpeed) { - if (!activeFormat) { - qDebugCamera() << Q_FUNC_INFO << "failed to obtain capture device format"; - return parameterRange; - } - - // CMTimeGetSeconds returns Float64, test the conversion below, if it's valid? - parameterRange << qreal(CMTimeGetSeconds(activeFormat.minExposureDuration)); - parameterRange << qreal(CMTimeGetSeconds(activeFormat.maxExposureDuration)); - - if (continuous) - *continuous = true; - } else if (parameter == QCameraExposureControl::ExposureMode) { - if ([captureDevice isExposureModeSupported:AVCaptureExposureModeCustom]) - parameterRange << QVariant::fromValue(QCameraExposure::ExposureManual); - - if ([captureDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) - parameterRange << QVariant::fromValue(QCameraExposure::ExposureAuto); - } -#else - Q_UNUSED(parameter); - Q_UNUSED(continuous); -#endif - return parameterRange; -} - -QVariant AVFCameraExposureControl::requestedValue(ExposureParameter parameter) const -{ - if (!isParameterSupported(parameter)) { - qDebugCamera() << Q_FUNC_INFO << "parameter not supported"; - return QVariant(); - } - - if (parameter == QCameraExposureControl::ExposureMode) - return m_requestedMode; - - if (parameter == QCameraExposureControl::ExposureCompensation) - return m_requestedCompensation; - - if (parameter == QCameraExposureControl::ShutterSpeed) - return m_requestedShutterSpeed; - - if (parameter == QCameraExposureControl::ISO) - return m_requestedISO; - - return QVariant(); -} - -QVariant AVFCameraExposureControl::actualValue(ExposureParameter parameter) const -{ -#ifdef Q_OS_IOS - AVCaptureDevice *captureDevice = m_session->videoCaptureDevice(); - if (!captureDevice || !isParameterSupported(parameter)) { - // Actually, at the moment !captiredevice => !isParameterSupported. - qDebugCamera() << Q_FUNC_INFO << "parameter not supported"; - return QVariant(); - } - - if (parameter == QCameraExposureControl::ExposureMode) { - // This code expects exposureMode to be continuous by default ... - if (captureDevice.exposureMode == AVCaptureExposureModeContinuousAutoExposure) - return QVariant::fromValue(QCameraExposure::ExposureAuto); - return QVariant::fromValue(QCameraExposure::ExposureManual); - } - - if (parameter == QCameraExposureControl::ExposureCompensation) - return captureDevice.exposureTargetBias; - - if (parameter == QCameraExposureControl::ShutterSpeed) - return qreal(CMTimeGetSeconds(captureDevice.exposureDuration)); - - if (parameter == QCameraExposureControl::ISO) { - if (captureDevice.activeFormat && qt_check_ISO_range(captureDevice.activeFormat) - && qt_check_ISO_conversion(captureDevice.ISO)) { - // Can be represented as int ... - return int(captureDevice.ISO); - } else { - qDebugCamera() << Q_FUNC_INFO << "ISO can not be represented as int"; - return QVariant(); - } - } -#else - Q_UNUSED(parameter); -#endif - return QVariant(); -} - -bool AVFCameraExposureControl::setValue(ExposureParameter parameter, const QVariant &value) -{ - if (parameter == QCameraExposureControl::ExposureMode) - return setExposureMode(value); - else if (parameter == QCameraExposureControl::ExposureCompensation) - return setExposureCompensation(value); - else if (parameter == QCameraExposureControl::ShutterSpeed) - return setShutterSpeed(value); - else if (parameter == QCameraExposureControl::ISO) - return setISO(value); - - return false; -} - -bool AVFCameraExposureControl::setExposureMode(const QVariant &value) -{ -#ifdef Q_OS_IOS - if (!value.canConvert<QCameraExposure::ExposureMode>()) { - qDebugCamera() << Q_FUNC_INFO << "invalid exposure mode value," - << "QCameraExposure::ExposureMode expected"; - return false; - } - - const QCameraExposure::ExposureMode qtMode = value.value<QCameraExposure::ExposureMode>(); - if (qtMode != QCameraExposure::ExposureAuto && qtMode != QCameraExposure::ExposureManual) { - qDebugCamera() << Q_FUNC_INFO << "exposure mode not supported"; - return false; - } - - AVCaptureDevice *captureDevice = m_session->videoCaptureDevice(); - if (!captureDevice) { - m_requestedMode = value; - Q_EMIT requestedValueChanged(int(QCameraExposureControl::ExposureMode)); - return true; - } - - AVCaptureExposureMode avMode = AVCaptureExposureModeAutoExpose; - if (!qt_convert_exposure_mode(captureDevice, qtMode, avMode)) { - qDebugCamera() << Q_FUNC_INFO << "exposure mode not supported"; - return false; - } - - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock a capture device" - << "for configuration"; - return false; - } - - m_requestedMode = value; - [captureDevice setExposureMode:avMode]; - Q_EMIT requestedValueChanged(int(QCameraExposureControl::ExposureMode)); - Q_EMIT actualValueChanged(int(QCameraExposureControl::ExposureMode)); - - return true; -#else - Q_UNUSED(value); - return false; -#endif -} - -bool AVFCameraExposureControl::setExposureCompensation(const QVariant &value) -{ -#ifdef Q_OS_IOS - if (!value.canConvert<qreal>()) { - qDebugCamera() << Q_FUNC_INFO << "invalid exposure compensation" - <<"value, floating point number expected"; - return false; - } - - const qreal bias = value.toReal(); - AVCaptureDevice *captureDevice = m_session->videoCaptureDevice(); - if (!captureDevice) { - m_requestedCompensation = value; - Q_EMIT requestedValueChanged(int(QCameraExposureControl::ExposureCompensation)); - return true; - } - - if (bias < captureDevice.minExposureTargetBias || bias > captureDevice.maxExposureTargetBias) { - // TODO: mixed fp types! - qDebugCamera() << Q_FUNC_INFO << "exposure compenstation value is" - << "out of range"; - return false; - } - - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration"; - return false; - } - - qt_set_exposure_bias(m_service, this, captureDevice, bias); - m_requestedCompensation = value; - Q_EMIT requestedValueChanged(int(QCameraExposureControl::ExposureCompensation)); - - return true; -#else - Q_UNUSED(value); - return false; -#endif -} - -bool AVFCameraExposureControl::setShutterSpeed(const QVariant &value) -{ -#ifdef Q_OS_IOS - if (value.isNull()) - return setExposureMode(QVariant::fromValue(QCameraExposure::ExposureAuto)); - - if (!value.canConvert<qreal>()) { - qDebugCamera() << Q_FUNC_INFO << "invalid shutter speed" - << "value, floating point number expected"; - return false; - } - - AVCaptureDevice *captureDevice = m_session->videoCaptureDevice(); - if (!captureDevice) { - m_requestedShutterSpeed = value; - Q_EMIT requestedValueChanged(int(QCameraExposureControl::ShutterSpeed)); - return true; - } - - const CMTime newDuration = CMTimeMakeWithSeconds(value.toReal(), - captureDevice.exposureDuration.timescale); - if (!qt_check_exposure_duration(captureDevice, newDuration)) { - qDebugCamera() << Q_FUNC_INFO << "shutter speed value is out of range"; - return false; - } - - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration"; - return false; - } - - // Setting the shutter speed (exposure duration in Apple's terms, - // since there is no shutter actually) will also reset - // exposure mode into custom mode. - qt_set_duration_iso(m_service, this, captureDevice, newDuration, AVCaptureISOCurrent); - - m_requestedShutterSpeed = value; - Q_EMIT requestedValueChanged(int(QCameraExposureControl::ShutterSpeed)); - - return true; -#else - Q_UNUSED(value); - return false; -#endif -} - -bool AVFCameraExposureControl::setISO(const QVariant &value) -{ -#ifdef Q_OS_IOS - if (value.isNull()) - return setExposureMode(QVariant::fromValue(QCameraExposure::ExposureAuto)); - - if (!value.canConvert<int>()) { - qDebugCamera() << Q_FUNC_INFO << "invalid ISO value, int expected"; - return false; - } - - AVCaptureDevice *captureDevice = m_session->videoCaptureDevice(); - if (!captureDevice) { - m_requestedISO = value; - Q_EMIT requestedValueChanged(int(QCameraExposureControl::ISO)); - return true; - } - - if (!qt_check_ISO_value(captureDevice, value.toInt())) { - qDebugCamera() << Q_FUNC_INFO << "ISO value is out of range"; - return false; - } - - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock a capture device" - << "for configuration"; - return false; - } - - // Setting the ISO will also reset - // exposure mode to the custom mode. - qt_set_duration_iso(m_service, this, captureDevice, AVCaptureExposureDurationCurrent, value.toInt()); - - m_requestedISO = value; - Q_EMIT requestedValueChanged(int(QCameraExposureControl::ISO)); - - return true; -#else - Q_UNUSED(value); - return false; -#endif -} - -void AVFCameraExposureControl::cameraStateChanged() -{ -#ifdef Q_OS_IOS - if (m_session->state() != QCamera::ActiveState) - return; - - AVCaptureDevice *captureDevice = m_session->videoCaptureDevice(); - if (!captureDevice) { - qDebugCamera() << Q_FUNC_INFO << "capture device is nil, but the session" - << "state is 'active'"; - return; - } - - Q_EMIT parameterRangeChanged(int(QCameraExposureControl::ExposureCompensation)); - Q_EMIT parameterRangeChanged(int(QCameraExposureControl::ExposureMode)); - Q_EMIT parameterRangeChanged(int(QCameraExposureControl::ShutterSpeed)); - Q_EMIT parameterRangeChanged(int(QCameraExposureControl::ISO)); - - const AVFConfigurationLock lock(captureDevice); - - CMTime newDuration = AVCaptureExposureDurationCurrent; - bool setCustomMode = false; - - if (!m_requestedShutterSpeed.isNull() - && !qt_exposure_duration_equal(captureDevice, m_requestedShutterSpeed.toReal())) { - newDuration = CMTimeMakeWithSeconds(m_requestedShutterSpeed.toReal(), - captureDevice.exposureDuration.timescale); - if (!qt_check_exposure_duration(captureDevice, newDuration)) { - qDebugCamera() << Q_FUNC_INFO << "requested exposure duration is out of range"; - return; - } - setCustomMode = true; - } - - float newISO = AVCaptureISOCurrent; - if (!m_requestedISO.isNull() && !qt_iso_equal(captureDevice, m_requestedISO.toInt())) { - newISO = m_requestedISO.toInt(); - if (!qt_check_ISO_value(captureDevice, newISO)) { - qDebugCamera() << Q_FUNC_INFO << "requested ISO value is out of range"; - return; - } - setCustomMode = true; - } - - if (!m_requestedCompensation.isNull() - && !qt_exposure_bias_equal(captureDevice, m_requestedCompensation.toReal())) { - // TODO: mixed fpns. - const qreal bias = m_requestedCompensation.toReal(); - if (bias < captureDevice.minExposureTargetBias || bias > captureDevice.maxExposureTargetBias) { - qDebugCamera() << Q_FUNC_INFO << "exposure compenstation value is" - << "out of range"; - return; - } - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration"; - return; - } - qt_set_exposure_bias(m_service, this, captureDevice, bias); - } - - // Setting shutter speed (exposure duration) or ISO values - // also reset exposure mode into Custom. With this settings - // we ignore any attempts to set exposure mode. - - if (setCustomMode) { - if (!lock) - qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration"; - else - qt_set_duration_iso(m_service, this, captureDevice, newDuration, newISO); - return; - } - - if (!m_requestedMode.isNull()) { - QCameraExposure::ExposureMode qtMode = m_requestedMode.value<QCameraExposure::ExposureMode>(); - AVCaptureExposureMode avMode = AVCaptureExposureModeContinuousAutoExposure; - if (!qt_convert_exposure_mode(captureDevice, qtMode, avMode)) { - qDebugCamera() << Q_FUNC_INFO << "requested exposure mode is not supported"; - return; - } - - if (avMode == captureDevice.exposureMode) - return; - - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration"; - return; - } - - [captureDevice setExposureMode:avMode]; - Q_EMIT actualValueChanged(int(QCameraExposureControl::ExposureMode)); - } -#endif -} - -QT_END_NAMESPACE - -#include "moc_avfcameraexposurecontrol.cpp" |