summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@qt.io>2016-06-29 16:11:26 +0200
committerLiang Qi <liang.qi@qt.io>2016-06-30 07:33:04 +0200
commit27681cba4695355f2a0a6b01b85c429186d11a34 (patch)
treef11df2ec52d983b552f2e1b673e0845bc7e3ef05
parentf7a93757c709e8b2902bc4707752edb8649d009c (diff)
parentbc53bb7913bbf68519508a0ab76c513335b3e5bb (diff)
Merge remote-tracking branch 'origin/5.6' into 5.7
Blacklisted a few functions in tst_QAudioInput. Conflicts: .qmake.conf src/plugins/avfoundation/camera/avfcameracontrol.mm src/plugins/avfoundation/camera/avfcameraservice.h src/plugins/avfoundation/camera/avfcameraservice.mm src/plugins/avfoundation/camera/avfcamerasession.h src/plugins/avfoundation/camera/avfcamerasession.mm src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm src/plugins/avfoundation/camera/avfimagecapturecontrol.mm src/plugins/avfoundation/camera/avfimageencodercontrol.mm src/plugins/avfoundation/camera/avfmediarecordercontrol.h src/plugins/avfoundation/camera/avfmediarecordercontrol.mm tests/auto/integration/qaudioinput/BLACKLIST Task-number: QTBUG-54459 Task-number: QTBUG-49736 Change-Id: I3a1fe8cef50b44d5c2785aaf4cf69fe3f16728e6
-rw-r--r--.qmake.conf1
-rw-r--r--LICENSE.GPLv32
-rw-r--r--LICENSE.LGPLv212
-rw-r--r--LICENSE.LGPLv32
-rw-r--r--src/multimedia/camera/qcamera_p.h3
-rw-r--r--src/plugins/alsa/qalsaaudiodeviceinfo.cpp96
-rw-r--r--src/plugins/alsa/qalsaaudiodeviceinfo.h1
-rw-r--r--src/plugins/alsa/qalsaaudioinput.cpp45
-rw-r--r--src/plugins/alsa/qalsaaudiooutput.cpp42
-rw-r--r--src/plugins/android/src/common/qandroidvideooutput.cpp4
-rw-r--r--src/plugins/android/src/wrappers/jni/androidcamera.cpp1
-rw-r--r--src/plugins/avfoundation/camera/avfaudioencodersettingscontrol.h69
-rw-r--r--src/plugins/avfoundation/camera/avfaudioencodersettingscontrol.mm220
-rw-r--r--src/plugins/avfoundation/camera/avfcameracontrol.mm1
-rw-r--r--src/plugins/avfoundation/camera/avfcameraservice.h12
-rw-r--r--src/plugins/avfoundation/camera/avfcameraservice.mm36
-rw-r--r--src/plugins/avfoundation/camera/avfcamerasession.h2
-rw-r--r--src/plugins/avfoundation/camera/avfcamerasession.mm26
-rw-r--r--src/plugins/avfoundation/camera/avfcamerautility.h7
-rw-r--r--src/plugins/avfoundation/camera/avfcamerautility.mm240
-rw-r--r--src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h2
-rw-r--r--src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm213
-rw-r--r--src/plugins/avfoundation/camera/avfimagecapturecontrol.mm1
-rw-r--r--src/plugins/avfoundation/camera/avfimageencodercontrol.mm14
-rw-r--r--src/plugins/avfoundation/camera/avfmediaassetwriter.h9
-rw-r--r--src/plugins/avfoundation/camera/avfmediaassetwriter.mm69
-rw-r--r--src/plugins/avfoundation/camera/avfmediacontainercontrol.h63
-rw-r--r--src/plugins/avfoundation/camera/avfmediacontainercontrol.mm106
-rw-r--r--src/plugins/avfoundation/camera/avfmediarecordercontrol.h5
-rw-r--r--src/plugins/avfoundation/camera/avfmediarecordercontrol.mm44
-rw-r--r--src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h6
-rw-r--r--src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm76
-rw-r--r--src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.h82
-rw-r--r--src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.mm395
-rw-r--r--src/plugins/avfoundation/camera/camera.pro10
-rw-r--r--src/plugins/directshow/player/directshowiosource.cpp169
-rw-r--r--src/plugins/directshow/player/directshowiosource.h5
-rw-r--r--src/plugins/pulseaudio/qpulseaudioengine.cpp118
-rw-r--r--src/plugins/pulseaudio/qpulseaudioengine.h9
-rw-r--r--src/plugins/winrt/qwinrtabstractvideorenderercontrol.cpp11
-rw-r--r--src/plugins/winrt/qwinrtcameracontrol.cpp90
-rw-r--r--tests/auto/integration/qaudioinput/BLACKLIST13
42 files changed, 1705 insertions, 617 deletions
diff --git a/.qmake.conf b/.qmake.conf
index 33a2710f6..4ef70163b 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,4 +1,3 @@
load(qt_build_config)
-CONFIG += qt_example_installs
MODULE_VERSION = 5.7.0
diff --git a/LICENSE.GPLv3 b/LICENSE.GPLv3
index 4e49b122a..71c4ad49c 100644
--- a/LICENSE.GPLv3
+++ b/LICENSE.GPLv3
@@ -3,7 +3,7 @@
The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd.
Contact: http://www.qt.io/licensing/
- You may use, distribute and copy the Qt GUI Toolkit under the terms of
+ You may use, distribute and copy the Qt Toolkit under the terms of
GNU Lesser General Public License version 3. That license references
the General Public License version 3, that is displayed below. Other
portions of the Qt Toolkit may be licensed directly under this license.
diff --git a/LICENSE.LGPLv21 b/LICENSE.LGPLv21
index 6e1846112..dfcab5e29 100644
--- a/LICENSE.LGPLv21
+++ b/LICENSE.LGPLv21
@@ -3,7 +3,7 @@
The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd.
Contact: http://www.qt.io/licensing/
- You may use, distribute and copy the Qt GUI Toolkit under the terms of
+ You may use, distribute and copy the Qt Toolkit under the terms of
GNU Lesser General Public License version 2.1, which is displayed below.
-------------------------------------------------------------------------
diff --git a/LICENSE.LGPLv3 b/LICENSE.LGPLv3
index 4d67bac0b..6bf924cd1 100644
--- a/LICENSE.LGPLv3
+++ b/LICENSE.LGPLv3
@@ -3,7 +3,7 @@
The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd.
Contact: http://www.qt.io/licensing/
- You may use, distribute and copy the Qt GUI Toolkit under the terms of
+ You may use, distribute and copy the Qt Toolkit under the terms of
GNU Lesser General Public License version 3, which is displayed below.
This license makes reference to the version 3 of the GNU General
Public License, which you can find in the LICENSE.GPLv3 file.
diff --git a/src/multimedia/camera/qcamera_p.h b/src/multimedia/camera/qcamera_p.h
index f4cfa6c2a..49b4376b2 100644
--- a/src/multimedia/camera/qcamera_p.h
+++ b/src/multimedia/camera/qcamera_p.h
@@ -78,6 +78,9 @@ public:
infoControl(0),
viewfinderSettingsControl(0),
viewfinderSettingsControl2(0),
+ cameraExposure(0),
+ cameraFocus(0),
+ imageProcessing(0),
viewfinder(0),
capture(0),
state(QCamera::UnloadedState),
diff --git a/src/plugins/alsa/qalsaaudiodeviceinfo.cpp b/src/plugins/alsa/qalsaaudiodeviceinfo.cpp
index 0342ca546..869e1897e 100644
--- a/src/plugins/alsa/qalsaaudiodeviceinfo.cpp
+++ b/src/plugins/alsa/qalsaaudiodeviceinfo.cpp
@@ -143,35 +143,18 @@ QList<QAudioFormat::SampleType> QAlsaAudioDeviceInfo::supportedSampleTypes()
bool QAlsaAudioDeviceInfo::open()
{
int err = 0;
- QString dev = device;
- QList<QByteArray> devices = availableDevices(mode);
+ QString dev;
- if(dev.compare(QLatin1String("default")) == 0) {
-#if SND_LIB_VERSION >= 0x1000e // 1.0.14
- if (devices.size() > 0)
- dev = QLatin1String(devices.first().constData());
- else
- return false;
-#else
- dev = QLatin1String("hw:0,0");
+ if (!availableDevices(mode).contains(device.toLocal8Bit()))
+ return false;
+
+#if SND_LIB_VERSION < 0x1000e // 1.0.14
+ if (device.compare(QLatin1String("default")) != 0)
+ dev = deviceFromCardName(device);
+ else
#endif
- } else {
-#if SND_LIB_VERSION >= 0x1000e // 1.0.14
dev = device;
-#else
- int idx = 0;
- char *name;
-
- QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1);
- while (snd_card_get_name(idx,&name) == 0) {
- if(dev.contains(QLatin1String(name)))
- break;
- idx++;
- }
- dev = QString(QLatin1String("hw:%1,0")).arg(idx);
-#endif
- }
if(mode == QAudio::AudioOutput) {
err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
} else {
@@ -200,30 +183,12 @@ bool QAlsaAudioDeviceInfo::testSettings(const QAudioFormat& format) const
snd_pcm_hw_params_t *params;
QString dev;
-#if SND_LIB_VERSION >= 0x1000e // 1.0.14
- dev = device;
- if (dev.compare(QLatin1String("default")) == 0) {
- QList<QByteArray> devices = availableDevices(QAudio::AudioOutput);
- if (!devices.isEmpty())
- dev = QLatin1String(devices.first().constData());
- }
-#else
- if (dev.compare(QLatin1String("default")) == 0) {
- dev = QLatin1String("hw:0,0");
- } else {
- int idx = 0;
- char *name;
-
- QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1);
-
- while(snd_card_get_name(idx,&name) == 0) {
- if(shortName.compare(QLatin1String(name)) == 0)
- break;
- idx++;
- }
- dev = QString(QLatin1String("hw:%1,0")).arg(idx);
- }
+#if SND_LIB_VERSION < 0x1000e // 1.0.14
+ if (device.compare(QLatin1String("default")) != 0)
+ dev = deviceFromCardName(device);
+ else
#endif
+ dev = device;
snd_pcm_stream_t stream = mode == QAudio::AudioOutput
? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE;
@@ -339,9 +304,11 @@ void QAlsaAudioDeviceInfo::updateLists()
QList<QByteArray> QAlsaAudioDeviceInfo::availableDevices(QAudio::Mode mode)
{
QList<QByteArray> devices;
- QByteArray filter;
+ bool hasDefault = false;
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
+ QByteArray filter;
+
// Create a list of all current audio devices that support mode
void **hints, **n;
char *name, *descr, *io;
@@ -365,12 +332,9 @@ QList<QByteArray> QAlsaAudioDeviceInfo::availableDevices(QAudio::Mode mode)
io = snd_device_name_get_hint(*n, "IOID");
if ((descr != NULL) && ((io == NULL) || (io == filter))) {
- QString deviceName = QLatin1String(name);
- QString deviceDescription = QLatin1String(descr);
- if (deviceDescription.contains(QLatin1String("Default Audio Device")))
- devices.prepend(deviceName.toLocal8Bit().constData());
- else
- devices.append(deviceName.toLocal8Bit().constData());
+ devices.append(name);
+ if (strcmp(name, "default") == 0)
+ hasDefault = true;
}
free(descr);
@@ -386,12 +350,14 @@ QList<QByteArray> QAlsaAudioDeviceInfo::availableDevices(QAudio::Mode mode)
while(snd_card_get_name(idx,&name) == 0) {
devices.append(name);
+ if (strcmp(name, "default") == 0)
+ hasDefault = true;
idx++;
}
#endif
- if (devices.size() > 0)
- devices.append("default");
+ if (!hasDefault && devices.size() > 0)
+ devices.prepend("default");
return devices;
}
@@ -454,4 +420,20 @@ void QAlsaAudioDeviceInfo::checkSurround()
snd_device_name_free_hint(hints);
}
+QString QAlsaAudioDeviceInfo::deviceFromCardName(const QString &card)
+{
+ int idx = 0;
+ char *name;
+
+ QStringRef shortName = card.midRef(card.indexOf(QLatin1String("="), 0) + 1);
+
+ while (snd_card_get_name(idx, &name) == 0) {
+ if (shortName.compare(QLatin1String(name)) == 0)
+ break;
+ idx++;
+ }
+
+ return QString(QLatin1String("hw:%1,0")).arg(idx);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/alsa/qalsaaudiodeviceinfo.h b/src/plugins/alsa/qalsaaudiodeviceinfo.h
index 0147a2cf9..97b59ebf3 100644
--- a/src/plugins/alsa/qalsaaudiodeviceinfo.h
+++ b/src/plugins/alsa/qalsaaudiodeviceinfo.h
@@ -91,6 +91,7 @@ public:
static QByteArray defaultInputDevice();
static QByteArray defaultOutputDevice();
static QList<QByteArray> availableDevices(QAudio::Mode);
+ static QString deviceFromCardName(const QString &card);
private:
bool open();
diff --git a/src/plugins/alsa/qalsaaudioinput.cpp b/src/plugins/alsa/qalsaaudioinput.cpp
index 6ad9a6c5b..8109e6932 100644
--- a/src/plugins/alsa/qalsaaudioinput.cpp
+++ b/src/plugins/alsa/qalsaaudioinput.cpp
@@ -127,6 +127,12 @@ int QAlsaAudioInput::xrun_recovery(int err)
int count = 0;
bool reset = false;
+ // ESTRPIPE is not available in all OSes where ALSA is available
+ int estrpipe = EIO;
+#ifdef ESTRPIPE
+ estrpipe = ESTRPIPE;
+#endif
+
if(err == -EPIPE) {
errorState = QAudio::UnderrunError;
err = snd_pcm_prepare(handle);
@@ -137,8 +143,7 @@ int QAlsaAudioInput::xrun_recovery(int err)
if (bytesAvailable <= 0)
reset = true;
}
-
- } else if((err == -ESTRPIPE)||(err == -EIO)) {
+ } else if ((err == -estrpipe)||(err == -EIO)) {
errorState = QAudio::IOError;
while((err = snd_pcm_resume(handle)) == -EAGAIN){
usleep(100);
@@ -306,34 +311,16 @@ bool QAlsaAudioInput::open()
}
- QString dev = QString(QLatin1String(m_device.constData()));
- QList<QByteArray> devices = QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioInput);
- if(dev.compare(QLatin1String("default")) == 0) {
-#if SND_LIB_VERSION >= 0x1000e // 1.0.14
- if (devices.size() > 0)
- dev = QLatin1String(devices.first());
- else
- return false;
-#else
- dev = QLatin1String("hw:0,0");
-#endif
- } else {
-#if SND_LIB_VERSION >= 0x1000e // 1.0.14
- dev = QLatin1String(m_device);
-#else
- int idx = 0;
- char *name;
-
- QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData());
+ if (!QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioOutput).contains(m_device))
+ return false;
- while(snd_card_get_name(idx,&name) == 0) {
- if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0)
- break;
- idx++;
- }
- dev = QString(QLatin1String("hw:%1,0")).arg(idx);
+ QString dev;
+#if SND_LIB_VERSION < 0x1000e // 1.0.14
+ if (m_device != "default")
+ dev = QAlsaAudioDeviceInfo::deviceFromCardName(m_device);
+ else
#endif
- }
+ dev = m_device;
// Step 1: try and open the device
while((count < 5) && (err < 0)) {
@@ -565,8 +552,10 @@ qint64 QAlsaAudioInput::read(char* data, qint64 len)
if(readFrames == -EPIPE) {
errorState = QAudio::UnderrunError;
err = snd_pcm_prepare(handle);
+#ifdef ESTRPIPE
} else if(readFrames == -ESTRPIPE) {
err = snd_pcm_prepare(handle);
+#endif
}
if(err != 0) break;
}
diff --git a/src/plugins/alsa/qalsaaudiooutput.cpp b/src/plugins/alsa/qalsaaudiooutput.cpp
index d59e2b740..5e444a0a9 100644
--- a/src/plugins/alsa/qalsaaudiooutput.cpp
+++ b/src/plugins/alsa/qalsaaudiooutput.cpp
@@ -120,6 +120,12 @@ int QAlsaAudioOutput::xrun_recovery(int err)
int count = 0;
bool reset = false;
+ // ESTRPIPE is not available in all OSes where ALSA is available
+ int estrpipe = EIO;
+#ifdef ESTRPIPE
+ estrpipe = ESTRPIPE;
+#endif
+
if(err == -EPIPE) {
errorState = QAudio::UnderrunError;
emit errorChanged(errorState);
@@ -127,7 +133,7 @@ int QAlsaAudioOutput::xrun_recovery(int err)
if(err < 0)
reset = true;
- } else if((err == -ESTRPIPE)||(err == -EIO)) {
+ } else if ((err == -estrpipe)||(err == -EIO)) {
errorState = QAudio::IOError;
emit errorChanged(errorState);
while((err = snd_pcm_resume(handle)) == -EAGAIN){
@@ -309,34 +315,16 @@ bool QAlsaAudioOutput::open()
return false;
}
- QString dev = QString(QLatin1String(m_device.constData()));
- QList<QByteArray> devices = QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
- if(dev.compare(QLatin1String("default")) == 0) {
-#if SND_LIB_VERSION >= 0x1000e // 1.0.14
- if (devices.size() > 0)
- dev = QLatin1String(devices.first());
- else
- return false;
-#else
- dev = QLatin1String("hw:0,0");
-#endif
- } else {
-#if SND_LIB_VERSION >= 0x1000e // 1.0.14
- dev = QLatin1String(m_device);
-#else
- int idx = 0;
- char *name;
-
- QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData());
+ if (!QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioOutput).contains(m_device))
+ return false;
- while (snd_card_get_name(idx,&name) == 0) {
- if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0)
- break;
- idx++;
- }
- dev = QString(QLatin1String("hw:%1,0")).arg(idx);
+ QString dev;
+#if SND_LIB_VERSION < 0x1000e // 1.0.14
+ if (m_device != "default")
+ dev = QAlsaAudioDeviceInfo::deviceFromCardName(m_device);
+ else
#endif
- }
+ dev = m_device;
// Step 1: try and open the device
while((count < 5) && (err < 0)) {
diff --git a/src/plugins/android/src/common/qandroidvideooutput.cpp b/src/plugins/android/src/common/qandroidvideooutput.cpp
index 4e96377d8..5c804ccc4 100644
--- a/src/plugins/android/src/common/qandroidvideooutput.cpp
+++ b/src/plugins/android/src/common/qandroidvideooutput.cpp
@@ -290,6 +290,10 @@ void QAndroidTextureVideoOutput::stop()
void QAndroidTextureVideoOutput::reset()
{
+ // flush pending frame
+ if (m_surface)
+ m_surface->present(QVideoFrame());
+
clearSurfaceTexture();
}
diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.cpp b/src/plugins/android/src/wrappers/jni/androidcamera.cpp
index fd5522e10..3295e4d33 100644
--- a/src/plugins/android/src/wrappers/jni/androidcamera.cpp
+++ b/src/plugins/android/src/wrappers/jni/androidcamera.cpp
@@ -315,7 +315,6 @@ AndroidCamera *AndroidCamera::open(int cameraId)
if (!ok) {
worker->quit();
worker->wait(5000);
- delete d;
delete worker;
return 0;
}
diff --git a/src/plugins/avfoundation/camera/avfaudioencodersettingscontrol.h b/src/plugins/avfoundation/camera/avfaudioencodersettingscontrol.h
new file mode 100644
index 000000000..213065443
--- /dev/null
+++ b/src/plugins/avfoundation/camera/avfaudioencodersettingscontrol.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef AVFAUDIOENCODERSETTINGSCONTROL_H
+#define AVFAUDIOENCODERSETTINGSCONTROL_H
+
+#include <qaudioencodersettingscontrol.h>
+
+@class NSDictionary;
+@class AVCaptureAudioDataOutput;
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraService;
+
+class AVFAudioEncoderSettingsControl : public QAudioEncoderSettingsControl
+{
+public:
+ explicit AVFAudioEncoderSettingsControl(AVFCameraService *service);
+
+ QStringList supportedAudioCodecs() const Q_DECL_OVERRIDE;
+ QString codecDescription(const QString &codecName) const Q_DECL_OVERRIDE;
+ QList<int> supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous = 0) const Q_DECL_OVERRIDE;
+ QAudioEncoderSettings audioSettings() const Q_DECL_OVERRIDE;
+ void setAudioSettings(const QAudioEncoderSettings &settings) Q_DECL_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/plugins/avfoundation/camera/avfaudioencodersettingscontrol.mm b/src/plugins/avfoundation/camera/avfaudioencodersettingscontrol.mm
new file mode 100644
index 000000000..143a444a6
--- /dev/null
+++ b/src/plugins/avfoundation/camera/avfaudioencodersettingscontrol.mm
@@ -0,0 +1,220 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "avfaudioencodersettingscontrol.h"
+
+#include "avfcameraservice.h"
+#include "avfcamerasession.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/plugins/avfoundation/camera/avfcameracontrol.mm b/src/plugins/avfoundation/camera/avfcameracontrol.mm
index 038e3b326..c47eecfdf 100644
--- a/src/plugins/avfoundation/camera/avfcameracontrol.mm
+++ b/src/plugins/avfoundation/camera/avfcameracontrol.mm
@@ -53,6 +53,7 @@ AVFCameraControl::AVFCameraControl(AVFCameraService *service, QObject *parent)
{
Q_UNUSED(service);
connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateStatus()));
+ connect(this, &AVFCameraControl::captureModeChanged, m_session, &AVFCameraSession::onCaptureModeChanged);
}
AVFCameraControl::~AVFCameraControl()
diff --git a/src/plugins/avfoundation/camera/avfcameraservice.h b/src/plugins/avfoundation/camera/avfcameraservice.h
index c65536b64..9ce637ee3 100644
--- a/src/plugins/avfoundation/camera/avfcameraservice.h
+++ b/src/plugins/avfoundation/camera/avfcameraservice.h
@@ -67,6 +67,9 @@ class AVFImageEncoderControl;
class AVFCameraFlashControl;
class AVFMediaRecorderControl;
class AVFMediaRecorderControlIOS;
+class AVFAudioEncoderSettingsControl;
+class AVFVideoEncoderSettingsControl;
+class AVFMediaContainerControl;
class AVFCameraService : public QMediaService
{
@@ -83,8 +86,7 @@ public:
AVFCameraDeviceControl *videoDeviceControl() const { return m_videoDeviceControl; }
AVFAudioInputSelectorControl *audioInputSelectorControl() const { return m_audioInputSelectorControl; }
AVFCameraMetaDataControl *metaDataControl() const { return m_metaDataControl; }
- AVFMediaRecorderControl *recorderControl() const;
- AVFMediaRecorderControlIOS *recorderControlIOS() const;
+ QMediaRecorderControl *recorderControl() const { return m_recorderControl; }
AVFImageCaptureControl *imageCaptureControl() const { return m_imageCaptureControl; }
AVFCameraFocusControl *cameraFocusControl() const { return m_cameraFocusControl; }
AVFCameraExposureControl *cameraExposureControl() const {return m_cameraExposureControl; }
@@ -94,6 +96,9 @@ public:
AVFCameraViewfinderSettingsControl *viewfinderSettingsControl() const {return m_viewfinderSettingsControl; }
AVFImageEncoderControl *imageEncoderControl() const {return m_imageEncoderControl; }
AVFCameraFlashControl *flashControl() const {return m_flashControl; }
+ AVFAudioEncoderSettingsControl *audioEncoderSettingsControl() const { return m_audioEncoderSettingsControl; }
+ AVFVideoEncoderSettingsControl *videoEncoderSettingsControl() const {return m_videoEncoderSettingsControl; }
+ AVFMediaContainerControl *mediaContainerControl() const { return m_mediaContainerControl; }
private:
AVFCameraSession *m_session;
@@ -112,6 +117,9 @@ private:
AVFCameraViewfinderSettingsControl *m_viewfinderSettingsControl;
AVFImageEncoderControl *m_imageEncoderControl;
AVFCameraFlashControl *m_flashControl;
+ AVFAudioEncoderSettingsControl *m_audioEncoderSettingsControl;
+ AVFVideoEncoderSettingsControl *m_videoEncoderSettingsControl;
+ AVFMediaContainerControl *m_mediaContainerControl;
};
QT_END_NAMESPACE
diff --git a/src/plugins/avfoundation/camera/avfcameraservice.mm b/src/plugins/avfoundation/camera/avfcameraservice.mm
index d3f254489..a1213be3b 100644
--- a/src/plugins/avfoundation/camera/avfcameraservice.mm
+++ b/src/plugins/avfoundation/camera/avfcameraservice.mm
@@ -59,6 +59,9 @@
#include "avfcameraviewfindersettingscontrol.h"
#include "avfimageencodercontrol.h"
#include "avfcameraflashcontrol.h"
+#include "avfaudioencodersettingscontrol.h"
+#include "avfvideoencodersettingscontrol.h"
+#include "avfmediacontainercontrol.h"
#ifdef Q_OS_IOS
#include "avfcamerazoomcontrol.h"
@@ -105,6 +108,9 @@ AVFCameraService::AVFCameraService(QObject *parent):
m_viewfinderSettingsControl = new AVFCameraViewfinderSettingsControl(this);
m_imageEncoderControl = new AVFImageEncoderControl(this);
m_flashControl = new AVFCameraFlashControl(this);
+ m_audioEncoderSettingsControl = new AVFAudioEncoderSettingsControl(this);
+ m_videoEncoderSettingsControl = new AVFVideoEncoderSettingsControl(this);
+ m_mediaContainerControl = new AVFMediaContainerControl(this);
}
AVFCameraService::~AVFCameraService()
@@ -136,6 +142,9 @@ AVFCameraService::~AVFCameraService()
delete m_viewfinderSettingsControl;
delete m_imageEncoderControl;
delete m_flashControl;
+ delete m_audioEncoderSettingsControl;
+ delete m_videoEncoderSettingsControl;
+ delete m_mediaContainerControl;
delete m_session;
}
@@ -182,6 +191,15 @@ QMediaControl *AVFCameraService::requestControl(const char *name)
if (qstrcmp(name, QCameraFlashControl_iid) == 0)
return m_flashControl;
+ 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 = 0;
videoProbe = new AVFMediaVideoProbeControl(this);
@@ -220,23 +238,5 @@ void AVFCameraService::releaseControl(QMediaControl *control)
}
}
-AVFMediaRecorderControl *AVFCameraService::recorderControl() const
-{
-#ifdef Q_OS_IOS
- return 0;
-#else
- return static_cast<AVFMediaRecorderControl *>(m_recorderControl);
-#endif
-}
-
-AVFMediaRecorderControlIOS *AVFCameraService::recorderControlIOS() const
-{
-#ifdef Q_OS_OSX
- return 0;
-#else
- return static_cast<AVFMediaRecorderControlIOS *>(m_recorderControl);
-#endif
-}
-
#include "moc_avfcameraservice.cpp"
diff --git a/src/plugins/avfoundation/camera/avfcamerasession.h b/src/plugins/avfoundation/camera/avfcamerasession.h
index 1c7aa40ab..3f90f1f7f 100644
--- a/src/plugins/avfoundation/camera/avfcamerasession.h
+++ b/src/plugins/avfoundation/camera/avfcamerasession.h
@@ -99,6 +99,8 @@ public Q_SLOTS:
void processSessionStarted();
void processSessionStopped();
+ void onCaptureModeChanged(QCamera::CaptureModes mode);
+
void onCameraFrameFetched(const QVideoFrame &frame);
Q_SIGNALS:
diff --git a/src/plugins/avfoundation/camera/avfcamerasession.mm b/src/plugins/avfoundation/camera/avfcamerasession.mm
index 5adc30296..c32b072ef 100644
--- a/src/plugins/avfoundation/camera/avfcamerasession.mm
+++ b/src/plugins/avfoundation/camera/avfcamerasession.mm
@@ -292,8 +292,8 @@ void AVFCameraSession::setState(QCamera::State newState)
m_defaultCodec = 0;
defaultCodec();
- bool activeFormatSet = applyImageEncoderSettings();
- activeFormatSet |= applyViewfinderSettings();
+ bool activeFormatSet = applyImageEncoderSettings()
+ | applyViewfinderSettings();
[m_captureSession commitConfiguration];
@@ -344,6 +344,17 @@ void AVFCameraSession::processSessionStopped()
}
}
+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:
@@ -387,18 +398,17 @@ bool AVFCameraSession::applyImageEncoderSettings()
bool AVFCameraSession::applyViewfinderSettings()
{
if (AVFCameraViewfinderSettingsControl2 *vfControl = m_service->viewfinderSettingsControl2()) {
+ QCamera::CaptureModes currentMode = m_service->cameraControl()->captureMode();
QCameraViewfinderSettings vfSettings(vfControl->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 (AVFImageEncoderControl *imControl = m_service->imageEncoderControl()) {
- const QSize imageResolution(imControl->requestedSettings().resolution());
- if (!imageResolution.isNull() && imageResolution.isValid()) {
+ if (currentMode.testFlag(QCamera::CaptureStillImage)) {
+ const QSize imageResolution(m_service->imageEncoderControl()->requestedSettings().resolution());
+ if (!imageResolution.isNull() && imageResolution.isValid())
vfSettings.setResolution(imageResolution);
- vfControl->setViewfinderSettings(vfSettings);
- }
}
- return vfControl->applySettings();
+ return vfControl->applySettings(vfSettings);
}
return false;
diff --git a/src/plugins/avfoundation/camera/avfcamerautility.h b/src/plugins/avfoundation/camera/avfcamerautility.h
index ae36dae73..4da5f751e 100644
--- a/src/plugins/avfoundation/camera/avfcamerautility.h
+++ b/src/plugins/avfoundation/camera/avfcamerautility.h
@@ -182,8 +182,15 @@ AVCaptureDeviceFormat *qt_find_best_framerate_match(AVCaptureDevice *captureDevi
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);
+
#endif
+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/plugins/avfoundation/camera/avfcamerautility.mm b/src/plugins/avfoundation/camera/avfcamerautility.mm
index bdf88e24f..279642e4f 100644
--- a/src/plugins/avfoundation/camera/avfcamerautility.mm
+++ b/src/plugins/avfoundation/camera/avfcamerautility.mm
@@ -177,8 +177,7 @@ QVector<AVCaptureDeviceFormat *> qt_unique_device_formats(AVCaptureDevice *captu
QSize qt_device_format_resolution(AVCaptureDeviceFormat *format)
{
- Q_ASSERT(format);
- if (!format.formatDescription)
+ if (!format || !format.formatDescription)
return QSize();
const CMVideoDimensions res = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
@@ -387,6 +386,243 @@ AVFrameRateRange *qt_find_supported_framerate_range(AVCaptureDeviceFormat *forma
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;
+}
+
#endif // SDK
+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;
+
+#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_5_0)
+#if QT_OSX_DEPLOYMENT_TARGET_BELOW(__MAC_10_9)
+ if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_9) {
+ if (minFPS > 0.)
+ qDebugCamera() << Q_FUNC_INFO << "minimum framerate is not supported";
+ } else
+#endif
+ {
+ 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;
+ }
+#else
+ if (minFPS > 0.)
+ qDebugCamera() << Q_FUNC_INFO << "minimum framerate is not supported";
+#endif
+}
+
+#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
+
+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];
+#else // Q_OS_OSX
+ 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 QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
+#if QT_OSX_DEPLOYMENT_TARGET_BELOW(__MAC_10_9)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9)
+#endif
+ {
+ if (CMTimeCompare(maxFrameDuration, kCMTimeInvalid))
+ [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration];
+ }
+#endif // QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
+#endif // Q_OS_OSX
+}
+
+#endif // Platform SDK >= 10.9, >= 7.0.
+
+void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection,
+ qreal minFPS, qreal maxFPS)
+{
+ Q_ASSERT(captureDevice);
+#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
+ if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_9, QSysInfo::MV_IOS_7_0))
+ qt_set_framerate_limits(captureDevice, minFPS, maxFPS);
+ else
+#endif
+ if (videoConnection)
+ qt_set_framerate_limits(videoConnection, minFPS, maxFPS);
+
+}
+
+AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection)
+{
+ Q_ASSERT(captureDevice);
+
+ AVFPSRange fps;
+#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
+ if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) {
+ const CMTime minDuration = captureDevice.activeVideoMinFrameDuration;
+ if (CMTimeCompare(minDuration, kCMTimeInvalid)) {
+ if (const Float64 minSeconds = CMTimeGetSeconds(minDuration))
+ fps.second = 1. / minSeconds; // Max FPS = 1 / MinDuration.
+ }
+
+#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
+#if QT_OSX_DEPLOYMENT_TARGET_BELOW(__MAC_10_9)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9)
+#endif
+ {
+ const CMTime maxDuration = captureDevice.activeVideoMaxFrameDuration;
+ if (CMTimeCompare(maxDuration, kCMTimeInvalid)) {
+ if (const Float64 maxSeconds = CMTimeGetSeconds(maxDuration))
+ fps.first = 1. / maxSeconds; // Min FPS = 1 / MaxDuration.
+ }
+ }
+#endif // QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
+
+ } else {
+#else // OSX < 10.7 or iOS < 7.0
+ {
+#endif // QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
+ if (videoConnection)
+ fps = qt_connection_framerates(videoConnection);
+ }
+
+ return fps;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h
index 138ecc332..bed755339 100644
--- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h
+++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h
@@ -82,7 +82,7 @@ private:
AVCaptureDeviceFormat *findBestFormatMatch(const QCameraViewfinderSettings &settings) const;
QVector<QVideoFrame::PixelFormat> viewfinderPixelFormats() const;
bool convertPixelFormatIfSupported(QVideoFrame::PixelFormat format, unsigned &avfFormat) const;
- bool applySettings();
+ bool applySettings(const QCameraViewfinderSettings &settings);
QCameraViewfinderSettings requestedSettings() const;
AVCaptureConnection *videoConnection() const;
diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm
index 924b0d76a..6ac6325b4 100644
--- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm
+++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm
@@ -72,194 +72,6 @@ bool qt_framerates_sane(const QCameraViewfinderSettings &settings)
return !maxFPS || maxFPS >= minFPS;
}
-void qt_set_framerate_limits(AVCaptureConnection *videoConnection,
- const QCameraViewfinderSettings &settings)
-{
- Q_ASSERT(videoConnection);
-
- if (!qt_framerates_sane(settings)) {
- qDebugCamera() << Q_FUNC_INFO << "invalid framerate (min, max):"
- << settings.minimumFrameRate() << settings.maximumFrameRate();
- return;
- }
-
- const qreal maxFPS = settings.maximumFrameRate();
- 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;
-
- const qreal minFPS = settings.minimumFrameRate();
-#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_5_0)
-#if QT_OSX_DEPLOYMENT_TARGET_BELOW(__MAC_10_9)
- if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_9) {
- if (minFPS > 0.)
- qDebugCamera() << Q_FUNC_INFO << "minimum framerate is not supported";
- } else
-#endif
- {
- 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;
- }
-#else
- if (minFPS > 0.)
- qDebugCamera() << Q_FUNC_INFO << "minimum framerate is not supported";
-#endif
-}
-
-#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
-
-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,
- const QCameraViewfinderSettings &settings)
-{
- Q_ASSERT(captureDevice);
- if (!captureDevice.activeFormat) {
- qDebugCamera() << Q_FUNC_INFO << "no active capture device format";
- return;
- }
-
- const qreal minFPS = settings.minimumFrameRate();
- const qreal maxFPS = settings.maximumFrameRate();
- if (!qt_framerates_sane(settings)) {
- 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."
-#ifdef Q_OS_IOS
- [captureDevice setActiveVideoMinFrameDuration:minFrameDuration];
- [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration];
-#else // Q_OS_OSX
-
- if (CMTimeCompare(minFrameDuration, kCMTimeInvalid))
- [captureDevice setActiveVideoMinFrameDuration:minFrameDuration];
-
-#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
-#if QT_OSX_DEPLOYMENT_TARGET_BELOW(__MAC_10_9)
- if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9)
-#endif
- {
- if (CMTimeCompare(maxFrameDuration, kCMTimeInvalid))
- [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration];
- }
-#endif // QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
-#endif // Q_OS_OSX
-}
-
-#endif // Platform SDK >= 10.9, >= 7.0.
-
-// 'Dispatchers':
-
-AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection)
-{
- Q_ASSERT(captureDevice);
-
- AVFPSRange fps;
-#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
- if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) {
- const CMTime minDuration = captureDevice.activeVideoMinFrameDuration;
- if (CMTimeCompare(minDuration, kCMTimeInvalid)) {
- if (const Float64 minSeconds = CMTimeGetSeconds(minDuration))
- fps.second = 1. / minSeconds; // Max FPS = 1 / MinDuration.
- }
-
-#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
-#if QT_OSX_DEPLOYMENT_TARGET_BELOW(__MAC_10_9)
- if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9)
-#endif
- {
- const CMTime maxDuration = captureDevice.activeVideoMaxFrameDuration;
- if (CMTimeCompare(maxDuration, kCMTimeInvalid)) {
- if (const Float64 maxSeconds = CMTimeGetSeconds(maxDuration))
- fps.first = 1. / maxSeconds; // Min FPS = 1 / MaxDuration.
- }
- }
-#endif // QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
-
- } else {
-#else // OSX < 10.7 or iOS < 7.0
- {
-#endif // QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
- if (videoConnection)
- fps = qt_connection_framerates(videoConnection);
- }
-
- return fps;
-}
-
-void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection,
- const QCameraViewfinderSettings &settings)
-{
- Q_ASSERT(captureDevice);
-#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
- if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_9, QSysInfo::MV_IOS_7_0))
- qt_set_framerate_limits(captureDevice, settings);
- else
-#endif
- if (videoConnection)
- qt_set_framerate_limits(videoConnection, settings);
-
-}
-
} // Unnamed namespace.
AVFCameraViewfinderSettingsControl2::AVFCameraViewfinderSettingsControl2(AVFCameraService *service)
@@ -399,7 +211,7 @@ void AVFCameraViewfinderSettingsControl2::setViewfinderSettings(const QCameraVie
return;
m_settings = settings;
- applySettings();
+ applySettings(m_settings);
}
QVideoFrame::PixelFormat AVFCameraViewfinderSettingsControl2::QtPixelFormatFromCVFormat(unsigned avPixelFormat)
@@ -484,8 +296,9 @@ AVCaptureDeviceFormat *AVFCameraViewfinderSettingsControl2::findBestFormatMatch(
const qreal minFPS(settings.minimumFrameRate());
const qreal maxFPS(settings.maximumFrameRate());
if (minFPS || maxFPS)
- return qt_find_best_framerate_match(captureDevice, maxFPS ? maxFPS : minFPS,
- m_service->session()->defaultCodec());
+ 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.
@@ -559,7 +372,7 @@ bool AVFCameraViewfinderSettingsControl2::convertPixelFormatIfSupported(QVideoFr
return found;
}
-bool AVFCameraViewfinderSettingsControl2::applySettings()
+bool AVFCameraViewfinderSettingsControl2::applySettings(const QCameraViewfinderSettings &settings)
{
if (m_service->session()->state() != QCamera::LoadedState &&
m_service->session()->state() != QCamera::ActiveState) {
@@ -573,17 +386,9 @@ bool AVFCameraViewfinderSettingsControl2::applySettings()
bool activeFormatChanged = false;
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
- AVCaptureDeviceFormat *match = findBestFormatMatch(m_settings);
+ AVCaptureDeviceFormat *match = findBestFormatMatch(settings);
if (match) {
- if (match != captureDevice.activeFormat) {
- const AVFConfigurationLock lock(captureDevice);
- if (lock) {
- captureDevice.activeFormat = match;
- activeFormatChanged = true;
- } else {
- qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
- }
- }
+ 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.
@@ -593,7 +398,7 @@ bool AVFCameraViewfinderSettingsControl2::applySettings()
AVCaptureVideoDataOutput *videoOutput = m_service->videoOutput() ? m_service->videoOutput()->videoDataOutput() : 0;
if (videoOutput) {
unsigned avfPixelFormat = 0;
- if (!convertPixelFormatIfSupported(m_settings.pixelFormat(), avfPixelFormat)) {
+ 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
@@ -629,7 +434,7 @@ bool AVFCameraViewfinderSettingsControl2::applySettings()
}
}
- qt_set_framerate_limits(captureDevice, videoConnection(), m_settings);
+ qt_set_framerate_limits(captureDevice, videoConnection(), settings.minimumFrameRate(), settings.maximumFrameRate());
return activeFormatChanged;
}
diff --git a/src/plugins/avfoundation/camera/avfimagecapturecontrol.mm b/src/plugins/avfoundation/camera/avfimagecapturecontrol.mm
index 6465e69e3..b59aa7bfd 100644
--- a/src/plugins/avfoundation/camera/avfimagecapturecontrol.mm
+++ b/src/plugins/avfoundation/camera/avfimagecapturecontrol.mm
@@ -73,6 +73,7 @@ AVFImageCaptureControl::AVFImageCaptureControl(AVFCameraService *service, QObjec
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,
diff --git a/src/plugins/avfoundation/camera/avfimageencodercontrol.mm b/src/plugins/avfoundation/camera/avfimageencodercontrol.mm
index 30e36262a..e5aa8a4c2 100644
--- a/src/plugins/avfoundation/camera/avfimageencodercontrol.mm
+++ b/src/plugins/avfoundation/camera/avfimageencodercontrol.mm
@@ -44,6 +44,7 @@
#include "avfcamerasession.h"
#include "avfcameraservice.h"
#include "avfcameradebug.h"
+#include "avfcameracontrol.h"
#include <QtMultimedia/qmediaencodersettings.h>
@@ -188,7 +189,8 @@ bool AVFImageEncoderControl::applySettings()
AVFCameraSession *session = m_service->session();
if (!session || (session->state() != QCamera::ActiveState
- && session->state() != QCamera::LoadedState)) {
+ && session->state() != QCamera::LoadedState)
+ || !m_service->cameraControl()->captureMode().testFlag(QCamera::CaptureStillImage)) {
return false;
}
@@ -231,15 +233,7 @@ bool AVFImageEncoderControl::applySettings()
return false;
}
- if (match != captureDevice.activeFormat) {
- const AVFConfigurationLock lock(captureDevice);
- if (!lock) {
- qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
- return false;
- }
- captureDevice.activeFormat = match;
- activeFormatChanged = true;
- }
+ activeFormatChanged = qt_set_active_format(captureDevice, match, true);
#if defined(Q_OS_IOS) && QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0)
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_8_0) {
diff --git a/src/plugins/avfoundation/camera/avfmediaassetwriter.h b/src/plugins/avfoundation/camera/avfmediaassetwriter.h
index c70deea10..fa1ec46a2 100644
--- a/src/plugins/avfoundation/camera/avfmediaassetwriter.h
+++ b/src/plugins/avfoundation/camera/avfmediaassetwriter.h
@@ -70,6 +70,7 @@ QT_END_NAMESPACE
QT_PREPEND_NAMESPACE(AVFScopedPointer)<AVCaptureDeviceInput> m_audioInput;
QT_PREPEND_NAMESPACE(AVFScopedPointer)<AVCaptureAudioDataOutput> m_audioOutput;
QT_PREPEND_NAMESPACE(AVFScopedPointer)<AVAssetWriterInput> m_audioWriterInput;
+ AVCaptureDevice *m_audioCaptureDevice;
// High priority serial queue for video output:
QT_PREPEND_NAMESPACE(AVFScopedPointer)<dispatch_queue_t> m_videoQueue;
@@ -92,13 +93,19 @@ QT_END_NAMESPACE
@private
CMTime m_startTime;
CMTime m_lastTimeStamp;
+
+ NSDictionary *m_audioSettings;
+ NSDictionary *m_videoSettings;
}
- (id)initWithQueue:(dispatch_queue_t)writerQueue
delegate:(QT_PREPEND_NAMESPACE(AVFMediaRecorderControlIOS) *)delegate;
- (bool)setupWithFileURL:(NSURL *)fileURL
- cameraService:(QT_PREPEND_NAMESPACE(AVFCameraService) *)service;
+ cameraService:(QT_PREPEND_NAMESPACE(AVFCameraService) *)service
+ audioSettings:(NSDictionary *)audioSettings
+ videoSettings:(NSDictionary *)videoSettings
+ transform:(CGAffineTransform)transform;
- (void)start;
- (void)stop;
diff --git a/src/plugins/avfoundation/camera/avfmediaassetwriter.mm b/src/plugins/avfoundation/camera/avfmediaassetwriter.mm
index 1b8c253e2..98c8f99ff 100644
--- a/src/plugins/avfoundation/camera/avfmediaassetwriter.mm
+++ b/src/plugins/avfoundation/camera/avfmediaassetwriter.mm
@@ -44,6 +44,7 @@
#include "avfcameraservice.h"
#include "avfcamerasession.h"
#include "avfcameradebug.h"
+#include "avfmediacontainercontrol.h"
//#include <QtCore/qmutexlocker.h>
#include <QtCore/qmetaobject.h>
@@ -79,8 +80,6 @@ bool qt_camera_service_isValid(AVFCameraService *service)
- (bool)addAudioCapture;
- (bool)addWriterInputs;
- (void)setQueues;
-- (NSDictionary *)videoSettings;
-- (NSDictionary *)audioSettings;
- (void)updateDuration:(CMTime)newTimeStamp;
@end
@@ -104,6 +103,8 @@ bool qt_camera_service_isValid(AVFCameraService *service)
m_startTime = kCMTimeInvalid;
m_lastTimeStamp = kCMTimeInvalid;
m_durationInMs.store(0);
+ m_audioSettings = nil;
+ m_videoSettings = nil;
}
return self;
@@ -111,6 +112,9 @@ bool qt_camera_service_isValid(AVFCameraService *service)
- (bool)setupWithFileURL:(NSURL *)fileURL
cameraService:(AVFCameraService *)service
+ audioSettings:(NSDictionary *)audioSettings
+ videoSettings:(NSDictionary *)videoSettings
+ transform:(CGAffineTransform)transform
{
Q_ASSERT(fileURL);
@@ -120,6 +124,8 @@ bool qt_camera_service_isValid(AVFCameraService *service)
}
m_service = service;
+ m_audioSettings = audioSettings;
+ m_videoSettings = videoSettings;
m_videoQueue.reset(dispatch_queue_create("video-output-queue", DISPATCH_QUEUE_SERIAL));
if (!m_videoQueue) {
@@ -133,7 +139,9 @@ bool qt_camera_service_isValid(AVFCameraService *service)
// But we still can write video!
}
- m_assetWriter.reset([[AVAssetWriter alloc] initWithURL:fileURL fileType:AVFileTypeQuickTimeMovie error:nil]);
+ 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;
@@ -151,10 +159,14 @@ bool qt_camera_service_isValid(AVFCameraService *service)
[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;
}
@@ -328,20 +340,22 @@ bool qt_camera_service_isValid(AVFCameraService *service)
AVCaptureSession *captureSession = m_service->session()->captureSession();
- AVCaptureDevice *audioDevice = m_service->audioInputSelectorControl()->createCaptureDevice();
- if (!audioDevice) {
+ 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:audioDevice error:&error] retain]);
+ 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 {
@@ -356,6 +370,7 @@ bool qt_camera_service_isValid(AVFCameraService *service)
} 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;
@@ -370,7 +385,9 @@ bool qt_camera_service_isValid(AVFCameraService *service)
&& m_service->videoOutput()->videoDataOutput());
Q_ASSERT(m_assetWriter);
- m_cameraWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:[self videoSettings]]);
+ 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;
@@ -387,7 +404,10 @@ bool qt_camera_service_isValid(AVFCameraService *service)
m_cameraWriterInput.data().expectsMediaDataInRealTime = YES;
if (m_audioOutput) {
- m_audioWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:[self audioSettings]]);
+ 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.
@@ -417,39 +437,6 @@ bool qt_camera_service_isValid(AVFCameraService *service)
}
}
-
-- (NSDictionary *)videoSettings
-{
- // TODO: these settings should be taken from
- // the video encoding settings control.
- // For now we either take recommended (iOS >= 7.0)
- // or some hardcoded values - they are still better than nothing (nil).
-#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_7_0)
- AVCaptureVideoDataOutput *videoOutput = m_service->videoOutput()->videoDataOutput();
- if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_7_0 && videoOutput)
- return [videoOutput recommendedVideoSettingsForAssetWriterWithOutputFileType:AVFileTypeQuickTimeMovie];
-#endif
- NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecH264, AVVideoCodecKey,
- [NSNumber numberWithInt:1280], AVVideoWidthKey,
- [NSNumber numberWithInt:720], AVVideoHeightKey, nil];
-
- return videoSettings;
-}
-
-- (NSDictionary *)audioSettings
-{
- // TODO: these settings should be taken from
- // the video/audio encoder settings control.
- // For now we either take recommended (iOS >= 7.0)
- // or nil - this seems to be good enough.
-#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_7_0)
- if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_7_0 && m_audioOutput)
- return [m_audioOutput recommendedAudioSettingsForAssetWriterWithOutputFileType:AVFileTypeQuickTimeMovie];
-#endif
-
- return nil;
-}
-
- (void)updateDuration:(CMTime)newTimeStamp
{
Q_ASSERT(CMTimeCompare(m_startTime, kCMTimeInvalid));
diff --git a/src/plugins/avfoundation/camera/avfmediacontainercontrol.h b/src/plugins/avfoundation/camera/avfmediacontainercontrol.h
new file mode 100644
index 000000000..da31d2d13
--- /dev/null
+++ b/src/plugins/avfoundation/camera/avfmediacontainercontrol.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef AVFMEDIACONTAINERCONTROL_H
+#define AVFMEDIACONTAINERCONTROL_H
+
+#include <qmediacontainercontrol.h>
+
+@class NSString;
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraService;
+
+class AVFMediaContainerControl : public QMediaContainerControl
+{
+public:
+ explicit AVFMediaContainerControl(AVFCameraService *service);
+
+ QStringList supportedContainers() const Q_DECL_OVERRIDE;
+ QString containerFormat() const Q_DECL_OVERRIDE;
+ void setContainerFormat(const QString &format) Q_DECL_OVERRIDE;
+ QString containerDescription(const QString &formatMimeType) const Q_DECL_OVERRIDE;
+
+ NSString *fileType() const;
+
+private:
+ QString m_format;
+};
+
+QT_END_NAMESPACE
+
+#endif // AVFMEDIACONTAINERCONTROL_H
diff --git a/src/plugins/avfoundation/camera/avfmediacontainercontrol.mm b/src/plugins/avfoundation/camera/avfmediacontainercontrol.mm
new file mode 100644
index 000000000..a8dc3c844
--- /dev/null
+++ b/src/plugins/avfoundation/camera/avfmediacontainercontrol.mm
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "avfmediacontainercontrol.h"
+
+#include <AVFoundation/AVMediaFormat.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/plugins/avfoundation/camera/avfmediarecordercontrol.h b/src/plugins/avfoundation/camera/avfmediarecordercontrol.h
index b6d2e1b2a..a4894b3da 100644
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol.h
+++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol.h
@@ -45,6 +45,7 @@
#import <AVFoundation/AVFoundation.h>
#include "avfstoragelocation.h"
+#include "avfcamerautility.h"
@class AVFMediaRecorderDelegate;
@@ -74,6 +75,7 @@ public:
qreal volume() const;
void applySettings();
+ void unapplySettings();
public Q_SLOTS:
void setState(QMediaRecorder::State state);
@@ -89,6 +91,7 @@ private Q_SLOTS:
void updateStatus();
private:
+ AVFCameraService *m_service;
AVFCameraControl *m_cameraControl;
AVFAudioInputSelectorControl *m_audioInputControl;
AVFCameraSession *m_session;
@@ -108,6 +111,8 @@ private:
AVCaptureMovieFileOutput *m_movieOutput;
AVFMediaRecorderDelegate *m_recorderDelagate;
AVFStorageLocation m_storageLocation;
+
+ AVFPSRange m_restoreFPS;
};
QT_END_NAMESPACE
diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm b/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
index 8460abe14..79bf2e932 100644
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
+++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
@@ -43,7 +43,9 @@
#include "avfcameraservice.h"
#include "avfcameracontrol.h"
#include "avfaudioinputselectorcontrol.h"
-#include "avfcamerautility.h"
+#include "avfaudioencodersettingscontrol.h"
+#include "avfvideoencodersettingscontrol.h"
+#include "avfmediacontainercontrol.h"
#include <QtCore/qurl.h>
#include <QtCore/qfileinfo.h>
@@ -121,6 +123,7 @@ QT_USE_NAMESPACE
AVFMediaRecorderControl::AVFMediaRecorderControl(AVFCameraService *service, QObject *parent)
: QMediaRecorderControl(parent)
+ , m_service(service)
, m_cameraControl(service->cameraControl())
, m_audioInputControl(service->audioInputSelectorControl())
, m_session(service->session())
@@ -132,6 +135,7 @@ AVFMediaRecorderControl::AVFMediaRecorderControl(AVFCameraService *service, QObj
, 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];
@@ -231,6 +235,29 @@ qreal AVFMediaRecorderControl::volume() const
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)
@@ -244,24 +271,28 @@ void AVFMediaRecorderControl::setState(QMediaRecorder::State state)
case QMediaRecorder::RecordingState:
{
if (m_connected) {
- m_state = QMediaRecorder::RecordingState;
- m_recordingStarted = false;
- m_recordingFinished = false;
-
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_"),
- QLatin1String("mp4")));
+ 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);
} else {
Q_EMIT error(QMediaRecorder::FormatError, tr("Recorder not configured"));
@@ -277,6 +308,7 @@ void AVFMediaRecorderControl::setState(QMediaRecorder::State state)
{
m_state = QMediaRecorder::StoppedState;
[m_movieOutput stopRecording];
+ unapplySettings();
}
}
diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h
index c3fe02c44..6736f4639 100644
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h
+++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h
@@ -45,6 +45,7 @@
#include "avfcamerautility.h"
#include <QtMultimedia/qmediarecordercontrol.h>
+#include <private/qvideooutputorientationhandler_p.h>
#include <QtCore/qglobal.h>
#include <QtCore/qurl.h>
@@ -76,6 +77,7 @@ public:
qreal volume() const Q_DECL_OVERRIDE;
void applySettings() Q_DECL_OVERRIDE;
+ void unapplySettings();
public Q_SLOTS:
void setState(QMediaRecorder::State state) Q_DECL_OVERRIDE;
@@ -104,6 +106,10 @@ private:
QMediaRecorder::State m_state;
QMediaRecorder::Status m_lastStatus;
+
+ NSDictionary *m_audioSettings;
+ NSDictionary *m_videoSettings;
+ QVideoOutputOrientationHandler m_orientationHandler;
};
QT_END_NAMESPACE
diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm
index 72386eeda..7c8725260 100644
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm
+++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm
@@ -44,6 +44,10 @@
#include "avfcameracontrol.h"
#include "avfcameraservice.h"
#include "avfcameradebug.h"
+#include "avfaudioencodersettingscontrol.h"
+#include "avfvideoencodersettingscontrol.h"
+#include "avfmediacontainercontrol.h"
+#include "avfcamerautility.h"
#include <QtCore/qdebug.h>
@@ -83,6 +87,8 @@ AVFMediaRecorderControlIOS::AVFMediaRecorderControlIOS(AVFCameraService *service
, m_service(service)
, m_state(QMediaRecorder::StoppedState)
, m_lastStatus(QMediaRecorder::UnloadedStatus)
+ , m_audioSettings(nil)
+ , m_videoSettings(nil)
{
Q_ASSERT(service);
@@ -113,6 +119,11 @@ AVFMediaRecorderControlIOS::AVFMediaRecorderControlIOS(AVFCameraService *service
AVFMediaRecorderControlIOS::~AVFMediaRecorderControlIOS()
{
[m_writer abort];
+
+ if (m_audioSettings)
+ [m_audioSettings release];
+ if (m_videoSettings)
+ [m_videoSettings release];
}
QUrl AVFMediaRecorderControlIOS::outputLocation() const
@@ -153,6 +164,43 @@ qreal AVFMediaRecorderControlIOS::volume() const
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)
@@ -189,7 +237,8 @@ void AVFMediaRecorderControlIOS::setState(QMediaRecorder::State state)
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_"), QLatin1String("mp4"))));
+ QLatin1String("clip_"),
+ m_service->mediaContainerControl()->containerFormat())));
NSURL *nsFileURL = fileURL.toNSURL();
if (!nsFileURL) {
@@ -217,7 +266,28 @@ void AVFMediaRecorderControlIOS::setState(QMediaRecorder::State state)
// generated, will restart in assetWriterStarted.
[session stopRunning];
- if ([m_writer setupWithFileURL:nsFileURL cameraService:m_service]) {
+ 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;
+
+ // convert to radians
+ rotation *= M_PI / 180.f;
+
+ if ([m_writer setupWithFileURL:nsFileURL
+ cameraService:m_service
+ audioSettings:m_audioSettings
+ videoSettings:m_videoSettings
+ transform:CGAffineTransformMakeRotation(rotation)]) {
+
m_state = QMediaRecorder::RecordingState;
m_lastStatus = QMediaRecorder::StartingStatus;
@@ -276,6 +346,8 @@ void AVFMediaRecorderControlIOS::assetWriterFinished()
else
m_lastStatus = QMediaRecorder::UnloadedStatus;
+ unapplySettings();
+
m_service->videoOutput()->resetCaptureDelegate();
[m_service->session()->captureSession() startRunning];
diff --git a/src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.h b/src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.h
new file mode 100644
index 000000000..200da90bd
--- /dev/null
+++ b/src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef AVFVIDEOENCODERSETTINGSCONTROL_H
+#define AVFVIDEOENCODERSETTINGSCONTROL_H
+
+#include <qvideoencodersettingscontrol.h>
+
+#include "avfcamerautility.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 = 0) const Q_DECL_OVERRIDE;
+
+ QList<qreal> supportedFrameRates(const QVideoEncoderSettings &requestedVideoSettings,
+ bool *continuous = 0) const Q_DECL_OVERRIDE;
+
+ QStringList supportedVideoCodecs() const Q_DECL_OVERRIDE;
+ QString videoCodecDescription(const QString &codecName) const Q_DECL_OVERRIDE;
+
+ QVideoEncoderSettings videoSettings() const Q_DECL_OVERRIDE;
+ void setVideoSettings(const QVideoEncoderSettings &requestedVideoSettings) Q_DECL_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/plugins/avfoundation/camera/avfvideoencodersettingscontrol.mm b/src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.mm
new file mode 100644
index 000000000..248997d57
--- /dev/null
+++ b/src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.mm
@@ -0,0 +1,395 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "avfvideoencodersettingscontrol.h"
+
+#include "avfcameraservice.h"
+#include "avfcamerautility.h"
+#include "avfcamerasession.h"
+#include "avfcamerarenderercontrol.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
+ ))
+
+#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
+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;
+}
+#endif
+
+static bool real_list_contains(const QList<qreal> &list, qreal value)
+{
+ Q_FOREACH (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));
+
+#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
+ if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) {
+ 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));
+ }
+ }
+#endif
+
+ if (resolutions.count() == 1)
+ resolutions.append(QSize(3840, 3840));
+
+ return resolutions;
+}
+
+QList<qreal> AVFVideoEncoderSettingsControl::supportedFrameRates(const QVideoEncoderSettings &settings,
+ bool *continuous) const
+{
+#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
+ QList<qreal> uniqueFrameRates;
+
+ if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) {
+ 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;
+#else
+ return QList<qreal>();
+#endif
+}
+
+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 QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
+ 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);
+ }
+ }
+#endif
+
+ 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 QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
+ if (m_restoreFormat) {
+ qt_set_active_format(device, m_restoreFormat, !needFpsChanged);
+ [m_restoreFormat release];
+ m_restoreFormat = nil;
+ }
+#endif
+
+ if (needFpsChanged) {
+ qt_set_framerate_limits(device, connection, m_restoreFps.first, m_restoreFps.second);
+ m_restoreFps = AVFPSRange();
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_avfvideoencodersettingscontrol.cpp"
diff --git a/src/plugins/avfoundation/camera/camera.pro b/src/plugins/avfoundation/camera/camera.pro
index 8563eb655..a17ff5a73 100644
--- a/src/plugins/avfoundation/camera/camera.pro
+++ b/src/plugins/avfoundation/camera/camera.pro
@@ -37,7 +37,10 @@ HEADERS += \
avfcamerautility.h \
avfcameraviewfindersettingscontrol.h \
avfimageencodercontrol.h \
- avfcameraflashcontrol.h
+ avfcameraflashcontrol.h \
+ avfvideoencodersettingscontrol.h \
+ avfmediacontainercontrol.h \
+ avfaudioencodersettingscontrol.h
OBJECTIVE_SOURCES += \
avfcameraserviceplugin.mm \
@@ -57,7 +60,10 @@ OBJECTIVE_SOURCES += \
avfcamerautility.mm \
avfcameraviewfindersettingscontrol.mm \
avfimageencodercontrol.mm \
- avfcameraflashcontrol.mm
+ avfcameraflashcontrol.mm \
+ avfvideoencodersettingscontrol.mm \
+ avfmediacontainercontrol.mm \
+ avfaudioencodersettingscontrol.mm
osx {
diff --git a/src/plugins/directshow/player/directshowiosource.cpp b/src/plugins/directshow/player/directshowiosource.cpp
index 6dee14fb0..bb4d0f00d 100644
--- a/src/plugins/directshow/player/directshowiosource.cpp
+++ b/src/plugins/directshow/player/directshowiosource.cpp
@@ -46,13 +46,6 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qurl.h>
-static const GUID directshow_subtypes[] =
-{
- MEDIASUBTYPE_Avi,
- MEDIASUBTYPE_WAVE,
- MEDIASUBTYPE_NULL
-};
-
DirectShowIOSource::DirectShowIOSource(DirectShowEventLoop *loop)
: m_ref(1)
, m_state(State_Stopped)
@@ -63,30 +56,21 @@ DirectShowIOSource::DirectShowIOSource(DirectShowEventLoop *loop)
, m_allocator(0)
, m_peerPin(0)
, m_pinId(QLatin1String("Data"))
+ , m_queriedForAsyncReader(false)
{
- QVector<AM_MEDIA_TYPE> mediaTypes;
-
- AM_MEDIA_TYPE type =
- {
- MEDIATYPE_Stream, // majortype
- MEDIASUBTYPE_NULL, // subtype
- TRUE, // bFixedSizeSamples
- FALSE, // bTemporalCompression
- 1, // lSampleSize
- GUID_NULL, // formattype
- 0, // pUnk
- 0, // cbFormat
- 0, // pbFormat
- };
-
- static const int count = sizeof(directshow_subtypes) / sizeof(GUID);
-
- for (int i = 0; i < count; ++i) {
- type.subtype = directshow_subtypes[i];
- mediaTypes.append(type);
- }
+ // This filter has only one possible output type, that is, a stream of data
+ // with no particular subtype. The graph builder will try every demux/decode filters
+ // to find one able to decode the stream.
+ //
+ // The filter works in pull mode, the downstream filter is responsible for requesting
+ // samples from this one.
+
+ m_outputType.majortype = MEDIATYPE_Stream;
+ m_outputType.subtype = MEDIASUBTYPE_NULL; // Wildcard
+ m_outputType.bFixedSizeSamples = TRUE;
+ m_outputType.lSampleSize = 1;
- setMediaTypes(mediaTypes);
+ setMediaTypes(QVector<AM_MEDIA_TYPE>() << m_outputType);
}
DirectShowIOSource::~DirectShowIOSource()
@@ -136,6 +120,7 @@ HRESULT DirectShowIOSource::QueryInterface(REFIID riid, void **ppvObject)
} else if (riid == IID_IPin) {
*ppvObject = static_cast<IPin *>(this);
} else if (riid == IID_IAsyncReader) {
+ m_queriedForAsyncReader = true;
*ppvObject = static_cast<IAsyncReader *>(m_reader);
} else {
*ppvObject = 0;
@@ -330,116 +315,40 @@ ULONG DirectShowIOSource::GetMiscFlags()
// IPin
HRESULT DirectShowIOSource::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
{
- QMutexLocker locker(&m_mutex);
- if (!pReceivePin) {
+ if (!pReceivePin)
return E_POINTER;
- } else if (m_state != State_Stopped) {
- return VFW_E_NOT_STOPPED;
- } else if (m_peerPin) {
- return VFW_E_ALREADY_CONNECTED;
- } else {
- HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED;
-
- m_peerPin = pReceivePin;
- m_peerPin->AddRef();
- if (!pmt) {
- IEnumMediaTypes *mediaTypes = 0;
- if (pReceivePin->EnumMediaTypes(&mediaTypes) == S_OK) {
- for (AM_MEDIA_TYPE *type = 0;
- mediaTypes->Next(1, &type, 0) == S_OK;
- DirectShowMediaType::deleteType(type)) {
- switch (tryConnect(pReceivePin, type)) {
- case S_OK:
- DirectShowMediaType::freeData(type);
- mediaTypes->Release();
- return S_OK;
- case VFW_E_NO_TRANSPORT:
- hr = VFW_E_NO_TRANSPORT;
- break;
- default:
- break;
- }
- }
- mediaTypes->Release();
- }
- AM_MEDIA_TYPE type =
- {
- MEDIATYPE_Stream, // majortype
- MEDIASUBTYPE_NULL, // subtype
- TRUE, // bFixedSizeSamples
- FALSE, // bTemporalCompression
- 1, // lSampleSize
- GUID_NULL, // formattype
- 0, // pUnk
- 0, // cbFormat
- 0, // pbFormat
- };
-
- static const int count = sizeof(directshow_subtypes) / sizeof(GUID);
-
- for (int i = 0; i < count; ++i) {
- type.subtype = directshow_subtypes[i];
-
- switch (tryConnect(pReceivePin, &type)) {
- case S_OK:
- return S_OK;
- case VFW_E_NO_TRANSPORT:
- hr = VFW_E_NO_TRANSPORT;
- break;
- default:
- break;
- }
- }
- } else if (pmt->majortype == MEDIATYPE_Stream && (hr = tryConnect(pReceivePin, pmt))) {
- return S_OK;
- }
-
- m_peerPin->Release();
- m_peerPin = 0;
+ QMutexLocker locker(&m_mutex);
- m_mediaType.clear();
+ if (m_state != State_Stopped)
+ return VFW_E_NOT_STOPPED;
- return hr;
- }
-}
+ if (m_peerPin)
+ return VFW_E_ALREADY_CONNECTED;
-HRESULT DirectShowIOSource::tryConnect(IPin *pin, const AM_MEDIA_TYPE *type)
-{
- m_mediaType = *type;
+ // If we get a type from the graph manager, check that we support that
+ if (pmt && (pmt->majortype != MEDIATYPE_Stream || pmt->subtype != MEDIASUBTYPE_NULL))
+ return VFW_E_TYPE_NOT_ACCEPTED;
- HRESULT hr = pin->ReceiveConnection(this, type);
+ // This filter only works in pull mode, the downstream filter must query for the
+ // AsyncReader interface during ReceiveConnection().
+ // If it doesn't, we can't connect to it.
+ m_queriedForAsyncReader = false;
+ HRESULT hr = pReceivePin->ReceiveConnection(this, pmt ? pmt : &m_outputType);
- if (!SUCCEEDED(hr)) {
+ if (SUCCEEDED(hr) && m_queriedForAsyncReader) {
+ m_peerPin = pReceivePin;
+ m_peerPin->AddRef();
+ } else {
+ pReceivePin->Disconnect();
if (m_allocator) {
m_allocator->Release();
m_allocator = 0;
}
- } else if (!m_allocator) {
- hr = VFW_E_NO_TRANSPORT;
-
- if (IMemInputPin *memPin = com_cast<IMemInputPin>(pin, IID_IMemInputPin)) {
- if ((m_allocator = com_new<IMemAllocator>(CLSID_MemoryAllocator))) {
- ALLOCATOR_PROPERTIES properties;
- if (memPin->GetAllocatorRequirements(&properties) == S_OK
- || m_allocator->GetProperties(&properties) == S_OK) {
- if (properties.cbAlign == 0)
- properties.cbAlign = 1;
-
- ALLOCATOR_PROPERTIES actualProperties;
- if (SUCCEEDED(hr = m_allocator->SetProperties(&properties, &actualProperties)))
- hr = memPin->NotifyAllocator(m_allocator, TRUE);
- }
- if (!SUCCEEDED(hr)) {
- m_allocator->Release();
- m_allocator = 0;
- }
- }
- memPin->Release();
- }
- if (!SUCCEEDED(hr))
- pin->Disconnect();
+ if (!m_queriedForAsyncReader)
+ hr = VFW_E_NO_TRANSPORT;
}
+
return hr;
}
@@ -453,6 +362,8 @@ HRESULT DirectShowIOSource::ReceiveConnection(IPin *pConnector, const AM_MEDIA_T
HRESULT DirectShowIOSource::Disconnect()
{
+ QMutexLocker locker(&m_mutex);
+
if (!m_peerPin) {
return S_FALSE;
} else if (m_state != State_Stopped) {
@@ -471,8 +382,6 @@ HRESULT DirectShowIOSource::Disconnect()
m_peerPin->Release();
m_peerPin = 0;
- m_mediaType.clear();
-
return S_OK;
}
}
@@ -510,7 +419,7 @@ HRESULT DirectShowIOSource::ConnectionMediaType(AM_MEDIA_TYPE *pmt)
return VFW_E_NOT_CONNECTED;
} else {
- DirectShowMediaType::copy(pmt, m_mediaType);
+ DirectShowMediaType::copy(pmt, m_outputType);
return S_OK;
}
diff --git a/src/plugins/directshow/player/directshowiosource.h b/src/plugins/directshow/player/directshowiosource.h
index bb90ba398..225cfc1ab 100644
--- a/src/plugins/directshow/player/directshowiosource.h
+++ b/src/plugins/directshow/player/directshowiosource.h
@@ -117,8 +117,6 @@ public:
HRESULT STDMETHODCALLTYPE QueryDirection(PIN_DIRECTION *pPinDir);
private:
- HRESULT tryConnect(IPin *pin, const AM_MEDIA_TYPE *type);
-
volatile LONG m_ref;
FILTER_STATE m_state;
DirectShowIOReader *m_reader;
@@ -127,9 +125,10 @@ private:
IReferenceClock *m_clock;
IMemAllocator *m_allocator;
IPin *m_peerPin;
- DirectShowMediaType m_mediaType;
+ DirectShowMediaType m_outputType;
QString m_filterName;
const QString m_pinId;
+ bool m_queriedForAsyncReader;
QMutex m_mutex;
};
diff --git a/src/plugins/pulseaudio/qpulseaudioengine.cpp b/src/plugins/pulseaudio/qpulseaudioengine.cpp
index 19ba7472f..286f310bc 100644
--- a/src/plugins/pulseaudio/qpulseaudioengine.cpp
+++ b/src/plugins/pulseaudio/qpulseaudioengine.cpp
@@ -81,8 +81,10 @@ static void serverInfoCallback(pa_context *context, const pa_server_info *info,
#endif
QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata);
+ pulseEngine->m_serverLock.lockForWrite();
pulseEngine->m_defaultSink = info->default_sink_name;
pulseEngine->m_defaultSource = info->default_source_name;
+ pulseEngine->m_serverLock.unlock();
pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
}
@@ -90,11 +92,6 @@ static void serverInfoCallback(pa_context *context, const pa_server_info *info,
static void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int isLast, void *userdata)
{
QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata);
- QMap<pa_sink_state, QString> stateMap;
- stateMap[PA_SINK_INVALID_STATE] = "n/a";
- stateMap[PA_SINK_RUNNING] = "RUNNING";
- stateMap[PA_SINK_IDLE] = "IDLE";
- stateMap[PA_SINK_SUSPENDED] = "SUSPENDED";
if (isLast < 0) {
qWarning() << QString("Failed to get sink information: %s").arg(pa_strerror(pa_context_errno(context)));
@@ -109,6 +106,12 @@ static void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int
Q_ASSERT(info);
#ifdef DEBUG_PULSE
+ QMap<pa_sink_state, QString> stateMap;
+ stateMap[PA_SINK_INVALID_STATE] = "n/a";
+ stateMap[PA_SINK_RUNNING] = "RUNNING";
+ stateMap[PA_SINK_IDLE] = "IDLE";
+ stateMap[PA_SINK_SUSPENDED] = "SUSPENDED";
+
qDebug() << QString("Sink #%1\n"
"\tState: %2\n"
"\tName: %3\n"
@@ -120,8 +123,10 @@ static void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int
#endif
QAudioFormat format = QPulseAudioInternal::sampleSpecToAudioFormat(info->sample_spec);
+
+ QWriteLocker locker(&pulseEngine->m_sinkLock);
pulseEngine->m_preferredFormats.insert(info->name, format);
- pulseEngine->m_sinks.append(info->name);
+ pulseEngine->m_sinks.insert(info->index, info->name);
}
static void sourceInfoCallback(pa_context *context, const pa_source_info *info, int isLast, void *userdata)
@@ -129,12 +134,6 @@ static void sourceInfoCallback(pa_context *context, const pa_source_info *info,
Q_UNUSED(context)
QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata);
- QMap<pa_source_state, QString> stateMap;
- stateMap[PA_SOURCE_INVALID_STATE] = "n/a";
- stateMap[PA_SOURCE_RUNNING] = "RUNNING";
- stateMap[PA_SOURCE_IDLE] = "IDLE";
- stateMap[PA_SOURCE_SUSPENDED] = "SUSPENDED";
-
if (isLast) {
pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
return;
@@ -143,6 +142,12 @@ static void sourceInfoCallback(pa_context *context, const pa_source_info *info,
Q_ASSERT(info);
#ifdef DEBUG_PULSE
+ QMap<pa_source_state, QString> stateMap;
+ stateMap[PA_SOURCE_INVALID_STATE] = "n/a";
+ stateMap[PA_SOURCE_RUNNING] = "RUNNING";
+ stateMap[PA_SOURCE_IDLE] = "IDLE";
+ stateMap[PA_SOURCE_SUSPENDED] = "SUSPENDED";
+
qDebug() << QString("Source #%1\n"
"\tState: %2\n"
"\tName: %3\n"
@@ -154,8 +159,57 @@ static void sourceInfoCallback(pa_context *context, const pa_source_info *info,
#endif
QAudioFormat format = QPulseAudioInternal::sampleSpecToAudioFormat(info->sample_spec);
+
+ QWriteLocker locker(&pulseEngine->m_sourceLock);
pulseEngine->m_preferredFormats.insert(info->name, format);
- pulseEngine->m_sources.append(info->name);
+ pulseEngine->m_sources.insert(info->index, info->name);
+}
+
+static void event_cb(pa_context* context, pa_subscription_event_type_t t, uint32_t index, void* userdata)
+{
+ QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata);
+
+ int type = t & PA_SUBSCRIPTION_EVENT_TYPE_MASK;
+ int facility = t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
+
+ switch (type) {
+ case PA_SUBSCRIPTION_EVENT_NEW:
+ case PA_SUBSCRIPTION_EVENT_CHANGE:
+ switch (facility) {
+ case PA_SUBSCRIPTION_EVENT_SERVER:
+ pa_operation_unref(pa_context_get_server_info(context, serverInfoCallback, userdata));
+ break;
+ case PA_SUBSCRIPTION_EVENT_SINK:
+ pa_operation_unref(pa_context_get_sink_info_by_index(context, index, sinkInfoCallback, userdata));
+ break;
+ case PA_SUBSCRIPTION_EVENT_SOURCE:
+ pa_operation_unref(pa_context_get_source_info_by_index(context, index, sourceInfoCallback, userdata));
+ break;
+ default:
+ break;
+ }
+ break;
+ case PA_SUBSCRIPTION_EVENT_REMOVE:
+ switch (facility) {
+ case PA_SUBSCRIPTION_EVENT_SINK:
+ pulseEngine->m_sinkLock.lockForWrite();
+ pulseEngine->m_preferredFormats.remove(pulseEngine->m_sinks.value(index));
+ pulseEngine->m_sinks.remove(index);
+ pulseEngine->m_sinkLock.unlock();
+ break;
+ case PA_SUBSCRIPTION_EVENT_SOURCE:
+ pulseEngine->m_sourceLock.lockForWrite();
+ pulseEngine->m_preferredFormats.remove(pulseEngine->m_sources.value(index));
+ pulseEngine->m_sources.remove(index);
+ pulseEngine->m_sourceLock.unlock();
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
}
static void contextStateCallbackInit(pa_context *context, void *userdata)
@@ -278,6 +332,13 @@ void QPulseAudioEngine::prepare()
if (ok) {
pa_context_set_state_callback(m_context, contextStateCallback, this);
+
+ pa_context_set_subscribe_callback(m_context, event_cb, this);
+ pa_operation_unref(pa_context_subscribe(m_context,
+ pa_subscription_mask_t(PA_SUBSCRIPTION_MASK_SINK |
+ PA_SUBSCRIPTION_MASK_SOURCE |
+ PA_SUBSCRIPTION_MASK_SERVER),
+ NULL, NULL));
} else {
pa_context_unref(m_context);
m_context = 0;
@@ -338,14 +399,6 @@ void QPulseAudioEngine::updateDevices()
pa_operation_unref(operation);
unlock();
-
- // Swap the default output to index 0
- m_sinks.removeOne(m_defaultSink);
- m_sinks.prepend(m_defaultSink);
-
- // Swap the default input to index 0
- m_sources.removeOne(m_defaultSource);
- m_sources.prepend(m_defaultSource);
}
void QPulseAudioEngine::onContextFailed()
@@ -366,7 +419,28 @@ QPulseAudioEngine *QPulseAudioEngine::instance()
QList<QByteArray> QPulseAudioEngine::availableDevices(QAudio::Mode mode) const
{
- return mode == QAudio::AudioOutput ? m_sinks : m_sources;
+ QList<QByteArray> devices;
+ QByteArray defaultDevice;
+
+ m_serverLock.lockForRead();
+
+ if (mode == QAudio::AudioOutput) {
+ QReadLocker locker(&m_sinkLock);
+ devices = m_sinks.values();
+ defaultDevice = m_defaultSink;
+ } else {
+ QReadLocker locker(&m_sourceLock);
+ devices = m_sources.values();
+ defaultDevice = m_defaultSource;
+ }
+
+ m_serverLock.unlock();
+
+ // Swap the default device to index 0
+ devices.removeOne(defaultDevice);
+ devices.prepend(defaultDevice);
+
+ return devices;
}
QT_END_NAMESPACE
diff --git a/src/plugins/pulseaudio/qpulseaudioengine.h b/src/plugins/pulseaudio/qpulseaudioengine.h
index 5eb96bf00..f03dbfd16 100644
--- a/src/plugins/pulseaudio/qpulseaudioengine.h
+++ b/src/plugins/pulseaudio/qpulseaudioengine.h
@@ -53,6 +53,7 @@
#include <QtCore/qmap.h>
#include <QtCore/qbytearray.h>
+#include <QtCore/qreadwritelock.h>
#include <qaudiosystemplugin.h>
#include <pulse/pulseaudio.h>
#include "qpulsehelpers.h"
@@ -104,13 +105,17 @@ private:
void release();
public:
- QList<QByteArray> m_sinks;
- QList<QByteArray> m_sources;
+ QMap<int, QByteArray> m_sinks;
+ QMap<int, QByteArray> m_sources;
QMap<QByteArray, QAudioFormat> m_preferredFormats;
QByteArray m_defaultSink;
QByteArray m_defaultSource;
+ mutable QReadWriteLock m_sinkLock;
+ mutable QReadWriteLock m_sourceLock;
+ mutable QReadWriteLock m_serverLock;
+
private:
pa_mainloop_api *m_mainLoopApi;
pa_threaded_mainloop *m_mainLoop;
diff --git a/src/plugins/winrt/qwinrtabstractvideorenderercontrol.cpp b/src/plugins/winrt/qwinrtabstractvideorenderercontrol.cpp
index 7dd2e6557..f109dc4cb 100644
--- a/src/plugins/winrt/qwinrtabstractvideorenderercontrol.cpp
+++ b/src/plugins/winrt/qwinrtabstractvideorenderercontrol.cpp
@@ -38,6 +38,7 @@
#include <QtCore/qfunctions_winrt.h>
#include <QtCore/QGlobalStatic>
+#include <QtCore/QLoggingCategory>
#include <QtCore/QMetaMethod>
#include <QtCore/QPointer>
#include <QtGui/QOpenGLContext>
@@ -58,6 +59,8 @@ using namespace Microsoft::WRL;
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcMMVideoRender, "qt.mm.videorender")
+
#define BREAK_IF_FAILED(msg) RETURN_IF_FAILED(msg, break)
#define CONTINUE_IF_FAILED(msg) RETURN_IF_FAILED(msg, continue)
@@ -66,6 +69,7 @@ struct QWinRTVideoRendererControlGlobal
{
QWinRTVideoRendererControlGlobal()
{
+ qCDebug(lcMMVideoRender) << __FUNCTION__;
HRESULT hr;
D3D_FEATURE_LEVEL featureLevels[] =
@@ -202,6 +206,7 @@ ID3D11Device *QWinRTAbstractVideoRendererControl::d3dDevice()
// This is required so that subclasses can stop the render thread before deletion
void QWinRTAbstractVideoRendererControl::shutdown()
{
+ qCDebug(lcMMVideoRender) << __FUNCTION__;
Q_D(QWinRTAbstractVideoRendererControl);
if (d->renderThread.isRunning()) {
d->renderThread.requestInterruption();
@@ -212,6 +217,7 @@ void QWinRTAbstractVideoRendererControl::shutdown()
QWinRTAbstractVideoRendererControl::QWinRTAbstractVideoRendererControl(const QSize &size, QObject *parent)
: QVideoRendererControl(parent), d_ptr(new QWinRTAbstractVideoRendererControlPrivate)
{
+ qCDebug(lcMMVideoRender) << __FUNCTION__;
Q_D(QWinRTAbstractVideoRendererControl);
d->format = QVideoSurfaceFormat(size, QVideoFrame::Format_BGRA32,
@@ -232,6 +238,7 @@ QWinRTAbstractVideoRendererControl::QWinRTAbstractVideoRendererControl(const QSi
QWinRTAbstractVideoRendererControl::~QWinRTAbstractVideoRendererControl()
{
+ qCDebug(lcMMVideoRender) << __FUNCTION__;
Q_D(QWinRTAbstractVideoRendererControl);
CriticalSectionLocker locker(&d->mutex);
shutdown();
@@ -253,6 +260,7 @@ void QWinRTAbstractVideoRendererControl::setSurface(QAbstractVideoSurface *surfa
void QWinRTAbstractVideoRendererControl::syncAndRender()
{
+ qCDebug(lcMMVideoRender) << __FUNCTION__;
Q_D(QWinRTAbstractVideoRendererControl);
QThread *currentThread = QThread::currentThread();
@@ -334,6 +342,7 @@ void QWinRTAbstractVideoRendererControl::setScanLineDirection(QVideoSurfaceForma
void QWinRTAbstractVideoRendererControl::setActive(bool active)
{
+ qCDebug(lcMMVideoRender) << __FUNCTION__ << active;
Q_D(QWinRTAbstractVideoRendererControl);
if (d->active == active)
@@ -351,7 +360,7 @@ void QWinRTAbstractVideoRendererControl::setActive(bool active)
return;
}
- d->renderThread.requestInterruption();
+ shutdown();
if (d->surface && d->surface->isActive())
d->surface->stop();
}
diff --git a/src/plugins/winrt/qwinrtcameracontrol.cpp b/src/plugins/winrt/qwinrtcameracontrol.cpp
index 856549438..390364eb8 100644
--- a/src/plugins/winrt/qwinrtcameracontrol.cpp
+++ b/src/plugins/winrt/qwinrtcameracontrol.cpp
@@ -1124,46 +1124,54 @@ bool QWinRTCameraControl::setFocus(QCameraFocus::FocusModes modes)
if (d->status == QCamera::UnloadedStatus)
return false;
- ComPtr<IFocusSettings> focusSettings;
- ComPtr<IInspectable> focusSettingsObject;
- HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_Devices_FocusSettings).Get(), &focusSettingsObject);
- Q_ASSERT_SUCCEEDED(hr);
- hr = focusSettingsObject.As(&focusSettings);
- Q_ASSERT_SUCCEEDED(hr);
- FocusMode mode;
- if (modes.testFlag(QCameraFocus::ContinuousFocus)) {
- mode = FocusMode_Continuous;
- } else if (modes.testFlag(QCameraFocus::AutoFocus)
- || modes.testFlag(QCameraFocus::MacroFocus)
- || modes.testFlag(QCameraFocus::InfinityFocus)) {
- // The Macro and infinity focus modes are only supported in auto focus mode on WinRT.
- // QML camera focus doesn't support combined focus flags settings. In the case of macro
- // and infinity Focus modes, the auto focus setting is applied.
- mode = FocusMode_Single;
- } else {
- emit error(QCamera::NotSupportedFeatureError, QStringLiteral("Unsupported camera focus modes."));
- return false;
- }
- hr = focusSettings->put_Mode(mode);
- Q_ASSERT_SUCCEEDED(hr);
- AutoFocusRange range = AutoFocusRange_Normal;
- if (modes.testFlag(QCameraFocus::MacroFocus))
- range = AutoFocusRange_Macro;
- else if (modes.testFlag(QCameraFocus::InfinityFocus))
- range = AutoFocusRange_FullRange;
- hr = focusSettings->put_AutoFocusRange(range);
- Q_ASSERT_SUCCEEDED(hr);
- hr = focusSettings->put_WaitForFocus(true);
- Q_ASSERT_SUCCEEDED(hr);
- hr = focusSettings->put_DisableDriverFallback(false);
- Q_ASSERT_SUCCEEDED(hr);
+ bool result = false;
+ HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([modes, &result, d, this]() {
+ ComPtr<IFocusSettings> focusSettings;
+ ComPtr<IInspectable> focusSettingsObject;
+ HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_Devices_FocusSettings).Get(), &focusSettingsObject);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = focusSettingsObject.As(&focusSettings);
+ Q_ASSERT_SUCCEEDED(hr);
+ FocusMode mode;
+ if (modes.testFlag(QCameraFocus::ContinuousFocus)) {
+ mode = FocusMode_Continuous;
+ } else if (modes.testFlag(QCameraFocus::AutoFocus)
+ || modes.testFlag(QCameraFocus::MacroFocus)
+ || modes.testFlag(QCameraFocus::InfinityFocus)) {
+ // The Macro and infinity focus modes are only supported in auto focus mode on WinRT.
+ // QML camera focus doesn't support combined focus flags settings. In the case of macro
+ // and infinity Focus modes, the auto focus setting is applied.
+ mode = FocusMode_Single;
+ } else {
+ emit error(QCamera::NotSupportedFeatureError, QStringLiteral("Unsupported camera focus modes."));
+ result = false;
+ return S_OK;
+ }
+ hr = focusSettings->put_Mode(mode);
+ Q_ASSERT_SUCCEEDED(hr);
+ AutoFocusRange range = AutoFocusRange_Normal;
+ if (modes.testFlag(QCameraFocus::MacroFocus))
+ range = AutoFocusRange_Macro;
+ else if (modes.testFlag(QCameraFocus::InfinityFocus))
+ range = AutoFocusRange_FullRange;
+ hr = focusSettings->put_AutoFocusRange(range);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = focusSettings->put_WaitForFocus(true);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = focusSettings->put_DisableDriverFallback(false);
+ Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IFocusControl2> focusControl2;
- hr = d->focusControl.As(&focusControl2);
+ ComPtr<IFocusControl2> focusControl2;
+ hr = d->focusControl.As(&focusControl2);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = focusControl2->Configure(focusSettings.Get());
+ result = SUCCEEDED(hr);
+ RETURN_OK_IF_FAILED("Failed to configure camera focus control");
+ return S_OK;
+ });
Q_ASSERT_SUCCEEDED(hr);
- hr = focusControl2->Configure(focusSettings.Get());
- RETURN_FALSE_IF_FAILED("Failed to configure camera focus control");
- return true;
+ Q_UNUSED(hr); // Silence release build
+ return result;
}
bool QWinRTCameraControl::setFocusPoint(const QPointF &focusPoint)
@@ -1227,7 +1235,11 @@ bool QWinRTCameraControl::focus()
if (!d->focusControl || status == AsyncStatus::Started)
return false;
- hr = d->focusControl->FocusAsync(&d->focusOperation);
+ QEventDispatcherWinRT::runOnXamlThread([&d, &hr]() {
+ hr = d->focusControl->FocusAsync(&d->focusOperation);
+ Q_ASSERT_SUCCEEDED(hr);
+ return S_OK;
+ });
const long errorCode = HRESULT_CODE(hr);
if (errorCode == ERROR_OPERATION_IN_PROGRESS
|| errorCode == ERROR_WRITE_PROTECT) {
diff --git a/tests/auto/integration/qaudioinput/BLACKLIST b/tests/auto/integration/qaudioinput/BLACKLIST
index ab3f9e037..994ea8f38 100644
--- a/tests/auto/integration/qaudioinput/BLACKLIST
+++ b/tests/auto/integration/qaudioinput/BLACKLIST
@@ -1,8 +1,9 @@
#QTBUG-49736
[pushSuspendResume]
-redhatenterpriselinuxworkstation-6.6
-rhel-7.2
-ubuntu-14.04
-ubuntu-16.04
-opensuse-13.1
-opensuse-42.1
+linux
+[pull]
+linux
+[pullSuspendResume]
+linux
+[push]
+linux