AVFoundation: implement QVideoEncoderSettingsControl.

Task-number: QTBUG-40338
Change-Id: Ic23dabaad94d7b293019460710ae1a097002e227
Reviewed-by: Christian Stromme <christian.stromme@qt.io>
This commit is contained in:
Yoann Lopes
2016-02-10 14:52:44 +01:00
committed by Yoann Lopes
parent c0319d1cfb
commit 4fa23e08a1
16 changed files with 781 additions and 258 deletions

View File

@@ -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

View File

@@ -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"

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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];

View File

@@ -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

View File

@@ -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"

View File

@@ -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 {