summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYoann Lopes <yoann.lopes@theqtcompany.com>2016-02-10 14:52:44 +0100
committerYoann Lopes <yoann.lopes@qt.io>2016-06-03 09:43:10 +0000
commit4fa23e08a116b4d9bb67e25771c511838104999f (patch)
tree533c4057ddc353774a2c8a34c0d8c74f32c2ebf3
parentc0319d1cfb056c2e26f436357cdfdb3bdecd10f0 (diff)
AVFoundation: implement QVideoEncoderSettingsControl.
Task-number: QTBUG-40338 Change-Id: Ic23dabaad94d7b293019460710ae1a097002e227 Reviewed-by: Christian Stromme <christian.stromme@qt.io>
-rw-r--r--src/plugins/avfoundation/camera/avfcameraservice.h8
-rw-r--r--src/plugins/avfoundation/camera/avfcameraservice.mm26
-rw-r--r--src/plugins/avfoundation/camera/avfcamerasession.mm6
-rw-r--r--src/plugins/avfoundation/camera/avfcamerautility.h4
-rw-r--r--src/plugins/avfoundation/camera/avfcamerautility.mm183
-rw-r--r--src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h4
-rw-r--r--src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm205
-rw-r--r--src/plugins/avfoundation/camera/avfmediaassetwriter.h5
-rw-r--r--src/plugins/avfoundation/camera/avfmediaassetwriter.mm27
-rw-r--r--src/plugins/avfoundation/camera/avfmediarecordercontrol.h7
-rw-r--r--src/plugins/avfoundation/camera/avfmediarecordercontrol.mm34
-rw-r--r--src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h3
-rw-r--r--src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm38
-rw-r--r--src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.h82
-rw-r--r--src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.mm401
-rw-r--r--src/plugins/avfoundation/camera/camera.pro6
16 files changed, 781 insertions, 258 deletions
diff --git a/src/plugins/avfoundation/camera/avfcameraservice.h b/src/plugins/avfoundation/camera/avfcameraservice.h
index 08b0ad26d..76241d7b2 100644
--- a/src/plugins/avfoundation/camera/avfcameraservice.h
+++ b/src/plugins/avfoundation/camera/avfcameraservice.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2016 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
@@ -61,6 +61,7 @@ class AVFImageEncoderControl;
class AVFCameraFlashControl;
class AVFMediaRecorderControl;
class AVFMediaRecorderControlIOS;
+class AVFVideoEncoderSettingsControl;
class AVFCameraService : public QMediaService
{
@@ -77,8 +78,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; }
@@ -88,6 +88,7 @@ public:
AVFCameraViewfinderSettingsControl *viewfinderSettingsControl() const {return m_viewfinderSettingsControl; }
AVFImageEncoderControl *imageEncoderControl() const {return m_imageEncoderControl; }
AVFCameraFlashControl *flashControl() const {return m_flashControl; }
+ AVFVideoEncoderSettingsControl *videoEncoderSettingsControl() const {return m_videoEncoderSettingsControl; }
private:
AVFCameraSession *m_session;
@@ -106,6 +107,7 @@ private:
AVFCameraViewfinderSettingsControl *m_viewfinderSettingsControl;
AVFImageEncoderControl *m_imageEncoderControl;
AVFCameraFlashControl *m_flashControl;
+ AVFVideoEncoderSettingsControl *m_videoEncoderSettingsControl;
};
QT_END_NAMESPACE
diff --git a/src/plugins/avfoundation/camera/avfcameraservice.mm b/src/plugins/avfoundation/camera/avfcameraservice.mm
index 20156f06f..dc50319d0 100644
--- a/src/plugins/avfoundation/camera/avfcameraservice.mm
+++ b/src/plugins/avfoundation/camera/avfcameraservice.mm
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
@@ -53,6 +53,7 @@
#include "avfcameraviewfindersettingscontrol.h"
#include "avfimageencodercontrol.h"
#include "avfcameraflashcontrol.h"
+#include "avfvideoencodersettingscontrol.h"
#ifdef Q_OS_IOS
#include "avfcamerazoomcontrol.h"
@@ -99,6 +100,7 @@ AVFCameraService::AVFCameraService(QObject *parent):
m_viewfinderSettingsControl = new AVFCameraViewfinderSettingsControl(this);
m_imageEncoderControl = new AVFImageEncoderControl(this);
m_flashControl = new AVFCameraFlashControl(this);
+ m_videoEncoderSettingsControl = new AVFVideoEncoderSettingsControl(this);
}
AVFCameraService::~AVFCameraService()
@@ -130,6 +132,7 @@ AVFCameraService::~AVFCameraService()
delete m_viewfinderSettingsControl;
delete m_imageEncoderControl;
delete m_flashControl;
+ delete m_videoEncoderSettingsControl;
delete m_session;
}
@@ -176,6 +179,9 @@ QMediaControl *AVFCameraService::requestControl(const char *name)
if (qstrcmp(name, QCameraFlashControl_iid) == 0)
return m_flashControl;
+ if (qstrcmp(name, QVideoEncoderSettingsControl_iid) == 0)
+ return m_videoEncoderSettingsControl;
+
if (qstrcmp(name,QMediaVideoProbeControl_iid) == 0) {
AVFMediaVideoProbeControl *videoProbe = 0;
videoProbe = new AVFMediaVideoProbeControl(this);
@@ -214,23 +220,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.mm b/src/plugins/avfoundation/camera/avfcamerasession.mm
index 993e28319..eac1b44ec 100644
--- a/src/plugins/avfoundation/camera/avfcamerasession.mm
+++ b/src/plugins/avfoundation/camera/avfcamerasession.mm
@@ -386,13 +386,11 @@ bool AVFCameraSession::applyViewfinderSettings()
// 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 (!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 64093645f..7a0de4a66 100644
--- a/src/plugins/avfoundation/camera/avfcamerautility.h
+++ b/src/plugins/avfoundation/camera/avfcamerautility.h
@@ -178,6 +178,10 @@ AVFrameRateRange *qt_find_supported_framerate_range(AVCaptureDeviceFormat *forma
#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 3668bb124..0caf07f07 100644
--- a/src/plugins/avfoundation/camera/avfcamerautility.mm
+++ b/src/plugins/avfoundation/camera/avfcamerautility.mm
@@ -171,8 +171,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);
@@ -383,4 +382,184 @@ AVFrameRateRange *qt_find_supported_framerate_range(AVCaptureDeviceFormat *forma
#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."
+#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.
+
+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 9a5bbd5de..85a63cb8e 100644
--- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h
+++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2016 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
@@ -76,7 +76,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 472e84ed1..1d2539893 100644
--- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm
+++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2016 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
@@ -66,194 +66,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)
@@ -393,7 +205,7 @@ void AVFCameraViewfinderSettingsControl2::setViewfinderSettings(const QCameraVie
return;
m_settings = settings;
- applySettings();
+ applySettings(m_settings);
}
QVideoFrame::PixelFormat AVFCameraViewfinderSettingsControl2::QtPixelFormatFromCVFormat(unsigned avPixelFormat)
@@ -478,8 +290,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.
@@ -553,7 +366,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) {
@@ -567,7 +380,7 @@ 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);
@@ -587,7 +400,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
@@ -623,7 +436,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/avfmediaassetwriter.h b/src/plugins/avfoundation/camera/avfmediaassetwriter.h
index 21915e9ee..dd5b60bf7 100644
--- a/src/plugins/avfoundation/camera/avfmediaassetwriter.h
+++ b/src/plugins/avfoundation/camera/avfmediaassetwriter.h
@@ -86,13 +86,16 @@ QT_END_NAMESPACE
@private
CMTime m_startTime;
CMTime m_lastTimeStamp;
+
+ 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
+ videoSettings:(NSDictionary *)videoSettings;
- (void)start;
- (void)stop;
diff --git a/src/plugins/avfoundation/camera/avfmediaassetwriter.mm b/src/plugins/avfoundation/camera/avfmediaassetwriter.mm
index a541956a8..b5e2bbc94 100644
--- a/src/plugins/avfoundation/camera/avfmediaassetwriter.mm
+++ b/src/plugins/avfoundation/camera/avfmediaassetwriter.mm
@@ -73,7 +73,6 @@ bool qt_camera_service_isValid(AVFCameraService *service)
- (bool)addAudioCapture;
- (bool)addWriterInputs;
- (void)setQueues;
-- (NSDictionary *)videoSettings;
- (NSDictionary *)audioSettings;
- (void)updateDuration:(CMTime)newTimeStamp;
@end
@@ -98,6 +97,7 @@ bool qt_camera_service_isValid(AVFCameraService *service)
m_startTime = kCMTimeInvalid;
m_lastTimeStamp = kCMTimeInvalid;
m_durationInMs.store(0);
+ m_videoSettings = nil;
}
return self;
@@ -105,6 +105,7 @@ bool qt_camera_service_isValid(AVFCameraService *service)
- (bool)setupWithFileURL:(NSURL *)fileURL
cameraService:(AVFCameraService *)service
+ videoSettings:(NSDictionary *)videoSettings
{
Q_ASSERT(fileURL);
@@ -114,6 +115,7 @@ bool qt_camera_service_isValid(AVFCameraService *service)
}
m_service = service;
+ m_videoSettings = videoSettings;
m_videoQueue.reset(dispatch_queue_create("video-output-queue", DISPATCH_QUEUE_SERIAL));
if (!m_videoQueue) {
@@ -364,7 +366,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;
@@ -411,25 +415,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
diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol.h b/src/plugins/avfoundation/camera/avfmediarecordercontrol.h
index 4ea25ae2b..8617f4f4c 100644
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol.h
+++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2016 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
@@ -39,6 +39,7 @@
#import <AVFoundation/AVFoundation.h>
#include "avfstoragelocation.h"
+#include "avfcamerautility.h"
@class AVFMediaRecorderDelegate;
@@ -68,6 +69,7 @@ public:
qreal volume() const;
void applySettings();
+ void unapplySettings();
public Q_SLOTS:
void setState(QMediaRecorder::State state);
@@ -83,6 +85,7 @@ private Q_SLOTS:
void updateStatus();
private:
+ AVFCameraService *m_service;
AVFCameraControl *m_cameraControl;
AVFAudioInputSelectorControl *m_audioInputControl;
AVFCameraSession *m_session;
@@ -102,6 +105,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 1b6e23ee5..4a3091f24 100644
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
+++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
@@ -37,7 +37,7 @@
#include "avfcameraservice.h"
#include "avfcameracontrol.h"
#include "avfaudioinputselectorcontrol.h"
-#include "avfcamerautility.h"
+#include "avfvideoencodersettingscontrol.h"
#include <QtCore/qurl.h>
#include <QtCore/qfileinfo.h>
@@ -115,6 +115,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())
@@ -126,6 +127,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];
@@ -225,6 +227,23 @@ 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;
+ }
+
+ 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->videoEncoderSettingsControl()->unapplySettings([m_movieOutput connectionWithMediaType:AVMediaTypeVideo]);
}
void AVFMediaRecorderControl::setState(QMediaRecorder::State state)
@@ -238,10 +257,6 @@ 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();
@@ -253,9 +268,15 @@ void AVFMediaRecorderControl::setState(QMediaRecorder::State state)
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"));
@@ -271,6 +292,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 a055e54f6..5ee873ff7 100644
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h
+++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h
@@ -70,6 +70,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;
@@ -98,6 +99,8 @@ private:
QMediaRecorder::State m_state;
QMediaRecorder::Status m_lastStatus;
+
+ NSDictionary *m_videoSettings;
};
QT_END_NAMESPACE
diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm
index 73e19e683..3cb33ab58 100644
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm
+++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm
@@ -38,6 +38,8 @@
#include "avfcameracontrol.h"
#include "avfcameraservice.h"
#include "avfcameradebug.h"
+#include "avfvideoencodersettingscontrol.h"
+#include "avfcamerautility.h"
#include <QtCore/qdebug.h>
@@ -77,6 +79,7 @@ AVFMediaRecorderControlIOS::AVFMediaRecorderControlIOS(AVFCameraService *service
, m_service(service)
, m_state(QMediaRecorder::StoppedState)
, m_lastStatus(QMediaRecorder::UnloadedStatus)
+ , m_videoSettings(nil)
{
Q_ASSERT(service);
@@ -107,6 +110,9 @@ AVFMediaRecorderControlIOS::AVFMediaRecorderControlIOS(AVFCameraService *service
AVFMediaRecorderControlIOS::~AVFMediaRecorderControlIOS()
{
[m_writer abort];
+
+ if (m_videoSettings)
+ [m_videoSettings release];
}
QUrl AVFMediaRecorderControlIOS::outputLocation() const
@@ -147,6 +153,32 @@ 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;
+ }
+
+ AVCaptureConnection *conn = [m_service->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo];
+
+ m_videoSettings = m_service->videoEncoderSettingsControl()->applySettings(conn);
+ if (m_videoSettings)
+ [m_videoSettings retain];
+}
+
+void AVFMediaRecorderControlIOS::unapplySettings()
+{
+ AVCaptureConnection *conn = [m_service->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo];
+ m_service->videoEncoderSettingsControl()->unapplySettings(conn);
+
+ if (m_videoSettings) {
+ [m_videoSettings release];
+ m_videoSettings = nil;
+ }
}
void AVFMediaRecorderControlIOS::setState(QMediaRecorder::State state)
@@ -211,7 +243,9 @@ void AVFMediaRecorderControlIOS::setState(QMediaRecorder::State state)
// generated, will restart in assetWriterStarted.
[session stopRunning];
- if ([m_writer setupWithFileURL:nsFileURL cameraService:m_service]) {
+ applySettings();
+
+ if ([m_writer setupWithFileURL:nsFileURL cameraService:m_service videoSettings:m_videoSettings]) {
m_state = QMediaRecorder::RecordingState;
m_lastStatus = QMediaRecorder::StartingStatus;
@@ -270,6 +304,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..87bc91129
--- /dev/null
+++ b/src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.mm
@@ -0,0 +1,401 @@
+/****************************************************************************
+**
+** 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;
+
+ 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 (device.activeFormat) {
+ CMFormatDescriptionRef formatDesc = device.activeFormat.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(device.activeFormat, 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 (newFormat && newFormat != device.activeFormat) {
+ const AVFConfigurationLock lock(device);
+ if (lock) {
+ m_restoreFormat = [device.activeFormat retain];
+ m_restoreFps = qt_current_framerates(device, connection);
+
+ device.activeFormat = newFormat;
+
+ 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
+
+ const qreal fps = m_requestedSettings.frameRate();
+ if (fps > qreal(0)) {
+ if (!m_restoreFps.first && !m_restoreFps.second)
+ m_restoreFps = qt_current_framerates(device, connection);
+ qt_set_framerate_limits(device, connection, fps, fps);
+ }
+ AVFPSRange currentFps = qt_current_framerates(device, connection);
+ m_actualSettings.setFrameRate(currentFps.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;
+
+#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
+ if (m_restoreFormat) {
+ const AVFConfigurationLock lock(device);
+ if (lock)
+ device.activeFormat = m_restoreFormat;
+
+ [m_restoreFormat release];
+ m_restoreFormat = nil;
+ }
+#endif
+
+ if (m_restoreFps.first || m_restoreFps.second) {
+ 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..bf3f82934 100644
--- a/src/plugins/avfoundation/camera/camera.pro
+++ b/src/plugins/avfoundation/camera/camera.pro
@@ -37,7 +37,8 @@ HEADERS += \
avfcamerautility.h \
avfcameraviewfindersettingscontrol.h \
avfimageencodercontrol.h \
- avfcameraflashcontrol.h
+ avfcameraflashcontrol.h \
+ avfvideoencodersettingscontrol.h
OBJECTIVE_SOURCES += \
avfcameraserviceplugin.mm \
@@ -57,7 +58,8 @@ OBJECTIVE_SOURCES += \
avfcamerautility.mm \
avfcameraviewfindersettingscontrol.mm \
avfimageencodercontrol.mm \
- avfcameraflashcontrol.mm
+ avfcameraflashcontrol.mm \
+ avfvideoencodersettingscontrol.mm
osx {