Source

Target

Commits (7)
Showing with 364 additions and 366 deletions
......@@ -72,6 +72,9 @@ public:
infoControl(0),
viewfinderSettingsControl(0),
viewfinderSettingsControl2(0),
cameraExposure(0),
cameraFocus(0),
imageProcessing(0),
viewfinder(0),
capture(0),
state(QCamera::UnloadedState),
......
......@@ -137,35 +137,18 @@ QList<QAudioFormat::SampleType> QAlsaAudioDeviceInfo::supportedSampleTypes()
bool QAlsaAudioDeviceInfo::open()
{
int err = 0;
QString dev = device;
QList<QByteArray> devices = availableDevices(mode);
QString dev;
if(dev.compare(QLatin1String("default")) == 0) {
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
if (devices.size() > 0)
dev = QLatin1String(devices.first().constData());
else
return false;
#else
dev = QLatin1String("hw:0,0");
if (!availableDevices(mode).contains(device.toLocal8Bit()))
return false;
#if SND_LIB_VERSION < 0x1000e // 1.0.14
if (device.compare(QLatin1String("default")) != 0)
dev = deviceFromCardName(device);
else
#endif
} else {
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
dev = device;
#else
int idx = 0;
char *name;
QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1);
while (snd_card_get_name(idx,&name) == 0) {
if(dev.contains(QLatin1String(name)))
break;
idx++;
}
dev = QString(QLatin1String("hw:%1,0")).arg(idx);
#endif
}
if(mode == QAudio::AudioOutput) {
err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
} else {
......@@ -194,30 +177,12 @@ bool QAlsaAudioDeviceInfo::testSettings(const QAudioFormat& format) const
snd_pcm_hw_params_t *params;
QString dev;
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
dev = device;
if (dev.compare(QLatin1String("default")) == 0) {
QList<QByteArray> devices = availableDevices(QAudio::AudioOutput);
if (!devices.isEmpty())
dev = QLatin1String(devices.first().constData());
}
#else
if (dev.compare(QLatin1String("default")) == 0) {
dev = QLatin1String("hw:0,0");
} else {
int idx = 0;
char *name;
QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1);
while(snd_card_get_name(idx,&name) == 0) {
if(shortName.compare(QLatin1String(name)) == 0)
break;
idx++;
}
dev = QString(QLatin1String("hw:%1,0")).arg(idx);
}
#if SND_LIB_VERSION < 0x1000e // 1.0.14
if (device.compare(QLatin1String("default")) != 0)
dev = deviceFromCardName(device);
else
#endif
dev = device;
snd_pcm_stream_t stream = mode == QAudio::AudioOutput
? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE;
......@@ -333,9 +298,11 @@ void QAlsaAudioDeviceInfo::updateLists()
QList<QByteArray> QAlsaAudioDeviceInfo::availableDevices(QAudio::Mode mode)
{
QList<QByteArray> devices;
QByteArray filter;
bool hasDefault = false;
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
QByteArray filter;
// Create a list of all current audio devices that support mode
void **hints, **n;
char *name, *descr, *io;
......@@ -359,12 +326,9 @@ QList<QByteArray> QAlsaAudioDeviceInfo::availableDevices(QAudio::Mode mode)
io = snd_device_name_get_hint(*n, "IOID");
if ((descr != NULL) && ((io == NULL) || (io == filter))) {
QString deviceName = QLatin1String(name);
QString deviceDescription = QLatin1String(descr);
if (deviceDescription.contains(QLatin1String("Default Audio Device")))
devices.prepend(deviceName.toLocal8Bit().constData());
else
devices.append(deviceName.toLocal8Bit().constData());
devices.append(name);
if (strcmp(name, "default") == 0)
hasDefault = true;
}
free(descr);
......@@ -380,12 +344,14 @@ QList<QByteArray> QAlsaAudioDeviceInfo::availableDevices(QAudio::Mode mode)
while(snd_card_get_name(idx,&name) == 0) {
devices.append(name);
if (strcmp(name, "default") == 0)
hasDefault = true;
idx++;
}
#endif
if (devices.size() > 0)
devices.append("default");
if (!hasDefault && devices.size() > 0)
devices.prepend("default");
return devices;
}
......@@ -448,4 +414,20 @@ void QAlsaAudioDeviceInfo::checkSurround()
snd_device_name_free_hint(hints);
}
QString QAlsaAudioDeviceInfo::deviceFromCardName(const QString &card)
{
int idx = 0;
char *name;
QStringRef shortName = card.midRef(card.indexOf(QLatin1String("="), 0) + 1);
while (snd_card_get_name(idx, &name) == 0) {
if (shortName.compare(QLatin1String(name)) == 0)
break;
idx++;
}
return QString(QLatin1String("hw:%1,0")).arg(idx);
}
QT_END_NAMESPACE
......@@ -85,6 +85,7 @@ public:
static QByteArray defaultInputDevice();
static QByteArray defaultOutputDevice();
static QList<QByteArray> availableDevices(QAudio::Mode);
static QString deviceFromCardName(const QString &card);
private:
bool open();
......
......@@ -300,34 +300,16 @@ bool QAlsaAudioInput::open()
}
QString dev = QString(QLatin1String(m_device.constData()));
QList<QByteArray> devices = QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioInput);
if(dev.compare(QLatin1String("default")) == 0) {
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
if (devices.size() > 0)
dev = QLatin1String(devices.first());
else
return false;
#else
dev = QLatin1String("hw:0,0");
#endif
} else {
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
dev = QLatin1String(m_device);
#else
int idx = 0;
char *name;
QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData());
if (!QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioOutput).contains(m_device))
return false;
while(snd_card_get_name(idx,&name) == 0) {
if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0)
break;
idx++;
}
dev = QString(QLatin1String("hw:%1,0")).arg(idx);
QString dev;
#if SND_LIB_VERSION < 0x1000e // 1.0.14
if (m_device != "default")
dev = QAlsaAudioDeviceInfo::deviceFromCardName(m_device);
else
#endif
}
dev = m_device;
// Step 1: try and open the device
while((count < 5) && (err < 0)) {
......
......@@ -303,34 +303,16 @@ bool QAlsaAudioOutput::open()
return false;
}
QString dev = QString(QLatin1String(m_device.constData()));
QList<QByteArray> devices = QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
if(dev.compare(QLatin1String("default")) == 0) {
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
if (devices.size() > 0)
dev = QLatin1String(devices.first());
else
return false;
#else
dev = QLatin1String("hw:0,0");
#endif
} else {
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
dev = QLatin1String(m_device);
#else
int idx = 0;
char *name;
QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData());
if (!QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioOutput).contains(m_device))
return false;
while (snd_card_get_name(idx,&name) == 0) {
if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0)
break;
idx++;
}
dev = QString(QLatin1String("hw:%1,0")).arg(idx);
QString dev;
#if SND_LIB_VERSION < 0x1000e // 1.0.14
if (m_device != "default")
dev = QAlsaAudioDeviceInfo::deviceFromCardName(m_device);
else
#endif
}
dev = m_device;
// Step 1: try and open the device
while((count < 5) && (err < 0)) {
......
......@@ -284,6 +284,10 @@ void QAndroidTextureVideoOutput::stop()
void QAndroidTextureVideoOutput::reset()
{
// flush pending frame
if (m_surface)
m_surface->present(QVideoFrame());
clearSurfaceTexture();
}
......
......@@ -302,7 +302,6 @@ AndroidCamera *AndroidCamera::open(int cameraId)
if (!ok) {
worker->quit();
worker->wait(5000);
delete d;
delete worker;
return 0;
}
......
/****************************************************************************
**
** 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
......
/****************************************************************************
**
** 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,191 @@ 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."
// Instead, use the first item in the supported frame rates.
#ifdef Q_OS_IOS
[captureDevice setActiveVideoMinFrameDuration:minFrameDuration];
[captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration];
#else // Q_OS_OSX
if (CMTimeCompare(minFrameDuration, kCMTimeInvalid) == 0
&& CMTimeCompare(maxFrameDuration, kCMTimeInvalid) == 0) {
AVFrameRateRange *range = captureDevice.activeFormat.videoSupportedFrameRateRanges.firstObject;
minFrameDuration = range.minFrameDuration;
maxFrameDuration = range.maxFrameDuration;
}
if (CMTimeCompare(minFrameDuration, kCMTimeInvalid))
[captureDevice setActiveVideoMinFrameDuration:minFrameDuration];
#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
#if QT_OSX_DEPLOYMENT_TARGET_BELOW(__MAC_10_9)
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9)
#endif
{
if (CMTimeCompare(maxFrameDuration, kCMTimeInvalid))
[captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration];
}
#endif // QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
#endif // Q_OS_OSX
}
#endif // Platform SDK >= 10.9, >= 7.0.
void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection,
qreal minFPS, qreal maxFPS)
{
Q_ASSERT(captureDevice);
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_9, QSysInfo::MV_IOS_7_0))
qt_set_framerate_limits(captureDevice, minFPS, maxFPS);
else
#endif
if (videoConnection)
qt_set_framerate_limits(videoConnection, minFPS, maxFPS);
}
AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection)
{
Q_ASSERT(captureDevice);
AVFPSRange fps;
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) {
const CMTime minDuration = captureDevice.activeVideoMinFrameDuration;
if (CMTimeCompare(minDuration, kCMTimeInvalid)) {
if (const Float64 minSeconds = CMTimeGetSeconds(minDuration))
fps.second = 1. / minSeconds; // Max FPS = 1 / MinDuration.
}
#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
#if QT_OSX_DEPLOYMENT_TARGET_BELOW(__MAC_10_9)
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9)
#endif
{
const CMTime maxDuration = captureDevice.activeVideoMaxFrameDuration;
if (CMTimeCompare(maxDuration, kCMTimeInvalid)) {
if (const Float64 maxSeconds = CMTimeGetSeconds(maxDuration))
fps.first = 1. / maxSeconds; // Min FPS = 1 / MaxDuration.
}
}
#endif // QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
} else {
#else // OSX < 10.7 or iOS < 7.0
{
#endif // QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
if (videoConnection)
fps = qt_connection_framerates(videoConnection);
}
return fps;
}
QT_END_NAMESPACE
/****************************************************************************
**
** 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;
......
/****************************************************************************
**
** 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
......
/****************************************************************************
**
** 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
......
/****************************************************************************
**
** 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];
......