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 <yoann.lopes@theqtcompany.com>
This commit is contained in:
83
src/plugins/avfoundation/camera/avfcameraexposurecontrol.h
Normal file
83
src/plugins/avfoundation/camera/avfcameraexposurecontrol.h
Normal file
@@ -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 <QtMultimedia/qcameraexposurecontrol.h>
|
||||
#include <QtMultimedia/qcameraexposure.h>
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
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
|
||||
656
src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm
Normal file
656
src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm
Normal file
@@ -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 <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
|
||||
// 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<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 // 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<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)
|
||||
{
|
||||
#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0)
|
||||
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)
|
||||
{
|
||||
#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0)
|
||||
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)
|
||||
{
|
||||
#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0)
|
||||
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()
|
||||
{
|
||||
#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<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"
|
||||
@@ -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
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
|
||||
#include <QtCore/qvariant.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qsysinfo.h>
|
||||
|
||||
#include "avfcameraservice.h"
|
||||
#include "avfcameracontrol.h"
|
||||
@@ -56,6 +57,7 @@
|
||||
#include "avfimagecapturecontrol.h"
|
||||
#include "avfmediavideoprobecontrol.h"
|
||||
#include "avfcamerafocuscontrol.h"
|
||||
#include "avfcameraexposurecontrol.h"
|
||||
|
||||
#include <private/qmediaplaylistnavigator_p.h>
|
||||
#include <qmediaplaylist.h>
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user