Source

Target

Commits (22)
Showing with 665 additions and 147 deletions
load(qt_build_config)
CONFIG += qt_example_installs
MODULE_VERSION = 5.7.0
......@@ -3,7 +3,7 @@
The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd.
Contact: http://www.qt.io/licensing/
You may use, distribute and copy the Qt GUI Toolkit under the terms of
You may use, distribute and copy the Qt Toolkit under the terms of
GNU Lesser General Public License version 3. That license references
the General Public License version 3, that is displayed below. Other
portions of the Qt Toolkit may be licensed directly under this license.
......
......@@ -3,7 +3,7 @@
The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd.
Contact: http://www.qt.io/licensing/
You may use, distribute and copy the Qt GUI Toolkit under the terms of
You may use, distribute and copy the Qt Toolkit under the terms of
GNU Lesser General Public License version 2.1, which is displayed below.
-------------------------------------------------------------------------
......
......@@ -3,7 +3,7 @@
The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd.
Contact: http://www.qt.io/licensing/
You may use, distribute and copy the Qt GUI Toolkit under the terms of
You may use, distribute and copy the Qt Toolkit under the terms of
GNU Lesser General Public License version 3, which is displayed below.
This license makes reference to the version 3 of the GNU General
Public License, which you can find in the LICENSE.GPLv3 file.
......
......@@ -78,6 +78,9 @@ public:
infoControl(0),
viewfinderSettingsControl(0),
viewfinderSettingsControl2(0),
cameraExposure(0),
cameraFocus(0),
imageProcessing(0),
viewfinder(0),
capture(0),
state(QCamera::UnloadedState),
......
......@@ -143,35 +143,18 @@ QList<QAudioFormat::SampleType> QAlsaAudioDeviceInfo::supportedSampleTypes()
bool QAlsaAudioDeviceInfo::open()
{
int err = 0;
QString dev = device;
QList<QByteArray> devices = availableDevices(mode);
QString dev;
if(dev.compare(QLatin1String("default")) == 0) {
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
if (devices.size() > 0)
dev = QLatin1String(devices.first().constData());
else
return false;
#else
dev = QLatin1String("hw:0,0");
if (!availableDevices(mode).contains(device.toLocal8Bit()))
return false;
#if SND_LIB_VERSION < 0x1000e // 1.0.14
if (device.compare(QLatin1String("default")) != 0)
dev = deviceFromCardName(device);
else
#endif
} else {
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
dev = device;
#else
int idx = 0;
char *name;
QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1);
while (snd_card_get_name(idx,&name) == 0) {
if(dev.contains(QLatin1String(name)))
break;
idx++;
}
dev = QString(QLatin1String("hw:%1,0")).arg(idx);
#endif
}
if(mode == QAudio::AudioOutput) {
err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
} else {
......@@ -200,30 +183,12 @@ bool QAlsaAudioDeviceInfo::testSettings(const QAudioFormat& format) const
snd_pcm_hw_params_t *params;
QString dev;
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
dev = device;
if (dev.compare(QLatin1String("default")) == 0) {
QList<QByteArray> devices = availableDevices(QAudio::AudioOutput);
if (!devices.isEmpty())
dev = QLatin1String(devices.first().constData());
}
#else
if (dev.compare(QLatin1String("default")) == 0) {
dev = QLatin1String("hw:0,0");
} else {
int idx = 0;
char *name;
QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1);
while(snd_card_get_name(idx,&name) == 0) {
if(shortName.compare(QLatin1String(name)) == 0)
break;
idx++;
}
dev = QString(QLatin1String("hw:%1,0")).arg(idx);
}
#if SND_LIB_VERSION < 0x1000e // 1.0.14
if (device.compare(QLatin1String("default")) != 0)
dev = deviceFromCardName(device);
else
#endif
dev = device;
snd_pcm_stream_t stream = mode == QAudio::AudioOutput
? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE;
......@@ -339,9 +304,11 @@ void QAlsaAudioDeviceInfo::updateLists()
QList<QByteArray> QAlsaAudioDeviceInfo::availableDevices(QAudio::Mode mode)
{
QList<QByteArray> devices;
QByteArray filter;
bool hasDefault = false;
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
QByteArray filter;
// Create a list of all current audio devices that support mode
void **hints, **n;
char *name, *descr, *io;
......@@ -365,12 +332,9 @@ QList<QByteArray> QAlsaAudioDeviceInfo::availableDevices(QAudio::Mode mode)
io = snd_device_name_get_hint(*n, "IOID");
if ((descr != NULL) && ((io == NULL) || (io == filter))) {
QString deviceName = QLatin1String(name);
QString deviceDescription = QLatin1String(descr);
if (deviceDescription.contains(QLatin1String("Default Audio Device")))
devices.prepend(deviceName.toLocal8Bit().constData());
else
devices.append(deviceName.toLocal8Bit().constData());
devices.append(name);
if (strcmp(name, "default") == 0)
hasDefault = true;
}
free(descr);
......@@ -386,12 +350,14 @@ QList<QByteArray> QAlsaAudioDeviceInfo::availableDevices(QAudio::Mode mode)
while(snd_card_get_name(idx,&name) == 0) {
devices.append(name);
if (strcmp(name, "default") == 0)
hasDefault = true;
idx++;
}
#endif
if (devices.size() > 0)
devices.append("default");
if (!hasDefault && devices.size() > 0)
devices.prepend("default");
return devices;
}
......@@ -454,4 +420,20 @@ void QAlsaAudioDeviceInfo::checkSurround()
snd_device_name_free_hint(hints);
}
QString QAlsaAudioDeviceInfo::deviceFromCardName(const QString &card)
{
int idx = 0;
char *name;
QStringRef shortName = card.midRef(card.indexOf(QLatin1String("="), 0) + 1);
while (snd_card_get_name(idx, &name) == 0) {
if (shortName.compare(QLatin1String(name)) == 0)
break;
idx++;
}
return QString(QLatin1String("hw:%1,0")).arg(idx);
}
QT_END_NAMESPACE
......@@ -91,6 +91,7 @@ public:
static QByteArray defaultInputDevice();
static QByteArray defaultOutputDevice();
static QList<QByteArray> availableDevices(QAudio::Mode);
static QString deviceFromCardName(const QString &card);
private:
bool open();
......
......@@ -127,6 +127,12 @@ int QAlsaAudioInput::xrun_recovery(int err)
int count = 0;
bool reset = false;
// ESTRPIPE is not available in all OSes where ALSA is available
int estrpipe = EIO;
#ifdef ESTRPIPE
estrpipe = ESTRPIPE;
#endif
if(err == -EPIPE) {
errorState = QAudio::UnderrunError;
err = snd_pcm_prepare(handle);
......@@ -137,8 +143,7 @@ int QAlsaAudioInput::xrun_recovery(int err)
if (bytesAvailable <= 0)
reset = true;
}
} else if((err == -ESTRPIPE)||(err == -EIO)) {
} else if ((err == -estrpipe)||(err == -EIO)) {
errorState = QAudio::IOError;
while((err = snd_pcm_resume(handle)) == -EAGAIN){
usleep(100);
......@@ -306,34 +311,16 @@ bool QAlsaAudioInput::open()
}
QString dev = QString(QLatin1String(m_device.constData()));
QList<QByteArray> devices = QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioInput);
if(dev.compare(QLatin1String("default")) == 0) {
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
if (devices.size() > 0)
dev = QLatin1String(devices.first());
else
return false;
#else
dev = QLatin1String("hw:0,0");
#endif
} else {
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
dev = QLatin1String(m_device);
#else
int idx = 0;
char *name;
QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData());
if (!QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioOutput).contains(m_device))
return false;
while(snd_card_get_name(idx,&name) == 0) {
if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0)
break;
idx++;
}
dev = QString(QLatin1String("hw:%1,0")).arg(idx);
QString dev;
#if SND_LIB_VERSION < 0x1000e // 1.0.14
if (m_device != "default")
dev = QAlsaAudioDeviceInfo::deviceFromCardName(m_device);
else
#endif
}
dev = m_device;
// Step 1: try and open the device
while((count < 5) && (err < 0)) {
......@@ -565,8 +552,10 @@ qint64 QAlsaAudioInput::read(char* data, qint64 len)
if(readFrames == -EPIPE) {
errorState = QAudio::UnderrunError;
err = snd_pcm_prepare(handle);
#ifdef ESTRPIPE
} else if(readFrames == -ESTRPIPE) {
err = snd_pcm_prepare(handle);
#endif
}
if(err != 0) break;
}
......
......@@ -120,6 +120,12 @@ int QAlsaAudioOutput::xrun_recovery(int err)
int count = 0;
bool reset = false;
// ESTRPIPE is not available in all OSes where ALSA is available
int estrpipe = EIO;
#ifdef ESTRPIPE
estrpipe = ESTRPIPE;
#endif
if(err == -EPIPE) {
errorState = QAudio::UnderrunError;
emit errorChanged(errorState);
......@@ -127,7 +133,7 @@ int QAlsaAudioOutput::xrun_recovery(int err)
if(err < 0)
reset = true;
} else if((err == -ESTRPIPE)||(err == -EIO)) {
} else if ((err == -estrpipe)||(err == -EIO)) {
errorState = QAudio::IOError;
emit errorChanged(errorState);
while((err = snd_pcm_resume(handle)) == -EAGAIN){
......@@ -309,34 +315,16 @@ bool QAlsaAudioOutput::open()
return false;
}
QString dev = QString(QLatin1String(m_device.constData()));
QList<QByteArray> devices = QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
if(dev.compare(QLatin1String("default")) == 0) {
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
if (devices.size() > 0)
dev = QLatin1String(devices.first());
else
return false;
#else
dev = QLatin1String("hw:0,0");
#endif
} else {
#if SND_LIB_VERSION >= 0x1000e // 1.0.14
dev = QLatin1String(m_device);
#else
int idx = 0;
char *name;
QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData());
if (!QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioOutput).contains(m_device))
return false;
while (snd_card_get_name(idx,&name) == 0) {
if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0)
break;
idx++;
}
dev = QString(QLatin1String("hw:%1,0")).arg(idx);
QString dev;
#if SND_LIB_VERSION < 0x1000e // 1.0.14
if (m_device != "default")
dev = QAlsaAudioDeviceInfo::deviceFromCardName(m_device);
else
#endif
}
dev = m_device;
// Step 1: try and open the device
while((count < 5) && (err < 0)) {
......
......@@ -290,6 +290,10 @@ void QAndroidTextureVideoOutput::stop()
void QAndroidTextureVideoOutput::reset()
{
// flush pending frame
if (m_surface)
m_surface->present(QVideoFrame());
clearSurfaceTexture();
}
......
......@@ -315,7 +315,6 @@ AndroidCamera *AndroidCamera::open(int cameraId)
if (!ok) {
worker->quit();
worker->wait(5000);
delete d;
delete worker;
return 0;
}
......
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef AVFAUDIOENCODERSETTINGSCONTROL_H
#define AVFAUDIOENCODERSETTINGSCONTROL_H
#include <qaudioencodersettingscontrol.h>
@class NSDictionary;
@class AVCaptureAudioDataOutput;
QT_BEGIN_NAMESPACE
class AVFCameraService;
class AVFAudioEncoderSettingsControl : public QAudioEncoderSettingsControl
{
public:
explicit AVFAudioEncoderSettingsControl(AVFCameraService *service);
QStringList supportedAudioCodecs() const Q_DECL_OVERRIDE;
QString codecDescription(const QString &codecName) const Q_DECL_OVERRIDE;
QList<int> supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous = 0) const Q_DECL_OVERRIDE;
QAudioEncoderSettings audioSettings() const Q_DECL_OVERRIDE;
void setAudioSettings(const QAudioEncoderSettings &settings) Q_DECL_OVERRIDE;
NSDictionary *applySettings();
void unapplySettings();
private:
AVFCameraService *m_service;
QAudioEncoderSettings m_requestedSettings;
QAudioEncoderSettings m_actualSettings;
};
QT_END_NAMESPACE
#endif // AVFAUDIOENCODERSETTINGSCONTROL_H
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "avfaudioencodersettingscontrol.h"
#include "avfcameraservice.h"
#include "avfcamerasession.h"
#include <AVFoundation/AVFoundation.h>
#include <CoreAudio/CoreAudioTypes.h>
QT_BEGIN_NAMESPACE
struct AudioCodecInfo
{
QString description;
int id;
AudioCodecInfo() : id(0) { }
AudioCodecInfo(const QString &desc, int i)
: description(desc), id(i)
{ }
};
typedef QMap<QString, AudioCodecInfo> SupportedAudioCodecs;
Q_GLOBAL_STATIC_WITH_ARGS(QString , defaultCodec, (QLatin1String("aac")))
Q_GLOBAL_STATIC(SupportedAudioCodecs, supportedCodecs)
AVFAudioEncoderSettingsControl::AVFAudioEncoderSettingsControl(AVFCameraService *service)
: QAudioEncoderSettingsControl()
, m_service(service)
{
if (supportedCodecs->isEmpty()) {
supportedCodecs->insert(QStringLiteral("lpcm"),
AudioCodecInfo(QStringLiteral("Linear PCM"),
kAudioFormatLinearPCM));
supportedCodecs->insert(QStringLiteral("ulaw"),
AudioCodecInfo(QStringLiteral("PCM Mu-Law 2:1"),
kAudioFormatULaw));
supportedCodecs->insert(QStringLiteral("alaw"),
AudioCodecInfo(QStringLiteral("PCM A-Law 2:1"),
kAudioFormatALaw));
supportedCodecs->insert(QStringLiteral("ima4"),
AudioCodecInfo(QStringLiteral("IMA 4:1 ADPCM"),
kAudioFormatAppleIMA4));
supportedCodecs->insert(QStringLiteral("alac"),
AudioCodecInfo(QStringLiteral("Apple Lossless Audio Codec"),
kAudioFormatAppleLossless));
supportedCodecs->insert(QStringLiteral("aac"),
AudioCodecInfo(QStringLiteral("MPEG-4 Low Complexity AAC"),
kAudioFormatMPEG4AAC));
supportedCodecs->insert(QStringLiteral("aach"),
AudioCodecInfo(QStringLiteral("MPEG-4 High Efficiency AAC"),
kAudioFormatMPEG4AAC_HE));
supportedCodecs->insert(QStringLiteral("aacl"),
AudioCodecInfo(QStringLiteral("MPEG-4 AAC Low Delay"),
kAudioFormatMPEG4AAC_LD));
supportedCodecs->insert(QStringLiteral("aace"),
AudioCodecInfo(QStringLiteral("MPEG-4 AAC Enhanced Low Delay"),
kAudioFormatMPEG4AAC_ELD));
supportedCodecs->insert(QStringLiteral("aacf"),
AudioCodecInfo(QStringLiteral("MPEG-4 AAC Enhanced Low Delay with SBR"),
kAudioFormatMPEG4AAC_ELD_SBR));
supportedCodecs->insert(QStringLiteral("aacp"),
AudioCodecInfo(QStringLiteral("MPEG-4 HE AAC V2"),
kAudioFormatMPEG4AAC_HE_V2));
supportedCodecs->insert(QStringLiteral("ilbc"),
AudioCodecInfo(QStringLiteral("iLBC"),
kAudioFormatiLBC));
}
}
QStringList AVFAudioEncoderSettingsControl::supportedAudioCodecs() const
{
return supportedCodecs->keys();
}
QString AVFAudioEncoderSettingsControl::codecDescription(const QString &codecName) const
{
return supportedCodecs->value(codecName).description;
}
QList<int> AVFAudioEncoderSettingsControl::supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous) const
{
Q_UNUSED(settings)
if (continuous)
*continuous = true;
return QList<int>() << 8000 << 96000;
}
QAudioEncoderSettings AVFAudioEncoderSettingsControl::audioSettings() const
{
return m_actualSettings;
}
void AVFAudioEncoderSettingsControl::setAudioSettings(const QAudioEncoderSettings &settings)
{
if (m_requestedSettings == settings)
return;
m_requestedSettings = m_actualSettings = settings;
}
NSDictionary *AVFAudioEncoderSettingsControl::applySettings()
{
if (m_service->session()->state() != QCamera::LoadedState &&
m_service->session()->state() != QCamera::ActiveState) {
return nil;
}
NSMutableDictionary *settings = [NSMutableDictionary dictionary];
QString codec = m_requestedSettings.codec().isEmpty() ? *defaultCodec : m_requestedSettings.codec();
if (!supportedCodecs->contains(codec)) {
qWarning("Unsupported codec: '%s'", codec.toLocal8Bit().constData());
codec = *defaultCodec;
}
[settings setObject:[NSNumber numberWithInt:supportedCodecs->value(codec).id] forKey:AVFormatIDKey];
m_actualSettings.setCodec(codec);
#ifdef Q_OS_OSX
if (m_requestedSettings.encodingMode() == QMultimedia::ConstantQualityEncoding) {
int quality;
switch (m_requestedSettings.quality()) {
case QMultimedia::VeryLowQuality:
quality = AVAudioQualityMin;
break;
case QMultimedia::LowQuality:
quality = AVAudioQualityLow;
break;
case QMultimedia::HighQuality:
quality = AVAudioQualityHigh;
break;
case QMultimedia::VeryHighQuality:
quality = AVAudioQualityMax;
break;
case QMultimedia::NormalQuality:
default:
quality = AVAudioQualityMedium;
break;
}
[settings setObject:[NSNumber numberWithInt:quality] forKey:AVEncoderAudioQualityKey];
} else
#endif
if (m_requestedSettings.bitRate() > 0){
[settings setObject:[NSNumber numberWithInt:m_requestedSettings.bitRate()] forKey:AVEncoderBitRateKey];
}
int sampleRate = m_requestedSettings.sampleRate();
int channelCount = m_requestedSettings.channelCount();
#ifdef Q_OS_IOS
// Some keys are mandatory only on iOS
if (codec == QLatin1String("lpcm")) {
[settings setObject:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
[settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsBigEndianKey];
[settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsFloatKey];
[settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsNonInterleaved];
}
if (codec == QLatin1String("alac"))
[settings setObject:[NSNumber numberWithInt:24] forKey:AVEncoderBitDepthHintKey];
if (sampleRate <= 0)
sampleRate = codec == QLatin1String("ilbc") ? 8000 : 44100;
if (channelCount <= 0)
channelCount = codec == QLatin1String("ilbc") ? 1 : 2;
#endif
if (sampleRate > 0) {
[settings setObject:[NSNumber numberWithInt:sampleRate] forKey:AVSampleRateKey];
m_actualSettings.setSampleRate(sampleRate);
}
if (channelCount > 0) {
[settings setObject:[NSNumber numberWithInt:channelCount] forKey:AVNumberOfChannelsKey];
m_actualSettings.setChannelCount(channelCount);
}
return settings;
}
void AVFAudioEncoderSettingsControl::unapplySettings()
{
m_actualSettings = m_requestedSettings;
}
QT_END_NAMESPACE
......@@ -53,6 +53,7 @@ AVFCameraControl::AVFCameraControl(AVFCameraService *service, QObject *parent)
{
Q_UNUSED(service);
connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateStatus()));
connect(this, &AVFCameraControl::captureModeChanged, m_session, &AVFCameraSession::onCaptureModeChanged);
}
AVFCameraControl::~AVFCameraControl()
......
......@@ -67,6 +67,9 @@ class AVFImageEncoderControl;
class AVFCameraFlashControl;
class AVFMediaRecorderControl;
class AVFMediaRecorderControlIOS;
class AVFAudioEncoderSettingsControl;
class AVFVideoEncoderSettingsControl;
class AVFMediaContainerControl;
class AVFCameraService : public QMediaService
{
......@@ -83,8 +86,7 @@ public:
AVFCameraDeviceControl *videoDeviceControl() const { return m_videoDeviceControl; }
AVFAudioInputSelectorControl *audioInputSelectorControl() const { return m_audioInputSelectorControl; }
AVFCameraMetaDataControl *metaDataControl() const { return m_metaDataControl; }
AVFMediaRecorderControl *recorderControl() const;
AVFMediaRecorderControlIOS *recorderControlIOS() const;
QMediaRecorderControl *recorderControl() const { return m_recorderControl; }
AVFImageCaptureControl *imageCaptureControl() const { return m_imageCaptureControl; }
AVFCameraFocusControl *cameraFocusControl() const { return m_cameraFocusControl; }
AVFCameraExposureControl *cameraExposureControl() const {return m_cameraExposureControl; }
......@@ -94,6 +96,9 @@ public:
AVFCameraViewfinderSettingsControl *viewfinderSettingsControl() const {return m_viewfinderSettingsControl; }
AVFImageEncoderControl *imageEncoderControl() const {return m_imageEncoderControl; }
AVFCameraFlashControl *flashControl() const {return m_flashControl; }
AVFAudioEncoderSettingsControl *audioEncoderSettingsControl() const { return m_audioEncoderSettingsControl; }
AVFVideoEncoderSettingsControl *videoEncoderSettingsControl() const {return m_videoEncoderSettingsControl; }
AVFMediaContainerControl *mediaContainerControl() const { return m_mediaContainerControl; }
private:
AVFCameraSession *m_session;
......@@ -112,6 +117,9 @@ private:
AVFCameraViewfinderSettingsControl *m_viewfinderSettingsControl;
AVFImageEncoderControl *m_imageEncoderControl;
AVFCameraFlashControl *m_flashControl;
AVFAudioEncoderSettingsControl *m_audioEncoderSettingsControl;
AVFVideoEncoderSettingsControl *m_videoEncoderSettingsControl;
AVFMediaContainerControl *m_mediaContainerControl;
};
QT_END_NAMESPACE
......
......@@ -59,6 +59,9 @@
#include "avfcameraviewfindersettingscontrol.h"
#include "avfimageencodercontrol.h"
#include "avfcameraflashcontrol.h"
#include "avfaudioencodersettingscontrol.h"
#include "avfvideoencodersettingscontrol.h"
#include "avfmediacontainercontrol.h"
#ifdef Q_OS_IOS
#include "avfcamerazoomcontrol.h"
......@@ -105,6 +108,9 @@ AVFCameraService::AVFCameraService(QObject *parent):
m_viewfinderSettingsControl = new AVFCameraViewfinderSettingsControl(this);
m_imageEncoderControl = new AVFImageEncoderControl(this);
m_flashControl = new AVFCameraFlashControl(this);
m_audioEncoderSettingsControl = new AVFAudioEncoderSettingsControl(this);
m_videoEncoderSettingsControl = new AVFVideoEncoderSettingsControl(this);
m_mediaContainerControl = new AVFMediaContainerControl(this);
}
AVFCameraService::~AVFCameraService()
......@@ -136,6 +142,9 @@ AVFCameraService::~AVFCameraService()
delete m_viewfinderSettingsControl;
delete m_imageEncoderControl;
delete m_flashControl;
delete m_audioEncoderSettingsControl;
delete m_videoEncoderSettingsControl;
delete m_mediaContainerControl;
delete m_session;
}
......@@ -182,6 +191,15 @@ QMediaControl *AVFCameraService::requestControl(const char *name)
if (qstrcmp(name, QCameraFlashControl_iid) == 0)
return m_flashControl;
if (qstrcmp(name, QAudioEncoderSettingsControl_iid) == 0)
return m_audioEncoderSettingsControl;
if (qstrcmp(name, QVideoEncoderSettingsControl_iid) == 0)
return m_videoEncoderSettingsControl;
if (qstrcmp(name, QMediaContainerControl_iid) == 0)
return m_mediaContainerControl;
if (qstrcmp(name,QMediaVideoProbeControl_iid) == 0) {
AVFMediaVideoProbeControl *videoProbe = 0;
videoProbe = new AVFMediaVideoProbeControl(this);
......@@ -220,23 +238,5 @@ void AVFCameraService::releaseControl(QMediaControl *control)
}
}
AVFMediaRecorderControl *AVFCameraService::recorderControl() const
{
#ifdef Q_OS_IOS
return 0;
#else
return static_cast<AVFMediaRecorderControl *>(m_recorderControl);
#endif
}
AVFMediaRecorderControlIOS *AVFCameraService::recorderControlIOS() const
{
#ifdef Q_OS_OSX
return 0;
#else
return static_cast<AVFMediaRecorderControlIOS *>(m_recorderControl);
#endif
}
#include "moc_avfcameraservice.cpp"
......@@ -99,6 +99,8 @@ public Q_SLOTS:
void processSessionStarted();
void processSessionStopped();
void onCaptureModeChanged(QCamera::CaptureModes mode);
void onCameraFrameFetched(const QVideoFrame &frame);
Q_SIGNALS:
......
......@@ -292,8 +292,8 @@ void AVFCameraSession::setState(QCamera::State newState)
m_defaultCodec = 0;
defaultCodec();
bool activeFormatSet = applyImageEncoderSettings();
activeFormatSet |= applyViewfinderSettings();
bool activeFormatSet = applyImageEncoderSettings()
| applyViewfinderSettings();
[m_captureSession commitConfiguration];
......@@ -344,6 +344,17 @@ void AVFCameraSession::processSessionStopped()
}
}
void AVFCameraSession::onCaptureModeChanged(QCamera::CaptureModes mode)
{
Q_UNUSED(mode)
const QCamera::State s = state();
if (s == QCamera::LoadedState || s == QCamera::ActiveState) {
applyImageEncoderSettings();
applyViewfinderSettings();
}
}
void AVFCameraSession::attachVideoInputDevice()
{
//Attach video input device:
......@@ -387,18 +398,17 @@ bool AVFCameraSession::applyImageEncoderSettings()
bool AVFCameraSession::applyViewfinderSettings()
{
if (AVFCameraViewfinderSettingsControl2 *vfControl = m_service->viewfinderSettingsControl2()) {
QCamera::CaptureModes currentMode = m_service->cameraControl()->captureMode();
QCameraViewfinderSettings vfSettings(vfControl->requestedSettings());
// Viewfinder and image capture solutions must be the same, if an image capture
// resolution is set, it takes precedence over the viewfinder resolution.
if (AVFImageEncoderControl *imControl = m_service->imageEncoderControl()) {
const QSize imageResolution(imControl->requestedSettings().resolution());
if (!imageResolution.isNull() && imageResolution.isValid()) {
if (currentMode.testFlag(QCamera::CaptureStillImage)) {
const QSize imageResolution(m_service->imageEncoderControl()->requestedSettings().resolution());
if (!imageResolution.isNull() && imageResolution.isValid())
vfSettings.setResolution(imageResolution);
vfControl->setViewfinderSettings(vfSettings);
}
}
return vfControl->applySettings();
return vfControl->applySettings(vfSettings);
}
return false;
......
......@@ -182,8 +182,15 @@ AVCaptureDeviceFormat *qt_find_best_framerate_match(AVCaptureDevice *captureDevi
Float64 fps);
AVFrameRateRange *qt_find_supported_framerate_range(AVCaptureDeviceFormat *format, Float64 fps);
bool qt_formats_are_equal(AVCaptureDeviceFormat *f1, AVCaptureDeviceFormat *f2);
bool qt_set_active_format(AVCaptureDevice *captureDevice, AVCaptureDeviceFormat *format, bool preserveFps);
#endif
AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection);
void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection,
qreal minFPS, qreal maxFPS);
QT_END_NAMESPACE
#endif
......@@ -177,8 +177,7 @@ QVector<AVCaptureDeviceFormat *> qt_unique_device_formats(AVCaptureDevice *captu
QSize qt_device_format_resolution(AVCaptureDeviceFormat *format)
{
Q_ASSERT(format);
if (!format.formatDescription)
if (!format || !format.formatDescription)
return QSize();
const CMVideoDimensions res = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
......@@ -387,6 +386,243 @@ AVFrameRateRange *qt_find_supported_framerate_range(AVCaptureDeviceFormat *forma
return match;
}
bool qt_formats_are_equal(AVCaptureDeviceFormat *f1, AVCaptureDeviceFormat *f2)
{
if (f1 == f2)
return true;
if (![f1.mediaType isEqualToString:f2.mediaType])
return false;
return CMFormatDescriptionEqual(f1.formatDescription, f2.formatDescription);
}
bool qt_set_active_format(AVCaptureDevice *captureDevice, AVCaptureDeviceFormat *format, bool preserveFps)
{
static bool firstSet = true;
if (!captureDevice || !format)
return false;
if (qt_formats_are_equal(captureDevice.activeFormat, format)) {
if (firstSet) {
// The capture device format is persistent. The first time we set a format, report that
// it changed even if the formats are the same.
// This prevents the session from resetting the format to the default value.
firstSet = false;
return true;
}
return false;
}
firstSet = false;
const AVFConfigurationLock lock(captureDevice);
if (!lock) {
qWarning("Failed to set active format (lock failed)");
return false;
}
// Changing the activeFormat resets the frame rate.
AVFPSRange fps;
if (preserveFps)
fps = qt_current_framerates(captureDevice, nil);
captureDevice.activeFormat = format;
if (preserveFps)
qt_set_framerate_limits(captureDevice, nil, fps.first, fps.second);
return true;
}
#endif // SDK
void qt_set_framerate_limits(AVCaptureConnection *videoConnection, qreal minFPS, qreal maxFPS)
{
Q_ASSERT(videoConnection);
if (minFPS < 0. || maxFPS < 0. || (maxFPS && maxFPS < minFPS)) {
qDebugCamera() << Q_FUNC_INFO << "invalid framerates (min, max):"
<< minFPS << maxFPS;
return;
}
CMTime minDuration = kCMTimeInvalid;
if (maxFPS > 0.) {
if (!videoConnection.supportsVideoMinFrameDuration)
qDebugCamera() << Q_FUNC_INFO << "maximum framerate is not supported";
else
minDuration = CMTimeMake(1, maxFPS);
}
if (videoConnection.supportsVideoMinFrameDuration)
videoConnection.videoMinFrameDuration = minDuration;
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_5_0)
#if QT_OSX_DEPLOYMENT_TARGET_BELOW(__MAC_10_9)
if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_9) {
if (minFPS > 0.)
qDebugCamera() << Q_FUNC_INFO << "minimum framerate is not supported";
} else
#endif
{
CMTime maxDuration = kCMTimeInvalid;
if (minFPS > 0.) {
if (!videoConnection.supportsVideoMaxFrameDuration)
qDebugCamera() << Q_FUNC_INFO << "minimum framerate is not supported";
else
maxDuration = CMTimeMake(1, minFPS);
}
if (videoConnection.supportsVideoMaxFrameDuration)
videoConnection.videoMaxFrameDuration = maxDuration;
}
#else
if (minFPS > 0.)
qDebugCamera() << Q_FUNC_INFO << "minimum framerate is not supported";
#endif
}
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
CMTime qt_adjusted_frame_duration(AVFrameRateRange *range, qreal fps)
{
Q_ASSERT(range);
Q_ASSERT(fps > 0.);
if (range.maxFrameRate - range.minFrameRate < 0.1) {
// Can happen on OS X.
return range.minFrameDuration;
}
if (fps <= range.minFrameRate)
return range.maxFrameDuration;
if (fps >= range.maxFrameRate)
return range.minFrameDuration;
int n, d;
qt_real_to_fraction(1. / fps, &n, &d);
return CMTimeMake(n, d);
}
void qt_set_framerate_limits(AVCaptureDevice *captureDevice, qreal minFPS, qreal maxFPS)
{
Q_ASSERT(captureDevice);
if (!captureDevice.activeFormat) {
qDebugCamera() << Q_FUNC_INFO << "no active capture device format";
return;
}
if (minFPS < 0. || maxFPS < 0. || (maxFPS && maxFPS < minFPS)) {
qDebugCamera() << Q_FUNC_INFO << "invalid framerates (min, max):"
<< minFPS << maxFPS;
return;
}
CMTime minFrameDuration = kCMTimeInvalid;
CMTime maxFrameDuration = kCMTimeInvalid;
if (maxFPS || minFPS) {
AVFrameRateRange *range = qt_find_supported_framerate_range(captureDevice.activeFormat,
maxFPS ? maxFPS : minFPS);
if (!range) {
qDebugCamera() << Q_FUNC_INFO << "no framerate range found, (min, max):"
<< minFPS << maxFPS;
return;
}
if (maxFPS)
minFrameDuration = qt_adjusted_frame_duration(range, maxFPS);
if (minFPS)
maxFrameDuration = qt_adjusted_frame_duration(range, minFPS);
}
const AVFConfigurationLock lock(captureDevice);
if (!lock) {
qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
return;
}
// While Apple's docs say kCMTimeInvalid will end in default
// settings for this format, kCMTimeInvalid on OS X ends with a runtime
// exception:
// "The activeVideoMinFrameDuration passed is not supported by the device."
// Instead, use the first item in the supported frame rates.
#ifdef Q_OS_IOS
[captureDevice setActiveVideoMinFrameDuration:minFrameDuration];
[captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration];
#else // Q_OS_OSX
if (CMTimeCompare(minFrameDuration, kCMTimeInvalid) == 0
&& CMTimeCompare(maxFrameDuration, kCMTimeInvalid) == 0) {
AVFrameRateRange *range = captureDevice.activeFormat.videoSupportedFrameRateRanges.firstObject;
minFrameDuration = range.minFrameDuration;
maxFrameDuration = range.maxFrameDuration;
}
if (CMTimeCompare(minFrameDuration, kCMTimeInvalid))
[captureDevice setActiveVideoMinFrameDuration:minFrameDuration];
#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
#if QT_OSX_DEPLOYMENT_TARGET_BELOW(__MAC_10_9)
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9)
#endif
{
if (CMTimeCompare(maxFrameDuration, kCMTimeInvalid))
[captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration];
}
#endif // QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
#endif // Q_OS_OSX
}
#endif // Platform SDK >= 10.9, >= 7.0.
void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection,
qreal minFPS, qreal maxFPS)
{
Q_ASSERT(captureDevice);
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_9, QSysInfo::MV_IOS_7_0))
qt_set_framerate_limits(captureDevice, minFPS, maxFPS);
else
#endif
if (videoConnection)
qt_set_framerate_limits(videoConnection, minFPS, maxFPS);
}
AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection)
{
Q_ASSERT(captureDevice);
AVFPSRange fps;
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) {
const CMTime minDuration = captureDevice.activeVideoMinFrameDuration;
if (CMTimeCompare(minDuration, kCMTimeInvalid)) {
if (const Float64 minSeconds = CMTimeGetSeconds(minDuration))
fps.second = 1. / minSeconds; // Max FPS = 1 / MinDuration.
}
#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
#if QT_OSX_DEPLOYMENT_TARGET_BELOW(__MAC_10_9)
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9)
#endif
{
const CMTime maxDuration = captureDevice.activeVideoMaxFrameDuration;
if (CMTimeCompare(maxDuration, kCMTimeInvalid)) {
if (const Float64 maxSeconds = CMTimeGetSeconds(maxDuration))
fps.first = 1. / maxSeconds; // Min FPS = 1 / MaxDuration.
}
}
#endif // QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
} else {
#else // OSX < 10.7 or iOS < 7.0
{
#endif // QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
if (videoConnection)
fps = qt_connection_framerates(videoConnection);
}
return fps;
}
QT_END_NAMESPACE