AVFoundation: implement QVideoEncoderSettingsControl.
Task-number: QTBUG-40338 Change-Id: Ic23dabaad94d7b293019460710ae1a097002e227 Reviewed-by: Christian Stromme <christian.stromme@qt.io>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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 {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user