summaryrefslogtreecommitdiffstats
path: root/src/multimedia/platform/avfoundation/camera
diff options
context:
space:
mode:
Diffstat (limited to 'src/multimedia/platform/avfoundation/camera')
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol.mm226
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol_p.h86
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol.mm119
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol_p.h94
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameracontrol.mm530
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameracontrol_p.h126
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameradebug_p.h68
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameradevicecontrol.mm142
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameradevicecontrol_p.h98
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol.mm831
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol_p.h112
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol.mm422
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol_p.h118
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol.mm83
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol_p.h81
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol.mm409
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol_p.h119
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameraservice.mm223
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameraservice_p.h126
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin.mm103
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin_p.h79
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerasession.mm491
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerasession_p.h155
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerautility.mm575
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerautility_p.h190
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol.mm262
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol_p.h129
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfimagecapturecontrol.mm278
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfimagecapturecontrol_p.h116
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfimageencodercontrol.mm239
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfimageencodercontrol_p.h96
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediaassetwriter.mm514
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediaassetwriter_p.h86
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediacontainercontrol.mm113
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediacontainercontrol_p.h80
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol.mm430
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios.mm414
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios_p.h126
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_p.h131
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol.mm61
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol_p.h71
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfstoragelocation.mm136
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfstoragelocation_p.h82
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol.mm385
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol_p.h99
-rw-r--r--src/multimedia/platform/avfoundation/camera/camera.pri60
46 files changed, 9514 insertions, 0 deletions
diff --git a/src/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol.mm b/src/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol.mm
new file mode 100644
index 000000000..b613ca32a
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol.mm
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** 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 "avfaudioencodersettingscontrol_p.h"
+
+#include "avfcameraservice_p.h"
+#include "avfcamerasession_p.h"
+
+#include <AVFoundation/AVFoundation.h>
+#include <CoreAudio/CoreAudioTypes.h>
+
+QT_BEGIN_NAMESPACE
+
+struct AudioCodecInfo
+{
+ QString description;
+ int id;
+
+ AudioCodecInfo() : id(0) { }
+ AudioCodecInfo(const QString &desc, int i)
+ : description(desc), id(i)
+ { }
+};
+
+typedef QMap<QString, AudioCodecInfo> SupportedAudioCodecs;
+Q_GLOBAL_STATIC_WITH_ARGS(QString , defaultCodec, (QLatin1String("aac")))
+Q_GLOBAL_STATIC(SupportedAudioCodecs, supportedCodecs)
+
+AVFAudioEncoderSettingsControl::AVFAudioEncoderSettingsControl(AVFCameraService *service)
+ : QAudioEncoderSettingsControl()
+ , m_service(service)
+{
+ if (supportedCodecs->isEmpty()) {
+ supportedCodecs->insert(QStringLiteral("lpcm"),
+ AudioCodecInfo(QStringLiteral("Linear PCM"),
+ kAudioFormatLinearPCM));
+ supportedCodecs->insert(QStringLiteral("ulaw"),
+ AudioCodecInfo(QStringLiteral("PCM Mu-Law 2:1"),
+ kAudioFormatULaw));
+ supportedCodecs->insert(QStringLiteral("alaw"),
+ AudioCodecInfo(QStringLiteral("PCM A-Law 2:1"),
+ kAudioFormatALaw));
+ supportedCodecs->insert(QStringLiteral("ima4"),
+ AudioCodecInfo(QStringLiteral("IMA 4:1 ADPCM"),
+ kAudioFormatAppleIMA4));
+ supportedCodecs->insert(QStringLiteral("alac"),
+ AudioCodecInfo(QStringLiteral("Apple Lossless Audio Codec"),
+ kAudioFormatAppleLossless));
+ supportedCodecs->insert(QStringLiteral("aac"),
+ AudioCodecInfo(QStringLiteral("MPEG-4 Low Complexity AAC"),
+ kAudioFormatMPEG4AAC));
+ supportedCodecs->insert(QStringLiteral("aach"),
+ AudioCodecInfo(QStringLiteral("MPEG-4 High Efficiency AAC"),
+ kAudioFormatMPEG4AAC_HE));
+ supportedCodecs->insert(QStringLiteral("aacl"),
+ AudioCodecInfo(QStringLiteral("MPEG-4 AAC Low Delay"),
+ kAudioFormatMPEG4AAC_LD));
+ supportedCodecs->insert(QStringLiteral("aace"),
+ AudioCodecInfo(QStringLiteral("MPEG-4 AAC Enhanced Low Delay"),
+ kAudioFormatMPEG4AAC_ELD));
+ supportedCodecs->insert(QStringLiteral("aacf"),
+ AudioCodecInfo(QStringLiteral("MPEG-4 AAC Enhanced Low Delay with SBR"),
+ kAudioFormatMPEG4AAC_ELD_SBR));
+ supportedCodecs->insert(QStringLiteral("aacp"),
+ AudioCodecInfo(QStringLiteral("MPEG-4 HE AAC V2"),
+ kAudioFormatMPEG4AAC_HE_V2));
+ supportedCodecs->insert(QStringLiteral("ilbc"),
+ AudioCodecInfo(QStringLiteral("iLBC"),
+ kAudioFormatiLBC));
+ }
+}
+
+QStringList AVFAudioEncoderSettingsControl::supportedAudioCodecs() const
+{
+ return supportedCodecs->keys();
+}
+
+QString AVFAudioEncoderSettingsControl::codecDescription(const QString &codecName) const
+{
+ return supportedCodecs->value(codecName).description;
+}
+
+QList<int> AVFAudioEncoderSettingsControl::supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous) const
+{
+ Q_UNUSED(settings);
+
+ if (continuous)
+ *continuous = true;
+
+ return QList<int>() << 8000 << 96000;
+}
+
+QAudioEncoderSettings AVFAudioEncoderSettingsControl::audioSettings() const
+{
+ return m_actualSettings;
+}
+
+void AVFAudioEncoderSettingsControl::setAudioSettings(const QAudioEncoderSettings &settings)
+{
+ if (m_requestedSettings == settings)
+ return;
+
+ m_requestedSettings = m_actualSettings = settings;
+}
+
+NSDictionary *AVFAudioEncoderSettingsControl::applySettings()
+{
+ if (m_service->session()->state() != QCamera::LoadedState &&
+ m_service->session()->state() != QCamera::ActiveState) {
+ return nil;
+ }
+
+ NSMutableDictionary *settings = [NSMutableDictionary dictionary];
+
+ QString codec = m_requestedSettings.codec().isEmpty() ? *defaultCodec : m_requestedSettings.codec();
+ if (!supportedCodecs->contains(codec)) {
+ qWarning("Unsupported codec: '%s'", codec.toLocal8Bit().constData());
+ codec = *defaultCodec;
+ }
+ [settings setObject:[NSNumber numberWithInt:supportedCodecs->value(codec).id] forKey:AVFormatIDKey];
+ m_actualSettings.setCodec(codec);
+
+#ifdef Q_OS_OSX
+ if (m_requestedSettings.encodingMode() == QMultimedia::ConstantQualityEncoding) {
+ int quality;
+ switch (m_requestedSettings.quality()) {
+ case QMultimedia::VeryLowQuality:
+ quality = AVAudioQualityMin;
+ break;
+ case QMultimedia::LowQuality:
+ quality = AVAudioQualityLow;
+ break;
+ case QMultimedia::HighQuality:
+ quality = AVAudioQualityHigh;
+ break;
+ case QMultimedia::VeryHighQuality:
+ quality = AVAudioQualityMax;
+ break;
+ case QMultimedia::NormalQuality:
+ default:
+ quality = AVAudioQualityMedium;
+ break;
+ }
+ [settings setObject:[NSNumber numberWithInt:quality] forKey:AVEncoderAudioQualityKey];
+
+ } else
+#endif
+ if (m_requestedSettings.bitRate() > 0){
+ [settings setObject:[NSNumber numberWithInt:m_requestedSettings.bitRate()] forKey:AVEncoderBitRateKey];
+ }
+
+ int sampleRate = m_requestedSettings.sampleRate();
+ int channelCount = m_requestedSettings.channelCount();
+
+#ifdef Q_OS_IOS
+ // Some keys are mandatory only on iOS
+ if (codec == QLatin1String("lpcm")) {
+ [settings setObject:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
+ [settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsBigEndianKey];
+ [settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsFloatKey];
+ [settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsNonInterleaved];
+ }
+
+ if (codec == QLatin1String("alac"))
+ [settings setObject:[NSNumber numberWithInt:24] forKey:AVEncoderBitDepthHintKey];
+
+ if (sampleRate <= 0)
+ sampleRate = codec == QLatin1String("ilbc") ? 8000 : 44100;
+ if (channelCount <= 0)
+ channelCount = codec == QLatin1String("ilbc") ? 1 : 2;
+#endif
+
+ if (sampleRate > 0) {
+ [settings setObject:[NSNumber numberWithInt:sampleRate] forKey:AVSampleRateKey];
+ m_actualSettings.setSampleRate(sampleRate);
+ }
+ if (channelCount > 0) {
+ [settings setObject:[NSNumber numberWithInt:channelCount] forKey:AVNumberOfChannelsKey];
+ m_actualSettings.setChannelCount(channelCount);
+ }
+
+ return settings;
+}
+
+void AVFAudioEncoderSettingsControl::unapplySettings()
+{
+ m_actualSettings = m_requestedSettings;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol_p.h
new file mode 100644
index 000000000..b1851a5bf
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFAUDIOENCODERSETTINGSCONTROL_H
+#define AVFAUDIOENCODERSETTINGSCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qaudioencodersettingscontrol.h>
+
+@class NSDictionary;
+@class AVCaptureAudioDataOutput;
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraService;
+
+class AVFAudioEncoderSettingsControl : public QAudioEncoderSettingsControl
+{
+public:
+ explicit AVFAudioEncoderSettingsControl(AVFCameraService *service);
+
+ QStringList supportedAudioCodecs() const override;
+ QString codecDescription(const QString &codecName) const override;
+ QList<int> supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous = nullptr) const override;
+ QAudioEncoderSettings audioSettings() const override;
+ void setAudioSettings(const QAudioEncoderSettings &settings) override;
+
+ NSDictionary *applySettings();
+ void unapplySettings();
+
+private:
+ AVFCameraService *m_service;
+
+ QAudioEncoderSettings m_requestedSettings;
+ QAudioEncoderSettings m_actualSettings;
+};
+
+QT_END_NAMESPACE
+
+#endif // AVFAUDIOENCODERSETTINGSCONTROL_H
diff --git a/src/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol.mm b/src/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol.mm
new file mode 100644
index 000000000..21ff70917
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol.mm
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "avfcameradebug_p.h"
+#include "avfaudioinputselectorcontrol_p.h"
+#include "avfcameraservice_p.h"
+
+#import <AVFoundation/AVFoundation.h>
+
+QT_USE_NAMESPACE
+
+AVFAudioInputSelectorControl::AVFAudioInputSelectorControl(AVFCameraService *service, QObject *parent)
+ : QAudioInputSelectorControl(parent)
+ , m_dirty(true)
+{
+ Q_UNUSED(service);
+ NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio];
+ for (AVCaptureDevice *device in videoDevices) {
+ QString deviceId = QString::fromUtf8([[device uniqueID] UTF8String]);
+ m_devices << deviceId;
+ m_deviceDescriptions.insert(deviceId,
+ QString::fromUtf8([[device localizedName] UTF8String]));
+ }
+
+ AVCaptureDevice *defaultDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
+ if (defaultDevice) {
+ m_defaultDevice = QString::fromUtf8([defaultDevice.uniqueID UTF8String]);
+ m_activeInput = m_defaultDevice;
+ }
+}
+
+AVFAudioInputSelectorControl::~AVFAudioInputSelectorControl()
+{
+}
+
+QList<QString> AVFAudioInputSelectorControl::availableInputs() const
+{
+ return m_devices;
+}
+
+QString AVFAudioInputSelectorControl::inputDescription(const QString &name) const
+{
+ return m_deviceDescriptions.value(name);
+}
+
+QString AVFAudioInputSelectorControl::defaultInput() const
+{
+ return m_defaultDevice;
+}
+
+QString AVFAudioInputSelectorControl::activeInput() const
+{
+ return m_activeInput;
+}
+
+void AVFAudioInputSelectorControl::setActiveInput(const QString &name)
+{
+ if (name != m_activeInput) {
+ m_activeInput = name;
+ m_dirty = true;
+
+ Q_EMIT activeInputChanged(m_activeInput);
+ }
+}
+
+AVCaptureDevice *AVFAudioInputSelectorControl::createCaptureDevice()
+{
+ m_dirty = false;
+ AVCaptureDevice *device = nullptr;
+
+ if (!m_activeInput.isEmpty()) {
+ device = [AVCaptureDevice deviceWithUniqueID:
+ [NSString stringWithUTF8String:
+ m_activeInput.toUtf8().constData()]];
+ }
+
+ if (!device)
+ device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
+
+ return device;
+}
+
+#include "moc_avfaudioinputselectorcontrol_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol_p.h
new file mode 100644
index 000000000..90a9bc3fc
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFAUDIOINPUTSELECTORCONTROL_H
+#define AVFAUDIOINPUTSELECTORCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtMultimedia/qaudioinputselectorcontrol.h>
+#include <QtCore/qstringlist.h>
+
+#import <AVFoundation/AVFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraSession;
+class AVFCameraService;
+
+class AVFAudioInputSelectorControl : public QAudioInputSelectorControl
+{
+Q_OBJECT
+public:
+ AVFAudioInputSelectorControl(AVFCameraService *service, QObject *parent = nullptr);
+ ~AVFAudioInputSelectorControl();
+
+ QList<QString> availableInputs() const override;
+ QString inputDescription(const QString &name) const override;
+ QString defaultInput() const override;
+ QString activeInput() const override;
+
+public Q_SLOTS:
+ void setActiveInput(const QString &name) override;
+
+public:
+ //device changed since the last createCaptureDevice()
+ bool isDirty() const { return m_dirty; }
+ AVCaptureDevice *createCaptureDevice();
+
+private:
+ QString m_activeInput;
+ bool m_dirty;
+ QString m_defaultDevice;
+ QStringList m_devices;
+ QMap<QString, QString> m_deviceDescriptions;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameracontrol.mm b/src/multimedia/platform/avfoundation/camera/avfcameracontrol.mm
new file mode 100644
index 000000000..27c28587f
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameracontrol.mm
@@ -0,0 +1,530 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "avfcameradebug_p.h"
+#include "avfcameracontrol_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcamerarenderercontrol_p.h"
+#include "qabstractvideosurface.h"
+
+QT_USE_NAMESPACE
+
+AVFCameraControl::AVFCameraControl(AVFCameraService *service, QObject *parent)
+ : QCameraControl(parent)
+ , m_session(service->session())
+ , m_service(service)
+ , m_state(QCamera::UnloadedState)
+ , m_lastStatus(QCamera::UnloadedStatus)
+ , m_captureMode(QCamera::CaptureStillImage)
+{
+ Q_UNUSED(service);
+ connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateStatus()));
+ connect(this, &AVFCameraControl::captureModeChanged, m_session, &AVFCameraSession::onCaptureModeChanged);
+}
+
+AVFCameraControl::~AVFCameraControl()
+{
+}
+
+QCamera::State AVFCameraControl::state() const
+{
+ return m_state;
+}
+
+void AVFCameraControl::setState(QCamera::State state)
+{
+ if (m_state == state)
+ return;
+ m_state = state;
+ m_session->setState(state);
+
+ Q_EMIT stateChanged(m_state);
+ updateStatus();
+}
+
+QCamera::Status AVFCameraControl::status() const
+{
+ static QCamera::Status statusTable[3][3] = {
+ { QCamera::UnloadedStatus, QCamera::UnloadingStatus, QCamera::StoppingStatus }, //Unloaded state
+ { QCamera::LoadingStatus, QCamera::LoadedStatus, QCamera::StoppingStatus }, //Loaded state
+ { QCamera::LoadingStatus, QCamera::StartingStatus, QCamera::ActiveStatus } //ActiveState
+ };
+
+ return statusTable[m_state][m_session->state()];
+}
+
+void AVFCameraControl::updateStatus()
+{
+ QCamera::Status newStatus = status();
+
+ if (m_lastStatus != newStatus) {
+ qDebugCamera() << "Camera status changed: " << m_lastStatus << " -> " << newStatus;
+ m_lastStatus = newStatus;
+ Q_EMIT statusChanged(m_lastStatus);
+ }
+}
+
+QCamera::CaptureModes AVFCameraControl::captureMode() const
+{
+ return m_captureMode;
+}
+
+void AVFCameraControl::setCaptureMode(QCamera::CaptureModes mode)
+{
+ if (m_captureMode == mode)
+ return;
+
+ m_captureMode = mode;
+ Q_EMIT captureModeChanged(mode);
+}
+
+bool AVFCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const
+{
+ return true;
+}
+
+bool AVFCameraControl::canChangeProperty(QCameraControl::PropertyChangeType changeType, QCamera::Status status) const
+{
+ Q_UNUSED(changeType);
+ Q_UNUSED(status);
+
+ return true;
+}
+
+QCamera::LockTypes AVFCameraControl::supportedLocks() const
+{
+ return {};
+}
+
+QCamera::LockStatus AVFCameraControl::lockStatus(QCamera::LockType) const
+{
+ return QCamera::Unlocked;
+}
+
+void AVFCameraControl::searchAndLock(QCamera::LockTypes locks)
+{
+ Q_UNUSED(locks);
+}
+
+void AVFCameraControl::unlock(QCamera::LockTypes locks)
+{
+ Q_UNUSED(locks);
+}
+
+
+namespace {
+
+bool qt_framerates_sane(const QCameraViewfinderSettings &settings)
+{
+ const qreal minFPS = settings.minimumFrameRate();
+ const qreal maxFPS = settings.maximumFrameRate();
+
+ if (minFPS < 0. || maxFPS < 0.)
+ return false;
+
+ return !maxFPS || maxFPS >= minFPS;
+}
+
+}
+
+QList<QCameraViewfinderSettings> AVFCameraControl::supportedViewfinderSettings() const
+{
+ QList<QCameraViewfinderSettings> supportedSettings;
+
+ AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
+ if (!captureDevice) {
+ qDebugCamera() << Q_FUNC_INFO << "no capture device found";
+ return supportedSettings;
+ }
+
+ QVector<AVFPSRange> framerates;
+
+ QVector<QVideoFrame::PixelFormat> pixelFormats(viewfinderPixelFormats());
+
+ if (!pixelFormats.size())
+ pixelFormats << QVideoFrame::Format_Invalid; // The default value.
+
+ if (!captureDevice.formats || !captureDevice.formats.count) {
+ qDebugCamera() << Q_FUNC_INFO << "no capture device formats found";
+ return supportedSettings;
+ }
+
+ const QVector<AVCaptureDeviceFormat *> formats(qt_unique_device_formats(captureDevice,
+ m_service->session()->defaultCodec()));
+ for (int i = 0; i < formats.size(); ++i) {
+ AVCaptureDeviceFormat *format = formats[i];
+
+ const QSize res(qt_device_format_resolution(format));
+ if (res.isNull() || !res.isValid())
+ continue;
+ const QSize par(qt_device_format_pixel_aspect_ratio(format));
+ if (par.isNull() || !par.isValid())
+ continue;
+
+ framerates = qt_device_format_framerates(format);
+ if (!framerates.size())
+ framerates << AVFPSRange(); // The default value.
+
+ for (int i = 0; i < pixelFormats.size(); ++i) {
+ for (int j = 0; j < framerates.size(); ++j) {
+ QCameraViewfinderSettings newSet;
+ newSet.setResolution(res);
+ newSet.setPixelAspectRatio(par);
+ newSet.setPixelFormat(pixelFormats[i]);
+ newSet.setMinimumFrameRate(framerates[j].first);
+ newSet.setMaximumFrameRate(framerates[j].second);
+ supportedSettings << newSet;
+ }
+ }
+ }
+
+ return supportedSettings;
+}
+
+QCameraViewfinderSettings AVFCameraControl::viewfinderSettings() const
+{
+ QCameraViewfinderSettings settings = m_settings;
+
+ AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
+ if (!captureDevice) {
+ qDebugCamera() << Q_FUNC_INFO << "no capture device found";
+ return settings;
+ }
+
+ if (m_service->session()->state() != QCamera::LoadedState &&
+ m_service->session()->state() != QCamera::ActiveState) {
+ return settings;
+ }
+
+ if (!captureDevice.activeFormat) {
+ qDebugCamera() << Q_FUNC_INFO << "no active capture device format";
+ return settings;
+ }
+
+ const QSize res(qt_device_format_resolution(captureDevice.activeFormat));
+ const QSize par(qt_device_format_pixel_aspect_ratio(captureDevice.activeFormat));
+ if (res.isNull() || !res.isValid() || par.isNull() || !par.isValid()) {
+ qDebugCamera() << Q_FUNC_INFO << "failed to obtain resolution/pixel aspect ratio";
+ return settings;
+ }
+
+ settings.setResolution(res);
+ settings.setPixelAspectRatio(par);
+
+ const AVFPSRange fps = qt_current_framerates(captureDevice, videoConnection());
+ settings.setMinimumFrameRate(fps.first);
+ settings.setMaximumFrameRate(fps.second);
+
+ AVCaptureVideoDataOutput *videoOutput = m_service->videoOutput() ? m_service->videoOutput()->videoDataOutput() : nullptr;
+ if (videoOutput) {
+ NSObject *obj = [videoOutput.videoSettings objectForKey:(id)kCVPixelBufferPixelFormatTypeKey];
+ if (obj && [obj isKindOfClass:[NSNumber class]]) {
+ NSNumber *nsNum = static_cast<NSNumber *>(obj);
+ settings.setPixelFormat(QtPixelFormatFromCVFormat([nsNum unsignedIntValue]));
+ }
+ }
+
+ return settings;
+}
+
+void AVFCameraControl::setViewfinderSettings(const QCameraViewfinderSettings &settings)
+{
+ if (m_settings == settings)
+ return;
+
+ m_settings = settings;
+#if defined(Q_OS_IOS)
+ bool active = m_service->session()->state() == QCamera::ActiveState;
+ if (active)
+ [m_service->session()->captureSession() beginConfiguration];
+ applySettings(m_settings);
+ if (active)
+ [m_service->session()->captureSession() commitConfiguration];
+#else
+ applySettings(m_settings);
+#endif
+}
+
+QVideoFrame::PixelFormat AVFCameraControl::QtPixelFormatFromCVFormat(unsigned avPixelFormat)
+{
+ // BGRA <-> ARGB "swap" is intentional:
+ // to work correctly with GL_RGBA, color swap shaders
+ // (in QSG node renderer etc.).
+ switch (avPixelFormat) {
+ case kCVPixelFormatType_32ARGB:
+ return QVideoFrame::Format_BGRA32;
+ case kCVPixelFormatType_32BGRA:
+ return QVideoFrame::Format_ARGB32;
+ case kCVPixelFormatType_24RGB:
+ return QVideoFrame::Format_RGB24;
+ case kCVPixelFormatType_24BGR:
+ return QVideoFrame::Format_BGR24;
+ case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
+ case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
+ return QVideoFrame::Format_NV12;
+ case kCVPixelFormatType_422YpCbCr8:
+ return QVideoFrame::Format_UYVY;
+ case kCVPixelFormatType_422YpCbCr8_yuvs:
+ return QVideoFrame::Format_YUYV;
+ default:
+ return QVideoFrame::Format_Invalid;
+ }
+}
+
+bool AVFCameraControl::CVPixelFormatFromQtFormat(QVideoFrame::PixelFormat qtFormat, unsigned &conv)
+{
+ // BGRA <-> ARGB "swap" is intentional:
+ // to work correctly with GL_RGBA, color swap shaders
+ // (in QSG node renderer etc.).
+ switch (qtFormat) {
+ case QVideoFrame::Format_ARGB32:
+ conv = kCVPixelFormatType_32BGRA;
+ break;
+ case QVideoFrame::Format_BGRA32:
+ conv = kCVPixelFormatType_32ARGB;
+ break;
+ case QVideoFrame::Format_NV12:
+ conv = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
+ break;
+ case QVideoFrame::Format_UYVY:
+ conv = kCVPixelFormatType_422YpCbCr8;
+ break;
+ case QVideoFrame::Format_YUYV:
+ conv = kCVPixelFormatType_422YpCbCr8_yuvs;
+ break;
+ // These two formats below are not supported
+ // by QSGVideoNodeFactory_RGB, so for now I have to
+ // disable them.
+ /*
+ case QVideoFrame::Format_RGB24:
+ conv = kCVPixelFormatType_24RGB;
+ break;
+ case QVideoFrame::Format_BGR24:
+ conv = kCVPixelFormatType_24BGR;
+ break;
+ */
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+AVCaptureDeviceFormat *AVFCameraControl::findBestFormatMatch(const QCameraViewfinderSettings &settings) const
+{
+ AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
+ if (!captureDevice || settings.isNull())
+ return nil;
+
+ const QSize &resolution = settings.resolution();
+ if (!resolution.isNull() && resolution.isValid()) {
+ // Either the exact match (including high resolution for images on iOS)
+ // or a format with a resolution close to the requested one.
+ return qt_find_best_resolution_match(captureDevice, resolution,
+ m_service->session()->defaultCodec(), false);
+ }
+
+ // No resolution requested, what about framerates?
+ if (!qt_framerates_sane(settings)) {
+ qDebugCamera() << Q_FUNC_INFO << "invalid framerate requested (min/max):"
+ << settings.minimumFrameRate() << settings.maximumFrameRate();
+ return nil;
+ }
+
+ const qreal minFPS(settings.minimumFrameRate());
+ const qreal maxFPS(settings.maximumFrameRate());
+ if (minFPS || maxFPS)
+ return qt_find_best_framerate_match(captureDevice,
+ m_service->session()->defaultCodec(),
+ maxFPS ? maxFPS : minFPS);
+ // Ignore PAR for the moment (PAR without resolution can
+ // pick a format with really bad resolution).
+ // No need to test pixel format, just return settings.
+
+ return nil;
+}
+
+QVector<QVideoFrame::PixelFormat> AVFCameraControl::viewfinderPixelFormats() const
+{
+ QVector<QVideoFrame::PixelFormat> qtFormats;
+
+ AVCaptureVideoDataOutput *videoOutput = m_service->videoOutput() ? m_service->videoOutput()->videoDataOutput() : nullptr;
+ if (!videoOutput) {
+ qDebugCamera() << Q_FUNC_INFO << "no video output found";
+ return qtFormats;
+ }
+
+ NSArray *pixelFormats = [videoOutput availableVideoCVPixelFormatTypes];
+
+ for (NSObject *obj in pixelFormats) {
+ if (![obj isKindOfClass:[NSNumber class]])
+ continue;
+
+ NSNumber *formatAsNSNumber = static_cast<NSNumber *>(obj);
+ // It's actually FourCharCode (== UInt32):
+ const QVideoFrame::PixelFormat qtFormat(QtPixelFormatFromCVFormat([formatAsNSNumber unsignedIntValue]));
+ if (qtFormat != QVideoFrame::Format_Invalid
+ && !qtFormats.contains(qtFormat)) { // Can happen, for example, with 8BiPlanar existing in video/full range.
+ qtFormats << qtFormat;
+ }
+ }
+
+ return qtFormats;
+}
+
+bool AVFCameraControl::convertPixelFormatIfSupported(QVideoFrame::PixelFormat qtFormat,
+ unsigned &avfFormat)const
+{
+ AVCaptureVideoDataOutput *videoOutput = m_service->videoOutput() ? m_service->videoOutput()->videoDataOutput() : nullptr;
+ if (!videoOutput)
+ return false;
+
+ unsigned conv = 0;
+ if (!CVPixelFormatFromQtFormat(qtFormat, conv))
+ return false;
+
+ NSArray *formats = [videoOutput availableVideoCVPixelFormatTypes];
+ if (!formats || !formats.count)
+ return false;
+
+ if (m_service->videoOutput()->surface()) {
+ const QAbstractVideoSurface *surface = m_service->videoOutput()->surface();
+ QAbstractVideoBuffer::HandleType h = m_service->videoOutput()->supportsTextures()
+ ? QAbstractVideoBuffer::GLTextureHandle
+ : QAbstractVideoBuffer::NoHandle;
+ if (!surface->supportedPixelFormats(h).contains(qtFormat))
+ return false;
+ }
+
+ bool found = false;
+ for (NSObject *obj in formats) {
+ if (![obj isKindOfClass:[NSNumber class]])
+ continue;
+
+ NSNumber *nsNum = static_cast<NSNumber *>(obj);
+ if ([nsNum unsignedIntValue] == conv) {
+ avfFormat = conv;
+ found = true;
+ }
+ }
+
+ return found;
+}
+
+bool AVFCameraControl::applySettings(const QCameraViewfinderSettings &settings)
+{
+ if (m_service->session()->state() != QCamera::LoadedState &&
+ m_service->session()->state() != QCamera::ActiveState) {
+ return false;
+ }
+
+ AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
+ if (!captureDevice)
+ return false;
+
+ bool activeFormatChanged = false;
+
+ AVCaptureDeviceFormat *match = findBestFormatMatch(settings);
+ if (match) {
+ activeFormatChanged = qt_set_active_format(captureDevice, match, false);
+ } else {
+ qDebugCamera() << Q_FUNC_INFO << "matching device format not found";
+ // We still can update the pixel format at least.
+ }
+
+ AVCaptureVideoDataOutput *videoOutput = m_service->videoOutput() ? m_service->videoOutput()->videoDataOutput() : nullptr;
+ if (videoOutput) {
+ unsigned avfPixelFormat = 0;
+ if (!convertPixelFormatIfSupported(settings.pixelFormat(), avfPixelFormat)) {
+ // If the the pixel format is not specified or invalid, pick the preferred video surface
+ // format, or if no surface is set, the preferred capture device format
+
+ const QVector<QVideoFrame::PixelFormat> deviceFormats = viewfinderPixelFormats();
+ QAbstractVideoSurface *surface = m_service->videoOutput()->surface();
+ QVideoFrame::PixelFormat pickedFormat = deviceFormats.first();
+ if (surface) {
+ pickedFormat = QVideoFrame::Format_Invalid;
+ QAbstractVideoBuffer::HandleType h = m_service->videoOutput()->supportsTextures()
+ ? QAbstractVideoBuffer::GLTextureHandle
+ : QAbstractVideoBuffer::NoHandle;
+ QList<QVideoFrame::PixelFormat> surfaceFormats = surface->supportedPixelFormats(h);
+ for (int i = 0; i < surfaceFormats.count(); ++i) {
+ const QVideoFrame::PixelFormat surfaceFormat = surfaceFormats.at(i);
+ if (deviceFormats.contains(surfaceFormat)) {
+ pickedFormat = surfaceFormat;
+ break;
+ }
+ }
+ }
+
+ CVPixelFormatFromQtFormat(pickedFormat, avfPixelFormat);
+ }
+
+ NSMutableDictionary *videoSettings = [NSMutableDictionary dictionaryWithCapacity:1];
+ [videoSettings setObject:[NSNumber numberWithUnsignedInt:avfPixelFormat]
+ forKey:(id)kCVPixelBufferPixelFormatTypeKey];
+
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock)
+ qWarning("Failed to set active format (lock failed)");
+
+ videoOutput.videoSettings = videoSettings;
+ }
+
+ qt_set_framerate_limits(captureDevice, videoConnection(), settings.minimumFrameRate(), settings.maximumFrameRate());
+
+ return activeFormatChanged;
+}
+
+QCameraViewfinderSettings AVFCameraControl::requestedSettings() const
+{
+ return m_settings;
+}
+
+AVCaptureConnection *AVFCameraControl::videoConnection() const
+{
+ if (!m_service->videoOutput() || !m_service->videoOutput()->videoDataOutput())
+ return nil;
+
+ return [m_service->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo];
+}
+
+#include "moc_avfcameracontrol_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameracontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfcameracontrol_p.h
new file mode 100644
index 000000000..2cb464fe5
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameracontrol_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFCAMERACONTROL_H
+#define AVFCAMERACONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qobject.h>
+
+#include <QtMultimedia/qcameracontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraSession;
+class AVFCameraService;
+@class AVCaptureDeviceFormat;
+@class AVCaptureConnection;
+
+class AVFCameraControl : public QCameraControl
+{
+Q_OBJECT
+public:
+ AVFCameraControl(AVFCameraService *service, QObject *parent = nullptr);
+ ~AVFCameraControl();
+
+ QCamera::State state() const override;
+ void setState(QCamera::State state) override;
+
+ QCamera::Status status() const override;
+
+ QCamera::CaptureModes captureMode() const override;
+ void setCaptureMode(QCamera::CaptureModes) override;
+ bool isCaptureModeSupported(QCamera::CaptureModes mode) const override;
+
+ bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const override;
+
+ QCamera::LockTypes supportedLocks() const override;
+
+ QCamera::LockStatus lockStatus(QCamera::LockType lock) const override;
+
+ void searchAndLock(QCamera::LockTypes locks) override;
+ void unlock(QCamera::LockTypes locks) override;
+
+ QList<QCameraViewfinderSettings> supportedViewfinderSettings() const override;
+ QCameraViewfinderSettings viewfinderSettings() const override;
+ void setViewfinderSettings(const QCameraViewfinderSettings &settings) override;
+
+ // "Converters":
+ static QVideoFrame::PixelFormat QtPixelFormatFromCVFormat(unsigned avPixelFormat);
+ static bool CVPixelFormatFromQtFormat(QVideoFrame::PixelFormat qtFormat, unsigned &conv);
+
+private:
+ void setResolution(const QSize &resolution);
+ void setFramerate(qreal minFPS, qreal maxFPS, bool useActive);
+ void setPixelFormat(QVideoFrame::PixelFormat newFormat);
+ AVCaptureDeviceFormat *findBestFormatMatch(const QCameraViewfinderSettings &settings) const;
+ QList<QVideoFrame::PixelFormat> viewfinderPixelFormats() const;
+ bool convertPixelFormatIfSupported(QVideoFrame::PixelFormat format, unsigned &avfFormat) const;
+ bool applySettings(const QCameraViewfinderSettings &settings);
+ QCameraViewfinderSettings requestedSettings() const;
+
+ AVCaptureConnection *videoConnection() const;
+
+private Q_SLOTS:
+ void updateStatus();
+
+private:
+ friend class AVFCameraSession;
+ AVFCameraSession *m_session;
+ AVFCameraService *m_service;
+ QCameraViewfinderSettings m_settings;
+
+ QCamera::State m_state;
+ QCamera::Status m_lastStatus;
+ QCamera::CaptureModes m_captureMode;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameradebug_p.h b/src/multimedia/platform/avfoundation/camera/avfcameradebug_p.h
new file mode 100644
index 000000000..616e53d99
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameradebug_p.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFDEBUG_H
+#define AVFDEBUG_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qtmultimediaglobal.h"
+
+#include <QtCore/qdebug.h>
+
+QT_USE_NAMESPACE
+
+//#define AVF_DEBUG_CAMERA
+
+#ifdef AVF_DEBUG_CAMERA
+#define qDebugCamera qDebug
+#else
+#define qDebugCamera QT_NO_QDEBUG_MACRO
+#endif
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameradevicecontrol.mm b/src/multimedia/platform/avfoundation/camera/avfcameradevicecontrol.mm
new file mode 100644
index 000000000..ac5711fb1
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameradevicecontrol.mm
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "avfcameradebug_p.h"
+#include "avfcameradevicecontrol_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcamerasession_p.h"
+
+QT_USE_NAMESPACE
+
+AVFCameraDeviceControl::AVFCameraDeviceControl(AVFCameraService *service, QObject *parent)
+ : QVideoDeviceSelectorControl(parent)
+ , m_service(service)
+ , m_selectedDevice(0)
+ , m_dirty(true)
+{
+ Q_UNUSED(m_service);
+}
+
+AVFCameraDeviceControl::~AVFCameraDeviceControl()
+{
+}
+
+int AVFCameraDeviceControl::deviceCount() const
+{
+ return AVFCameraSession::availableCameraDevices().count();
+}
+
+QString AVFCameraDeviceControl::deviceName(int index) const
+{
+ const QList<AVFCameraInfo> &devices = AVFCameraSession::availableCameraDevices();
+ if (index < 0 || index >= devices.count())
+ return QString();
+
+ return QString::fromUtf8(devices.at(index).deviceId);
+}
+
+QString AVFCameraDeviceControl::deviceDescription(int index) const
+{
+ const QList<AVFCameraInfo> &devices = AVFCameraSession::availableCameraDevices();
+ if (index < 0 || index >= devices.count())
+ return QString();
+
+ return devices.at(index).description;
+}
+
+QCamera::Position AVFCameraDeviceControl::cameraPosition(int index) const
+{
+ const QList<AVFCameraInfo> &devices = AVFCameraSession::availableCameraDevices();
+ if (index < 0 || index >= devices.count())
+ return QCamera::UnspecifiedPosition;
+
+ return devices.at(index).position;
+}
+
+int AVFCameraDeviceControl::cameraOrientation(int index) const
+{
+ const QList<AVFCameraInfo> &devices = AVFCameraSession::availableCameraDevices();
+ if (index < 0 || index >= devices.count())
+ return 0;
+
+ return devices.at(index).orientation;
+}
+
+
+int AVFCameraDeviceControl::defaultDevice() const
+{
+ return AVFCameraSession::defaultCameraIndex();
+}
+
+int AVFCameraDeviceControl::selectedDevice() const
+{
+ return m_selectedDevice;
+}
+
+void AVFCameraDeviceControl::setSelectedDevice(int index)
+{
+ if (index >= 0 &&
+ index < deviceCount() &&
+ index != m_selectedDevice) {
+ m_dirty = true;
+ m_selectedDevice = index;
+ Q_EMIT selectedDeviceChanged(index);
+ Q_EMIT selectedDeviceChanged(deviceName(index));
+ }
+}
+
+AVCaptureDevice *AVFCameraDeviceControl::createCaptureDevice()
+{
+ m_dirty = false;
+ AVCaptureDevice *device = nullptr;
+
+ QString deviceId = deviceName(m_selectedDevice);
+ if (!deviceId.isEmpty()) {
+ device = [AVCaptureDevice deviceWithUniqueID:
+ [NSString stringWithUTF8String:
+ deviceId.toUtf8().constData()]];
+ }
+
+ if (!device)
+ device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
+
+ return device;
+}
+
+#include "moc_avfcameradevicecontrol_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameradevicecontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfcameradevicecontrol_p.h
new file mode 100644
index 000000000..0fb8628b2
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameradevicecontrol_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFCAMERADEVICECONTROL_H
+#define AVFCAMERADEVICECONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtMultimedia/qvideodeviceselectorcontrol.h>
+#include <QtCore/qstringlist.h>
+
+#import <AVFoundation/AVFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraSession;
+class AVFCameraService;
+
+class AVFCameraDeviceControl : public QVideoDeviceSelectorControl
+{
+Q_OBJECT
+public:
+ AVFCameraDeviceControl(AVFCameraService *service, QObject *parent = nullptr);
+ ~AVFCameraDeviceControl();
+
+ int deviceCount() const override;
+
+ QString deviceName(int index) const override;
+ QString deviceDescription(int index) const override;
+ QCamera::Position cameraPosition(int index) const override;
+ int cameraOrientation(int index) const override;
+
+ int defaultDevice() const override;
+ int selectedDevice() const override;
+
+public Q_SLOTS:
+ void setSelectedDevice(int index) override;
+
+public:
+ //device changed since the last createCaptureDevice()
+ bool isDirty() const { return m_dirty; }
+ AVCaptureDevice *createCaptureDevice();
+
+private:
+ AVFCameraService *m_service;
+
+ int m_selectedDevice;
+ bool m_dirty;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol.mm b/src/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol.mm
new file mode 100644
index 000000000..a0b2ae06d
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol.mm
@@ -0,0 +1,831 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcameradebug_p.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(QCamera::State)));
+}
+
+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(QCamera::State newState)
+{
+#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
+
+ if (newState == QCamera::UnloadedState) {
+ m_supportedModes = QCameraExposure::FlashOff;
+ Q_EMIT flashReady(false);
+ } else if (newState == QCamera::ActiveState) {
+ m_supportedModes = QCameraExposure::FlashOff;
+ AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
+ if (!captureDevice) {
+ qDebugCamera() << Q_FUNC_INFO << "no capture device in 'Active' state";
+ Q_EMIT flashReady(false);
+ return;
+ }
+
+ if (captureDevice.hasFlash) {
+ if ([captureDevice isFlashModeSupported:AVCaptureFlashModeOn])
+ m_supportedModes |= QCameraExposure::FlashOn;
+ if ([captureDevice isFlashModeSupported:AVCaptureFlashModeAuto])
+ m_supportedModes |= QCameraExposure::FlashAuto;
+ }
+
+ if (captureDevice.hasTorch && [captureDevice isTorchModeSupported:AVCaptureTorchModeOn])
+ m_supportedModes |= QCameraExposure::FlashVideoLight;
+
+ Q_EMIT flashReady(applyFlashSettings());
+ }
+}
+
+
+
+QCameraExposure::FlashModes AVFCameraExposureControl::flashMode() const
+{
+ return m_flashMode;
+}
+
+void AVFCameraExposureControl::setFlashMode(QCameraExposure::FlashModes mode)
+{
+ if (m_flashMode == mode)
+ return;
+
+ if (m_session->state() == QCamera::ActiveState && !isFlashModeSupported(mode)) {
+ qDebugCamera() << Q_FUNC_INFO << "unsupported mode" << mode;
+ return;
+ }
+
+ m_flashMode = mode;
+
+ if (m_session->state() != QCamera::ActiveState)
+ return;
+
+ applyFlashSettings();
+}
+
+bool AVFCameraExposureControl::isFlashModeSupported(QCameraExposure::FlashModes mode) const
+{
+ // From what QCameraExposure has, we can support only these:
+ // FlashAuto = 0x1,
+ // FlashOff = 0x2,
+ // FlashOn = 0x4,
+ // AVCaptureDevice has these flash modes:
+ // AVCaptureFlashModeAuto
+ // AVCaptureFlashModeOff
+ // AVCaptureFlashModeOn
+ // QCameraExposure also has:
+ // FlashTorch = 0x20, --> "Constant light source."
+ // FlashVideoLight = 0x40. --> "Constant light source."
+ // AVCaptureDevice:
+ // AVCaptureTorchModeOff (no mapping)
+ // AVCaptureTorchModeOn --> FlashVideoLight
+ // AVCaptureTorchModeAuto (no mapping)
+
+ return m_supportedModes & mode;
+}
+
+bool AVFCameraExposureControl::isFlashReady() const
+{
+ if (m_session->state() != QCamera::ActiveState)
+ return false;
+
+ AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
+ if (!captureDevice)
+ return false;
+
+ if (!captureDevice.hasFlash && !captureDevice.hasTorch)
+ return false;
+
+ if (!isFlashModeSupported(m_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."
+ if (m_flashMode != QCameraExposure::FlashVideoLight)
+ return [captureDevice isFlashAvailable];
+
+ return [captureDevice isTorchAvailable];
+#endif
+
+ return true;
+}
+
+bool AVFCameraExposureControl::applyFlashSettings()
+{
+ Q_ASSERT(m_session->requestedState() == QCamera::ActiveState);
+
+ AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
+ if (!captureDevice) {
+ qDebugCamera() << Q_FUNC_INFO << "no capture device found";
+ return false;
+ }
+
+ if (!isFlashModeSupported(m_flashMode)) {
+ qDebugCamera() << Q_FUNC_INFO << "unsupported mode" << m_flashMode;
+ return false;
+ }
+
+ if (!captureDevice.hasFlash && !captureDevice.hasTorch) {
+ // FlashOff is the only mode we support.
+ // Return false - flash is not ready.
+ return false;
+ }
+
+ const AVFConfigurationLock lock(captureDevice);
+
+ if (m_flashMode != QCameraExposure::FlashVideoLight) {
+ if (captureDevice.torchMode != AVCaptureTorchModeOff) {
+#ifdef Q_OS_IOS
+ if (![captureDevice isTorchAvailable]) {
+ qDebugCamera() << Q_FUNC_INFO << "torch is not available at the moment";
+ return false;
+ }
+#endif
+ captureDevice.torchMode = AVCaptureTorchModeOff;
+ }
+#ifdef Q_OS_IOS
+ if (![captureDevice isFlashAvailable]) {
+ // We'd like to switch flash (into some mode), but it's not available:
+ qDebugCamera() << Q_FUNC_INFO << "flash is not available at the moment";
+ return false;
+ }
+#endif
+ } else {
+ if (captureDevice.flashMode != AVCaptureFlashModeOff) {
+#ifdef Q_OS_IOS
+ if (![captureDevice isFlashAvailable]) {
+ qDebugCamera() << Q_FUNC_INFO << "flash is not available at the moment";
+ return false;
+ }
+#endif
+ captureDevice.flashMode = AVCaptureFlashModeOff;
+ }
+
+#ifdef Q_OS_IOS
+ if (![captureDevice isTorchAvailable]) {
+ qDebugCamera() << Q_FUNC_INFO << "torch is not available at the moment";
+ return false;
+ }
+#endif
+ }
+
+ if (m_flashMode == QCameraExposure::FlashOff)
+ captureDevice.flashMode = AVCaptureFlashModeOff;
+ else if (m_flashMode == QCameraExposure::FlashOn)
+ captureDevice.flashMode = AVCaptureFlashModeOn;
+ else if (m_flashMode == QCameraExposure::FlashAuto)
+ captureDevice.flashMode = AVCaptureFlashModeAuto;
+ else if (m_flashMode == QCameraExposure::FlashVideoLight)
+ captureDevice.torchMode = AVCaptureTorchModeOn;
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_avfcameraexposurecontrol_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol_p.h
new file mode 100644
index 000000000..9cf60a7b8
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol_p.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFCAMERAEXPOSURECONTROL_H
+#define AVFCAMERAEXPOSURECONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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 override;
+ QVariantList supportedParameterRange(ExposureParameter parameter,
+ bool *continuous) const override;
+
+ QVariant requestedValue(ExposureParameter parameter) const override;
+ QVariant actualValue(ExposureParameter parameter) const override;
+ bool setValue(ExposureParameter parameter, const QVariant &value) override;
+
+ QCameraExposure::FlashModes flashMode() const override;
+ void setFlashMode(QCameraExposure::FlashModes mode) override;
+ bool isFlashModeSupported(QCameraExposure::FlashModes mode) const override;
+ bool isFlashReady() const override;
+
+private Q_SLOTS:
+ void cameraStateChanged(QCamera::State newState);
+
+private:
+ bool applyFlashSettings();
+
+ 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);
+
+ // Set of bits:
+ QCameraExposure::FlashModes m_supportedModes = QCameraExposure::FlashOff;
+ // Only one bit set actually:
+ QCameraExposure::FlashModes m_flashMode = QCameraExposure::FlashOff;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol.mm b/src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol.mm
new file mode 100644
index 000000000..62a7d55e2
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol.mm
@@ -0,0 +1,422 @@
+/****************************************************************************
+**
+** 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 "avfcamerafocuscontrol_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameradebug_p.h"
+
+#include <QtCore/qdebug.h>
+
+#include <AVFoundation/AVFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+bool qt_focus_mode_supported(QCameraFocus::FocusModes mode)
+{
+ // Check if QCameraFocus::FocusMode has counterpart in AVFoundation.
+
+ // AVFoundation has 'Manual', 'Auto' and 'Continuous',
+ // where 'Manual' is actually 'Locked' + writable property 'lensPosition'.
+ // Since Qt does not provide an API to manipulate a lens position, 'Maual' mode
+ // (at the moment) is not supported.
+ return mode == QCameraFocus::AutoFocus
+ || mode == QCameraFocus::ContinuousFocus;
+}
+
+bool qt_focus_point_mode_supported(QCameraFocus::FocusPointMode mode)
+{
+ return mode == QCameraFocus::FocusPointAuto
+ || mode == QCameraFocus::FocusPointCustom
+ || mode == QCameraFocus::FocusPointCenter;
+}
+
+AVCaptureFocusMode avf_focus_mode(QCameraFocus::FocusModes requestedMode)
+{
+ if (requestedMode == QCameraFocus::AutoFocus)
+ return AVCaptureFocusModeAutoFocus;
+
+ return AVCaptureFocusModeContinuousAutoFocus;
+}
+
+}
+
+AVFCameraFocusControl::AVFCameraFocusControl(AVFCameraService *service)
+ : m_session(service->session()),
+ m_focusMode(QCameraFocus::ContinuousFocus),
+ m_focusPointMode(QCameraFocus::FocusPointAuto),
+ m_customFocusPoint(0.5f, 0.5f),
+ m_actualFocusPoint(m_customFocusPoint)
+{
+ Q_ASSERT(m_session);
+ connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(cameraStateChanged()));
+}
+
+QCameraFocus::FocusModes AVFCameraFocusControl::focusMode() const
+{
+ return m_focusMode;
+}
+
+void AVFCameraFocusControl::setFocusMode(QCameraFocus::FocusModes mode)
+{
+ if (m_focusMode == mode)
+ return;
+
+ AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
+ if (!captureDevice) {
+ if (qt_focus_mode_supported(mode)) {
+ m_focusMode = mode;
+ Q_EMIT focusModeChanged(m_focusMode);
+ } else {
+ qDebugCamera() << Q_FUNC_INFO
+ << "focus mode not supported";
+ }
+ return;
+ }
+
+ if (isFocusModeSupported(mode)) {
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qDebugCamera() << Q_FUNC_INFO
+ << "failed to lock for configuration";
+ return;
+ }
+
+ captureDevice.focusMode = avf_focus_mode(mode);
+ m_focusMode = mode;
+ } else {
+ qDebugCamera() << Q_FUNC_INFO << "focus mode not supported";
+ return;
+ }
+
+ Q_EMIT focusModeChanged(m_focusMode);
+}
+
+bool AVFCameraFocusControl::isFocusModeSupported(QCameraFocus::FocusModes mode) const
+{
+ AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
+ if (!captureDevice)
+ return false;
+
+ if (!qt_focus_mode_supported(mode))
+ return false;
+
+ return [captureDevice isFocusModeSupported:avf_focus_mode(mode)];
+}
+
+QCameraFocus::FocusPointMode AVFCameraFocusControl::focusPointMode() const
+{
+ return m_focusPointMode;
+}
+
+void AVFCameraFocusControl::setFocusPointMode(QCameraFocus::FocusPointMode mode)
+{
+ if (m_focusPointMode == mode)
+ return;
+
+ AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
+ if (!captureDevice) {
+ if (qt_focus_point_mode_supported(mode)) {
+ m_focusPointMode = mode;
+ Q_EMIT focusPointModeChanged(mode);
+ }
+ return;
+ }
+
+ if (isFocusPointModeSupported(mode)) {
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
+ return;
+ }
+
+ bool resetPOI = false;
+ if (mode == QCameraFocus::FocusPointCenter || mode == QCameraFocus::FocusPointAuto) {
+ if (m_actualFocusPoint != QPointF(0.5, 0.5)) {
+ m_actualFocusPoint = QPointF(0.5, 0.5);
+ resetPOI = true;
+ }
+ } else if (mode == QCameraFocus::FocusPointCustom) {
+ if (m_actualFocusPoint != m_customFocusPoint) {
+ m_actualFocusPoint = m_customFocusPoint;
+ resetPOI = true;
+ }
+ } // else for any other mode in future.
+
+ if (resetPOI) {
+ const CGPoint focusPOI = CGPointMake(m_actualFocusPoint.x(), m_actualFocusPoint.y());
+ [captureDevice setFocusPointOfInterest:focusPOI];
+ }
+ m_focusPointMode = mode;
+ } else {
+ qDebugCamera() << Q_FUNC_INFO << "focus point mode is not supported";
+ return;
+ }
+
+ Q_EMIT focusPointModeChanged(mode);
+}
+
+bool AVFCameraFocusControl::isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const
+{
+ AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
+ if (!captureDevice)
+ return false;
+
+ if (!qt_focus_point_mode_supported(mode))
+ return false;
+
+ return [captureDevice isFocusPointOfInterestSupported];
+}
+
+QPointF AVFCameraFocusControl::customFocusPoint() const
+{
+ return m_customFocusPoint;
+}
+
+void AVFCameraFocusControl::setCustomFocusPoint(const QPointF &point)
+{
+ if (m_customFocusPoint == point)
+ return;
+
+ if (!QRectF(0.f, 0.f, 1.f, 1.f).contains(point)) {
+ qDebugCamera() << Q_FUNC_INFO << "invalid focus point (out of range)";
+ return;
+ }
+
+ m_customFocusPoint = point;
+ Q_EMIT customFocusPointChanged(m_customFocusPoint);
+
+ AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
+ if (!captureDevice || m_focusPointMode != QCameraFocus::FocusPointCustom)
+ return;
+
+ if ([captureDevice isFocusPointOfInterestSupported]) {
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
+ return;
+ }
+
+ m_actualFocusPoint = m_customFocusPoint;
+ const CGPoint focusPOI = CGPointMake(point.x(), point.y());
+ [captureDevice setFocusPointOfInterest:focusPOI];
+ if (m_focusMode != QCameraFocus::ContinuousFocus)
+ [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
+ } else {
+ qDebugCamera() << Q_FUNC_INFO << "focus point of interest not supported";
+ return;
+ }
+}
+
+QCameraFocusZoneList AVFCameraFocusControl::focusZones() const
+{
+ // Unsupported.
+ return QCameraFocusZoneList();
+}
+
+void AVFCameraFocusControl::cameraStateChanged()
+{
+ if (m_session->state() != QCamera::ActiveState)
+ return;
+
+ AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
+ if (!captureDevice) {
+ qDebugCamera() << Q_FUNC_INFO << "capture device is nil in 'active' state";
+ return;
+ }
+
+ const AVFConfigurationLock lock(captureDevice);
+ if (m_customFocusPoint != m_actualFocusPoint
+ && m_focusPointMode == QCameraFocus::FocusPointCustom) {
+ if (![captureDevice isFocusPointOfInterestSupported]) {
+ qDebugCamera() << Q_FUNC_INFO
+ << "focus point of interest not supported";
+ return;
+ }
+
+ if (!lock) {
+ qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
+ return;
+ }
+
+ m_actualFocusPoint = m_customFocusPoint;
+ const CGPoint focusPOI = CGPointMake(m_customFocusPoint.x(), m_customFocusPoint.y());
+ [captureDevice setFocusPointOfInterest:focusPOI];
+ }
+
+ if (m_focusMode != QCameraFocus::ContinuousFocus) {
+ const AVCaptureFocusMode avMode = avf_focus_mode(m_focusMode);
+ if (captureDevice.focusMode != avMode) {
+ if (![captureDevice isFocusModeSupported:avMode]) {
+ qDebugCamera() << Q_FUNC_INFO << "focus mode not supported";
+ return;
+ }
+
+ if (!lock) {
+ qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
+ return;
+ }
+
+ [captureDevice setFocusMode:avMode];
+ }
+ }
+
+#ifdef Q_OS_IOS
+ const QCamera::State state = m_session->state();
+ if (state != QCamera::ActiveState) {
+ if (state == QCamera::UnloadedState && m_maxZoomFactor > 1.) {
+ m_maxZoomFactor = 1.;
+ Q_EMIT maximumDigitalZoomChanged(1.);
+ }
+ return;
+ }
+
+ if (!captureDevice || !captureDevice.activeFormat) {
+ qDebugCamera() << Q_FUNC_INFO << "camera state is active, but"
+ << "video capture device and/or active format is nil";
+ return;
+ }
+
+ if (captureDevice.activeFormat.videoMaxZoomFactor > 1.) {
+ if (!qFuzzyCompare(m_maxZoomFactor, captureDevice.activeFormat.videoMaxZoomFactor)) {
+ m_maxZoomFactor = captureDevice.activeFormat.videoMaxZoomFactor;
+ Q_EMIT maximumDigitalZoomChanged(m_maxZoomFactor);
+ }
+ } else if (!qFuzzyCompare(m_maxZoomFactor, CGFloat(1.))) {
+ m_maxZoomFactor = 1.;
+
+ Q_EMIT maximumDigitalZoomChanged(1.);
+ }
+
+ zoomToRequestedDigital();
+#endif
+}
+
+qreal AVFCameraFocusControl::maximumOpticalZoom() const
+{
+ // Not supported.
+ return 1.;
+}
+
+qreal AVFCameraFocusControl::maximumDigitalZoom() const
+{
+ return m_maxZoomFactor;
+}
+
+qreal AVFCameraFocusControl::requestedOpticalZoom() const
+{
+ // Not supported.
+ return 1;
+}
+
+qreal AVFCameraFocusControl::requestedDigitalZoom() const
+{
+ return m_requestedZoomFactor;
+}
+
+qreal AVFCameraFocusControl::currentOpticalZoom() const
+{
+ // Not supported.
+ return 1.;
+}
+
+qreal AVFCameraFocusControl::currentDigitalZoom() const
+{
+ return m_zoomFactor;
+}
+
+void AVFCameraFocusControl::zoomTo(qreal optical, qreal digital)
+{
+ Q_UNUSED(optical);
+ Q_UNUSED(digital);
+
+#ifdef QOS_IOS
+ if (qFuzzyCompare(CGFloat(digital), m_requestedZoomFactor))
+ return;
+
+ m_requestedZoomFactor = digital;
+ Q_EMIT requestedDigitalZoomChanged(digital);
+
+ zoomToRequestedDigital();
+#endif
+}
+
+#ifdef QOS_IOS
+void AVFCameraFocusControl::zoomToRequestedDigital()
+{
+ AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
+ if (!captureDevice || !captureDevice.activeFormat)
+ return;
+
+ if (qFuzzyCompare(captureDevice.activeFormat.videoMaxZoomFactor, CGFloat(1.)))
+ return;
+
+ const CGFloat clampedZoom = qBound(CGFloat(1.), m_requestedZoomFactor,
+ captureDevice.activeFormat.videoMaxZoomFactor);
+ const CGFloat deviceZoom = captureDevice.videoZoomFactor;
+ if (qFuzzyCompare(clampedZoom, deviceZoom)) {
+ // Nothing to set, but check if a signal must be emitted:
+ if (!qFuzzyCompare(m_zoomFactor, deviceZoom)) {
+ m_zoomFactor = deviceZoom;
+ Q_EMIT currentDigitalZoomChanged(deviceZoom);
+ }
+ return;
+ }
+
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
+ return;
+ }
+
+ captureDevice.videoZoomFactor = clampedZoom;
+
+ if (!qFuzzyCompare(clampedZoom, m_zoomFactor)) {
+ m_zoomFactor = clampedZoom;
+ Q_EMIT currentDigitalZoomChanged(clampedZoom);
+ }
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_avfcamerafocuscontrol_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol_p.h
new file mode 100644
index 000000000..3527d48b6
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol_p.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFCAMERAFOCUSCONTROL_H
+#define AVFCAMERAFOCUSCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qscopedpointer.h>
+#include <QtCore/qglobal.h>
+
+#include <qcamerafocuscontrol.h>
+
+#include <AVFoundation/AVFoundation.h>
+
+@class AVCaptureDevice;
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraService;
+class AVFCameraSession;
+
+class AVFCameraFocusControl : public QCameraFocusControl
+{
+ Q_OBJECT
+public:
+ explicit AVFCameraFocusControl(AVFCameraService *service);
+
+ QCameraFocus::FocusModes focusMode() const override;
+ void setFocusMode(QCameraFocus::FocusModes mode) override;
+ bool isFocusModeSupported(QCameraFocus::FocusModes mode) const override;
+
+ QCameraFocus::FocusPointMode focusPointMode() const override;
+ void setFocusPointMode(QCameraFocus::FocusPointMode mode) override;
+ bool isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const override;
+ QPointF customFocusPoint() const override;
+ void setCustomFocusPoint(const QPointF &point) override;
+
+ QCameraFocusZoneList focusZones() const override;
+
+ qreal maximumOpticalZoom() const override;
+ qreal maximumDigitalZoom() const override;
+
+ qreal requestedOpticalZoom() const override;
+ qreal requestedDigitalZoom() const override;
+ qreal currentOpticalZoom() const override;
+ qreal currentDigitalZoom() const override;
+
+ void zoomTo(qreal optical, qreal digital) override;
+
+private Q_SLOTS:
+ void cameraStateChanged();
+
+private:
+#ifdef QOS_IOS
+ void zoomToRequestedDigital();
+#endif
+
+ AVFCameraSession *m_session;
+ QCameraFocus::FocusModes m_focusMode;
+ QCameraFocus::FocusPointMode m_focusPointMode;
+ QPointF m_customFocusPoint;
+ QPointF m_actualFocusPoint;
+
+ CGFloat m_maxZoomFactor;
+ CGFloat m_zoomFactor;
+ CGFloat m_requestedZoomFactor;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // AVFCAMERAFOCUSCONTROL_H
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol.mm b/src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol.mm
new file mode 100644
index 000000000..4addfd938
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol.mm
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "avfcamerametadatacontrol_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.h"
+
+QT_USE_NAMESPACE
+
+//metadata support is not implemented yet
+
+AVFCameraMetaDataControl::AVFCameraMetaDataControl(AVFCameraService *service, QObject *parent)
+ :QMetaDataWriterControl(parent)
+{
+ Q_UNUSED(service);
+}
+
+AVFCameraMetaDataControl::~AVFCameraMetaDataControl()
+{
+}
+
+bool AVFCameraMetaDataControl::isMetaDataAvailable() const
+{
+ return !m_tags.isEmpty();
+}
+
+bool AVFCameraMetaDataControl::isWritable() const
+{
+ return false;
+}
+
+QVariant AVFCameraMetaDataControl::metaData(const QString &key) const
+{
+ return m_tags.value(key);
+}
+
+void AVFCameraMetaDataControl::setMetaData(const QString &key, const QVariant &value)
+{
+ m_tags.insert(key, value);
+}
+
+QStringList AVFCameraMetaDataControl::availableMetaData() const
+{
+ return m_tags.keys();
+}
+
+#include "moc_avfcamerametadatacontrol_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol_p.h
new file mode 100644
index 000000000..2f9138986
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFCAMERAMETADATACONTROL_H
+#define AVFCAMERAMETADATACONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmetadatawritercontrol.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraService;
+
+class AVFCameraMetaDataControl : public QMetaDataWriterControl
+{
+ Q_OBJECT
+public:
+ AVFCameraMetaDataControl(AVFCameraService *service, QObject *parent = nullptr);
+ virtual ~AVFCameraMetaDataControl();
+
+ bool isMetaDataAvailable() const override;
+ bool isWritable() const override;
+
+ QVariant metaData(const QString &key) const override;
+ void setMetaData(const QString &key, const QVariant &value) override;
+ QStringList availableMetaData() const override;
+
+private:
+ QMap<QString, QVariant> m_tags;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol.mm b/src/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol.mm
new file mode 100644
index 000000000..13131766c
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol.mm
@@ -0,0 +1,409 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "private/qabstractvideobuffer_p.h"
+#include "avfcamerarenderercontrol_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcameradebug_p.h"
+#include "avfcameracontrol_p.h"
+
+#ifdef Q_OS_IOS
+#include <QtGui/qopengl.h>
+#endif
+
+#include <QtMultimedia/qabstractvideosurface.h>
+#include <QtMultimedia/qabstractvideobuffer.h>
+
+#include <QtMultimedia/qvideosurfaceformat.h>
+
+QT_USE_NAMESPACE
+
+class CVImageVideoBuffer : public QAbstractVideoBuffer
+{
+public:
+ CVImageVideoBuffer(CVImageBufferRef buffer, AVFCameraRendererControl *renderer)
+#ifndef Q_OS_IOS
+ : QAbstractVideoBuffer(NoHandle)
+#else
+ : QAbstractVideoBuffer(renderer->supportsTextures()
+ && CVPixelBufferGetPixelFormatType(buffer) == kCVPixelFormatType_32BGRA
+ ? GLTextureHandle : NoHandle)
+ , m_texture(nullptr)
+ , m_renderer(renderer)
+#endif
+ , m_buffer(buffer)
+ , m_mode(NotMapped)
+ {
+#ifndef Q_OS_IOS
+ Q_UNUSED(renderer);
+#endif // Q_OS_IOS
+ CVPixelBufferRetain(m_buffer);
+ }
+
+ ~CVImageVideoBuffer()
+ {
+ CVImageVideoBuffer::unmap();
+#ifdef Q_OS_IOS
+ if (m_texture)
+ CFRelease(m_texture);
+#endif
+ CVPixelBufferRelease(m_buffer);
+ }
+
+ MapMode mapMode() const { return m_mode; }
+
+ MapData map(QAbstractVideoBuffer::MapMode mode)
+ {
+ MapData mapData;
+
+ // We only support RGBA or NV12 (or Apple's version of NV12),
+ // they are either 0 planes or 2.
+ mapData.nPlanes = CVPixelBufferGetPlaneCount(m_buffer);
+ Q_ASSERT(mapData.nPlanes <= 2);
+
+ if (!mapData.nPlanes) {
+ mapData.data[0] = map(mode, &mapData.nBytes, &mapData.bytesPerLine[0]);
+ mapData.nPlanes = mapData.data[0] ? 1 : 0;
+ return mapData;
+ }
+
+ // For a bi-planar format we have to set the parameters correctly:
+ if (mode != QAbstractVideoBuffer::NotMapped && m_mode == QAbstractVideoBuffer::NotMapped) {
+ CVPixelBufferLockBaseAddress(m_buffer, mode == QAbstractVideoBuffer::ReadOnly
+ ? kCVPixelBufferLock_ReadOnly
+ : 0);
+
+ mapData.nBytes = CVPixelBufferGetDataSize(m_buffer);
+
+ // At the moment we handle only bi-planar format.
+ mapData.bytesPerLine[0] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, 0);
+ mapData.bytesPerLine[1] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, 1);
+
+ mapData.data[0] = static_cast<uchar*>(CVPixelBufferGetBaseAddressOfPlane(m_buffer, 0));
+ mapData.data[1] = static_cast<uchar*>(CVPixelBufferGetBaseAddressOfPlane(m_buffer, 1));
+
+ m_mode = mode;
+ }
+
+ return mapData;
+ }
+
+ uchar *map(MapMode mode, qsizetype *numBytes, int *bytesPerLine)
+ {
+ if (mode != NotMapped && m_mode == NotMapped) {
+ CVPixelBufferLockBaseAddress(m_buffer, mode == QAbstractVideoBuffer::ReadOnly
+ ? kCVPixelBufferLock_ReadOnly
+ : 0);
+ if (numBytes)
+ *numBytes = CVPixelBufferGetDataSize(m_buffer);
+
+ if (bytesPerLine)
+ *bytesPerLine = CVPixelBufferGetBytesPerRow(m_buffer);
+
+ m_mode = mode;
+ return static_cast<uchar*>(CVPixelBufferGetBaseAddress(m_buffer));
+ } else {
+ return nullptr;
+ }
+ }
+
+ void unmap()
+ {
+ if (m_mode != NotMapped) {
+ CVPixelBufferUnlockBaseAddress(m_buffer, m_mode == QAbstractVideoBuffer::ReadOnly
+ ? kCVPixelBufferLock_ReadOnly
+ : 0);
+ m_mode = NotMapped;
+ }
+ }
+
+ QVariant handle() const
+ {
+#ifdef Q_OS_IOS
+ // Called from the render thread, so there is a current OpenGL context
+
+ if (!m_renderer->m_textureCache) {
+ CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault,
+ nullptr,
+ [EAGLContext currentContext],
+ nullptr,
+ &m_renderer->m_textureCache);
+
+ if (err != kCVReturnSuccess)
+ qWarning("Error creating texture cache");
+ }
+
+ if (m_renderer->m_textureCache && !m_texture) {
+ CVOpenGLESTextureCacheFlush(m_renderer->m_textureCache, 0);
+
+ CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
+ m_renderer->m_textureCache,
+ m_buffer,
+ nullptr,
+ GL_TEXTURE_2D,
+ GL_RGBA,
+ CVPixelBufferGetWidth(m_buffer),
+ CVPixelBufferGetHeight(m_buffer),
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ 0,
+ &m_texture);
+ if (err != kCVReturnSuccess)
+ qWarning("Error creating texture from buffer");
+ }
+
+ if (m_texture)
+ return CVOpenGLESTextureGetName(m_texture);
+ else
+ return 0;
+#else
+ return QVariant();
+#endif
+ }
+
+private:
+#ifdef Q_OS_IOS
+ mutable CVOpenGLESTextureRef m_texture;
+ AVFCameraRendererControl *m_renderer;
+#endif
+ CVImageBufferRef m_buffer;
+ MapMode m_mode;
+};
+
+
+@interface AVFCaptureFramesDelegate : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
+
+- (AVFCaptureFramesDelegate *) initWithRenderer:(AVFCameraRendererControl*)renderer;
+
+- (void) captureOutput:(AVCaptureOutput *)captureOutput
+ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
+ fromConnection:(AVCaptureConnection *)connection;
+
+@end
+
+@implementation AVFCaptureFramesDelegate
+{
+@private
+ AVFCameraRendererControl *m_renderer;
+}
+
+- (AVFCaptureFramesDelegate *) initWithRenderer:(AVFCameraRendererControl*)renderer
+{
+ if (!(self = [super init]))
+ return nil;
+
+ self->m_renderer = renderer;
+ return self;
+}
+
+- (void)captureOutput:(AVCaptureOutput *)captureOutput
+ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
+ fromConnection:(AVCaptureConnection *)connection
+{
+ Q_UNUSED(connection);
+ Q_UNUSED(captureOutput);
+
+ // NB: on iOS captureOutput/connection can be nil (when recording a video -
+ // avfmediaassetwriter).
+
+ CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
+
+ int width = CVPixelBufferGetWidth(imageBuffer);
+ int height = CVPixelBufferGetHeight(imageBuffer);
+ QVideoFrame::PixelFormat format =
+ AVFCameraControl::QtPixelFormatFromCVFormat(CVPixelBufferGetPixelFormatType(imageBuffer));
+ if (format == QVideoFrame::Format_Invalid)
+ return;
+
+ QVideoFrame frame(new CVImageVideoBuffer(imageBuffer, m_renderer),
+ QSize(width, height),
+ format);
+
+ m_renderer->syncHandleViewfinderFrame(frame);
+}
+
+@end
+
+
+AVFCameraRendererControl::AVFCameraRendererControl(QObject *parent)
+ : QVideoRendererControl(parent)
+ , m_surface(nullptr)
+ , m_supportsTextures(false)
+ , m_needsHorizontalMirroring(false)
+#ifdef Q_OS_IOS
+ , m_textureCache(nullptr)
+#endif
+{
+ m_viewfinderFramesDelegate = [[AVFCaptureFramesDelegate alloc] initWithRenderer:this];
+}
+
+AVFCameraRendererControl::~AVFCameraRendererControl()
+{
+ [m_cameraSession->captureSession() removeOutput:m_videoDataOutput];
+ [m_viewfinderFramesDelegate release];
+ if (m_delegateQueue)
+ dispatch_release(m_delegateQueue);
+#ifdef Q_OS_IOS
+ if (m_textureCache)
+ CFRelease(m_textureCache);
+#endif
+}
+
+QAbstractVideoSurface *AVFCameraRendererControl::surface() const
+{
+ return m_surface;
+}
+
+void AVFCameraRendererControl::setSurface(QAbstractVideoSurface *surface)
+{
+ if (m_surface != surface) {
+ m_surface = surface;
+ m_supportsTextures = m_surface
+ ? !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty()
+ : false;
+ Q_EMIT surfaceChanged(surface);
+ }
+}
+
+void AVFCameraRendererControl::configureAVCaptureSession(AVFCameraSession *cameraSession)
+{
+ m_cameraSession = cameraSession;
+ connect(m_cameraSession, SIGNAL(readyToConfigureConnections()),
+ this, SLOT(updateCaptureConnection()));
+
+ m_needsHorizontalMirroring = false;
+
+ m_videoDataOutput = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
+
+ // Configure video output
+ m_delegateQueue = dispatch_queue_create("vf_queue", nullptr);
+ [m_videoDataOutput
+ setSampleBufferDelegate:m_viewfinderFramesDelegate
+ queue:m_delegateQueue];
+
+ [m_cameraSession->captureSession() addOutput:m_videoDataOutput];
+}
+
+void AVFCameraRendererControl::updateCaptureConnection()
+{
+ AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
+ if (connection == nil || !m_cameraSession->videoCaptureDevice())
+ return;
+
+ // Frames of front-facing cameras should be mirrored horizontally (it's the default when using
+ // AVCaptureVideoPreviewLayer but not with AVCaptureVideoDataOutput)
+ if (connection.isVideoMirroringSupported)
+ connection.videoMirrored = m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront;
+
+ // If the connection does't support mirroring, we'll have to do it ourselves
+ m_needsHorizontalMirroring = !connection.isVideoMirrored
+ && m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront;
+}
+
+//can be called from non main thread
+void AVFCameraRendererControl::syncHandleViewfinderFrame(const QVideoFrame &frame)
+{
+ QMutexLocker lock(&m_vfMutex);
+ if (!m_lastViewfinderFrame.isValid()) {
+ static QMetaMethod handleViewfinderFrameSlot = metaObject()->method(
+ metaObject()->indexOfMethod("handleViewfinderFrame()"));
+
+ handleViewfinderFrameSlot.invoke(this, Qt::QueuedConnection);
+ }
+
+ m_lastViewfinderFrame = frame;
+
+ if (m_cameraSession && m_lastViewfinderFrame.isValid())
+ m_cameraSession->onCameraFrameFetched(m_lastViewfinderFrame);
+}
+
+AVCaptureVideoDataOutput *AVFCameraRendererControl::videoDataOutput() const
+{
+ return m_videoDataOutput;
+}
+
+#ifdef Q_OS_IOS
+
+AVFCaptureFramesDelegate *AVFCameraRendererControl::captureDelegate() const
+{
+ return m_viewfinderFramesDelegate;
+}
+
+void AVFCameraRendererControl::resetCaptureDelegate() const
+{
+ [m_videoDataOutput setSampleBufferDelegate:m_viewfinderFramesDelegate queue:m_delegateQueue];
+}
+
+#endif
+
+void AVFCameraRendererControl::handleViewfinderFrame()
+{
+ QVideoFrame frame;
+ {
+ QMutexLocker lock(&m_vfMutex);
+ frame = m_lastViewfinderFrame;
+ m_lastViewfinderFrame = QVideoFrame();
+ }
+
+ if (m_surface && frame.isValid()) {
+ if (m_surface->isActive() && (m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat()
+ || m_surface->surfaceFormat().frameSize() != frame.size())) {
+ m_surface->stop();
+ }
+
+ if (!m_surface->isActive()) {
+ QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), frame.handleType());
+ if (m_needsHorizontalMirroring)
+ format.setProperty("mirrored", true);
+
+ if (!m_surface->start(format)) {
+ qWarning() << "Failed to start viewfinder m_surface, format:" << format;
+ } else {
+ qDebugCamera() << "Viewfinder started: " << format;
+ }
+ }
+
+ if (m_surface->isActive())
+ m_surface->present(frame);
+ }
+}
+
+
+#include "moc_avfcamerarenderercontrol_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol_p.h
new file mode 100644
index 000000000..ade916ed6
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol_p.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFCAMERARENDERERCONTROL_H
+#define AVFCAMERARENDERERCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtMultimedia/qvideorenderercontrol.h>
+#include <QtMultimedia/qvideoframe.h>
+#include <QtCore/qmutex.h>
+
+#import <AVFoundation/AVFoundation.h>
+
+@class AVFCaptureFramesDelegate;
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraSession;
+class AVFCameraService;
+class AVFCameraRendererControl;
+
+class AVFCameraRendererControl : public QVideoRendererControl
+{
+Q_OBJECT
+public:
+ AVFCameraRendererControl(QObject *parent = nullptr);
+ ~AVFCameraRendererControl();
+
+ QAbstractVideoSurface *surface() const override;
+ void setSurface(QAbstractVideoSurface *surface) override;
+
+ void configureAVCaptureSession(AVFCameraSession *cameraSession);
+ void syncHandleViewfinderFrame(const QVideoFrame &frame);
+
+ AVCaptureVideoDataOutput *videoDataOutput() const;
+
+ bool supportsTextures() const { return m_supportsTextures; }
+
+#ifdef Q_OS_IOS
+ AVFCaptureFramesDelegate *captureDelegate() const;
+ void resetCaptureDelegate() const;
+#endif
+
+Q_SIGNALS:
+ void surfaceChanged(QAbstractVideoSurface *surface);
+
+private Q_SLOTS:
+ void handleViewfinderFrame();
+ void updateCaptureConnection();
+
+private:
+ QAbstractVideoSurface *m_surface;
+ AVFCaptureFramesDelegate *m_viewfinderFramesDelegate;
+ AVFCameraSession *m_cameraSession;
+ AVCaptureVideoDataOutput *m_videoDataOutput;
+
+ bool m_supportsTextures;
+ bool m_needsHorizontalMirroring;
+
+#ifdef Q_OS_IOS
+ CVOpenGLESTextureCacheRef m_textureCache;
+#endif
+
+ QVideoFrame m_lastViewfinderFrame;
+ QMutex m_vfMutex;
+ dispatch_queue_t m_delegateQueue;
+
+ friend class CVImageVideoBuffer;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameraservice.mm b/src/multimedia/platform/avfoundation/camera/avfcameraservice.mm
new file mode 100644
index 000000000..9fcab0ead
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameraservice.mm
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include <QtCore/qvariant.h>
+#include <QtCore/qdebug.h>
+
+#include "avfcameraservice_p.h"
+#include "avfcameracontrol_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameradevicecontrol_p.h"
+#include "avfaudioinputselectorcontrol_p.h"
+#include "avfcamerametadatacontrol_p.h"
+#include "avfmediarecordercontrol_p.h"
+#include "avfimagecapturecontrol_p.h"
+#include "avfcamerarenderercontrol_p.h"
+#include "avfmediarecordercontrol_p.h"
+#include "avfimagecapturecontrol_p.h"
+#include "avfmediavideoprobecontrol_p.h"
+#include "avfcamerafocuscontrol_p.h"
+#include "avfcameraexposurecontrol_p.h"
+#include "avfimageencodercontrol_p.h"
+#include "avfaudioencodersettingscontrol_p.h"
+#include "avfvideoencodersettingscontrol_p.h"
+#include "avfmediacontainercontrol_p.h"
+#include "avfcamerawindowcontrol_p.h"
+
+#ifdef Q_OS_IOS
+#include "avfmediarecordercontrol_ios_p.h"
+#endif
+
+QT_USE_NAMESPACE
+
+AVFCameraService::AVFCameraService(QObject *parent):
+ QMediaService(parent),
+ m_videoOutput(nullptr),
+ m_captureWindowControl(nullptr)
+{
+ m_session = new AVFCameraSession(this);
+ m_cameraControl = new AVFCameraControl(this);
+ m_videoDeviceControl = new AVFCameraDeviceControl(this);
+ m_audioInputSelectorControl = new AVFAudioInputSelectorControl(this);
+
+ m_metaDataControl = new AVFCameraMetaDataControl(this);
+#ifndef Q_OS_IOS
+ // This will connect a slot to 'captureModeChanged'
+ // and will break viewfinder by attaching AVCaptureMovieFileOutput
+ // in this slot.
+ m_recorderControl = new AVFMediaRecorderControl(this);
+#else
+ m_recorderControl = new AVFMediaRecorderControlIOS(this);
+#endif
+ m_imageCaptureControl = new AVFImageCaptureControl(this);
+ m_cameraFocusControl = new AVFCameraFocusControl(this);
+ m_cameraExposureControl = nullptr;
+#ifdef Q_OS_IOS
+ m_cameraExposureControl = new AVFCameraExposureControl(this);
+#endif
+
+ m_imageEncoderControl = new AVFImageEncoderControl(this);
+ m_audioEncoderSettingsControl = new AVFAudioEncoderSettingsControl(this);
+ m_videoEncoderSettingsControl = new AVFVideoEncoderSettingsControl(this);
+ m_mediaContainerControl = new AVFMediaContainerControl(this);
+}
+
+AVFCameraService::~AVFCameraService()
+{
+ m_cameraControl->setState(QCamera::UnloadedState);
+
+#ifdef Q_OS_IOS
+ delete m_recorderControl;
+#endif
+
+ if (m_captureWindowControl) {
+ m_session->setCapturePreviewOutput(nullptr);
+ delete m_captureWindowControl;
+ m_captureWindowControl = nullptr;
+ }
+
+ if (m_videoOutput) {
+ m_session->setVideoOutput(nullptr);
+ delete m_videoOutput;
+ m_videoOutput = nullptr;
+ }
+
+ //delete controls before session,
+ //so they have a chance to do deinitialization
+ delete m_imageCaptureControl;
+ //delete m_recorderControl;
+ delete m_metaDataControl;
+ delete m_cameraControl;
+ delete m_cameraFocusControl;
+ delete m_cameraExposureControl;
+ delete m_imageEncoderControl;
+ delete m_audioEncoderSettingsControl;
+ delete m_videoEncoderSettingsControl;
+ delete m_mediaContainerControl;
+
+ delete m_session;
+}
+
+QObject *AVFCameraService::requestControl(const char *name)
+{
+ if (qstrcmp(name, QCameraControl_iid) == 0)
+ return m_cameraControl;
+
+ if (qstrcmp(name, QVideoDeviceSelectorControl_iid) == 0)
+ return m_videoDeviceControl;
+
+ if (qstrcmp(name, QAudioInputSelectorControl_iid) == 0)
+ return m_audioInputSelectorControl;
+
+ //metadata support is not implemented yet
+ //if (qstrcmp(name, QMetaDataWriterControl_iid) == 0)
+ // return m_metaDataControl;
+
+ if (qstrcmp(name, QMediaRecorderControl_iid) == 0)
+ return m_recorderControl;
+
+ 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;
+
+ if (qstrcmp(name, QImageEncoderControl_iid) == 0)
+ return m_imageEncoderControl;
+
+ if (qstrcmp(name, QAudioEncoderSettingsControl_iid) == 0)
+ return m_audioEncoderSettingsControl;
+
+ if (qstrcmp(name, QVideoEncoderSettingsControl_iid) == 0)
+ return m_videoEncoderSettingsControl;
+
+ if (qstrcmp(name, QMediaContainerControl_iid) == 0)
+ return m_mediaContainerControl;
+
+ if (qstrcmp(name,QMediaVideoProbeControl_iid) == 0) {
+ AVFMediaVideoProbeControl *videoProbe = nullptr;
+ videoProbe = new AVFMediaVideoProbeControl(this);
+ m_session->addProbe(videoProbe);
+ return videoProbe;
+ }
+
+ if (!m_captureWindowControl) {
+ if (qstrcmp(name, QVideoWindowControl_iid) == 0) {
+ m_captureWindowControl = new AVFCameraWindowControl(this);
+ m_session->setCapturePreviewOutput(m_captureWindowControl);
+ return m_captureWindowControl;
+ }
+ }
+
+ if (!m_videoOutput) {
+ if (qstrcmp(name, QVideoRendererControl_iid) == 0)
+ m_videoOutput = new AVFCameraRendererControl(this);
+
+ if (m_videoOutput) {
+ m_session->setVideoOutput(m_videoOutput);
+ return m_videoOutput;
+ }
+ }
+
+ return nullptr;
+}
+
+void AVFCameraService::releaseControl(QObject *control)
+{
+ AVFMediaVideoProbeControl *videoProbe = qobject_cast<AVFMediaVideoProbeControl *>(control);
+ if (videoProbe) {
+ m_session->removeProbe(videoProbe);
+ delete videoProbe;
+ } else if (m_videoOutput == control) {
+ m_session->setVideoOutput(nullptr);
+ delete m_videoOutput;
+ m_videoOutput = nullptr;
+ }
+ else if (m_captureWindowControl == control) {
+ m_session->setCapturePreviewOutput(nullptr);
+ delete m_captureWindowControl;
+ m_captureWindowControl = nullptr;
+ }
+}
+
+
+#include "moc_avfcameraservice_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameraservice_p.h b/src/multimedia/platform/avfoundation/camera/avfcameraservice_p.h
new file mode 100644
index 000000000..9efe4e9cb
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameraservice_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFCAMERASERVICE_H
+#define AVFCAMERASERVICE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qobject.h>
+#include <QtCore/qset.h>
+#include <qmediaservice.h>
+
+
+QT_BEGIN_NAMESPACE
+class QCameraControl;
+class QMediaRecorderControl;
+class AVFCameraControl;
+class AVFCameraMetaDataControl;
+class AVFVideoWindowControl;
+class AVFVideoWidgetControl;
+class AVFCameraRendererControl;
+class AVFImageCaptureControl;
+class AVFCameraSession;
+class AVFCameraDeviceControl;
+class AVFAudioInputSelectorControl;
+class AVFCameraFocusControl;
+class AVFCameraExposureControl;
+class AVFImageEncoderControl;
+class AVFMediaRecorderControl;
+class AVFMediaRecorderControlIOS;
+class AVFAudioEncoderSettingsControl;
+class AVFVideoEncoderSettingsControl;
+class AVFMediaContainerControl;
+class AVFCameraWindowControl;
+
+class AVFCameraService : public QMediaService
+{
+Q_OBJECT
+public:
+ AVFCameraService(QObject *parent = nullptr);
+ ~AVFCameraService();
+
+ QObject *requestControl(const char *name);
+ void releaseControl(QObject *control);
+
+ AVFCameraSession *session() const { return m_session; }
+ AVFCameraControl *cameraControl() const { return m_cameraControl; }
+ AVFCameraDeviceControl *videoDeviceControl() const { return m_videoDeviceControl; }
+ AVFAudioInputSelectorControl *audioInputSelectorControl() const { return m_audioInputSelectorControl; }
+ AVFCameraMetaDataControl *metaDataControl() const { return m_metaDataControl; }
+ QMediaRecorderControl *recorderControl() const { return m_recorderControl; }
+ AVFImageCaptureControl *imageCaptureControl() const { return m_imageCaptureControl; }
+ AVFCameraFocusControl *cameraFocusControl() const { return m_cameraFocusControl; }
+ AVFCameraExposureControl *cameraExposureControl() const {return m_cameraExposureControl; }
+ AVFCameraRendererControl *videoOutput() const {return m_videoOutput; }
+ AVFImageEncoderControl *imageEncoderControl() const {return m_imageEncoderControl; }
+ AVFAudioEncoderSettingsControl *audioEncoderSettingsControl() const { return m_audioEncoderSettingsControl; }
+ AVFVideoEncoderSettingsControl *videoEncoderSettingsControl() const {return m_videoEncoderSettingsControl; }
+ AVFMediaContainerControl *mediaContainerControl() const { return m_mediaContainerControl; }
+
+private:
+ AVFCameraSession *m_session;
+ AVFCameraControl *m_cameraControl;
+ AVFCameraDeviceControl *m_videoDeviceControl;
+ AVFAudioInputSelectorControl *m_audioInputSelectorControl;
+ AVFCameraRendererControl *m_videoOutput;
+ AVFCameraMetaDataControl *m_metaDataControl;
+ QMediaRecorderControl *m_recorderControl;
+ AVFImageCaptureControl *m_imageCaptureControl;
+ AVFCameraFocusControl *m_cameraFocusControl;
+ AVFCameraExposureControl *m_cameraExposureControl;
+ AVFImageEncoderControl *m_imageEncoderControl;
+ AVFAudioEncoderSettingsControl *m_audioEncoderSettingsControl;
+ AVFVideoEncoderSettingsControl *m_videoEncoderSettingsControl;
+ AVFMediaContainerControl *m_mediaContainerControl;
+ AVFCameraWindowControl *m_captureWindowControl;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin.mm b/src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin.mm
new file mode 100644
index 000000000..2fd8d3fad
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin.mm
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include <QtCore/qstring.h>
+#include <QtCore/qdebug.h>
+
+#include "avfcameraserviceplugin_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcamerasession_p.h"
+
+#include <qmediaserviceproviderplugin.h>
+
+QT_BEGIN_NAMESPACE
+
+AVFServicePlugin::AVFServicePlugin()
+{
+}
+
+QMediaService* AVFServicePlugin::create(QString const& key)
+{
+ if (key == QLatin1String(Q_MEDIASERVICE_CAMERA))
+ return new AVFCameraService;
+ else
+ qWarning() << "unsupported key:" << key;
+
+ return nullptr;
+}
+
+void AVFServicePlugin::release(QMediaService *service)
+{
+ delete service;
+}
+
+QByteArray AVFServicePlugin::defaultDevice(const QByteArray &service) const
+{
+ if (service == Q_MEDIASERVICE_CAMERA) {
+ int i = AVFCameraSession::defaultCameraIndex();
+ if (i != -1)
+ return AVFCameraSession::availableCameraDevices().at(i).deviceId;
+ }
+
+ return QByteArray();
+}
+
+QList<QByteArray> AVFServicePlugin::devices(const QByteArray &service) const
+{
+ QList<QByteArray> devs;
+
+ if (service == Q_MEDIASERVICE_CAMERA) {
+ const QList<AVFCameraInfo> &cameras = AVFCameraSession::availableCameraDevices();
+ devs.reserve(cameras.size());
+ for (const AVFCameraInfo &info : cameras)
+ devs.append(info.deviceId);
+ }
+
+ return devs;
+}
+
+QString AVFServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device)
+{
+ if (service == Q_MEDIASERVICE_CAMERA)
+ return AVFCameraSession::cameraDeviceInfo(device).description;
+
+ return QString();
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin_p.h b/src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin_p.h
new file mode 100644
index 000000000..e9028542c
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+
+#ifndef AVFSERVICEPLUGIN_H
+#define AVFSERVICEPLUGIN_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmediaserviceproviderplugin.h>
+#include <QtCore/qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFServicePlugin : public QMediaServiceProviderPlugin,
+ public QMediaServiceSupportedDevicesInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
+
+public:
+ AVFServicePlugin();
+
+ QMediaService* create(QString const &key) override;
+ void release(QMediaService *service) override;
+
+ QByteArray defaultDevice(const QByteArray &service) const override;
+ QList<QByteArray> devices(const QByteArray &service) const override;
+ QString deviceDescription(const QByteArray &service, const QByteArray &device) override;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerasession.mm b/src/multimedia/platform/avfoundation/camera/avfcamerasession.mm
new file mode 100644
index 000000000..914ae6907
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerasession.mm
@@ -0,0 +1,491 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "avfcameradebug_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcameracontrol_p.h"
+#include "avfcamerarenderercontrol_p.h"
+#include "avfcameradevicecontrol_p.h"
+#include "avfaudioinputselectorcontrol_p.h"
+#include "avfmediavideoprobecontrol_p.h"
+#include "avfimageencodercontrol_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcamerawindowcontrol_p.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Foundation/Foundation.h>
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qelapsedtimer.h>
+
+#include <QtCore/qdebug.h>
+
+QT_USE_NAMESPACE
+
+int AVFCameraSession::m_defaultCameraIndex;
+QList<AVFCameraInfo> AVFCameraSession::m_cameraDevices;
+
+@interface AVFCameraSessionObserver : NSObject
+
+- (AVFCameraSessionObserver *) initWithCameraSession:(AVFCameraSession*)session;
+- (void) processRuntimeError:(NSNotification *)notification;
+- (void) processSessionStarted:(NSNotification *)notification;
+- (void) processSessionStopped:(NSNotification *)notification;
+
+@end
+
+@implementation AVFCameraSessionObserver
+{
+@private
+ AVFCameraSession *m_session;
+ AVCaptureSession *m_captureSession;
+}
+
+- (AVFCameraSessionObserver *) initWithCameraSession:(AVFCameraSession*)session
+{
+ if (!(self = [super init]))
+ return nil;
+
+ self->m_session = session;
+ self->m_captureSession = session->captureSession();
+
+ [m_captureSession retain];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(processRuntimeError:)
+ name:AVCaptureSessionRuntimeErrorNotification
+ object:m_captureSession];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(processSessionStarted:)
+ name:AVCaptureSessionDidStartRunningNotification
+ object:m_captureSession];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(processSessionStopped:)
+ name:AVCaptureSessionDidStopRunningNotification
+ object:m_captureSession];
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:AVCaptureSessionRuntimeErrorNotification
+ object:m_captureSession];
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:AVCaptureSessionDidStartRunningNotification
+ object:m_captureSession];
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:AVCaptureSessionDidStopRunningNotification
+ object:m_captureSession];
+ [m_captureSession release];
+ [super dealloc];
+}
+
+- (void) processRuntimeError:(NSNotification *)notification
+{
+ Q_UNUSED(notification);
+ QMetaObject::invokeMethod(m_session, "processRuntimeError", Qt::AutoConnection);
+}
+
+- (void) processSessionStarted:(NSNotification *)notification
+{
+ Q_UNUSED(notification);
+ QMetaObject::invokeMethod(m_session, "processSessionStarted", Qt::AutoConnection);
+}
+
+- (void) processSessionStopped:(NSNotification *)notification
+{
+ Q_UNUSED(notification);
+ QMetaObject::invokeMethod(m_session, "processSessionStopped", Qt::AutoConnection);
+}
+
+@end
+
+AVFCameraSession::AVFCameraSession(AVFCameraService *service, QObject *parent)
+ : QObject(parent)
+ , m_service(service)
+ , m_capturePreviewWindowOutput(nullptr)
+ , m_state(QCamera::UnloadedState)
+ , m_active(false)
+ , m_videoInput(nil)
+ , m_defaultCodec(0)
+{
+ m_captureSession = [[AVCaptureSession alloc] init];
+ m_observer = [[AVFCameraSessionObserver alloc] initWithCameraSession:this];
+
+ //configuration is commited during transition to Active state
+ [m_captureSession beginConfiguration];
+}
+
+AVFCameraSession::~AVFCameraSession()
+{
+ if (m_capturePreviewWindowOutput) {
+ m_capturePreviewWindowOutput->setLayer(nil);
+ }
+
+ if (m_videoInput) {
+ [m_captureSession removeInput:m_videoInput];
+ [m_videoInput release];
+ }
+
+ [m_observer release];
+ [m_captureSession release];
+}
+
+int AVFCameraSession::defaultCameraIndex()
+{
+ updateCameraDevices();
+ return m_defaultCameraIndex;
+}
+
+const QList<AVFCameraInfo> &AVFCameraSession::availableCameraDevices()
+{
+ updateCameraDevices();
+ return m_cameraDevices;
+}
+
+AVFCameraInfo AVFCameraSession::cameraDeviceInfo(const QByteArray &device)
+{
+ updateCameraDevices();
+
+ for (const AVFCameraInfo &info : qAsConst(m_cameraDevices)) {
+ if (info.deviceId == device)
+ return info;
+ }
+
+ return AVFCameraInfo();
+}
+
+void AVFCameraSession::updateCameraDevices()
+{
+#ifdef Q_OS_IOS
+ // Cameras can't change dynamically on iOS. Update only once.
+ if (!m_cameraDevices.isEmpty())
+ return;
+#else
+ // On OS X, cameras can be added or removed. Update the list every time, but not more than
+ // once every 500 ms
+ static QElapsedTimer timer;
+ if (timer.isValid() && timer.elapsed() < 500) // ms
+ return;
+#endif
+
+ m_defaultCameraIndex = -1;
+ m_cameraDevices.clear();
+
+ AVCaptureDevice *defaultDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
+ NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
+ for (AVCaptureDevice *device in videoDevices) {
+ if (defaultDevice && [defaultDevice.uniqueID isEqualToString:device.uniqueID])
+ m_defaultCameraIndex = m_cameraDevices.count();
+
+ AVFCameraInfo info;
+ info.deviceId = QByteArray([[device uniqueID] UTF8String]);
+ info.description = QString::fromNSString([device localizedName]);
+
+ // There is no API to get the camera sensor orientation, however, cameras are always
+ // mounted in landscape on iDevices.
+ // - Back-facing cameras have the top side of the sensor aligned with the right side of
+ // the screen when held in portrait ==> 270 degrees clockwise angle
+ // - Front-facing cameras have the top side of the sensor aligned with the left side of
+ // the screen when held in portrait ==> 270 degrees clockwise angle
+ // On OS X, the position will always be unspecified and the sensor orientation unknown.
+ switch (device.position) {
+ case AVCaptureDevicePositionBack:
+ info.position = QCamera::BackFace;
+ info.orientation = 270;
+ break;
+ case AVCaptureDevicePositionFront:
+ info.position = QCamera::FrontFace;
+ info.orientation = 270;
+ break;
+ default:
+ info.position = QCamera::UnspecifiedPosition;
+ info.orientation = 0;
+ break;
+ }
+
+ m_cameraDevices.append(info);
+ }
+
+#ifndef Q_OS_IOS
+ timer.restart();
+#endif
+}
+
+void AVFCameraSession::setVideoOutput(AVFCameraRendererControl *output)
+{
+ m_videoOutput = output;
+ if (output)
+ output->configureAVCaptureSession(this);
+}
+
+void AVFCameraSession::setCapturePreviewOutput(AVFCameraWindowControl *output)
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO << output;
+#endif
+
+ if (m_capturePreviewWindowOutput == output)
+ return;
+
+ if (m_capturePreviewWindowOutput)
+ m_capturePreviewWindowOutput->setLayer(nil);
+
+ m_capturePreviewWindowOutput = output;
+
+ if (m_capturePreviewWindowOutput) {
+ AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:m_captureSession];
+ m_capturePreviewWindowOutput->setLayer(previewLayer);
+ if (auto *camera = m_service->cameraControl()) {
+ m_capturePreviewWindowOutput->setNativeSize(camera->viewfinderSettings().resolution());
+ }
+ }
+}
+
+AVCaptureDevice *AVFCameraSession::videoCaptureDevice() const
+{
+ if (m_videoInput)
+ return m_videoInput.device;
+
+ return nullptr;
+}
+
+QCamera::State AVFCameraSession::state() const
+{
+ if (m_active)
+ return QCamera::ActiveState;
+
+ return m_state == QCamera::ActiveState ? QCamera::LoadedState : m_state;
+}
+
+void AVFCameraSession::setState(QCamera::State newState)
+{
+ if (m_state == newState)
+ return;
+
+ qDebugCamera() << Q_FUNC_INFO << m_state << " -> " << newState;
+
+ QCamera::State oldState = m_state;
+ m_state = newState;
+
+ //attach video input during Unloaded->Loaded transition
+ if (oldState == QCamera::UnloadedState)
+ attachVideoInputDevice();
+
+ if (m_state == QCamera::ActiveState) {
+ Q_EMIT readyToConfigureConnections();
+ m_defaultCodec = 0;
+ defaultCodec();
+
+ bool activeFormatSet = applyImageEncoderSettings()
+ | applyViewfinderSettings();
+
+ [m_captureSession commitConfiguration];
+
+ if (activeFormatSet) {
+ // According to the doc, the capture device must be locked before
+ // startRunning to prevent the format we set to be overriden by the
+ // session preset.
+ [videoCaptureDevice() lockForConfiguration:nil];
+ }
+
+ [m_captureSession startRunning];
+
+ if (activeFormatSet)
+ [videoCaptureDevice() unlockForConfiguration];
+ }
+
+ if (oldState == QCamera::ActiveState) {
+ [m_captureSession stopRunning];
+ [m_captureSession beginConfiguration];
+ }
+
+ Q_EMIT stateChanged(m_state);
+}
+
+void AVFCameraSession::processRuntimeError()
+{
+ qWarning() << tr("Runtime camera error");
+ Q_EMIT error(QCamera::CameraError, tr("Runtime camera error"));
+}
+
+void AVFCameraSession::processSessionStarted()
+{
+ qDebugCamera() << Q_FUNC_INFO;
+ if (!m_active) {
+ m_active = true;
+ Q_EMIT activeChanged(m_active);
+ Q_EMIT stateChanged(state());
+ }
+}
+
+void AVFCameraSession::processSessionStopped()
+{
+ qDebugCamera() << Q_FUNC_INFO;
+ if (m_active) {
+ m_active = false;
+ Q_EMIT activeChanged(m_active);
+ Q_EMIT stateChanged(state());
+ }
+}
+
+void AVFCameraSession::onCaptureModeChanged(QCamera::CaptureModes mode)
+{
+ Q_UNUSED(mode);
+
+ const QCamera::State s = state();
+ if (s == QCamera::LoadedState || s == QCamera::ActiveState) {
+ applyImageEncoderSettings();
+ applyViewfinderSettings();
+ }
+}
+
+void AVFCameraSession::attachVideoInputDevice()
+{
+ //Attach video input device:
+ if (m_service->videoDeviceControl()->isDirty()) {
+ if (m_videoInput) {
+ [m_captureSession removeInput:m_videoInput];
+ [m_videoInput release];
+ m_videoInput = nullptr;
+ m_activeCameraInfo = AVFCameraInfo();
+ }
+
+ AVCaptureDevice *videoDevice = m_service->videoDeviceControl()->createCaptureDevice();
+
+ NSError *error = nil;
+ m_videoInput = [AVCaptureDeviceInput
+ deviceInputWithDevice:videoDevice
+ error:&error];
+
+ if (!m_videoInput) {
+ qWarning() << "Failed to create video device input";
+ } else {
+ if ([m_captureSession canAddInput:m_videoInput]) {
+ m_activeCameraInfo = m_cameraDevices.at(m_service->videoDeviceControl()->selectedDevice());
+ [m_videoInput retain];
+ [m_captureSession addInput:m_videoInput];
+ } else {
+ qWarning() << "Failed to connect video device input";
+ }
+ }
+ }
+}
+
+bool AVFCameraSession::applyImageEncoderSettings()
+{
+ if (AVFImageEncoderControl *control = m_service->imageEncoderControl())
+ return control->applySettings();
+
+ return false;
+}
+
+bool AVFCameraSession::applyViewfinderSettings()
+{
+ if (auto *camera = m_service->cameraControl()) {
+ QCamera::CaptureModes currentMode = m_service->cameraControl()->captureMode();
+ QCameraViewfinderSettings vfSettings(camera->requestedSettings());
+ // Viewfinder and image capture solutions must be the same, if an image capture
+ // resolution is set, it takes precedence over the viewfinder resolution.
+ if (currentMode.testFlag(QCamera::CaptureStillImage)) {
+ const QSize imageResolution(m_service->imageEncoderControl()->requestedSettings().resolution());
+ if (!imageResolution.isNull() && imageResolution.isValid())
+ vfSettings.setResolution(imageResolution);
+ }
+
+ camera->applySettings(vfSettings);
+
+ if (m_capturePreviewWindowOutput)
+ m_capturePreviewWindowOutput->setNativeSize(camera->viewfinderSettings().resolution());
+
+ return !vfSettings.isNull();
+ }
+
+ return false;
+}
+
+void AVFCameraSession::addProbe(AVFMediaVideoProbeControl *probe)
+{
+ m_videoProbesMutex.lock();
+ if (probe)
+ m_videoProbes << probe;
+ m_videoProbesMutex.unlock();
+}
+
+void AVFCameraSession::removeProbe(AVFMediaVideoProbeControl *probe)
+{
+ m_videoProbesMutex.lock();
+ m_videoProbes.remove(probe);
+ m_videoProbesMutex.unlock();
+}
+
+FourCharCode AVFCameraSession::defaultCodec()
+{
+ if (!m_defaultCodec) {
+ if (AVCaptureDevice *device = videoCaptureDevice()) {
+ AVCaptureDeviceFormat *format = device.activeFormat;
+ if (!format || !format.formatDescription)
+ return m_defaultCodec;
+ m_defaultCodec = CMVideoFormatDescriptionGetCodecType(format.formatDescription);
+ }
+ }
+ return m_defaultCodec;
+}
+
+void AVFCameraSession::onCameraFrameFetched(const QVideoFrame &frame)
+{
+ Q_EMIT newViewfinderFrame(frame);
+
+ m_videoProbesMutex.lock();
+ QSet<AVFMediaVideoProbeControl *>::const_iterator i = m_videoProbes.constBegin();
+ while (i != m_videoProbes.constEnd()) {
+ (*i)->newFrameProbed(frame);
+ ++i;
+ }
+ m_videoProbesMutex.unlock();
+}
+
+#include "moc_avfcamerasession_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerasession_p.h b/src/multimedia/platform/avfoundation/camera/avfcamerasession_p.h
new file mode 100644
index 000000000..19f7945a3
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerasession_p.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFCAMERASESSION_H
+#define AVFCAMERASESSION_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qmutex.h>
+#include <QtMultimedia/qcamera.h>
+#include <QVideoFrame>
+
+#import <AVFoundation/AVFoundation.h>
+
+@class AVFCameraSessionObserver;
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraControl;
+class AVFCameraService;
+class AVFCameraRendererControl;
+class AVFMediaVideoProbeControl;
+class AVFCameraWindowControl;
+
+struct AVFCameraInfo
+{
+ AVFCameraInfo() : position(QCamera::UnspecifiedPosition), orientation(0)
+ { }
+
+ QByteArray deviceId;
+ QString description;
+ QCamera::Position position;
+ int orientation;
+};
+
+class AVFCameraSession : public QObject
+{
+ Q_OBJECT
+public:
+ AVFCameraSession(AVFCameraService *service, QObject *parent = nullptr);
+ ~AVFCameraSession();
+
+ static int defaultCameraIndex();
+ static const QList<AVFCameraInfo> &availableCameraDevices();
+ static AVFCameraInfo cameraDeviceInfo(const QByteArray &device);
+ AVFCameraInfo activeCameraInfo() const { return m_activeCameraInfo; }
+
+ void setVideoOutput(AVFCameraRendererControl *output);
+ void setCapturePreviewOutput(AVFCameraWindowControl *output);
+ AVCaptureSession *captureSession() const { return m_captureSession; }
+ AVCaptureDevice *videoCaptureDevice() const;
+
+ QCamera::State state() const;
+ QCamera::State requestedState() const { return m_state; }
+ bool isActive() const { return m_active; }
+
+ void addProbe(AVFMediaVideoProbeControl *probe);
+ void removeProbe(AVFMediaVideoProbeControl *probe);
+ FourCharCode defaultCodec();
+
+ AVCaptureDeviceInput *videoInput() const {return m_videoInput;}
+
+public Q_SLOTS:
+ void setState(QCamera::State state);
+
+ void processRuntimeError();
+ void processSessionStarted();
+ void processSessionStopped();
+
+ void onCaptureModeChanged(QCamera::CaptureModes mode);
+
+ void onCameraFrameFetched(const QVideoFrame &frame);
+
+Q_SIGNALS:
+ void readyToConfigureConnections();
+ void stateChanged(QCamera::State newState);
+ void activeChanged(bool);
+ void newViewfinderFrame(const QVideoFrame &frame);
+ void error(int error, const QString &errorString);
+
+private:
+ static void updateCameraDevices();
+ void attachVideoInputDevice();
+ bool applyImageEncoderSettings();
+ bool applyViewfinderSettings();
+
+ static int m_defaultCameraIndex;
+ static QList<AVFCameraInfo> m_cameraDevices;
+ AVFCameraInfo m_activeCameraInfo;
+
+ AVFCameraService *m_service;
+ AVFCameraRendererControl *m_videoOutput;
+ AVFCameraWindowControl *m_capturePreviewWindowOutput;
+
+ QCamera::State m_state;
+ bool m_active;
+
+ AVCaptureSession *m_captureSession;
+ AVCaptureDeviceInput *m_videoInput;
+ AVFCameraSessionObserver *m_observer;
+
+ QSet<AVFMediaVideoProbeControl *> m_videoProbes;
+ QMutex m_videoProbesMutex;
+
+ FourCharCode m_defaultCodec;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerautility.mm b/src/multimedia/platform/avfoundation/camera/avfcamerautility.mm
new file mode 100644
index 000000000..fe56517df
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerautility.mm
@@ -0,0 +1,575 @@
+/****************************************************************************
+**
+** 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 "avfcamerautility_p.h"
+#include "avfcameradebug_p.h"
+
+#include <QtCore/qvector.h>
+#include <QtCore/qpair.h>
+#include <private/qmultimediautils_p.h>
+
+#include <functional>
+#include <algorithm>
+#include <limits>
+#include <tuple>
+
+QT_BEGIN_NAMESPACE
+
+AVFPSRange qt_connection_framerates(AVCaptureConnection *videoConnection)
+{
+ Q_ASSERT(videoConnection);
+
+ AVFPSRange newRange;
+ // "The value in the videoMinFrameDuration is equivalent to the reciprocal
+ // of the maximum framerate, the value in the videoMaxFrameDuration is equivalent
+ // to the reciprocal of the minimum framerate."
+ if (videoConnection.supportsVideoMinFrameDuration) {
+ const CMTime cmMin = videoConnection.videoMinFrameDuration;
+ if (CMTimeCompare(cmMin, kCMTimeInvalid)) { // Has some non-default value:
+ if (const Float64 minSeconds = CMTimeGetSeconds(cmMin))
+ newRange.second = 1. / minSeconds;
+ }
+ }
+
+ if (videoConnection.supportsVideoMaxFrameDuration) {
+ const CMTime cmMax = videoConnection.videoMaxFrameDuration;
+ if (CMTimeCompare(cmMax, kCMTimeInvalid)) {
+ if (const Float64 maxSeconds = CMTimeGetSeconds(cmMax))
+ newRange.first = 1. / maxSeconds;
+ }
+ }
+
+ return newRange;
+}
+
+namespace {
+
+inline bool qt_area_sane(const QSize &size)
+{
+ return !size.isNull() && size.isValid()
+ && std::numeric_limits<int>::max() / size.width() >= size.height();
+}
+
+template <template <typename...> class Comp> // std::less or std::greater (or std::equal_to)
+struct ByResolution
+{
+ bool operator() (AVCaptureDeviceFormat *f1, AVCaptureDeviceFormat *f2)const
+ {
+ Q_ASSERT(f1 && f2);
+ const QSize r1(qt_device_format_resolution(f1));
+ const QSize r2(qt_device_format_resolution(f2));
+ // use std::tuple for lexicograpical sorting:
+ const Comp<std::tuple<int, int>> op = {};
+ return op(std::make_tuple(r1.width(), r1.height()),
+ std::make_tuple(r2.width(), r2.height()));
+ }
+};
+
+struct FormatHasNoFPSRange : std::unary_function<AVCaptureDeviceFormat *, bool>
+{
+ bool operator() (AVCaptureDeviceFormat *format)
+ {
+ Q_ASSERT(format);
+ return !format.videoSupportedFrameRateRanges || !format.videoSupportedFrameRateRanges.count;
+ }
+};
+
+Float64 qt_find_min_framerate_distance(AVCaptureDeviceFormat *format, Float64 fps)
+{
+ Q_ASSERT(format && format.videoSupportedFrameRateRanges
+ && format.videoSupportedFrameRateRanges.count);
+
+ AVFrameRateRange *range = [format.videoSupportedFrameRateRanges objectAtIndex:0];
+ Float64 distance = qAbs(range.maxFrameRate - fps);
+ for (NSUInteger i = 1, e = format.videoSupportedFrameRateRanges.count; i < e; ++i) {
+ range = [format.videoSupportedFrameRateRanges objectAtIndex:i];
+ distance = qMin(distance, qAbs(range.maxFrameRate - fps));
+ }
+
+ return distance;
+}
+
+} // Unnamed namespace.
+
+QVector<AVCaptureDeviceFormat *> qt_unique_device_formats(AVCaptureDevice *captureDevice, FourCharCode filter)
+{
+ // 'filter' is the format we prefer if we have duplicates.
+ Q_ASSERT(captureDevice);
+
+ QVector<AVCaptureDeviceFormat *> formats;
+
+ if (!captureDevice.formats || !captureDevice.formats.count)
+ return formats;
+
+ formats.reserve(captureDevice.formats.count);
+ for (AVCaptureDeviceFormat *format in captureDevice.formats) {
+ const QSize resolution(qt_device_format_resolution(format));
+ if (resolution.isNull() || !resolution.isValid())
+ continue;
+ formats << format;
+ }
+
+ if (!formats.size())
+ return formats;
+
+ std::sort(formats.begin(), formats.end(), ByResolution<std::less>());
+
+ QSize size(qt_device_format_resolution(formats[0]));
+ FourCharCode codec = CMVideoFormatDescriptionGetCodecType(formats[0].formatDescription);
+ int last = 0;
+ for (int i = 1; i < formats.size(); ++i) {
+ const QSize nextSize(qt_device_format_resolution(formats[i]));
+ if (nextSize == size) {
+ if (codec == filter)
+ continue;
+ formats[last] = formats[i];
+ } else {
+ ++last;
+ formats[last] = formats[i];
+ size = nextSize;
+ }
+ codec = CMVideoFormatDescriptionGetCodecType(formats[i].formatDescription);
+ }
+ formats.resize(last + 1);
+
+ return formats;
+}
+
+QSize qt_device_format_resolution(AVCaptureDeviceFormat *format)
+{
+ if (!format || !format.formatDescription)
+ return QSize();
+
+ const CMVideoDimensions res = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
+ return QSize(res.width, res.height);
+}
+
+QSize qt_device_format_high_resolution(AVCaptureDeviceFormat *format)
+{
+ Q_ASSERT(format);
+ QSize res;
+#if defined(Q_OS_IOS)
+ const CMVideoDimensions hrDim(format.highResolutionStillImageDimensions);
+ res.setWidth(hrDim.width);
+ res.setHeight(hrDim.height);
+#endif
+ return res;
+}
+
+QVector<AVFPSRange> qt_device_format_framerates(AVCaptureDeviceFormat *format)
+{
+ Q_ASSERT(format);
+
+ QVector<AVFPSRange> qtRanges;
+
+ if (!format.videoSupportedFrameRateRanges || !format.videoSupportedFrameRateRanges.count)
+ return qtRanges;
+
+ qtRanges.reserve(format.videoSupportedFrameRateRanges.count);
+ for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges)
+ qtRanges << AVFPSRange(range.minFrameRate, range.maxFrameRate);
+
+ return qtRanges;
+}
+
+QSize qt_device_format_pixel_aspect_ratio(AVCaptureDeviceFormat *format)
+{
+ Q_ASSERT(format);
+
+ if (!format.formatDescription) {
+ qDebugCamera() << Q_FUNC_INFO << "no format description found";
+ return QSize();
+ }
+
+ const CMVideoDimensions res = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
+ const CGSize resPAR = CMVideoFormatDescriptionGetPresentationDimensions(format.formatDescription, true, false);
+
+ if (qAbs(resPAR.width - res.width) < 1.) {
+ // "Pixel aspect ratio is used to adjust the width, leaving the height alone."
+ return QSize(1, 1);
+ }
+
+ if (!res.width || !resPAR.width)
+ return QSize();
+
+ int n, d;
+ qt_real_to_fraction(resPAR.width > res.width
+ ? res.width / qreal(resPAR.width)
+ : resPAR.width / qreal(res.width),
+ &n, &d);
+
+ return QSize(n, d);
+}
+
+AVCaptureDeviceFormat *qt_find_best_resolution_match(AVCaptureDevice *captureDevice,
+ const QSize &request,
+ FourCharCode filter,
+ bool stillImage)
+{
+ Q_ASSERT(captureDevice);
+ Q_ASSERT(!request.isNull() && request.isValid());
+
+ if (!captureDevice.formats || !captureDevice.formats.count)
+ return nullptr;
+
+ QVector<AVCaptureDeviceFormat *> formats(qt_unique_device_formats(captureDevice, filter));
+
+ for (int i = 0; i < formats.size(); ++i) {
+ AVCaptureDeviceFormat *format = formats[i];
+ if (qt_device_format_resolution(format) == request)
+ return format;
+ // iOS only (still images).
+ if (stillImage && qt_device_format_high_resolution(format) == request)
+ return format;
+ }
+
+ if (!qt_area_sane(request))
+ return nullptr;
+
+ typedef QPair<QSize, AVCaptureDeviceFormat *> FormatPair;
+
+ QVector<FormatPair> pairs; // default|HR sizes
+ pairs.reserve(formats.size());
+
+ for (int i = 0; i < formats.size(); ++i) {
+ AVCaptureDeviceFormat *format = formats[i];
+ const QSize res(qt_device_format_resolution(format));
+ if (!res.isNull() && res.isValid() && qt_area_sane(res))
+ pairs << FormatPair(res, format);
+ const QSize highRes(qt_device_format_high_resolution(format));
+ if (stillImage && !highRes.isNull() && highRes.isValid() && qt_area_sane(highRes))
+ pairs << FormatPair(highRes, format);
+ }
+
+ if (!pairs.size())
+ return nullptr;
+
+ AVCaptureDeviceFormat *best = pairs[0].second;
+ QSize next(pairs[0].first);
+ int wDiff = qAbs(request.width() - next.width());
+ int hDiff = qAbs(request.height() - next.height());
+ const int area = request.width() * request.height();
+ int areaDiff = qAbs(area - next.width() * next.height());
+ for (int i = 1; i < pairs.size(); ++i) {
+ next = pairs[i].first;
+ const int newWDiff = qAbs(next.width() - request.width());
+ const int newHDiff = qAbs(next.height() - request.height());
+ const int newAreaDiff = qAbs(area - next.width() * next.height());
+
+ if ((newWDiff < wDiff && newHDiff < hDiff)
+ || ((newWDiff <= wDiff || newHDiff <= hDiff) && newAreaDiff <= areaDiff)) {
+ wDiff = newWDiff;
+ hDiff = newHDiff;
+ best = pairs[i].second;
+ areaDiff = newAreaDiff;
+ }
+ }
+
+ return best;
+}
+
+AVCaptureDeviceFormat *qt_find_best_framerate_match(AVCaptureDevice *captureDevice,
+ FourCharCode filter,
+ Float64 fps)
+{
+ Q_ASSERT(captureDevice);
+ Q_ASSERT(fps > 0.);
+
+ const qreal epsilon = 0.1;
+
+ QVector<AVCaptureDeviceFormat *>sorted(qt_unique_device_formats(captureDevice, filter));
+ // Sort formats by their resolution in decreasing order:
+ std::sort(sorted.begin(), sorted.end(), ByResolution<std::greater>());
+ // We can use only formats with framerate ranges:
+ sorted.erase(std::remove_if(sorted.begin(), sorted.end(), FormatHasNoFPSRange()), sorted.end());
+
+ if (!sorted.size())
+ return nil;
+
+ for (int i = 0; i < sorted.size(); ++i) {
+ AVCaptureDeviceFormat *format = sorted[i];
+ for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) {
+ if (range.maxFrameRate - range.minFrameRate < epsilon) {
+ // On OS X ranges are points (built-in camera).
+ if (qAbs(fps - range.maxFrameRate) < epsilon)
+ return format;
+ }
+
+ if (fps >= range.minFrameRate && fps <= range.maxFrameRate)
+ return format;
+ }
+ }
+
+ Float64 distance = qt_find_min_framerate_distance(sorted[0], fps);
+ AVCaptureDeviceFormat *match = sorted[0];
+ for (int i = 1; i < sorted.size(); ++i) {
+ const Float64 newDistance = qt_find_min_framerate_distance(sorted[i], fps);
+ if (newDistance < distance) {
+ distance = newDistance;
+ match = sorted[i];
+ }
+ }
+
+ return match;
+}
+
+AVFrameRateRange *qt_find_supported_framerate_range(AVCaptureDeviceFormat *format, Float64 fps)
+{
+ Q_ASSERT(format && format.videoSupportedFrameRateRanges
+ && format.videoSupportedFrameRateRanges.count);
+
+ const qreal epsilon = 0.1;
+
+ for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) {
+ if (range.maxFrameRate - range.minFrameRate < epsilon) {
+ // On OS X ranges are points (built-in camera).
+ if (qAbs(fps - range.maxFrameRate) < epsilon)
+ return range;
+ }
+
+ if (fps >= range.minFrameRate && fps <= range.maxFrameRate)
+ return range;
+ }
+
+ AVFrameRateRange *match = [format.videoSupportedFrameRateRanges objectAtIndex:0];
+ Float64 distance = qAbs(match.maxFrameRate - fps);
+ for (NSUInteger i = 1, e = format.videoSupportedFrameRateRanges.count; i < e; ++i) {
+ AVFrameRateRange *range = [format.videoSupportedFrameRateRanges objectAtIndex:i];
+ const Float64 newDistance = qAbs(range.maxFrameRate - fps);
+ if (newDistance < distance) {
+ distance = newDistance;
+ match = range;
+ }
+ }
+
+ return match;
+}
+
+bool qt_formats_are_equal(AVCaptureDeviceFormat *f1, AVCaptureDeviceFormat *f2)
+{
+ if (f1 == f2)
+ return true;
+
+ if (![f1.mediaType isEqualToString:f2.mediaType])
+ return false;
+
+ return CMFormatDescriptionEqual(f1.formatDescription, f2.formatDescription);
+}
+
+bool qt_set_active_format(AVCaptureDevice *captureDevice, AVCaptureDeviceFormat *format, bool preserveFps)
+{
+ static bool firstSet = true;
+
+ if (!captureDevice || !format)
+ return false;
+
+ if (qt_formats_are_equal(captureDevice.activeFormat, format)) {
+ if (firstSet) {
+ // The capture device format is persistent. The first time we set a format, report that
+ // it changed even if the formats are the same.
+ // This prevents the session from resetting the format to the default value.
+ firstSet = false;
+ return true;
+ }
+ return false;
+ }
+
+ firstSet = false;
+
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qWarning("Failed to set active format (lock failed)");
+ return false;
+ }
+
+ // Changing the activeFormat resets the frame rate.
+ AVFPSRange fps;
+ if (preserveFps)
+ fps = qt_current_framerates(captureDevice, nil);
+
+ captureDevice.activeFormat = format;
+
+ if (preserveFps)
+ qt_set_framerate_limits(captureDevice, nil, fps.first, fps.second);
+
+ return true;
+}
+
+void qt_set_framerate_limits(AVCaptureConnection *videoConnection, qreal minFPS, qreal maxFPS)
+{
+ Q_ASSERT(videoConnection);
+
+ if (minFPS < 0. || maxFPS < 0. || (maxFPS && maxFPS < minFPS)) {
+ qDebugCamera() << Q_FUNC_INFO << "invalid framerates (min, max):"
+ << minFPS << maxFPS;
+ return;
+ }
+
+ CMTime minDuration = kCMTimeInvalid;
+ if (maxFPS > 0.) {
+ if (!videoConnection.supportsVideoMinFrameDuration)
+ qDebugCamera() << Q_FUNC_INFO << "maximum framerate is not supported";
+ else
+ minDuration = CMTimeMake(1, maxFPS);
+ }
+ if (videoConnection.supportsVideoMinFrameDuration)
+ videoConnection.videoMinFrameDuration = minDuration;
+
+ CMTime maxDuration = kCMTimeInvalid;
+ if (minFPS > 0.) {
+ if (!videoConnection.supportsVideoMaxFrameDuration)
+ qDebugCamera() << Q_FUNC_INFO << "minimum framerate is not supported";
+ else
+ maxDuration = CMTimeMake(1, minFPS);
+ }
+ if (videoConnection.supportsVideoMaxFrameDuration)
+ videoConnection.videoMaxFrameDuration = maxDuration;
+}
+
+CMTime qt_adjusted_frame_duration(AVFrameRateRange *range, qreal fps)
+{
+ Q_ASSERT(range);
+ Q_ASSERT(fps > 0.);
+
+ if (range.maxFrameRate - range.minFrameRate < 0.1) {
+ // Can happen on OS X.
+ return range.minFrameDuration;
+ }
+
+ if (fps <= range.minFrameRate)
+ return range.maxFrameDuration;
+ if (fps >= range.maxFrameRate)
+ return range.minFrameDuration;
+
+ int n, d;
+ qt_real_to_fraction(1. / fps, &n, &d);
+ return CMTimeMake(n, d);
+}
+
+void qt_set_framerate_limits(AVCaptureDevice *captureDevice, qreal minFPS, qreal maxFPS)
+{
+ Q_ASSERT(captureDevice);
+ if (!captureDevice.activeFormat) {
+ qDebugCamera() << Q_FUNC_INFO << "no active capture device format";
+ return;
+ }
+
+ if (minFPS < 0. || maxFPS < 0. || (maxFPS && maxFPS < minFPS)) {
+ qDebugCamera() << Q_FUNC_INFO << "invalid framerates (min, max):"
+ << minFPS << maxFPS;
+ return;
+ }
+
+ CMTime minFrameDuration = kCMTimeInvalid;
+ CMTime maxFrameDuration = kCMTimeInvalid;
+ if (maxFPS || minFPS) {
+ AVFrameRateRange *range = qt_find_supported_framerate_range(captureDevice.activeFormat,
+ maxFPS ? maxFPS : minFPS);
+ if (!range) {
+ qDebugCamera() << Q_FUNC_INFO << "no framerate range found, (min, max):"
+ << minFPS << maxFPS;
+ return;
+ }
+
+ if (maxFPS)
+ minFrameDuration = qt_adjusted_frame_duration(range, maxFPS);
+ if (minFPS)
+ maxFrameDuration = qt_adjusted_frame_duration(range, minFPS);
+ }
+
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
+ return;
+ }
+
+ // While Apple's docs say kCMTimeInvalid will end in default
+ // settings for this format, kCMTimeInvalid on OS X ends with a runtime
+ // exception:
+ // "The activeVideoMinFrameDuration passed is not supported by the device."
+ // Instead, use the first item in the supported frame rates.
+#ifdef Q_OS_IOS
+ [captureDevice setActiveVideoMinFrameDuration:minFrameDuration];
+ [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration];
+#elif defined(Q_OS_MACOS)
+ if (CMTimeCompare(minFrameDuration, kCMTimeInvalid) == 0
+ && CMTimeCompare(maxFrameDuration, kCMTimeInvalid) == 0) {
+ AVFrameRateRange *range = captureDevice.activeFormat.videoSupportedFrameRateRanges.firstObject;
+ minFrameDuration = range.minFrameDuration;
+ maxFrameDuration = range.maxFrameDuration;
+ }
+
+ if (CMTimeCompare(minFrameDuration, kCMTimeInvalid))
+ [captureDevice setActiveVideoMinFrameDuration:minFrameDuration];
+
+ if (CMTimeCompare(maxFrameDuration, kCMTimeInvalid))
+ [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration];
+#endif // Q_OS_MACOS
+}
+
+void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection,
+ qreal minFPS, qreal maxFPS)
+{
+ Q_UNUSED(videoConnection);
+ Q_ASSERT(captureDevice);
+ qt_set_framerate_limits(captureDevice, minFPS, maxFPS);
+}
+
+AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection)
+{
+ Q_UNUSED(videoConnection);
+ Q_ASSERT(captureDevice);
+
+ AVFPSRange fps;
+ const CMTime minDuration = captureDevice.activeVideoMinFrameDuration;
+ if (CMTimeCompare(minDuration, kCMTimeInvalid)) {
+ if (const Float64 minSeconds = CMTimeGetSeconds(minDuration))
+ fps.second = 1. / minSeconds; // Max FPS = 1 / MinDuration.
+ }
+
+ const CMTime maxDuration = captureDevice.activeVideoMaxFrameDuration;
+ if (CMTimeCompare(maxDuration, kCMTimeInvalid)) {
+ if (const Float64 maxSeconds = CMTimeGetSeconds(maxDuration))
+ fps.first = 1. / maxSeconds; // Min FPS = 1 / MaxDuration.
+ }
+
+ return fps;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerautility_p.h b/src/multimedia/platform/avfoundation/camera/avfcamerautility_p.h
new file mode 100644
index 000000000..ffa9630a7
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerautility_p.h
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFCAMERAUTILITY_H
+#define AVFCAMERAUTILITY_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qsize.h>
+
+#include <AVFoundation/AVFoundation.h>
+
+// In case we have SDK below 10.7/7.0:
+@class AVCaptureDeviceFormat;
+
+QT_BEGIN_NAMESPACE
+
+class AVFConfigurationLock
+{
+public:
+ explicit AVFConfigurationLock(AVCaptureDevice *captureDevice)
+ : m_captureDevice(captureDevice),
+ m_locked(false)
+ {
+ Q_ASSERT(m_captureDevice);
+ NSError *error = nil;
+ m_locked = [m_captureDevice lockForConfiguration:&error];
+ }
+
+ ~AVFConfigurationLock()
+ {
+ if (m_locked)
+ [m_captureDevice unlockForConfiguration];
+ }
+
+ operator bool() const
+ {
+ return m_locked;
+ }
+
+private:
+ Q_DISABLE_COPY(AVFConfigurationLock)
+
+ AVCaptureDevice *m_captureDevice;
+ bool m_locked;
+};
+
+struct AVFObjectDeleter {
+ static void cleanup(NSObject *obj)
+ {
+ if (obj)
+ [obj release];
+ }
+};
+
+template<class T>
+class AVFScopedPointer : public QScopedPointer<NSObject, AVFObjectDeleter>
+{
+public:
+ AVFScopedPointer() {}
+ explicit AVFScopedPointer(T *ptr) : QScopedPointer(ptr) {}
+ operator T*() const
+ {
+ // Quite handy operator to enable Obj-C messages: [ptr someMethod];
+ return data();
+ }
+
+ T *data() const
+ {
+ return static_cast<T *>(QScopedPointer::data());
+ }
+
+ T *take()
+ {
+ return static_cast<T *>(QScopedPointer::take());
+ }
+};
+
+template<>
+class AVFScopedPointer<dispatch_queue_t>
+{
+public:
+ AVFScopedPointer() : m_queue(nullptr) {}
+ explicit AVFScopedPointer(dispatch_queue_t q) : m_queue(q) {}
+
+ ~AVFScopedPointer()
+ {
+ if (m_queue)
+ dispatch_release(m_queue);
+ }
+
+ operator dispatch_queue_t() const
+ {
+ // Quite handy operator to enable Obj-C messages: [ptr someMethod];
+ return m_queue;
+ }
+
+ dispatch_queue_t data() const
+ {
+ return m_queue;
+ }
+
+ void reset(dispatch_queue_t q = nullptr)
+ {
+ if (m_queue)
+ dispatch_release(m_queue);
+ m_queue = q;
+ }
+
+private:
+ dispatch_queue_t m_queue;
+
+ Q_DISABLE_COPY(AVFScopedPointer)
+};
+
+typedef QPair<qreal, qreal> AVFPSRange;
+AVFPSRange qt_connection_framerates(AVCaptureConnection *videoConnection);
+
+QList<AVCaptureDeviceFormat *> qt_unique_device_formats(AVCaptureDevice *captureDevice,
+ FourCharCode preferredFormat);
+QSize qt_device_format_resolution(AVCaptureDeviceFormat *format);
+QSize qt_device_format_high_resolution(AVCaptureDeviceFormat *format);
+QSize qt_device_format_pixel_aspect_ratio(AVCaptureDeviceFormat *format);
+QList<AVFPSRange> qt_device_format_framerates(AVCaptureDeviceFormat *format);
+AVCaptureDeviceFormat *qt_find_best_resolution_match(AVCaptureDevice *captureDevice, const QSize &res,
+ FourCharCode preferredFormat, bool stillImage = true);
+AVCaptureDeviceFormat *qt_find_best_framerate_match(AVCaptureDevice *captureDevice,
+ FourCharCode preferredFormat,
+ Float64 fps);
+AVFrameRateRange *qt_find_supported_framerate_range(AVCaptureDeviceFormat *format, Float64 fps);
+
+bool qt_formats_are_equal(AVCaptureDeviceFormat *f1, AVCaptureDeviceFormat *f2);
+bool qt_set_active_format(AVCaptureDevice *captureDevice, AVCaptureDeviceFormat *format, bool preserveFps);
+
+AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection);
+void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection,
+ qreal minFPS, qreal maxFPS);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol.mm b/src/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol.mm
new file mode 100644
index 000000000..6c457318d
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol.mm
@@ -0,0 +1,262 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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$
+**
+****************************************************************************/
+
+#include "avfcamerawindowcontrol_p.h"
+
+#import <AVFoundation/AVFoundation.h>
+#import <QuartzCore/CATransaction.h>
+
+#if QT_HAS_INCLUDE(<AppKit/AppKit.h>)
+#import <AppKit/AppKit.h>
+#endif
+
+#if QT_HAS_INCLUDE(<UIKit/UIKit.h>)
+#import <UIKit/UIKit.h>
+#endif
+
+QT_USE_NAMESPACE
+
+AVFCameraWindowControl::AVFCameraWindowControl(QObject *parent)
+ : QVideoWindowControl(parent)
+{
+ setObjectName(QStringLiteral("AVFCameraWindowControl"));
+}
+
+AVFCameraWindowControl::~AVFCameraWindowControl()
+{
+ releaseNativeLayer();
+}
+
+WId AVFCameraWindowControl::winId() const
+{
+ return m_winId;
+}
+
+void AVFCameraWindowControl::setWinId(WId id)
+{
+ if (m_winId == id)
+ return;
+
+ m_winId = id;
+
+ detachNativeLayer();
+ m_nativeView = (NativeView*)m_winId;
+ attachNativeLayer();
+}
+
+QRect AVFCameraWindowControl::displayRect() const
+{
+ return m_displayRect;
+}
+
+void AVFCameraWindowControl::setDisplayRect(const QRect &rect)
+{
+ if (m_displayRect != rect) {
+ m_displayRect = rect;
+ updateCaptureLayerBounds();
+ }
+}
+
+bool AVFCameraWindowControl::isFullScreen() const
+{
+ return m_fullscreen;
+}
+
+void AVFCameraWindowControl::setFullScreen(bool fullscreen)
+{
+ if (m_fullscreen != fullscreen) {
+ m_fullscreen = fullscreen;
+ Q_EMIT fullScreenChanged(fullscreen);
+ }
+}
+
+void AVFCameraWindowControl::repaint()
+{
+ if (m_captureLayer)
+ [m_captureLayer setNeedsDisplay];
+}
+
+QSize AVFCameraWindowControl::nativeSize() const
+{
+ return m_nativeSize;
+}
+
+void AVFCameraWindowControl::setNativeSize(QSize size)
+{
+ if (m_nativeSize != size) {
+ m_nativeSize = size;
+ Q_EMIT nativeSizeChanged();
+ }
+}
+
+Qt::AspectRatioMode AVFCameraWindowControl::aspectRatioMode() const
+{
+ return m_aspectRatioMode;
+}
+
+void AVFCameraWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode)
+{
+ if (m_aspectRatioMode != mode) {
+ m_aspectRatioMode = mode;
+ updateAspectRatio();
+ }
+}
+
+int AVFCameraWindowControl::brightness() const
+{
+ return 0;
+}
+
+void AVFCameraWindowControl::setBrightness(int brightness)
+{
+ if (0 != brightness)
+ qWarning("AVFCameraWindowControl doesn't support changing Brightness");
+}
+
+int AVFCameraWindowControl::contrast() const
+{
+ return 0;
+}
+
+void AVFCameraWindowControl::setContrast(int contrast)
+{
+ if (0 != contrast)
+ qWarning("AVFCameraWindowControl doesn't support changing Contrast");
+}
+
+int AVFCameraWindowControl::hue() const
+{
+ return 0;
+}
+
+void AVFCameraWindowControl::setHue(int hue)
+{
+ if (0 != hue)
+ qWarning("AVFCameraWindowControl doesn't support changing Hue");
+}
+
+int AVFCameraWindowControl::saturation() const
+{
+ return 0;
+}
+
+void AVFCameraWindowControl::setSaturation(int saturation)
+{
+ if (0 != saturation)
+ qWarning("AVFCameraWindowControl doesn't support changing Saturation");
+}
+
+void AVFCameraWindowControl::setLayer(AVCaptureVideoPreviewLayer *capturePreviewLayer)
+{
+ if (m_captureLayer == capturePreviewLayer)
+ return;
+
+ releaseNativeLayer();
+
+ m_captureLayer = capturePreviewLayer;
+
+ if (m_captureLayer)
+ retainNativeLayer();
+}
+
+void AVFCameraWindowControl::updateAspectRatio()
+{
+ if (m_captureLayer) {
+ switch (m_aspectRatioMode) {
+ case Qt::IgnoreAspectRatio:
+ [m_captureLayer setVideoGravity:AVLayerVideoGravityResize];
+ break;
+ case Qt::KeepAspectRatio:
+ [m_captureLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
+ break;
+ case Qt::KeepAspectRatioByExpanding:
+ [m_captureLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void AVFCameraWindowControl::updateCaptureLayerBounds()
+{
+ if (m_captureLayer && m_nativeView) {
+ [CATransaction begin];
+ [CATransaction setDisableActions: YES]; // disable animation/flicks
+ m_captureLayer.frame = m_displayRect.toCGRect();
+ [CATransaction commit];
+ }
+}
+
+void AVFCameraWindowControl::retainNativeLayer()
+{
+ [m_captureLayer retain];
+
+ updateAspectRatio();
+ attachNativeLayer();
+}
+
+void AVFCameraWindowControl::releaseNativeLayer()
+{
+ if (m_captureLayer) {
+ detachNativeLayer();
+ [m_captureLayer release];
+ m_captureLayer = nullptr;
+ }
+}
+
+void AVFCameraWindowControl::attachNativeLayer()
+{
+ if (m_captureLayer && m_nativeView) {
+#if defined(Q_OS_MACOS)
+ m_nativeView.wantsLayer = YES;
+#endif
+ CALayer *nativeLayer = m_nativeView.layer;
+ [nativeLayer addSublayer:m_captureLayer];
+ updateCaptureLayerBounds();
+ }
+}
+
+void AVFCameraWindowControl::detachNativeLayer()
+{
+ if (m_captureLayer && m_nativeView)
+ [m_captureLayer removeFromSuperlayer];
+}
+
+#include "moc_avfcamerawindowcontrol_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol_p.h
new file mode 100644
index 000000000..d1a950e38
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol_p.h
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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$
+**
+****************************************************************************/
+
+#ifndef AVFCAMERAWINDOWCONTROL_H
+#define AVFCAMERAWINDOWCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QVideoWindowControl>
+
+@class AVCaptureVideoPreviewLayer;
+#if defined(Q_OS_MACOS)
+@class NSView;
+typedef NSView NativeView;
+#else
+@class UIView;
+typedef UIView NativeView;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraWindowControl : public QVideoWindowControl
+{
+ Q_OBJECT
+public:
+ AVFCameraWindowControl(QObject *parent = nullptr);
+ virtual ~AVFCameraWindowControl() override;
+
+ // QVideoWindowControl interface
+public:
+ WId winId() const override;
+ void setWinId(WId id) override;
+
+ QRect displayRect() const override;
+ void setDisplayRect(const QRect &rect) override;
+
+ bool isFullScreen() const override;
+ void setFullScreen(bool fullScreen) override;
+
+ void repaint() override;
+
+ QSize nativeSize() const override;
+
+ Qt::AspectRatioMode aspectRatioMode() const override;
+ void setAspectRatioMode(Qt::AspectRatioMode mode) override;
+
+ int brightness() const override;
+ void setBrightness(int brightness) override;
+
+ int contrast() const override;
+ void setContrast(int contrast) override;
+
+ int hue() const override;
+ void setHue(int hue) override;
+
+ int saturation() const override;
+ void setSaturation(int saturation) override;
+
+ // AVF Camera implementation details
+ void setNativeSize(QSize size);
+ void setLayer(AVCaptureVideoPreviewLayer *capturePreviewLayer);
+
+private:
+ void updateAspectRatio();
+ void updateCaptureLayerBounds();
+
+ void retainNativeLayer();
+ void releaseNativeLayer();
+
+ void attachNativeLayer();
+ void detachNativeLayer();
+
+ WId m_winId{0};
+ QRect m_displayRect;
+ bool m_fullscreen{false};
+ Qt::AspectRatioMode m_aspectRatioMode{Qt::IgnoreAspectRatio};
+ QSize m_nativeSize;
+ AVCaptureVideoPreviewLayer *m_captureLayer{nullptr};
+ NativeView *m_nativeView{nullptr};
+};
+
+QT_END_NAMESPACE
+
+#endif // AVFCAMERAWINDOWCONTROL_H
diff --git a/src/multimedia/platform/avfoundation/camera/avfimagecapturecontrol.mm b/src/multimedia/platform/avfoundation/camera/avfimagecapturecontrol.mm
new file mode 100644
index 000000000..b019cf2ef
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfimagecapturecontrol.mm
@@ -0,0 +1,278 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "avfcameradebug_p.h"
+#include "avfimagecapturecontrol_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcameracontrol_p.h"
+#include <private/qmemoryvideobuffer_p.h>
+
+#include <QtCore/qurl.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qbuffer.h>
+#include <QtConcurrent/qtconcurrentrun.h>
+#include <QtGui/qimagereader.h>
+
+QT_USE_NAMESPACE
+
+AVFImageCaptureControl::AVFImageCaptureControl(AVFCameraService *service, QObject *parent)
+ : QCameraImageCaptureControl(parent)
+ , m_service(service)
+ , m_session(service->session())
+ , m_cameraControl(service->cameraControl())
+ , m_ready(false)
+ , m_lastCaptureId(0)
+ , m_videoConnection(nil)
+{
+ Q_UNUSED(service);
+ m_stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
+
+ NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
+ AVVideoCodecJPEG, AVVideoCodecKey, nil];
+
+ [m_stillImageOutput setOutputSettings:outputSettings];
+ [outputSettings release];
+ connect(m_cameraControl, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(updateReadyStatus()));
+ connect(m_cameraControl, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateReadyStatus()));
+
+ connect(m_session, SIGNAL(readyToConfigureConnections()), SLOT(updateCaptureConnection()));
+ connect(m_cameraControl, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(updateCaptureConnection()));
+
+ connect(m_session, &AVFCameraSession::newViewfinderFrame,
+ this, &AVFImageCaptureControl::onNewViewfinderFrame,
+ Qt::DirectConnection);
+}
+
+AVFImageCaptureControl::~AVFImageCaptureControl()
+{
+}
+
+bool AVFImageCaptureControl::isReadyForCapture() const
+{
+ return m_videoConnection &&
+ m_cameraControl->captureMode().testFlag(QCamera::CaptureStillImage) &&
+ m_cameraControl->status() == QCamera::ActiveStatus;
+}
+
+void AVFImageCaptureControl::updateReadyStatus()
+{
+ if (m_ready != isReadyForCapture()) {
+ m_ready = !m_ready;
+ qDebugCamera() << "ReadyToCapture status changed:" << m_ready;
+ Q_EMIT readyForCaptureChanged(m_ready);
+ }
+}
+
+int AVFImageCaptureControl::capture(const QString &fileName)
+{
+ m_lastCaptureId++;
+
+ if (!isReadyForCapture()) {
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(int, m_lastCaptureId),
+ Q_ARG(int, QCameraImageCapture::NotReadyError),
+ Q_ARG(QString, tr("Camera not ready")));
+ return m_lastCaptureId;
+ }
+
+ auto destination = m_service->imageCaptureControl()->captureDestination();
+ QString actualFileName;
+ if (destination & QCameraImageCapture::CaptureToFile) {
+ actualFileName = m_storageLocation.generateFileName(fileName,
+ QCamera::CaptureStillImage,
+ QLatin1String("img_"),
+ QLatin1String("jpg"));
+
+ qDebugCamera() << "Capture image to" << actualFileName;
+ }
+
+ CaptureRequest request = { m_lastCaptureId, QSharedPointer<QSemaphore>::create()};
+ m_requestsMutex.lock();
+ m_captureRequests.enqueue(request);
+ m_requestsMutex.unlock();
+
+ [m_stillImageOutput captureStillImageAsynchronouslyFromConnection:m_videoConnection
+ completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
+
+ if (error) {
+ QStringList messageParts;
+ messageParts << QString::fromUtf8([[error localizedDescription] UTF8String]);
+ messageParts << QString::fromUtf8([[error localizedFailureReason] UTF8String]);
+ messageParts << QString::fromUtf8([[error localizedRecoverySuggestion] UTF8String]);
+
+ QString errorMessage = messageParts.join(" ");
+ qDebugCamera() << "Image capture failed:" << errorMessage;
+
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(int, request.captureId),
+ Q_ARG(int, QCameraImageCapture::ResourceError),
+ Q_ARG(QString, errorMessage));
+ return;
+ }
+
+ // Wait for the preview to be generated before saving the JPEG (but only
+ // if we have AVFCameraRendererControl attached).
+ // It is possible to stop camera immediately after trying to capture an
+ // image; this can result in a blocked callback's thread, waiting for a
+ // new viewfinder's frame to arrive/semaphore to be released. It is also
+ // unspecified on which thread this callback gets executed, (probably it's
+ // not the same thread that initiated a capture and stopped the camera),
+ // so we cannot reliably check the camera's status. Instead, we wait
+ // with a timeout and treat a failure to acquire a semaphore as an error.
+ if (!m_service->videoOutput() || request.previewReady->tryAcquire(1, 1000)) {
+ qDebugCamera() << "Image capture completed";
+
+ NSData *nsJpgData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
+ QByteArray jpgData = QByteArray::fromRawData((const char *)[nsJpgData bytes], [nsJpgData length]);
+
+ if (destination & QCameraImageCapture::CaptureToBuffer) {
+ QBuffer data(&jpgData);
+ QImageReader reader(&data, "JPEG");
+ QSize size = reader.size();
+ QVideoFrame frame(new QMemoryVideoBuffer(QByteArray(jpgData.constData(), jpgData.size()), -1), size, QVideoFrame::Format_Jpeg);
+ QMetaObject::invokeMethod(this, "imageAvailable", Qt::QueuedConnection,
+ Q_ARG(int, request.captureId),
+ Q_ARG(QVideoFrame, frame));
+ }
+
+ if (!(destination & QCameraImageCapture::CaptureToFile))
+ return;
+
+ QFile f(actualFileName);
+ if (f.open(QFile::WriteOnly)) {
+ if (f.write(jpgData) != -1) {
+ QMetaObject::invokeMethod(this, "imageSaved", Qt::QueuedConnection,
+ Q_ARG(int, request.captureId),
+ Q_ARG(QString, actualFileName));
+ } else {
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(int, request.captureId),
+ Q_ARG(int, QCameraImageCapture::OutOfSpaceError),
+ Q_ARG(QString, f.errorString()));
+ }
+ } else {
+ QString errorMessage = tr("Could not open destination file:\n%1").arg(actualFileName);
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(int, request.captureId),
+ Q_ARG(int, QCameraImageCapture::ResourceError),
+ Q_ARG(QString, errorMessage));
+ }
+ } else {
+ const QLatin1String errorMessage("Image capture failed: timed out waiting"
+ " for a preview frame.");
+ qDebugCamera() << errorMessage;
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(int, request.captureId),
+ Q_ARG(int, QCameraImageCapture::ResourceError),
+ Q_ARG(QString, errorMessage));
+ }
+ }];
+
+ return request.captureId;
+}
+
+void AVFImageCaptureControl::onNewViewfinderFrame(const QVideoFrame &frame)
+{
+ QMutexLocker locker(&m_requestsMutex);
+
+ if (m_captureRequests.isEmpty())
+ return;
+
+ CaptureRequest request = m_captureRequests.dequeue();
+ Q_EMIT imageExposed(request.captureId);
+
+ QtConcurrent::run(&AVFImageCaptureControl::makeCapturePreview, this,
+ request,
+ frame,
+ 0 /* rotation */);
+}
+
+void AVFImageCaptureControl::makeCapturePreview(CaptureRequest request,
+ const QVideoFrame &frame,
+ int rotation)
+{
+ QTransform transform;
+ transform.rotate(rotation);
+
+ Q_EMIT imageCaptured(request.captureId, frame.image().transformed(transform));
+
+ request.previewReady->release();
+}
+
+void AVFImageCaptureControl::cancelCapture()
+{
+ //not supported
+}
+
+QCameraImageCapture::CaptureDestinations AVFImageCaptureControl::captureDestination() const
+{
+ return m_destination;
+}
+
+void AVFImageCaptureControl::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination)
+{
+ if (m_destination != destination) {
+ m_destination = destination;
+ updateCaptureConnection();
+ }
+}
+
+void AVFImageCaptureControl::updateCaptureConnection()
+{
+ if (m_session->videoCaptureDevice()
+ && m_cameraControl->captureMode().testFlag(QCamera::CaptureStillImage)) {
+ qDebugCamera() << Q_FUNC_INFO;
+ AVCaptureSession *captureSession = m_session->captureSession();
+
+ if (![captureSession.outputs containsObject:m_stillImageOutput]) {
+ if ([captureSession canAddOutput:m_stillImageOutput]) {
+ // Lock the video capture device to make sure the active format is not reset
+ const AVFConfigurationLock lock(m_session->videoCaptureDevice());
+ [captureSession addOutput:m_stillImageOutput];
+ m_videoConnection = [m_stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
+ updateReadyStatus();
+ }
+ } else {
+ m_videoConnection = [m_stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
+ }
+ }
+}
+
+#include "moc_avfimagecapturecontrol_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfimagecapturecontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfimagecapturecontrol_p.h
new file mode 100644
index 000000000..3c781a475
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfimagecapturecontrol_p.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFIMAGECAPTURECONTROL_H
+#define AVFIMAGECAPTURECONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#import <AVFoundation/AVFoundation.h>
+
+#include <QtCore/qqueue.h>
+#include <QtCore/qsemaphore.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtMultimedia/qcameraimagecapturecontrol.h>
+#include "avfcamerasession_p.h"
+#include "avfstoragelocation_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class AVFImageCaptureControl : public QCameraImageCaptureControl
+{
+Q_OBJECT
+public:
+ struct CaptureRequest {
+ int captureId;
+ QSharedPointer<QSemaphore> previewReady;
+ };
+
+ AVFImageCaptureControl(AVFCameraService *service, QObject *parent = nullptr);
+ ~AVFImageCaptureControl();
+
+ bool isReadyForCapture() const override;
+
+ QCameraImageCapture::DriveMode driveMode() const override { return QCameraImageCapture::SingleImageCapture; }
+ void setDriveMode(QCameraImageCapture::DriveMode ) override {}
+ AVCaptureStillImageOutput *stillImageOutput() const {return m_stillImageOutput;}
+
+ int capture(const QString &fileName) override;
+ void cancelCapture() override;
+
+ QCameraImageCapture::CaptureDestinations captureDestination() const override;
+ void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination) override;
+
+private Q_SLOTS:
+ void updateCaptureConnection();
+ void updateReadyStatus();
+ void onNewViewfinderFrame(const QVideoFrame &frame);
+
+private:
+ void makeCapturePreview(CaptureRequest request, const QVideoFrame &frame, int rotation);
+
+ AVFCameraService *m_service;
+ AVFCameraSession *m_session;
+ AVFCameraControl *m_cameraControl;
+ bool m_ready;
+ int m_lastCaptureId;
+ AVCaptureStillImageOutput *m_stillImageOutput;
+ AVCaptureConnection *m_videoConnection;
+ AVFStorageLocation m_storageLocation;
+
+ QMutex m_requestsMutex;
+ QQueue<CaptureRequest> m_captureRequests;
+
+ QCameraImageCapture::CaptureDestinations m_destination = QCameraImageCapture::CaptureToFile;
+};
+
+Q_DECLARE_TYPEINFO(AVFImageCaptureControl::CaptureRequest, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfimageencodercontrol.mm b/src/multimedia/platform/avfoundation/camera/avfimageencodercontrol.mm
new file mode 100644
index 000000000..113fdb62b
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfimageencodercontrol.mm
@@ -0,0 +1,239 @@
+/****************************************************************************
+**
+** 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 "avfimageencodercontrol_p.h"
+#include "avfimagecapturecontrol_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcameradebug_p.h"
+#include "avfcameracontrol_p.h"
+
+#include <QtMultimedia/qmediaencodersettings.h>
+
+#include <QtCore/qdebug.h>
+
+#include <AVFoundation/AVFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+AVFImageEncoderControl::AVFImageEncoderControl(AVFCameraService *service)
+ : m_service(service)
+{
+ Q_ASSERT(service);
+}
+
+QStringList AVFImageEncoderControl::supportedImageCodecs() const
+{
+ return QStringList() << QLatin1String("jpeg");
+}
+
+QString AVFImageEncoderControl::imageCodecDescription(const QString &codecName) const
+{
+ if (codecName == QLatin1String("jpeg"))
+ return tr("JPEG image");
+
+ return QString();
+}
+
+QList<QSize> AVFImageEncoderControl::supportedResolutions(const QImageEncoderSettings &settings,
+ bool *continuous) const
+{
+ Q_UNUSED(settings);
+
+ QList<QSize> resolutions;
+
+ if (!videoCaptureDeviceIsValid())
+ return resolutions;
+
+ AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
+ const QVector<AVCaptureDeviceFormat *> formats(qt_unique_device_formats(captureDevice,
+ m_service->session()->defaultCodec()));
+
+ for (int i = 0; i < formats.size(); ++i) {
+ AVCaptureDeviceFormat *format = formats[i];
+
+ const QSize res(qt_device_format_resolution(format));
+ if (!res.isNull() && res.isValid())
+ resolutions << res;
+#ifdef Q_OS_IOS
+ // From Apple's docs (iOS):
+ // By default, AVCaptureStillImageOutput emits images with the same dimensions as
+ // its source AVCaptureDevice instance’s activeFormat.formatDescription. However,
+ // if you set this property to YES, the receiver emits still images at the capture
+ // device’s highResolutionStillImageDimensions value.
+ const QSize hrRes(qt_device_format_high_resolution(format));
+ if (!hrRes.isNull() && hrRes.isValid())
+ resolutions << res;
+#endif
+ }
+
+ if (continuous)
+ *continuous = false;
+
+ return resolutions;
+}
+
+QImageEncoderSettings AVFImageEncoderControl::requestedSettings() const
+{
+ return m_settings;
+}
+
+QImageEncoderSettings AVFImageEncoderControl::imageSettings() const
+{
+ QImageEncoderSettings settings;
+
+ if (!videoCaptureDeviceIsValid())
+ return settings;
+
+ AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
+ if (!captureDevice.activeFormat) {
+ qDebugCamera() << Q_FUNC_INFO << "no active format";
+ return settings;
+ }
+
+ QSize res(qt_device_format_resolution(captureDevice.activeFormat));
+#ifdef Q_OS_IOS
+ if (!m_service->imageCaptureControl() || !m_service->imageCaptureControl()->stillImageOutput()) {
+ qDebugCamera() << Q_FUNC_INFO << "no still image output";
+ return settings;
+ }
+
+ AVCaptureStillImageOutput *stillImageOutput = m_service->imageCaptureControl()->stillImageOutput();
+ if (stillImageOutput.highResolutionStillImageOutputEnabled)
+ res = qt_device_format_high_resolution(captureDevice.activeFormat);
+#endif
+ if (res.isNull() || !res.isValid()) {
+ qDebugCamera() << Q_FUNC_INFO << "failed to exctract the image resolution";
+ return settings;
+ }
+
+ settings.setResolution(res);
+
+ settings.setCodec(QLatin1String("jpeg"));
+
+ return settings;
+}
+
+void AVFImageEncoderControl::setImageSettings(const QImageEncoderSettings &settings)
+{
+ if (m_settings == settings)
+ return;
+
+ m_settings = settings;
+ applySettings();
+}
+
+bool AVFImageEncoderControl::applySettings()
+{
+ if (!videoCaptureDeviceIsValid())
+ return false;
+
+ AVFCameraSession *session = m_service->session();
+ if (!session || (session->state() != QCamera::ActiveState
+ && session->state() != QCamera::LoadedState)
+ || !m_service->cameraControl()->captureMode().testFlag(QCamera::CaptureStillImage)) {
+ return false;
+ }
+
+ if (!m_service->imageCaptureControl()
+ || !m_service->imageCaptureControl()->stillImageOutput()) {
+ qDebugCamera() << Q_FUNC_INFO << "no still image output";
+ return false;
+ }
+
+ if (m_settings.codec().size()
+ && m_settings.codec() != QLatin1String("jpeg")) {
+ qDebugCamera() << Q_FUNC_INFO << "unsupported codec:" << m_settings.codec();
+ return false;
+ }
+
+ QSize res(m_settings.resolution());
+ if (res.isNull()) {
+ qDebugCamera() << Q_FUNC_INFO << "invalid resolution:" << res;
+ return false;
+ }
+
+ if (!res.isValid()) {
+ // Invalid == default value.
+ // Here we could choose the best format available, but
+ // activeFormat is already equal to 'preset high' by default,
+ // which is good enough, otherwise we can end in some format with low framerates.
+ return false;
+ }
+
+ bool activeFormatChanged = false;
+
+ AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
+ AVCaptureDeviceFormat *match = qt_find_best_resolution_match(captureDevice, res,
+ m_service->session()->defaultCodec());
+
+ if (!match) {
+ qDebugCamera() << Q_FUNC_INFO << "unsupported resolution:" << res;
+ return false;
+ }
+
+ activeFormatChanged = qt_set_active_format(captureDevice, match, true);
+
+#ifdef Q_OS_IOS
+ AVCaptureStillImageOutput *imageOutput = m_service->imageCaptureControl()->stillImageOutput();
+ if (res == qt_device_format_high_resolution(captureDevice.activeFormat))
+ imageOutput.highResolutionStillImageOutputEnabled = YES;
+ else
+ imageOutput.highResolutionStillImageOutputEnabled = NO;
+#endif
+
+ return activeFormatChanged;
+}
+
+bool AVFImageEncoderControl::videoCaptureDeviceIsValid() const
+{
+ if (!m_service->session() || !m_service->session()->videoCaptureDevice())
+ return false;
+
+ AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
+ if (!captureDevice.formats || !captureDevice.formats.count)
+ return false;
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_avfimageencodercontrol_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfimageencodercontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfimageencodercontrol_p.h
new file mode 100644
index 000000000..8c2742d35
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfimageencodercontrol_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFIMAGEENCODERCONTROL_H
+#define AVFIMAGEENCODERCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtMultimedia/qmediaencodersettings.h>
+#include <QtMultimedia/qimageencodercontrol.h>
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qlist.h>
+
+@class AVCaptureDeviceFormat;
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraService;
+
+class AVFImageEncoderControl : public QImageEncoderControl
+{
+ Q_OBJECT
+
+ friend class AVFCameraSession;
+public:
+ AVFImageEncoderControl(AVFCameraService *service);
+
+ QStringList supportedImageCodecs() const override;
+ QString imageCodecDescription(const QString &codecName) const override;
+ QList<QSize> supportedResolutions(const QImageEncoderSettings &settings,
+ bool *continuous) const override;
+ QImageEncoderSettings imageSettings() const override;
+ void setImageSettings(const QImageEncoderSettings &settings) override;
+
+ QImageEncoderSettings requestedSettings() const;
+
+private:
+ AVFCameraService *m_service;
+ QImageEncoderSettings m_settings;
+
+ bool applySettings();
+ bool videoCaptureDeviceIsValid() const;
+};
+
+QSize qt_image_high_resolution(AVCaptureDeviceFormat *fomat);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfmediaassetwriter.mm b/src/multimedia/platform/avfoundation/camera/avfmediaassetwriter.mm
new file mode 100644
index 000000000..57c5cb8c5
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediaassetwriter.mm
@@ -0,0 +1,514 @@
+/****************************************************************************
+**
+** 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 "avfaudioinputselectorcontrol.h"
+#include "avfmediarecordercontrol_ios.h"
+#include "avfcamerarenderercontrol.h"
+#include "avfmediaassetwriter.h"
+#include "avfcameraservice.h"
+#include "avfcamerasession.h"
+#include "avfcameradebug.h"
+#include "avfmediacontainercontrol.h"
+
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qatomic.h>
+
+QT_USE_NAMESPACE
+
+namespace {
+
+bool qt_camera_service_isValid(AVFCameraService *service)
+{
+ if (!service || !service->session())
+ return false;
+
+ AVFCameraSession *session = service->session();
+ if (!session->captureSession())
+ return false;
+
+ if (!session->videoInput())
+ return false;
+
+ if (!service->videoOutput()
+ || !service->videoOutput()->videoDataOutput()) {
+ return false;
+ }
+
+ return true;
+}
+
+enum WriterState
+{
+ WriterStateIdle,
+ WriterStateActive,
+ WriterStateAborted
+};
+
+using AVFAtomicInt64 = QAtomicInteger<qint64>;
+
+} // unnamed namespace
+
+@interface QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) (PrivateAPI)
+- (bool)addAudioCapture;
+- (bool)addWriterInputs;
+- (void)setQueues;
+- (void)updateDuration:(CMTime)newTimeStamp;
+@end
+
+@implementation QT_MANGLE_NAMESPACE(AVFMediaAssetWriter)
+{
+@private
+ AVFCameraService *m_service;
+
+ AVFScopedPointer<AVAssetWriterInput> m_cameraWriterInput;
+ AVFScopedPointer<AVCaptureDeviceInput> m_audioInput;
+ AVFScopedPointer<AVCaptureAudioDataOutput> m_audioOutput;
+ AVFScopedPointer<AVAssetWriterInput> m_audioWriterInput;
+
+ AVCaptureDevice *m_audioCaptureDevice;
+
+ // Queue to write sample buffers:
+ AVFScopedPointer<dispatch_queue_t> m_writerQueue;
+ // High priority serial queue for video output:
+ AVFScopedPointer<dispatch_queue_t> m_videoQueue;
+ // Serial queue for audio output:
+ AVFScopedPointer<dispatch_queue_t> m_audioQueue;
+
+ AVFScopedPointer<AVAssetWriter> m_assetWriter;
+
+ AVFMediaRecorderControlIOS *m_delegate;
+
+ bool m_setStartTime;
+
+ QAtomicInt m_state;
+
+ CMTime m_startTime;
+ CMTime m_lastTimeStamp;
+
+ NSDictionary *m_audioSettings;
+ NSDictionary *m_videoSettings;
+
+ AVFAtomicInt64 m_durationInMs;
+}
+
+- (id)initWithDelegate:(AVFMediaRecorderControlIOS *)delegate
+{
+ Q_ASSERT(delegate);
+
+ if (self = [super init]) {
+ m_delegate = delegate;
+ m_setStartTime = true;
+ m_state.storeRelaxed(WriterStateIdle);
+ m_startTime = kCMTimeInvalid;
+ m_lastTimeStamp = kCMTimeInvalid;
+ m_durationInMs.storeRelaxed(0);
+ m_audioSettings = nil;
+ m_videoSettings = nil;
+ }
+
+ return self;
+}
+
+- (bool)setupWithFileURL:(NSURL *)fileURL
+ cameraService:(AVFCameraService *)service
+ audioSettings:(NSDictionary *)audioSettings
+ videoSettings:(NSDictionary *)videoSettings
+ transform:(CGAffineTransform)transform
+{
+ Q_ASSERT(fileURL);
+
+ if (!qt_camera_service_isValid(service)) {
+ qDebugCamera() << Q_FUNC_INFO << "invalid camera service";
+ return false;
+ }
+
+ m_service = service;
+ m_audioSettings = audioSettings;
+ m_videoSettings = videoSettings;
+
+ m_writerQueue.reset(dispatch_queue_create("asset-writer-queue", DISPATCH_QUEUE_SERIAL));
+ if (!m_writerQueue) {
+ qDebugCamera() << Q_FUNC_INFO << "failed to create an asset writer's queue";
+ return false;
+ }
+
+ m_videoQueue.reset(dispatch_queue_create("video-output-queue", DISPATCH_QUEUE_SERIAL));
+ if (!m_videoQueue) {
+ qDebugCamera() << Q_FUNC_INFO << "failed to create video queue";
+ return false;
+ }
+ dispatch_set_target_queue(m_videoQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
+ m_audioQueue.reset(dispatch_queue_create("audio-output-queue", DISPATCH_QUEUE_SERIAL));
+ if (!m_audioQueue) {
+ qDebugCamera() << Q_FUNC_INFO << "failed to create audio queue";
+ // But we still can write video!
+ }
+
+ m_assetWriter.reset([[AVAssetWriter alloc] initWithURL:fileURL
+ fileType:m_service->mediaContainerControl()->fileType()
+ error:nil]);
+ if (!m_assetWriter) {
+ qDebugCamera() << Q_FUNC_INFO << "failed to create asset writer";
+ return false;
+ }
+
+ bool audioCaptureOn = false;
+
+ if (m_audioQueue)
+ audioCaptureOn = [self addAudioCapture];
+
+ if (![self addWriterInputs]) {
+ if (audioCaptureOn) {
+ AVCaptureSession *session = m_service->session()->captureSession();
+ [session removeOutput:m_audioOutput];
+ [session removeInput:m_audioInput];
+ m_audioOutput.reset();
+ m_audioInput.reset();
+ m_audioCaptureDevice = 0;
+ }
+ m_assetWriter.reset();
+ return false;
+ }
+
+ m_cameraWriterInput.data().transform = transform;
+
+ // Ready to start ...
+ return true;
+}
+
+- (void)start
+{
+ [self setQueues];
+
+ m_setStartTime = true;
+
+ m_state.storeRelease(WriterStateActive);
+
+ [m_assetWriter startWriting];
+ AVCaptureSession *session = m_service->session()->captureSession();
+ if (!session.running)
+ [session startRunning];
+}
+
+- (void)stop
+{
+ if (m_state.loadAcquire() != WriterStateActive)
+ return;
+
+ if ([m_assetWriter status] != AVAssetWriterStatusWriting)
+ return;
+
+ // Do this here so that -
+ // 1. '-abort' should not try calling finishWriting again and
+ // 2. async block (see below) will know if recorder control was deleted
+ // before the block's execution:
+ m_state.storeRelease(WriterStateIdle);
+ // Now, since we have to ensure no sample buffers are
+ // appended after a call to finishWriting, we must
+ // ensure writer's queue sees this change in m_state
+ // _before_ we call finishWriting:
+ dispatch_sync(m_writerQueue, ^{});
+ // Done, but now we also want to prevent video queue
+ // from updating our viewfinder:
+ dispatch_sync(m_videoQueue, ^{});
+
+ // Now we're safe to stop:
+ [m_assetWriter finishWritingWithCompletionHandler:^{
+ // This block is async, so by the time it's executed,
+ // it's possible that render control was deleted already ...
+ if (m_state.loadAcquire() == WriterStateAborted)
+ return;
+
+ AVCaptureSession *session = m_service->session()->captureSession();
+ if (session.running)
+ [session stopRunning];
+ [session removeOutput:m_audioOutput];
+ [session removeInput:m_audioInput];
+ QMetaObject::invokeMethod(m_delegate, "assetWriterFinished", Qt::QueuedConnection);
+ }];
+}
+
+- (void)abort
+{
+ // -abort is to be called from recorder control's dtor.
+
+ if (m_state.fetchAndStoreRelease(WriterStateAborted) != WriterStateActive) {
+ // Not recording, nothing to stop.
+ return;
+ }
+
+ // From Apple's docs:
+ // "To guarantee that all sample buffers are successfully written,
+ // you must ensure that all calls to appendSampleBuffer: and
+ // appendPixelBuffer:withPresentationTime: have returned before
+ // invoking this method."
+ //
+ // The only way we can ensure this is:
+ dispatch_sync(m_writerQueue, ^{});
+ // At this point next block (if any) on the writer's queue
+ // will see m_state preventing it from any further processing.
+ dispatch_sync(m_videoQueue, ^{});
+ // After this point video queue will not try to modify our
+ // viewfider, so we're safe to delete now.
+
+ [m_assetWriter finishWritingWithCompletionHandler:^{
+ }];
+}
+
+- (void)setStartTimeFrom:(CMSampleBufferRef)sampleBuffer
+{
+ // Writer's queue only.
+ Q_ASSERT(m_setStartTime);
+ Q_ASSERT(sampleBuffer);
+
+ if (m_state.loadAcquire() != WriterStateActive)
+ return;
+
+ QMetaObject::invokeMethod(m_delegate, "assetWriterStarted", Qt::QueuedConnection);
+
+ m_durationInMs.storeRelease(0);
+ m_startTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
+ m_lastTimeStamp = m_startTime;
+ [m_assetWriter startSessionAtSourceTime:m_startTime];
+ m_setStartTime = false;
+}
+
+- (void)writeVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer
+{
+ // This code is executed only on a writer's queue.
+ Q_ASSERT(sampleBuffer);
+
+ if (m_state.loadAcquire() == WriterStateActive) {
+ if (m_setStartTime)
+ [self setStartTimeFrom:sampleBuffer];
+
+ if (m_cameraWriterInput.data().readyForMoreMediaData) {
+ [self updateDuration:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
+ [m_cameraWriterInput appendSampleBuffer:sampleBuffer];
+ }
+ }
+
+ CFRelease(sampleBuffer);
+}
+
+- (void)writeAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer
+{
+ Q_ASSERT(sampleBuffer);
+
+ // This code is executed only on a writer's queue.
+ if (m_state.loadAcquire() == WriterStateActive) {
+ if (m_setStartTime)
+ [self setStartTimeFrom:sampleBuffer];
+
+ if (m_audioWriterInput.data().readyForMoreMediaData) {
+ [self updateDuration:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
+ [m_audioWriterInput appendSampleBuffer:sampleBuffer];
+ }
+ }
+
+ CFRelease(sampleBuffer);
+}
+
+- (void)captureOutput:(AVCaptureOutput *)captureOutput
+ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
+ fromConnection:(AVCaptureConnection *)connection
+{
+ Q_UNUSED(connection);
+
+ if (m_state.loadAcquire() != WriterStateActive)
+ return;
+
+ if (!CMSampleBufferDataIsReady(sampleBuffer)) {
+ qDebugCamera() << Q_FUNC_INFO << "sample buffer is not ready, skipping.";
+ return;
+ }
+
+ CFRetain(sampleBuffer);
+
+ if (captureOutput != m_audioOutput.data()) {
+ if (m_state.loadRelaxed() != WriterStateActive) {
+ CFRelease(sampleBuffer);
+ return;
+ }
+ // Find renderercontrol's delegate and invoke its method to
+ // show updated viewfinder's frame.
+ if (m_service && m_service->videoOutput()) {
+ NSObject<AVCaptureVideoDataOutputSampleBufferDelegate> *vfDelegate =
+ (NSObject<AVCaptureVideoDataOutputSampleBufferDelegate> *)m_service->videoOutput()->captureDelegate();
+ if (vfDelegate)
+ [vfDelegate captureOutput:nil didOutputSampleBuffer:sampleBuffer fromConnection:nil];
+ }
+
+ dispatch_async(m_writerQueue, ^{
+ [self writeVideoSampleBuffer:sampleBuffer];
+ });
+ } else {
+ dispatch_async(m_writerQueue, ^{
+ [self writeAudioSampleBuffer:sampleBuffer];
+ });
+ }
+}
+
+- (bool)addAudioCapture
+{
+ Q_ASSERT(m_service && m_service->session() && m_service->session()->captureSession());
+
+ if (!m_service->audioInputSelectorControl())
+ return false;
+
+ AVCaptureSession *captureSession = m_service->session()->captureSession();
+
+ m_audioCaptureDevice = m_service->audioInputSelectorControl()->createCaptureDevice();
+ if (!m_audioCaptureDevice) {
+ qWarning() << Q_FUNC_INFO << "no audio input device available";
+ return false;
+ } else {
+ NSError *error = nil;
+ m_audioInput.reset([[AVCaptureDeviceInput deviceInputWithDevice:m_audioCaptureDevice error:&error] retain]);
+
+ if (!m_audioInput || error) {
+ qWarning() << Q_FUNC_INFO << "failed to create audio device input";
+ m_audioCaptureDevice = 0;
+ m_audioInput.reset();
+ return false;
+ } else if (![captureSession canAddInput:m_audioInput]) {
+ qWarning() << Q_FUNC_INFO << "could not connect the audio input";
+ m_audioCaptureDevice = 0;
+ m_audioInput.reset();
+ return false;
+ } else {
+ [captureSession addInput:m_audioInput];
+ }
+ }
+
+
+ m_audioOutput.reset([[AVCaptureAudioDataOutput alloc] init]);
+ if (m_audioOutput.data() && [captureSession canAddOutput:m_audioOutput]) {
+ [captureSession addOutput:m_audioOutput];
+ } else {
+ qDebugCamera() << Q_FUNC_INFO << "failed to add audio output";
+ [captureSession removeInput:m_audioInput];
+ m_audioCaptureDevice = 0;
+ m_audioInput.reset();
+ m_audioOutput.reset();
+ return false;
+ }
+
+ return true;
+}
+
+- (bool)addWriterInputs
+{
+ Q_ASSERT(m_service && m_service->videoOutput()
+ && m_service->videoOutput()->videoDataOutput());
+ Q_ASSERT(m_assetWriter.data());
+
+ m_cameraWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo
+ outputSettings:m_videoSettings
+ sourceFormatHint:m_service->session()->videoCaptureDevice().activeFormat.formatDescription]);
+ if (!m_cameraWriterInput) {
+ qDebugCamera() << Q_FUNC_INFO << "failed to create camera writer input";
+ return false;
+ }
+
+ if ([m_assetWriter canAddInput:m_cameraWriterInput]) {
+ [m_assetWriter addInput:m_cameraWriterInput];
+ } else {
+ qDebugCamera() << Q_FUNC_INFO << "failed to add camera writer input";
+ m_cameraWriterInput.reset();
+ return false;
+ }
+
+ m_cameraWriterInput.data().expectsMediaDataInRealTime = YES;
+
+ if (m_audioOutput.data()) {
+ CMFormatDescriptionRef sourceFormat = m_audioCaptureDevice ? m_audioCaptureDevice.activeFormat.formatDescription : 0;
+ m_audioWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio
+ outputSettings:m_audioSettings
+ sourceFormatHint:sourceFormat]);
+ if (!m_audioWriterInput) {
+ qDebugCamera() << Q_FUNC_INFO << "failed to create audio writer input";
+ // But we still can record video.
+ } else if ([m_assetWriter canAddInput:m_audioWriterInput]) {
+ [m_assetWriter addInput:m_audioWriterInput];
+ m_audioWriterInput.data().expectsMediaDataInRealTime = YES;
+ } else {
+ qDebugCamera() << Q_FUNC_INFO << "failed to add audio writer input";
+ m_audioWriterInput.reset();
+ // We can (still) write video though ...
+ }
+ }
+
+ return true;
+}
+
+- (void)setQueues
+{
+ Q_ASSERT(m_service && m_service->videoOutput() && m_service->videoOutput()->videoDataOutput());
+ Q_ASSERT(m_videoQueue);
+
+ [m_service->videoOutput()->videoDataOutput() setSampleBufferDelegate:self queue:m_videoQueue];
+
+ if (m_audioOutput.data()) {
+ Q_ASSERT(m_audioQueue);
+ [m_audioOutput setSampleBufferDelegate:self queue:m_audioQueue];
+ }
+}
+
+- (void)updateDuration:(CMTime)newTimeStamp
+{
+ Q_ASSERT(CMTimeCompare(m_startTime, kCMTimeInvalid));
+ Q_ASSERT(CMTimeCompare(m_lastTimeStamp, kCMTimeInvalid));
+ if (CMTimeCompare(newTimeStamp, m_lastTimeStamp) > 0) {
+
+ const CMTime duration = CMTimeSubtract(newTimeStamp, m_startTime);
+ if (!CMTimeCompare(duration, kCMTimeInvalid))
+ return;
+
+ m_durationInMs.storeRelease(CMTimeGetSeconds(duration) * 1000);
+ m_lastTimeStamp = newTimeStamp;
+ }
+}
+
+- (qint64)durationInMs
+{
+ return m_durationInMs.loadAcquire();
+}
+
+@end
diff --git a/src/multimedia/platform/avfoundation/camera/avfmediaassetwriter_p.h b/src/multimedia/platform/avfoundation/camera/avfmediaassetwriter_p.h
new file mode 100644
index 000000000..61d7d46bc
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediaassetwriter_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFMEDIAASSETWRITER_H
+#define AVFMEDIAASSETWRITER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "avfcamerautility.h"
+
+#include <QtCore/qglobal.h>
+
+#include <AVFoundation/AVFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFMediaRecorderControlIOS;
+class AVFCameraService;
+
+QT_END_NAMESPACE
+
+@interface QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) : NSObject<AVCaptureVideoDataOutputSampleBufferDelegate,
+ AVCaptureAudioDataOutputSampleBufferDelegate>
+- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(AVFMediaRecorderControlIOS) *)delegate;
+
+- (bool)setupWithFileURL:(NSURL *)fileURL
+ cameraService:(QT_PREPEND_NAMESPACE(AVFCameraService) *)service
+ audioSettings:(NSDictionary *)audioSettings
+ videoSettings:(NSDictionary *)videoSettings
+ transform:(CGAffineTransform)transform;
+
+// This to be called from the recorder control's thread:
+- (void)start;
+- (void)stop;
+// This to be called from the recorder control's dtor:
+- (void)abort;
+- (qint64)durationInMs;
+
+@end
+
+#endif // AVFMEDIAASSETWRITER_H
diff --git a/src/multimedia/platform/avfoundation/camera/avfmediacontainercontrol.mm b/src/multimedia/platform/avfoundation/camera/avfmediacontainercontrol.mm
new file mode 100644
index 000000000..09049de0b
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediacontainercontrol.mm
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** 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 "avfmediacontainercontrol_p.h"
+
+#include <AVFoundation/AVMediaFormat.h>
+#include <QtCore/qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+struct ContainerInfo
+{
+ QString description;
+ NSString *fileType;
+
+ ContainerInfo() : fileType(nil) { }
+ ContainerInfo(const QString &desc, NSString *type)
+ : description(desc), fileType(type)
+ { }
+};
+
+typedef QMap<QString, ContainerInfo> SupportedContainers;
+Q_GLOBAL_STATIC(SupportedContainers, containers);
+
+AVFMediaContainerControl::AVFMediaContainerControl(AVFCameraService *)
+ : QMediaContainerControl()
+ , m_format(QStringLiteral("mov")) // .mov is the default container format on Apple platforms
+{
+ if (containers->isEmpty()) {
+ containers->insert(QStringLiteral("mov"),
+ ContainerInfo(QStringLiteral("QuickTime movie file format"),
+ AVFileTypeQuickTimeMovie));
+ containers->insert(QStringLiteral("mp4"),
+ ContainerInfo(QStringLiteral("MPEG-4 file format"),
+ AVFileTypeMPEG4));
+ containers->insert(QStringLiteral("m4v"),
+ ContainerInfo(QStringLiteral("iTunes video file format"),
+ AVFileTypeAppleM4V));
+#ifdef Q_OS_IOS
+ containers->insert(QStringLiteral("3gp"),
+ ContainerInfo(QStringLiteral("3GPP file format"),
+ AVFileType3GPP));
+#endif
+ }
+}
+
+QStringList AVFMediaContainerControl::supportedContainers() const
+{
+ return containers->keys();
+}
+
+QString AVFMediaContainerControl::containerFormat() const
+{
+ return m_format;
+}
+
+void AVFMediaContainerControl::setContainerFormat(const QString &format)
+{
+ if (!containers->contains(format)) {
+ qWarning("Unsupported container format: '%s'", format.toLocal8Bit().constData());
+ return;
+ }
+
+ m_format = format;
+}
+
+QString AVFMediaContainerControl::containerDescription(const QString &formatMimeType) const
+{
+ return containers->value(formatMimeType).description;
+}
+
+NSString *AVFMediaContainerControl::fileType() const
+{
+ return containers->value(m_format).fileType;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/avfoundation/camera/avfmediacontainercontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfmediacontainercontrol_p.h
new file mode 100644
index 000000000..9450dc16a
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediacontainercontrol_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFMEDIACONTAINERCONTROL_H
+#define AVFMEDIACONTAINERCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmediacontainercontrol.h>
+
+@class NSString;
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraService;
+
+class AVFMediaContainerControl : public QMediaContainerControl
+{
+public:
+ explicit AVFMediaContainerControl(AVFCameraService *service);
+
+ QStringList supportedContainers() const override;
+ QString containerFormat() const override;
+ void setContainerFormat(const QString &format) override;
+ QString containerDescription(const QString &formatMimeType) const override;
+
+ NSString *fileType() const;
+
+private:
+ QString m_format;
+};
+
+QT_END_NAMESPACE
+
+#endif // AVFMEDIACONTAINERCONTROL_H
diff --git a/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol.mm b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol.mm
new file mode 100644
index 000000000..27f78fea4
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol.mm
@@ -0,0 +1,430 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "avfcameradebug_p.h"
+#include "avfmediarecordercontrol_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcameracontrol_p.h"
+#include "avfaudioinputselectorcontrol_p.h"
+#include "avfaudioencodersettingscontrol_p.h"
+#include "avfvideoencodersettingscontrol_p.h"
+#include "avfmediacontainercontrol_p.h"
+
+#include <QtCore/qurl.h>
+#include <QtCore/qfileinfo.h>
+#include <QtMultimedia/qcameracontrol.h>
+
+
+QT_USE_NAMESPACE
+
+@interface AVFMediaRecorderDelegate : NSObject <AVCaptureFileOutputRecordingDelegate>
+{
+@private
+ AVFMediaRecorderControl *m_recorder;
+}
+
+- (AVFMediaRecorderDelegate *) initWithRecorder:(AVFMediaRecorderControl*)recorder;
+
+- (void) captureOutput:(AVCaptureFileOutput *)captureOutput
+ didStartRecordingToOutputFileAtURL:(NSURL *)fileURL
+ fromConnections:(NSArray *)connections;
+
+- (void) captureOutput:(AVCaptureFileOutput *)captureOutput
+ didFinishRecordingToOutputFileAtURL:(NSURL *)fileURL
+ fromConnections:(NSArray *)connections
+ error:(NSError *)error;
+@end
+
+@implementation AVFMediaRecorderDelegate
+
+- (AVFMediaRecorderDelegate *) initWithRecorder:(AVFMediaRecorderControl*)recorder
+{
+ if (!(self = [super init]))
+ return nil;
+
+ self->m_recorder = recorder;
+ return self;
+}
+
+- (void) captureOutput:(AVCaptureFileOutput *)captureOutput
+ didStartRecordingToOutputFileAtURL:(NSURL *)fileURL
+ fromConnections:(NSArray *)connections
+{
+ Q_UNUSED(captureOutput);
+ Q_UNUSED(fileURL);
+ Q_UNUSED(connections);
+
+ QMetaObject::invokeMethod(m_recorder, "handleRecordingStarted", Qt::QueuedConnection);
+}
+
+- (void) captureOutput:(AVCaptureFileOutput *)captureOutput
+ didFinishRecordingToOutputFileAtURL:(NSURL *)fileURL
+ fromConnections:(NSArray *)connections
+ error:(NSError *)error
+{
+ Q_UNUSED(captureOutput);
+ Q_UNUSED(fileURL);
+ Q_UNUSED(connections);
+
+ if (error) {
+ QStringList messageParts;
+ messageParts << QString::fromUtf8([[error localizedDescription] UTF8String]);
+ messageParts << QString::fromUtf8([[error localizedFailureReason] UTF8String]);
+ messageParts << QString::fromUtf8([[error localizedRecoverySuggestion] UTF8String]);
+
+ QString errorMessage = messageParts.join(" ");
+
+ QMetaObject::invokeMethod(m_recorder, "handleRecordingFailed", Qt::QueuedConnection,
+ Q_ARG(QString, errorMessage));
+ } else {
+ QMetaObject::invokeMethod(m_recorder, "handleRecordingFinished", Qt::QueuedConnection);
+ }
+}
+
+@end
+
+
+AVFMediaRecorderControl::AVFMediaRecorderControl(AVFCameraService *service, QObject *parent)
+ : QMediaRecorderControl(parent)
+ , m_service(service)
+ , m_cameraControl(service->cameraControl())
+ , m_audioInputControl(service->audioInputSelectorControl())
+ , m_session(service->session())
+ , m_connected(false)
+ , m_state(QMediaRecorder::StoppedState)
+ , m_lastStatus(QMediaRecorder::UnloadedStatus)
+ , m_recordingStarted(false)
+ , m_recordingFinished(false)
+ , m_muted(false)
+ , m_volume(1.0)
+ , m_audioInput(nil)
+ , m_restoreFPS(-1, -1)
+{
+ m_movieOutput = [[AVCaptureMovieFileOutput alloc] init];
+ m_recorderDelagate = [[AVFMediaRecorderDelegate alloc] initWithRecorder:this];
+
+ connect(m_cameraControl, SIGNAL(stateChanged(QCamera::State)), SLOT(updateStatus()));
+ connect(m_cameraControl, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateStatus()));
+ connect(m_cameraControl, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(setupSessionForCapture()));
+ connect(m_session, SIGNAL(readyToConfigureConnections()), SLOT(setupSessionForCapture()));
+ connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(setupSessionForCapture()));
+}
+
+AVFMediaRecorderControl::~AVFMediaRecorderControl()
+{
+ if (m_movieOutput) {
+ [m_session->captureSession() removeOutput:m_movieOutput];
+ [m_movieOutput release];
+ }
+
+ if (m_audioInput) {
+ [m_session->captureSession() removeInput:m_audioInput];
+ [m_audioInput release];
+ }
+
+ [m_recorderDelagate release];
+}
+
+QUrl AVFMediaRecorderControl::outputLocation() const
+{
+ return m_outputLocation;
+}
+
+bool AVFMediaRecorderControl::setOutputLocation(const QUrl &location)
+{
+ m_outputLocation = location;
+ return location.scheme() == QLatin1String("file") || location.scheme().isEmpty();
+}
+
+QMediaRecorder::State AVFMediaRecorderControl::state() const
+{
+ return m_state;
+}
+
+QMediaRecorder::Status AVFMediaRecorderControl::status() const
+{
+ QMediaRecorder::Status status = m_lastStatus;
+ //bool videoEnabled = m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo);
+
+ if (m_cameraControl->status() == QCamera::ActiveStatus && m_connected) {
+ if (m_state == QMediaRecorder::StoppedState) {
+ if (m_recordingStarted && !m_recordingFinished)
+ status = QMediaRecorder::FinalizingStatus;
+ else
+ status = QMediaRecorder::LoadedStatus;
+ } else {
+ status = m_recordingStarted ? QMediaRecorder::RecordingStatus :
+ QMediaRecorder::StartingStatus;
+ }
+ } else {
+ //camera not started yet
+ status = m_cameraControl->state() == QCamera::ActiveState && m_connected ?
+ QMediaRecorder::LoadingStatus:
+ QMediaRecorder::UnloadedStatus;
+ }
+
+ return status;
+}
+
+void AVFMediaRecorderControl::updateStatus()
+{
+ QMediaRecorder::Status newStatus = status();
+
+ if (m_lastStatus != newStatus) {
+ qDebugCamera() << "Camera recorder status changed: " << m_lastStatus << " -> " << newStatus;
+ m_lastStatus = newStatus;
+ Q_EMIT statusChanged(m_lastStatus);
+ }
+}
+
+
+qint64 AVFMediaRecorderControl::duration() const
+{
+ if (!m_movieOutput)
+ return 0;
+
+ return qint64(CMTimeGetSeconds(m_movieOutput.recordedDuration) * 1000);
+}
+
+bool AVFMediaRecorderControl::isMuted() const
+{
+ return m_muted;
+}
+
+qreal AVFMediaRecorderControl::volume() const
+{
+ return m_volume;
+}
+
+void AVFMediaRecorderControl::applySettings()
+{
+ if (m_state != QMediaRecorder::StoppedState
+ || (m_session->state() != QCamera::ActiveState && m_session->state() != QCamera::LoadedState)
+ || !m_service->cameraControl()->captureMode().testFlag(QCamera::CaptureVideo)) {
+ return;
+ }
+
+ // Configure audio settings
+ [m_movieOutput setOutputSettings:m_service->audioEncoderSettingsControl()->applySettings()
+ forConnection:[m_movieOutput connectionWithMediaType:AVMediaTypeAudio]];
+
+ // Configure video settings
+ AVCaptureConnection *videoConnection = [m_movieOutput connectionWithMediaType:AVMediaTypeVideo];
+ NSDictionary *videoSettings = m_service->videoEncoderSettingsControl()->applySettings(videoConnection);
+
+ const AVFConfigurationLock lock(m_session->videoCaptureDevice()); // prevents activeFormat from being overridden
+
+ [m_movieOutput setOutputSettings:videoSettings forConnection:videoConnection];
+}
+
+void AVFMediaRecorderControl::unapplySettings()
+{
+ m_service->audioEncoderSettingsControl()->unapplySettings();
+ m_service->videoEncoderSettingsControl()->unapplySettings([m_movieOutput connectionWithMediaType:AVMediaTypeVideo]);
+}
+
+void AVFMediaRecorderControl::setState(QMediaRecorder::State state)
+{
+ if (m_state == state)
+ return;
+
+ qDebugCamera() << Q_FUNC_INFO << m_state << " -> " << state;
+
+ switch (state) {
+ case QMediaRecorder::RecordingState:
+ {
+ if (m_connected) {
+ QString outputLocationPath = m_outputLocation.scheme() == QLatin1String("file") ?
+ m_outputLocation.path() : m_outputLocation.toString();
+
+ QString extension = m_service->mediaContainerControl()->containerFormat();
+
+ QUrl actualLocation = QUrl::fromLocalFile(
+ m_storageLocation.generateFileName(outputLocationPath,
+ QCamera::CaptureVideo,
+ QLatin1String("clip_"),
+ extension));
+
+ qDebugCamera() << "Video capture location:" << actualLocation.toString();
+
+ applySettings();
+
+ [m_movieOutput startRecordingToOutputFileURL:actualLocation.toNSURL()
+ recordingDelegate:m_recorderDelagate];
+
+ m_state = QMediaRecorder::RecordingState;
+ m_recordingStarted = false;
+ m_recordingFinished = false;
+
+ Q_EMIT actualLocationChanged(actualLocation);
+ updateStatus();
+ Q_EMIT stateChanged(m_state);
+ } else {
+ Q_EMIT error(QMediaRecorder::FormatError, tr("Recorder not configured"));
+ }
+
+ } break;
+ case QMediaRecorder::PausedState:
+ {
+ Q_EMIT error(QMediaRecorder::FormatError, tr("Recording pause not supported"));
+ return;
+ } break;
+ case QMediaRecorder::StoppedState:
+ {
+ m_lastStatus = QMediaRecorder::FinalizingStatus;
+ Q_EMIT statusChanged(m_lastStatus);
+ [m_movieOutput stopRecording];
+ unapplySettings();
+ }
+ }
+}
+
+void AVFMediaRecorderControl::setMuted(bool muted)
+{
+ if (m_muted != muted) {
+ m_muted = muted;
+ Q_EMIT mutedChanged(muted);
+ }
+}
+
+void AVFMediaRecorderControl::setVolume(qreal volume)
+{
+ if (m_volume != volume) {
+ m_volume = volume;
+ Q_EMIT volumeChanged(volume);
+ }
+}
+
+void AVFMediaRecorderControl::handleRecordingStarted()
+{
+ m_recordingStarted = true;
+ updateStatus();
+}
+
+void AVFMediaRecorderControl::handleRecordingFinished()
+{
+ m_recordingFinished = true;
+ if (m_state != QMediaRecorder::StoppedState) {
+ m_state = QMediaRecorder::StoppedState;
+ Q_EMIT stateChanged(m_state);
+ }
+ updateStatus();
+}
+
+void AVFMediaRecorderControl::handleRecordingFailed(const QString &message)
+{
+ m_recordingFinished = true;
+ if (m_state != QMediaRecorder::StoppedState) {
+ m_state = QMediaRecorder::StoppedState;
+ Q_EMIT stateChanged(m_state);
+ }
+ updateStatus();
+
+ Q_EMIT error(QMediaRecorder::ResourceError, message);
+}
+
+void AVFMediaRecorderControl::setupSessionForCapture()
+{
+ if (!m_session->videoCaptureDevice())
+ return;
+
+ //adding movie output causes high CPU usage even when while recording is not active,
+ //connect it only while video capture mode is enabled.
+ // Similarly, connect the Audio input only in that mode, since it's only necessary
+ // when recording anyway. Adding an Audio input will trigger the microphone permission
+ // request on iOS, but it shoudn't do so until we actually try to record.
+ AVCaptureSession *captureSession = m_session->captureSession();
+
+ if (!m_connected
+ && m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo)
+ && m_session->state() != QCamera::UnloadedState) {
+
+ // Lock the video capture device to make sure the active format is not reset
+ const AVFConfigurationLock lock(m_session->videoCaptureDevice());
+
+ // Add audio input
+ // Allow recording even if something wrong happens with the audio input initialization
+ AVCaptureDevice *audioDevice = m_audioInputControl->createCaptureDevice();
+ if (!audioDevice) {
+ qWarning("No audio input device available");
+ } else {
+ NSError *error = nil;
+ m_audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];
+
+ if (!m_audioInput) {
+ qWarning() << "Failed to create audio device input";
+ } else if (![captureSession canAddInput:m_audioInput]) {
+ qWarning() << "Could not connect the audio input";
+ m_audioInput = nullptr;
+ } else {
+ [m_audioInput retain];
+ [captureSession addInput:m_audioInput];
+ }
+ }
+
+ if ([captureSession canAddOutput:m_movieOutput]) {
+ [captureSession addOutput:m_movieOutput];
+ m_connected = true;
+ } else {
+ Q_EMIT error(QMediaRecorder::ResourceError, tr("Could not connect the video recorder"));
+ qWarning() << "Could not connect the video recorder";
+ }
+ } else if (m_connected
+ && (!m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo)
+ || m_session->state() == QCamera::UnloadedState)) {
+
+ // Lock the video capture device to make sure the active format is not reset
+ const AVFConfigurationLock lock(m_session->videoCaptureDevice());
+
+ [captureSession removeOutput:m_movieOutput];
+
+ if (m_audioInput) {
+ [captureSession removeInput:m_audioInput];
+ [m_audioInput release];
+ m_audioInput = nil;
+ }
+
+ m_connected = false;
+ }
+
+ updateStatus();
+}
+
+#include "moc_avfmediarecordercontrol_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios.mm b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios.mm
new file mode 100644
index 000000000..33064827d
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios.mm
@@ -0,0 +1,414 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+
+#include "avfmediarecordercontrol_ios.h"
+#include "avfcamerarenderercontrol.h"
+#include "avfcamerasession.h"
+#include "avfcameracontrol.h"
+#include "avfcameraservice.h"
+#include "avfcameradebug.h"
+#include "avfaudioencodersettingscontrol.h"
+#include "avfvideoencodersettingscontrol.h"
+#include "avfmediacontainercontrol.h"
+#include "avfcamerautility.h"
+
+#include <QtCore/qmath.h>
+#include <QtCore/qdebug.h>
+
+QT_USE_NAMESPACE
+
+namespace {
+
+bool qt_is_writable_file_URL(NSURL *fileURL)
+{
+ Q_ASSERT(fileURL);
+
+ if (![fileURL isFileURL])
+ return false;
+
+ if (NSString *path = [[fileURL path] stringByExpandingTildeInPath]) {
+ return [[NSFileManager defaultManager]
+ isWritableFileAtPath:[path stringByDeletingLastPathComponent]];
+ }
+
+ return false;
+}
+
+bool qt_file_exists(NSURL *fileURL)
+{
+ Q_ASSERT(fileURL);
+
+ if (NSString *path = [[fileURL path] stringByExpandingTildeInPath])
+ return [[NSFileManager defaultManager] fileExistsAtPath:path];
+
+ return false;
+}
+
+}
+
+AVFMediaRecorderControlIOS::AVFMediaRecorderControlIOS(AVFCameraService *service, QObject *parent)
+ : QMediaRecorderControl(parent)
+ , m_service(service)
+ , m_state(QMediaRecorder::StoppedState)
+ , m_lastStatus(QMediaRecorder::UnloadedStatus)
+ , m_audioSettings(nil)
+ , m_videoSettings(nil)
+{
+ Q_ASSERT(service);
+
+ m_writer.reset([[QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) alloc] initWithDelegate:this]);
+ if (!m_writer) {
+ qDebugCamera() << Q_FUNC_INFO << "failed to create an asset writer";
+ return;
+ }
+
+ AVFCameraControl *cameraControl = m_service->cameraControl();
+ if (!cameraControl) {
+ qDebugCamera() << Q_FUNC_INFO << "camera control is nil";
+ return;
+ }
+
+ connect(cameraControl, SIGNAL(captureModeChanged(QCamera::CaptureModes)),
+ SLOT(captureModeChanged(QCamera::CaptureModes)));
+ connect(cameraControl, SIGNAL(statusChanged(QCamera::Status)),
+ SLOT(cameraStatusChanged(QCamera::Status)));
+}
+
+AVFMediaRecorderControlIOS::~AVFMediaRecorderControlIOS()
+{
+ [m_writer abort];
+
+ if (m_audioSettings)
+ [m_audioSettings release];
+ if (m_videoSettings)
+ [m_videoSettings release];
+}
+
+QUrl AVFMediaRecorderControlIOS::outputLocation() const
+{
+ return m_outputLocation;
+}
+
+bool AVFMediaRecorderControlIOS::setOutputLocation(const QUrl &location)
+{
+ m_outputLocation = location;
+ return location.scheme() == QLatin1String("file") || location.scheme().isEmpty();
+}
+
+QMediaRecorder::State AVFMediaRecorderControlIOS::state() const
+{
+ return m_state;
+}
+
+QMediaRecorder::Status AVFMediaRecorderControlIOS::status() const
+{
+ return m_lastStatus;
+}
+
+qint64 AVFMediaRecorderControlIOS::duration() const
+{
+ return m_writer.data().durationInMs;
+}
+
+bool AVFMediaRecorderControlIOS::isMuted() const
+{
+ return false;
+}
+
+qreal AVFMediaRecorderControlIOS::volume() const
+{
+ return 1.;
+}
+
+void AVFMediaRecorderControlIOS::applySettings()
+{
+ AVFCameraSession *session = m_service->session();
+ if (!session)
+ return;
+
+ if (m_state != QMediaRecorder::StoppedState
+ || (session->state() != QCamera::ActiveState && session->state() != QCamera::LoadedState)
+ || !m_service->cameraControl()->captureMode().testFlag(QCamera::CaptureVideo)) {
+ return;
+ }
+
+ // audio settings
+ m_audioSettings = m_service->audioEncoderSettingsControl()->applySettings();
+ if (m_audioSettings)
+ [m_audioSettings retain];
+
+ // video settings
+ AVCaptureConnection *conn = [m_service->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo];
+ m_videoSettings = m_service->videoEncoderSettingsControl()->applySettings(conn);
+ if (m_videoSettings)
+ [m_videoSettings retain];
+}
+
+void AVFMediaRecorderControlIOS::unapplySettings()
+{
+ m_service->audioEncoderSettingsControl()->unapplySettings();
+
+ AVCaptureConnection *conn = [m_service->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo];
+ m_service->videoEncoderSettingsControl()->unapplySettings(conn);
+
+ if (m_audioSettings) {
+ [m_audioSettings release];
+ m_audioSettings = nil;
+ }
+ if (m_videoSettings) {
+ [m_videoSettings release];
+ m_videoSettings = nil;
+ }
+}
+
+void AVFMediaRecorderControlIOS::setState(QMediaRecorder::State state)
+{
+ Q_ASSERT(m_service->session()
+ && m_service->session()->captureSession());
+
+ if (!m_writer) {
+ qDebugCamera() << Q_FUNC_INFO << "Invalid recorder";
+ return;
+ }
+
+ if (state == m_state)
+ return;
+
+ switch (state) {
+ case QMediaRecorder::RecordingState:
+ {
+ AVFCameraControl *cameraControl = m_service->cameraControl();
+ Q_ASSERT(cameraControl);
+
+ if (!(cameraControl->captureMode() & QCamera::CaptureVideo)) {
+ qDebugCamera() << Q_FUNC_INFO << "wrong capture mode, CaptureVideo expected";
+ Q_EMIT error(QMediaRecorder::ResourceError, tr("Failed to start recording"));
+ return;
+ }
+
+ if (cameraControl->status() != QCamera::ActiveStatus) {
+ qDebugCamera() << Q_FUNC_INFO << "can not start record while camera is not active";
+ Q_EMIT error(QMediaRecorder::ResourceError, tr("Failed to start recording"));
+ return;
+ }
+
+ const QString path(m_outputLocation.scheme() == QLatin1String("file") ?
+ m_outputLocation.path() : m_outputLocation.toString());
+ const QUrl fileURL(QUrl::fromLocalFile(m_storageLocation.generateFileName(path, QCamera::CaptureVideo,
+ QLatin1String("clip_"),
+ m_service->mediaContainerControl()->containerFormat())));
+
+ NSURL *nsFileURL = fileURL.toNSURL();
+ if (!nsFileURL) {
+ qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL;
+ Q_EMIT error(QMediaRecorder::ResourceError, tr("Invalid output file URL"));
+ return;
+ }
+ if (!qt_is_writable_file_URL(nsFileURL)) {
+ qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL
+ << "(the location is not writable)";
+ Q_EMIT error(QMediaRecorder::ResourceError, tr("Non-writeable file location"));
+ return;
+ }
+ if (qt_file_exists(nsFileURL)) {
+ // We test for/handle this error here since AWAssetWriter will raise an
+ // Objective-C exception, which is not good at all.
+ qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL
+ << "(file already exists)";
+ Q_EMIT error(QMediaRecorder::ResourceError, tr("File already exists"));
+ return;
+ }
+
+ AVCaptureSession *session = m_service->session()->captureSession();
+ // We stop session now so that no more frames for renderer's queue
+ // generated, will restart in assetWriterStarted.
+ [session stopRunning];
+
+ applySettings();
+
+ // Make sure the video is recorded in device orientation.
+ // The top of the video will match the side of the device which is on top
+ // when recording starts (regardless of the UI orientation).
+ AVFCameraInfo cameraInfo = m_service->session()->activeCameraInfo();
+ int screenOrientation = 360 - m_orientationHandler.currentOrientation();
+ float rotation = 0;
+ if (cameraInfo.position == QCamera::FrontFace)
+ rotation = (screenOrientation + cameraInfo.orientation) % 360;
+ else
+ rotation = (screenOrientation + (360 - cameraInfo.orientation)) % 360;
+
+ if ([m_writer setupWithFileURL:nsFileURL
+ cameraService:m_service
+ audioSettings:m_audioSettings
+ videoSettings:m_videoSettings
+ transform:CGAffineTransformMakeRotation(qDegreesToRadians(rotation))]) {
+
+ m_state = QMediaRecorder::RecordingState;
+ m_lastStatus = QMediaRecorder::StartingStatus;
+
+ Q_EMIT actualLocationChanged(fileURL);
+ Q_EMIT stateChanged(m_state);
+ Q_EMIT statusChanged(m_lastStatus);
+
+ // Apple recommends to call startRunning and do all
+ // setup on a special queue, and that's what we had
+ // initially (dispatch_async to writerQueue). Unfortunately,
+ // writer's queue is not the only queue/thread that can
+ // access/modify the session, and as a result we have
+ // all possible data/race-conditions with Obj-C exceptions
+ // at best and something worse in general.
+ // Now we try to only modify session on the same thread.
+ [m_writer start];
+ } else {
+ [session startRunning];
+ Q_EMIT error(QMediaRecorder::FormatError, tr("Failed to start recording"));
+ }
+ } break;
+ case QMediaRecorder::PausedState:
+ {
+ Q_EMIT error(QMediaRecorder::FormatError, tr("Recording pause not supported"));
+ return;
+ } break;
+ case QMediaRecorder::StoppedState:
+ {
+ // Do not check the camera status, we can stop if we started.
+ stopWriter();
+ }
+ }
+}
+
+void AVFMediaRecorderControlIOS::setMuted(bool muted)
+{
+ Q_UNUSED(muted);
+ qDebugCamera() << Q_FUNC_INFO << "not implemented";
+}
+
+void AVFMediaRecorderControlIOS::setVolume(qreal volume)
+{
+ Q_UNUSED(volume);
+ qDebugCamera() << Q_FUNC_INFO << "not implemented";
+}
+
+void AVFMediaRecorderControlIOS::assetWriterStarted()
+{
+ m_lastStatus = QMediaRecorder::RecordingStatus;
+ Q_EMIT statusChanged(QMediaRecorder::RecordingStatus);
+}
+
+void AVFMediaRecorderControlIOS::assetWriterFinished()
+{
+ AVFCameraControl *cameraControl = m_service->cameraControl();
+ Q_ASSERT(cameraControl);
+
+ const QMediaRecorder::Status lastStatus = m_lastStatus;
+ const QMediaRecorder::State lastState = m_state;
+ if (cameraControl->captureMode() & QCamera::CaptureVideo)
+ m_lastStatus = QMediaRecorder::LoadedStatus;
+ else
+ m_lastStatus = QMediaRecorder::UnloadedStatus;
+
+ unapplySettings();
+
+ m_service->videoOutput()->resetCaptureDelegate();
+ [m_service->session()->captureSession() startRunning];
+ m_state = QMediaRecorder::StoppedState;
+ if (m_lastStatus != lastStatus)
+ Q_EMIT statusChanged(m_lastStatus);
+ if (m_state != lastState)
+ Q_EMIT stateChanged(m_state);
+}
+
+void AVFMediaRecorderControlIOS::captureModeChanged(QCamera::CaptureModes newMode)
+{
+ AVFCameraControl *cameraControl = m_service->cameraControl();
+ Q_ASSERT(cameraControl);
+
+ const QMediaRecorder::Status lastStatus = m_lastStatus;
+
+ if (newMode & QCamera::CaptureVideo) {
+ if (cameraControl->status() == QCamera::ActiveStatus)
+ m_lastStatus = QMediaRecorder::LoadedStatus;
+ } else {
+ if (m_lastStatus == QMediaRecorder::RecordingStatus)
+ return stopWriter();
+ else
+ m_lastStatus = QMediaRecorder::UnloadedStatus;
+ }
+
+ if (m_lastStatus != lastStatus)
+ Q_EMIT statusChanged(m_lastStatus);
+}
+
+void AVFMediaRecorderControlIOS::cameraStatusChanged(QCamera::Status newStatus)
+{
+ AVFCameraControl *cameraControl = m_service->cameraControl();
+ Q_ASSERT(cameraControl);
+
+ const QMediaRecorder::Status lastStatus = m_lastStatus;
+ const bool isCapture = cameraControl->captureMode() & QCamera::CaptureVideo;
+ if (newStatus == QCamera::StartingStatus) {
+ if (isCapture && m_lastStatus == QMediaRecorder::UnloadedStatus)
+ m_lastStatus = QMediaRecorder::LoadingStatus;
+ } else if (newStatus == QCamera::ActiveStatus) {
+ if (isCapture && m_lastStatus == QMediaRecorder::LoadingStatus)
+ m_lastStatus = QMediaRecorder::LoadedStatus;
+ } else {
+ if (m_lastStatus == QMediaRecorder::RecordingStatus)
+ return stopWriter();
+ if (newStatus == QCamera::UnloadedStatus)
+ m_lastStatus = QMediaRecorder::UnloadedStatus;
+ }
+
+ if (lastStatus != m_lastStatus)
+ Q_EMIT statusChanged(m_lastStatus);
+}
+
+void AVFMediaRecorderControlIOS::stopWriter()
+{
+ if (m_lastStatus == QMediaRecorder::RecordingStatus) {
+ m_lastStatus = QMediaRecorder::FinalizingStatus;
+
+ Q_EMIT statusChanged(m_lastStatus);
+
+ [m_writer stop];
+ }
+}
+
+#include "moc_avfmediarecordercontrol_ios.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios_p.h b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios_p.h
new file mode 100644
index 000000000..178c75cad
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFMEDIARECORDERCONTROL_IOS_H
+#define AVFMEDIARECORDERCONTROL_IOS_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "avfmediaassetwriter.h"
+#include "avfstoragelocation.h"
+#include "avfcamerautility.h"
+
+#include <QtMultimedia/qmediarecordercontrol.h>
+#include <private/qvideooutputorientationhandler_p.h>
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qurl.h>
+
+#include <AVFoundation/AVFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraService;
+class QString;
+class QUrl;
+
+class AVFMediaRecorderControlIOS : public QMediaRecorderControl
+{
+ Q_OBJECT
+public:
+ AVFMediaRecorderControlIOS(AVFCameraService *service, QObject *parent = nullptr);
+ ~AVFMediaRecorderControlIOS() override;
+
+ QUrl outputLocation() const override;
+ bool setOutputLocation(const QUrl &location) override;
+
+ QMediaRecorder::State state() const override;
+ QMediaRecorder::Status status() const override;
+
+ qint64 duration() const override;
+
+ bool isMuted() const override;
+ qreal volume() const override;
+
+ void applySettings() override;
+ void unapplySettings();
+
+public Q_SLOTS:
+ void setState(QMediaRecorder::State state) override;
+ void setMuted(bool muted) override;
+ void setVolume(qreal volume) override;
+
+private:
+
+ Q_INVOKABLE void assetWriterStarted();
+ Q_INVOKABLE void assetWriterFinished();
+
+private Q_SLOTS:
+ void captureModeChanged(QCamera::CaptureModes);
+ void cameraStatusChanged(QCamera::Status newStatus);
+
+private:
+ void stopWriter();
+
+ AVFCameraService *m_service;
+ AVFScopedPointer<QT_MANGLE_NAMESPACE(AVFMediaAssetWriter)> m_writer;
+
+ QUrl m_outputLocation;
+ AVFStorageLocation m_storageLocation;
+
+ QMediaRecorder::State m_state;
+ QMediaRecorder::Status m_lastStatus;
+
+ NSDictionary *m_audioSettings;
+ NSDictionary *m_videoSettings;
+ QVideoOutputOrientationHandler m_orientationHandler;
+};
+
+QT_END_NAMESPACE
+
+#endif // AVFMEDIARECORDERCONTROL_IOS_H
diff --git a/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_p.h
new file mode 100644
index 000000000..9d8f6566b
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_p.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFMEDIARECORDERCONTROL_H
+#define AVFMEDIARECORDERCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qurl.h>
+#include <QtMultimedia/qmediarecordercontrol.h>
+
+#import <AVFoundation/AVFoundation.h>
+#include "avfstoragelocation_p.h"
+#include "avfcamerautility_p.h"
+
+@class AVFMediaRecorderDelegate;
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraSession;
+class AVFCameraControl;
+class AVFAudioInputSelectorControl;
+class AVFCameraService;
+
+class AVFMediaRecorderControl : public QMediaRecorderControl
+{
+Q_OBJECT
+public:
+ AVFMediaRecorderControl(AVFCameraService *service, QObject *parent = nullptr);
+ ~AVFMediaRecorderControl();
+
+ QUrl outputLocation() const override;
+ bool setOutputLocation(const QUrl &location) override;
+
+ QMediaRecorder::State state() const override;
+ QMediaRecorder::Status status() const override;
+
+ qint64 duration() const override;
+
+ bool isMuted() const override;
+ qreal volume() const override;
+
+ void applySettings() override;
+ void unapplySettings();
+
+public Q_SLOTS:
+ void setState(QMediaRecorder::State state) override;
+ void setMuted(bool muted) override;
+ void setVolume(qreal volume) override;
+
+ void handleRecordingStarted();
+ void handleRecordingFinished();
+ void handleRecordingFailed(const QString &message);
+
+private Q_SLOTS:
+ void setupSessionForCapture();
+ void updateStatus();
+
+private:
+ AVFCameraService *m_service;
+ AVFCameraControl *m_cameraControl;
+ AVFAudioInputSelectorControl *m_audioInputControl;
+ AVFCameraSession *m_session;
+
+ bool m_connected;
+ QUrl m_outputLocation;
+ QMediaRecorder::State m_state;
+ QMediaRecorder::Status m_lastStatus;
+
+ bool m_recordingStarted;
+ bool m_recordingFinished;
+
+ bool m_muted;
+ qreal m_volume;
+
+ AVCaptureDeviceInput *m_audioInput;
+ AVCaptureMovieFileOutput *m_movieOutput;
+ AVFMediaRecorderDelegate *m_recorderDelagate;
+ AVFStorageLocation m_storageLocation;
+
+ AVFPSRange m_restoreFPS;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol.mm b/src/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol.mm
new file mode 100644
index 000000000..c97ab1919
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol.mm
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Copyright (C) 2016 Integrated Computer Solutions, Inc
+** 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 "avfmediavideoprobecontrol_p.h"
+#include <qvideoframe.h>
+
+QT_BEGIN_NAMESPACE
+
+AVFMediaVideoProbeControl::AVFMediaVideoProbeControl(QObject *parent) :
+ QMediaVideoProbeControl(parent)
+{
+}
+
+AVFMediaVideoProbeControl::~AVFMediaVideoProbeControl()
+{
+
+}
+
+void AVFMediaVideoProbeControl::newFrameProbed(const QVideoFrame &frame)
+{
+ Q_EMIT videoFrameProbed(frame);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol_p.h
new file mode 100644
index 000000000..c18bc4181
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFMEDIAVIDEOPROBECONTROL_H
+#define AVFMEDIAVIDEOPROBECONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmediavideoprobecontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFMediaVideoProbeControl : public QMediaVideoProbeControl
+{
+ Q_OBJECT
+public:
+ explicit AVFMediaVideoProbeControl(QObject *parent = nullptr);
+ ~AVFMediaVideoProbeControl();
+
+ void newFrameProbed(const QVideoFrame& frame);
+
+};
+
+QT_END_NAMESPACE
+
+#endif // AVFMEDIAVIDEOPROBECONTROL_H
diff --git a/src/multimedia/platform/avfoundation/camera/avfstoragelocation.mm b/src/multimedia/platform/avfoundation/camera/avfstoragelocation.mm
new file mode 100644
index 000000000..1855f8ec7
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfstoragelocation.mm
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "avfstoragelocation_p.h"
+#include "QtCore/qstandardpaths.h"
+
+
+QT_BEGIN_NAMESPACE
+
+
+AVFStorageLocation::AVFStorageLocation()
+{
+}
+
+AVFStorageLocation::~AVFStorageLocation()
+{
+}
+
+/*!
+ * Generate the actual file name from user requested one.
+ * requestedName may be either empty (the default dir and naming theme is used),
+ * points to existing dir (the default name used)
+ * or specify the full actual path.
+ */
+QString AVFStorageLocation::generateFileName(const QString &requestedName,
+ QCamera::CaptureMode mode,
+ const QString &prefix,
+ const QString &ext) const
+{
+ if (requestedName.isEmpty())
+ return generateFileName(prefix, defaultDir(mode), ext);
+
+ if (QFileInfo(requestedName).isDir())
+ return generateFileName(prefix, QDir(requestedName), ext);
+
+ return requestedName;
+}
+
+QDir AVFStorageLocation::defaultDir(QCamera::CaptureMode mode) const
+{
+ QStringList dirCandidates;
+
+ if (mode == QCamera::CaptureVideo) {
+ dirCandidates << QStandardPaths::writableLocation(QStandardPaths::MoviesLocation);
+ } else {
+ dirCandidates << QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
+ }
+
+ dirCandidates << QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
+ dirCandidates << QDir::homePath();
+ dirCandidates << QDir::currentPath();
+ dirCandidates << QDir::tempPath();
+
+ for (const QString &path : qAsConst(dirCandidates)) {
+ if (QFileInfo(path).isWritable())
+ return QDir(path);
+ }
+
+ return QDir();
+}
+
+QString AVFStorageLocation::generateFileName(const QString &prefix, const QDir &dir, const QString &ext) const
+{
+ QString lastClipKey = dir.absolutePath()+QLatin1Char(' ')+prefix+QLatin1Char(' ')+ext;
+ int lastClip = m_lastUsedIndex.value(lastClipKey, 0);
+
+ if (lastClip == 0) {
+ //first run, find the maximum clip number during the fist capture
+ const auto list = dir.entryList(QStringList() << QString("%1*.%2").arg(prefix).arg(ext));
+ for (const QString &fileName : list) {
+ int imgNumber = QStringView{fileName}.mid(prefix.length(), fileName.size()-prefix.length()-ext.length()-1).toInt();
+ lastClip = qMax(lastClip, imgNumber);
+ }
+ }
+
+
+ //don't just rely on cached lastClip value,
+ //someone else may create a file after camera started
+ while (true) {
+ QString name = QString("%1%2.%3").arg(prefix)
+ .arg(lastClip+1,
+ 4, //fieldWidth
+ 10,
+ QLatin1Char('0'))
+ .arg(ext);
+
+ QString path = dir.absoluteFilePath(name);
+ if (!QFileInfo(path).exists()) {
+ m_lastUsedIndex[lastClipKey] = lastClip+1;
+ return path;
+ }
+
+ lastClip++;
+ }
+
+ return QString();
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/avfoundation/camera/avfstoragelocation_p.h b/src/multimedia/platform/avfoundation/camera/avfstoragelocation_p.h
new file mode 100644
index 000000000..8794f0fae
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfstoragelocation_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFSTORAGE_H
+#define AVFSTORAGE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qtmultimediaglobal.h"
+
+#include <QtCore/qdir.h>
+#include <QtMultimedia/qcamera.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFStorageLocation
+{
+public:
+ AVFStorageLocation();
+ ~AVFStorageLocation();
+
+ QString generateFileName(const QString &requestedName,
+ QCamera::CaptureMode mode,
+ const QString &prefix,
+ const QString &ext) const;
+
+
+ QDir defaultDir(QCamera::CaptureMode mode) const;
+ QString generateFileName(const QString &prefix, const QDir &dir, const QString &ext) const;
+
+private:
+ mutable QMap<QString, int> m_lastUsedIndex;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol.mm b/src/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol.mm
new file mode 100644
index 000000000..0800e2021
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol.mm
@@ -0,0 +1,385 @@
+/****************************************************************************
+**
+** 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 "avfvideoencodersettingscontrol_p.h"
+
+#include "avfcameraservice_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcamerarenderercontrol_p.h"
+
+#include <AVFoundation/AVFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC_WITH_ARGS(QStringList, supportedCodecs, (QStringList() << QLatin1String("avc1")
+ << QLatin1String("jpeg")
+ #ifdef Q_OS_OSX
+ << QLatin1String("ap4h")
+ << QLatin1String("apcn")
+ #endif
+ ))
+
+static bool format_supports_framerate(AVCaptureDeviceFormat *format, qreal fps)
+{
+ if (format && fps > qreal(0)) {
+ const qreal epsilon = 0.1;
+ for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) {
+ if (range.maxFrameRate - range.minFrameRate < epsilon) {
+ if (qAbs(fps - range.maxFrameRate) < epsilon)
+ return true;
+ }
+
+ if (fps >= range.minFrameRate && fps <= range.maxFrameRate)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool real_list_contains(const QList<qreal> &list, qreal value)
+{
+ for (qreal r : list) {
+ if (qFuzzyCompare(r, value))
+ return true;
+ }
+ return false;
+}
+
+AVFVideoEncoderSettingsControl::AVFVideoEncoderSettingsControl(AVFCameraService *service)
+ : QVideoEncoderSettingsControl()
+ , m_service(service)
+ , m_restoreFormat(nil)
+{
+}
+
+QList<QSize> AVFVideoEncoderSettingsControl::supportedResolutions(const QVideoEncoderSettings &settings,
+ bool *continuous) const
+{
+ Q_UNUSED(settings);
+
+ if (continuous)
+ *continuous = true;
+
+ // AVFoundation seems to support any resolution for recording, with the following limitations:
+ // - The recording resolution can't be higher than the camera's active resolution
+ // - On OS X, the recording resolution is automatically adjusted to have the same aspect ratio as
+ // the camera's active resolution
+ QList<QSize> resolutions;
+ resolutions.append(QSize(32, 32));
+
+ AVCaptureDevice *device = m_service->session()->videoCaptureDevice();
+ if (device) {
+ int maximumWidth = 0;
+ const QVector<AVCaptureDeviceFormat *> formats(qt_unique_device_formats(device,
+ m_service->session()->defaultCodec()));
+ for (int i = 0; i < formats.size(); ++i) {
+ const QSize res(qt_device_format_resolution(formats[i]));
+ if (res.width() > maximumWidth)
+ maximumWidth = res.width();
+ }
+
+ if (maximumWidth > 0)
+ resolutions.append(QSize(maximumWidth, maximumWidth));
+ }
+
+ if (resolutions.count() == 1)
+ resolutions.append(QSize(3840, 3840));
+
+ return resolutions;
+}
+
+QList<qreal> AVFVideoEncoderSettingsControl::supportedFrameRates(const QVideoEncoderSettings &settings,
+ bool *continuous) const
+{
+ QList<qreal> uniqueFrameRates;
+
+ AVCaptureDevice *device = m_service->session()->videoCaptureDevice();
+ if (!device)
+ return uniqueFrameRates;
+
+ if (continuous)
+ *continuous = false;
+
+ QVector<AVFPSRange> allRates;
+
+ if (!settings.resolution().isValid()) {
+ const QVector<AVCaptureDeviceFormat *> formats(qt_unique_device_formats(device, 0));
+ for (int i = 0; i < formats.size(); ++i) {
+ AVCaptureDeviceFormat *format = formats.at(i);
+ allRates += qt_device_format_framerates(format);
+ }
+ } else {
+ AVCaptureDeviceFormat *format = qt_find_best_resolution_match(device,
+ settings.resolution(),
+ m_service->session()->defaultCodec());
+ if (format)
+ allRates = qt_device_format_framerates(format);
+ }
+
+ for (int j = 0; j < allRates.size(); ++j) {
+ if (!real_list_contains(uniqueFrameRates, allRates[j].first))
+ uniqueFrameRates.append(allRates[j].first);
+ if (!real_list_contains(uniqueFrameRates, allRates[j].second))
+ uniqueFrameRates.append(allRates[j].second);
+ }
+
+ return uniqueFrameRates;
+}
+
+QStringList AVFVideoEncoderSettingsControl::supportedVideoCodecs() const
+{
+ return *supportedCodecs;
+}
+
+QString AVFVideoEncoderSettingsControl::videoCodecDescription(const QString &codecName) const
+{
+ if (codecName == QLatin1String("avc1"))
+ return QStringLiteral("H.264");
+ else if (codecName == QLatin1String("jpeg"))
+ return QStringLiteral("M-JPEG");
+#ifdef Q_OS_OSX
+ else if (codecName == QLatin1String("ap4h"))
+ return QStringLiteral("Apple ProRes 4444");
+ else if (codecName == QLatin1String("apcn"))
+ return QStringLiteral("Apple ProRes 422 Standard Definition");
+#endif
+
+ return QString();
+}
+
+QVideoEncoderSettings AVFVideoEncoderSettingsControl::videoSettings() const
+{
+ return m_actualSettings;
+}
+
+void AVFVideoEncoderSettingsControl::setVideoSettings(const QVideoEncoderSettings &settings)
+{
+ if (m_requestedSettings == settings)
+ return;
+
+ m_requestedSettings = m_actualSettings = settings;
+}
+
+NSDictionary *AVFVideoEncoderSettingsControl::applySettings(AVCaptureConnection *connection)
+{
+ if (m_service->session()->state() != QCamera::LoadedState &&
+ m_service->session()->state() != QCamera::ActiveState) {
+ return nil;
+ }
+
+ AVCaptureDevice *device = m_service->session()->videoCaptureDevice();
+ if (!device)
+ return nil;
+
+ AVFPSRange currentFps = qt_current_framerates(device, connection);
+ const bool needFpsChange = m_requestedSettings.frameRate() > 0
+ && m_requestedSettings.frameRate() != currentFps.second;
+
+ NSMutableDictionary *videoSettings = [NSMutableDictionary dictionary];
+
+ // -- Codec
+
+ // AVVideoCodecKey is the only mandatory key
+ QString codec = m_requestedSettings.codec().isEmpty() ? supportedCodecs->first() : m_requestedSettings.codec();
+ if (!supportedCodecs->contains(codec)) {
+ qWarning("Unsupported codec: '%s'", codec.toLocal8Bit().constData());
+ codec = supportedCodecs->first();
+ }
+ [videoSettings setObject:codec.toNSString() forKey:AVVideoCodecKey];
+ m_actualSettings.setCodec(codec);
+
+ // -- Resolution
+
+ int w = m_requestedSettings.resolution().width();
+ int h = m_requestedSettings.resolution().height();
+
+ if (AVCaptureDeviceFormat *currentFormat = device.activeFormat) {
+ CMFormatDescriptionRef formatDesc = currentFormat.formatDescription;
+ CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDesc);
+
+ // We have to change the device's activeFormat in 3 cases:
+ // - the requested recording resolution is higher than the current device resolution
+ // - the requested recording resolution has a different aspect ratio than the current device aspect ratio
+ // - the requested frame rate is not available for the current device format
+ AVCaptureDeviceFormat *newFormat = nil;
+ if ((w <= 0 || h <= 0)
+ && m_requestedSettings.frameRate() > 0
+ && !format_supports_framerate(currentFormat, m_requestedSettings.frameRate())) {
+
+ newFormat = qt_find_best_framerate_match(device,
+ m_service->session()->defaultCodec(),
+ m_requestedSettings.frameRate());
+
+ } else if (w > 0 && h > 0) {
+ AVCaptureDeviceFormat *f = qt_find_best_resolution_match(device,
+ m_requestedSettings.resolution(),
+ m_service->session()->defaultCodec());
+
+ if (f) {
+ CMVideoDimensions d = CMVideoFormatDescriptionGetDimensions(f.formatDescription);
+ qreal fAspectRatio = qreal(d.width) / d.height;
+
+ if (w > dim.width || h > dim.height
+ || qAbs((qreal(dim.width) / dim.height) - fAspectRatio) > 0.01) {
+ newFormat = f;
+ }
+ }
+ }
+
+ if (qt_set_active_format(device, newFormat, !needFpsChange)) {
+ m_restoreFormat = [currentFormat retain];
+ formatDesc = newFormat.formatDescription;
+ dim = CMVideoFormatDescriptionGetDimensions(formatDesc);
+ }
+
+ if (w > 0 && h > 0) {
+ // Make sure the recording resolution has the same aspect ratio as the device's
+ // current resolution
+ qreal deviceAspectRatio = qreal(dim.width) / dim.height;
+ qreal recAspectRatio = qreal(w) / h;
+ if (qAbs(deviceAspectRatio - recAspectRatio) > 0.01) {
+ if (recAspectRatio > deviceAspectRatio)
+ w = qRound(h * deviceAspectRatio);
+ else
+ h = qRound(w / deviceAspectRatio);
+ }
+
+ // recording resolution can't be higher than the device's active resolution
+ w = qMin(w, dim.width);
+ h = qMin(h, dim.height);
+ }
+ }
+
+ if (w > 0 && h > 0) {
+ // Width and height must be divisible by 2
+ w += w & 1;
+ h += h & 1;
+
+ [videoSettings setObject:[NSNumber numberWithInt:w] forKey:AVVideoWidthKey];
+ [videoSettings setObject:[NSNumber numberWithInt:h] forKey:AVVideoHeightKey];
+ m_actualSettings.setResolution(w, h);
+ } else {
+ m_actualSettings.setResolution(qt_device_format_resolution(device.activeFormat));
+ }
+
+ // -- FPS
+
+ if (needFpsChange) {
+ m_restoreFps = currentFps;
+ const qreal fps = m_requestedSettings.frameRate();
+ qt_set_framerate_limits(device, connection, fps, fps);
+ }
+ m_actualSettings.setFrameRate(qt_current_framerates(device, connection).second);
+
+ // -- Codec Settings
+
+ NSMutableDictionary *codecProperties = [NSMutableDictionary dictionary];
+ int bitrate = -1;
+ float quality = -1.f;
+
+ if (m_requestedSettings.encodingMode() == QMultimedia::ConstantQualityEncoding) {
+ if (m_requestedSettings.quality() != QMultimedia::NormalQuality) {
+ if (codec != QLatin1String("jpeg")) {
+ qWarning("ConstantQualityEncoding is not supported for codec: '%s'", codec.toLocal8Bit().constData());
+ } else {
+ switch (m_requestedSettings.quality()) {
+ case QMultimedia::VeryLowQuality:
+ quality = 0.f;
+ break;
+ case QMultimedia::LowQuality:
+ quality = 0.25f;
+ break;
+ case QMultimedia::HighQuality:
+ quality = 0.75f;
+ break;
+ case QMultimedia::VeryHighQuality:
+ quality = 1.f;
+ break;
+ default:
+ quality = -1.f; // NormalQuality, let the system decide
+ break;
+ }
+ }
+ }
+ } else if (m_requestedSettings.encodingMode() == QMultimedia::AverageBitRateEncoding){
+ if (codec != QLatin1String("avc1"))
+ qWarning("AverageBitRateEncoding is not supported for codec: '%s'", codec.toLocal8Bit().constData());
+ else
+ bitrate = m_requestedSettings.bitRate();
+ } else {
+ qWarning("Encoding mode is not supported");
+ }
+
+ if (bitrate != -1)
+ [codecProperties setObject:[NSNumber numberWithInt:bitrate] forKey:AVVideoAverageBitRateKey];
+ if (quality != -1.f)
+ [codecProperties setObject:[NSNumber numberWithFloat:quality] forKey:AVVideoQualityKey];
+
+ [videoSettings setObject:codecProperties forKey:AVVideoCompressionPropertiesKey];
+
+ return videoSettings;
+}
+
+void AVFVideoEncoderSettingsControl::unapplySettings(AVCaptureConnection *connection)
+{
+ m_actualSettings = m_requestedSettings;
+
+ AVCaptureDevice *device = m_service->session()->videoCaptureDevice();
+ if (!device)
+ return;
+
+ const bool needFpsChanged = m_restoreFps.first || m_restoreFps.second;
+
+ if (m_restoreFormat) {
+ qt_set_active_format(device, m_restoreFormat, !needFpsChanged);
+ [m_restoreFormat release];
+ m_restoreFormat = nil;
+ }
+
+ if (needFpsChanged) {
+ qt_set_framerate_limits(device, connection, m_restoreFps.first, m_restoreFps.second);
+ m_restoreFps = AVFPSRange();
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_avfvideoencodersettingscontrol_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol_p.h
new file mode 100644
index 000000000..6d44d4a33
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef AVFVIDEOENCODERSETTINGSCONTROL_H
+#define AVFVIDEOENCODERSETTINGSCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qvideoencodersettingscontrol.h>
+
+#include "avfcamerautility_p.h"
+#import <AVFoundation/AVFoundation.h>
+
+@class NSDictionary;
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraService;
+
+class AVFVideoEncoderSettingsControl : public QVideoEncoderSettingsControl
+{
+ Q_OBJECT
+
+public:
+ explicit AVFVideoEncoderSettingsControl(AVFCameraService *service);
+
+ QList<QSize> supportedResolutions(const QVideoEncoderSettings &requestedVideoSettings,
+ bool *continuous = nullptr) const override;
+
+ QList<qreal> supportedFrameRates(const QVideoEncoderSettings &requestedVideoSettings,
+ bool *continuous = nullptr) const override;
+
+ QStringList supportedVideoCodecs() const override;
+ QString videoCodecDescription(const QString &codecName) const override;
+
+ QVideoEncoderSettings videoSettings() const override;
+ void setVideoSettings(const QVideoEncoderSettings &requestedVideoSettings) override;
+
+ NSDictionary *applySettings(AVCaptureConnection *connection);
+ void unapplySettings(AVCaptureConnection *connection);
+
+private:
+ AVFCameraService *m_service;
+
+ QVideoEncoderSettings m_requestedSettings;
+ QVideoEncoderSettings m_actualSettings;
+
+ AVCaptureDeviceFormat *m_restoreFormat;
+ AVFPSRange m_restoreFps;
+};
+
+QT_END_NAMESPACE
+
+#endif // AVFVIDEOENCODERSETTINGSCONTROL_H
diff --git a/src/multimedia/platform/avfoundation/camera/camera.pri b/src/multimedia/platform/avfoundation/camera/camera.pri
new file mode 100644
index 000000000..430094d14
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/camera.pri
@@ -0,0 +1,60 @@
+HEADERS += \
+ $$PWD/avfcameradebug_p.h \
+ $$PWD/avfcameraserviceplugin_p.h \
+ $$PWD/avfcameracontrol_p.h \
+ $$PWD/avfcamerametadatacontrol_p.h \
+ $$PWD/avfimagecapturecontrol_p.h \
+ $$PWD/avfcameraservice_p.h \
+ $$PWD/avfcamerasession_p.h \
+ $$PWD/avfstoragelocation_p.h \
+ $$PWD/avfaudioinputselectorcontrol_p.h \
+ $$PWD/avfmediavideoprobecontrol_p.h \
+ $$PWD/avfcamerarenderercontrol_p.h \
+ $$PWD/avfcameradevicecontrol_p.h \
+ $$PWD/avfcamerafocuscontrol_p.h \
+ $$PWD/avfcameraexposurecontrol_p.h \
+ $$PWD/avfcamerautility_p.h \
+ $$PWD/avfimageencodercontrol_p.h \
+ $$PWD/avfvideoencodersettingscontrol_p.h \
+ $$PWD/avfmediacontainercontrol_p.h \
+ $$PWD/avfaudioencodersettingscontrol_p.h \
+ $$PWD/avfcamerawindowcontrol_p.h \
+
+SOURCES += \
+ $$PWD/avfcameraserviceplugin.mm \
+ $$PWD/avfcameracontrol.mm \
+ $$PWD/avfcamerametadatacontrol.mm \
+ $$PWD/avfimagecapturecontrol.mm \
+ $$PWD/avfcameraservice.mm \
+ $$PWD/avfcamerasession.mm \
+ $$PWD/avfstoragelocation.mm \
+ $$PWD/avfaudioinputselectorcontrol.mm \
+ $$PWD/avfmediavideoprobecontrol.mm \
+ $$PWD/avfcameradevicecontrol.mm \
+ $$PWD/avfcamerarenderercontrol.mm \
+ $$PWD/avfcamerafocuscontrol.mm \
+ $$PWD/avfcameraexposurecontrol.mm \
+ $$PWD/avfcamerautility.mm \
+ $$PWD/avfimageencodercontrol.mm \
+ $$PWD/avfvideoencodersettingscontrol.mm \
+ $$PWD/avfmediacontainercontrol.mm \
+ $$PWD/avfaudioencodersettingscontrol.mm \
+ $$PWD/avfcamerawindowcontrol.mm \
+
+osx {
+
+HEADERS += $$PWD/avfmediarecordercontrol_p.h
+SOURCES += $$PWD/avfmediarecordercontrol.mm
+
+}
+
+ios {
+
+HEADERS += \
+ $$PWD/avfmediaassetwriter_p.h \
+ $$PWD/avfmediarecordercontrol_ios_p.h
+SOURCES += \
+ $$PWD/avfmediaassetwriter.mm \
+ $$PWD/avfmediarecordercontrol_ios.mm
+
+}