From 39f5de4106b87c5bda159290a512fcf56bc1dc4f Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Wed, 14 Jan 2015 12:18:53 +0100 Subject: AVFCameraExposureControl - exposure control for iOS Exposure control, version for AVFoundation plugin (this code is using quite a new API, iOS >=8 only). Change-Id: I6871a758e8dfb98ab46b66d91a44142163e0bb44 Reviewed-by: Yoann Lopes --- .../avfoundation/camera/avfcameraexposurecontrol.h | 83 +++ .../camera/avfcameraexposurecontrol.mm | 656 +++++++++++++++++++++ src/plugins/avfoundation/camera/avfcameraservice.h | 3 + .../avfoundation/camera/avfcameraservice.mm | 12 +- src/plugins/avfoundation/camera/camera.pro | 4 +- 5 files changed, 756 insertions(+), 2 deletions(-) create mode 100644 src/plugins/avfoundation/camera/avfcameraexposurecontrol.h create mode 100644 src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm (limited to 'src') diff --git a/src/plugins/avfoundation/camera/avfcameraexposurecontrol.h b/src/plugins/avfoundation/camera/avfcameraexposurecontrol.h new file mode 100644 index 000000000..df7fe8a63 --- /dev/null +++ b/src/plugins/avfoundation/camera/avfcameraexposurecontrol.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AVFCAMERAEXPOSURECONTROL_H +#define AVFCAMERAEXPOSURECONTROL_H + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class AVFCameraSession; +class AVFCameraService; + +class AVFCameraExposureControl : public QCameraExposureControl +{ + Q_OBJECT + +public: + AVFCameraExposureControl(AVFCameraService *service); + + bool isParameterSupported(ExposureParameter parameter) const Q_DECL_OVERRIDE; + QVariantList supportedParameterRange(ExposureParameter parameter, + bool *continuous) const Q_DECL_OVERRIDE; + + QVariant requestedValue(ExposureParameter parameter) const Q_DECL_OVERRIDE; + QVariant actualValue(ExposureParameter parameter) const Q_DECL_OVERRIDE; + bool setValue(ExposureParameter parameter, const QVariant &value) Q_DECL_OVERRIDE; + +private Q_SLOTS: + void cameraStateChanged(); + +private: + AVFCameraService *m_service; + AVFCameraSession *m_session; + + QVariant m_requestedMode; + QVariant m_requestedCompensation; + QVariant m_requestedShutterSpeed; + QVariant m_requestedISO; + + // Aux. setters: + bool setExposureMode(const QVariant &value); + bool setExposureCompensation(const QVariant &value); + bool setShutterSpeed(const QVariant &value); + bool setISO(const QVariant &value); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm b/src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm new file mode 100644 index 000000000..57b2a85fa --- /dev/null +++ b/src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm @@ -0,0 +1,656 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "avfcameraexposurecontrol.h" +#include "avfconfigurationlock.h" +#include "avfcamerasession.h" +#include "avfcameraservice.h" +#include "avfcameradebug.h" + +#include +#include +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace { + +// All these methods to work with exposure/ISO/SS in custom mode +// are quite new (iOS 8 or later and no OS X support). + +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + +// Misc. helpers to check values/ranges: + +bool qt_check_ISO_conversion(float isoValue) +{ + if (isoValue >= std::numeric_limits::max()) + return false; + if (isoValue <= std::numeric_limits::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 service, QPointer 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 service, QPointer 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 // QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + +} // Unnamed namespace. + +AVFCameraExposureControl::AVFCameraExposureControl(AVFCameraService *service) + : m_service(service), + m_session(Q_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 +{ +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + 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; +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + + 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 +{ +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + 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) +{ +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + if (!value.canConvert()) { + qDebugCamera() << Q_FUNC_INFO << "invalid exposure mode value," + << "QCameraExposure::ExposureMode expected"; + return false; + } + + const QCameraExposure::ExposureMode qtMode = value.value(); + 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) +{ +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + if (!value.canConvert()) { + 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) +{ +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + if (value.isNull()) + return setExposureMode(QVariant::fromValue(QCameraExposure::ExposureAuto)); + + if (!value.canConvert()) { + 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) +{ +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + if (value.isNull()) + return setExposureMode(QVariant::fromValue(QCameraExposure::ExposureAuto)); + + if (!value.canConvert()) { + 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() +{ +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + 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(); + 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" diff --git a/src/plugins/avfoundation/camera/avfcameraservice.h b/src/plugins/avfoundation/camera/avfcameraservice.h index d948b0e8d..e49470484 100644 --- a/src/plugins/avfoundation/camera/avfcameraservice.h +++ b/src/plugins/avfoundation/camera/avfcameraservice.h @@ -53,6 +53,7 @@ class AVFCameraSession; class AVFCameraDeviceControl; class AVFAudioInputSelectorControl; class AVFCameraFocusControl; +class AVFCameraExposureControl; class AVFCameraService : public QMediaService { @@ -72,6 +73,7 @@ public: AVFMediaRecorderControl *recorderControl() const { return m_recorderControl; } AVFImageCaptureControl *imageCaptureControl() const { return m_imageCaptureControl; } AVFCameraFocusControl *cameraFocusControl() const { return m_cameraFocusControl; } + AVFCameraExposureControl *cameraExposureControl() const {return m_cameraExposureControl; } private: AVFCameraSession *m_session; @@ -84,6 +86,7 @@ private: AVFMediaRecorderControl *m_recorderControl; AVFImageCaptureControl *m_imageCaptureControl; AVFCameraFocusControl *m_cameraFocusControl; + AVFCameraExposureControl *m_cameraExposureControl; }; QT_END_NAMESPACE diff --git a/src/plugins/avfoundation/camera/avfcameraservice.mm b/src/plugins/avfoundation/camera/avfcameraservice.mm index d617dc954..c1ac0b739 100644 --- a/src/plugins/avfoundation/camera/avfcameraservice.mm +++ b/src/plugins/avfoundation/camera/avfcameraservice.mm @@ -41,6 +41,7 @@ #include #include +#include #include "avfcameraservice.h" #include "avfcameracontrol.h" @@ -56,6 +57,7 @@ #include "avfimagecapturecontrol.h" #include "avfmediavideoprobecontrol.h" #include "avfcamerafocuscontrol.h" +#include "avfcameraexposurecontrol.h" #include #include @@ -75,8 +77,12 @@ AVFCameraService::AVFCameraService(QObject *parent): m_metaDataControl = new AVFCameraMetaDataControl(this); m_recorderControl = new AVFMediaRecorderControl(this); m_imageCaptureControl = new AVFImageCaptureControl(this); - m_cameraFocusControl = new AVFCameraFocusControl(this); + m_cameraExposureControl = 0; +#if defined(Q_OS_IOS) && QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_8_0) + m_cameraExposureControl = new AVFCameraExposureControl(this); +#endif } AVFCameraService::~AVFCameraService() @@ -96,6 +102,7 @@ AVFCameraService::~AVFCameraService() delete m_metaDataControl; delete m_cameraControl; delete m_cameraFocusControl; + delete m_cameraExposureControl; delete m_session; } @@ -124,6 +131,9 @@ QMediaControl *AVFCameraService::requestControl(const char *name) if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0) return m_imageCaptureControl; + if (qstrcmp(name, QCameraExposureControl_iid) == 0) + return m_cameraExposureControl; + if (qstrcmp(name, QCameraFocusControl_iid) == 0) return m_cameraFocusControl; diff --git a/src/plugins/avfoundation/camera/camera.pro b/src/plugins/avfoundation/camera/camera.pro index 4e49ebe22..fcf344fae 100644 --- a/src/plugins/avfoundation/camera/camera.pro +++ b/src/plugins/avfoundation/camera/camera.pro @@ -38,6 +38,7 @@ HEADERS += \ avfcamerarenderercontrol.h \ avfcameradevicecontrol.h \ avfcamerafocuscontrol.h \ + avfcameraexposurecontrol.h \ avfconfigurationlock.h OBJECTIVE_SOURCES += \ @@ -55,5 +56,6 @@ OBJECTIVE_SOURCES += \ avfcamerainfocontrol.mm \ avfcameradevicecontrol.mm \ avfcamerarenderercontrol.mm \ - avfcamerafocuscontrol.mm + avfcamerafocuscontrol.mm \ + avfcameraexposurecontrol.mm -- cgit v1.2.3