diff options
67 files changed, 3529 insertions, 360 deletions
diff --git a/.qmake.conf b/.qmake.conf index fd7c2fa95..a9b6a3290 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,4 +1,4 @@ load(qt_build_config) CONFIG += qt_example_installs -MODULE_VERSION = 5.5.1 +MODULE_VERSION = 5.6.0 diff --git a/src/imports/audioengine/audioengine.cpp b/src/imports/audioengine/audioengine.cpp index 612524447..4d29528f9 100644 --- a/src/imports/audioengine/audioengine.cpp +++ b/src/imports/audioengine/audioengine.cpp @@ -68,6 +68,10 @@ public: qmlRegisterType<QDeclarativeAttenuationModelLinear>(uri, 1, 0, "AttenuationModelLinear"); qmlRegisterType<QDeclarativeAttenuationModelInverse>(uri, 1, 0, "AttenuationModelInverse"); + + // Dynamically adding audio engine related objects is only supported through revision 1 + qmlRegisterRevision<QDeclarativeAudioEngine, 1>(uri, 1, 1); + qmlRegisterRevision<QDeclarativeSound, 1>(uri, 1, 1); } }; diff --git a/src/imports/audioengine/qdeclarative_attenuationmodel_p.cpp b/src/imports/audioengine/qdeclarative_attenuationmodel_p.cpp index 0814e9d62..7ecdb41ae 100644 --- a/src/imports/audioengine/qdeclarative_attenuationmodel_p.cpp +++ b/src/imports/audioengine/qdeclarative_attenuationmodel_p.cpp @@ -32,6 +32,7 @@ ****************************************************************************/ #include "qdeclarative_attenuationmodel_p.h" +#include "qdeclarative_audioengine_p.h" #include "qdebug.h" #define DEBUG_AUDIOENGINE @@ -40,7 +41,7 @@ QT_USE_NAMESPACE QDeclarativeAttenuationModel::QDeclarativeAttenuationModel(QObject *parent) : QObject(parent) - , m_complete(false) + , m_engine(0) { } @@ -48,22 +49,9 @@ QDeclarativeAttenuationModel::~QDeclarativeAttenuationModel() { } -void QDeclarativeAttenuationModel::classBegin() +void QDeclarativeAttenuationModel::setEngine(QDeclarativeAudioEngine *engine) { - if (!parent() || !parent()->inherits("QDeclarativeAudioEngine")) { - qWarning("AttenuationModel must be defined inside AudioEngine!"); - //TODO: COMPILE_EXCEPTION ? - return; - } -} - -void QDeclarativeAttenuationModel::componentComplete() -{ - if (m_name.isEmpty()) { - qWarning("AttenuationModel must have a name!"); - return; - } - m_complete = true; + m_engine = engine; } QString QDeclarativeAttenuationModel::name() const @@ -73,7 +61,7 @@ QString QDeclarativeAttenuationModel::name() const void QDeclarativeAttenuationModel::setName(const QString& name) { - if (m_complete) { + if (m_engine) { qWarning("AttenuationModel: you can not change name after initialization."); return; } @@ -93,7 +81,9 @@ void QDeclarativeAttenuationModel::setName(const QString& name) This type is part of the \b{QtAudioEngine 1.0} module. - AttenuationModelLinear must be defined inside \l AudioEngine. + AttenuationModelLinear must be defined inside \l AudioEngine or be added to it using + \l{QtAudioEngine::AudioEngine::addAttenuationModel()}{AudioEngine.addAttenuationModel()} + if AttenuationModelLinear is created dynamically. \qml import QtQuick 2.0 @@ -144,13 +134,13 @@ QDeclarativeAttenuationModelLinear::QDeclarativeAttenuationModelLinear(QObject * { } -void QDeclarativeAttenuationModelLinear::componentComplete() +void QDeclarativeAttenuationModelLinear::setEngine(QDeclarativeAudioEngine *engine) { if (m_start > m_end) { qSwap(m_start, m_end); qWarning() << "AttenuationModelLinear[" << m_name << "]: start must be less or equal than end."; } - QDeclarativeAttenuationModel::componentComplete(); + QDeclarativeAttenuationModel::setEngine(engine); } /*! @@ -167,7 +157,7 @@ qreal QDeclarativeAttenuationModelLinear::startDistance() const void QDeclarativeAttenuationModelLinear::setStartDistance(qreal startDist) { - if (m_complete) { + if (m_engine) { qWarning() << "AttenuationModelLinear[" << m_name << "]: you can not change properties after initialization."; return; } @@ -192,7 +182,7 @@ qreal QDeclarativeAttenuationModelLinear::endDistance() const void QDeclarativeAttenuationModelLinear::setEndDistance(qreal endDist) { - if (m_complete) { + if (m_engine) { qWarning() << "AttenuationModelLinear[" << m_name << "]: you can not change properties after initialization."; return; } @@ -226,7 +216,9 @@ qreal QDeclarativeAttenuationModelLinear::calculateGain(const QVector3D &listene This type is part of the \b{QtAudioEngine 1.0} module. - AttenuationModelInverse must be defined inside AudioEngine. + AttenuationModelInverse must be defined inside \l AudioEngine or be added to it using + \l{QtAudioEngine::AudioEngine::addAttenuationModel()}{AudioEngine.addAttenuationModel()} + if AttenuationModelInverse is created dynamically. \qml import QtQuick 2.0 @@ -309,13 +301,13 @@ QDeclarativeAttenuationModelInverse::QDeclarativeAttenuationModelInverse(QObject { } -void QDeclarativeAttenuationModelInverse::componentComplete() +void QDeclarativeAttenuationModelInverse::setEngine(QDeclarativeAudioEngine *engine) { if (m_ref > m_max) { qSwap(m_ref, m_max); qWarning() << "AttenuationModelInverse[" << m_name << "]: referenceDistance must be less or equal than maxDistance."; } - QDeclarativeAttenuationModel::componentComplete(); + QDeclarativeAttenuationModel::setEngine(engine); } qreal QDeclarativeAttenuationModelInverse::referenceDistance() const @@ -325,7 +317,7 @@ qreal QDeclarativeAttenuationModelInverse::referenceDistance() const void QDeclarativeAttenuationModelInverse::setReferenceDistance(qreal referenceDistance) { - if (m_complete) { + if (m_engine) { qWarning() << "AttenuationModelInverse[" << m_name << "]: you can not change properties after initialization."; return; } @@ -343,7 +335,7 @@ qreal QDeclarativeAttenuationModelInverse::maxDistance() const void QDeclarativeAttenuationModelInverse::setMaxDistance(qreal maxDistance) { - if (m_complete) { + if (m_engine) { qWarning() << "AttenuationModelInverse[" << m_name << "]: you can not change properties after initialization."; return; } @@ -361,7 +353,7 @@ qreal QDeclarativeAttenuationModelInverse::rolloffFactor() const void QDeclarativeAttenuationModelInverse::setRolloffFactor(qreal rolloffFactor) { - if (m_complete) { + if (m_engine) { qWarning() << "AttenuationModelInverse[" << m_name << "]: you can not change properties after initialization."; return; } diff --git a/src/imports/audioengine/qdeclarative_attenuationmodel_p.h b/src/imports/audioengine/qdeclarative_attenuationmodel_p.h index dc8bff36f..f276757de 100644 --- a/src/imports/audioengine/qdeclarative_attenuationmodel_p.h +++ b/src/imports/audioengine/qdeclarative_attenuationmodel_p.h @@ -35,32 +35,31 @@ #define QDECLARATIVEATTENUATIONMODEL_P_H #include <QtQml/qqml.h> -#include <QtQml/qqmlcomponent.h> #include <QVector3D> QT_BEGIN_NAMESPACE -class QDeclarativeAttenuationModel : public QObject, public QQmlParserStatus +class QDeclarativeAudioEngine; + +class QDeclarativeAttenuationModel : public QObject { Q_OBJECT - Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(QString name READ name WRITE setName) public: QDeclarativeAttenuationModel(QObject *parent = 0); ~QDeclarativeAttenuationModel(); - void classBegin(); - void componentComplete(); - QString name() const; void setName(const QString& name); virtual qreal calculateGain(const QVector3D &listenerPosition, const QVector3D &sourcePosition) const = 0; + virtual void setEngine(QDeclarativeAudioEngine *engine); + protected: - bool m_complete; QString m_name; + QDeclarativeAudioEngine *m_engine; private: Q_DISABLE_COPY(QDeclarativeAttenuationModel); @@ -75,8 +74,6 @@ class QDeclarativeAttenuationModelLinear : public QDeclarativeAttenuationModel public: QDeclarativeAttenuationModelLinear(QObject *parent = 0); - void componentComplete(); - qreal startDistance() const; void setStartDistance(qreal startDist); @@ -85,6 +82,8 @@ public: qreal calculateGain(const QVector3D &listenerPosition, const QVector3D &sourcePosition) const; + void setEngine(QDeclarativeAudioEngine *engine); + private: Q_DISABLE_COPY(QDeclarativeAttenuationModelLinear); qreal m_start; @@ -101,8 +100,6 @@ class QDeclarativeAttenuationModelInverse : public QDeclarativeAttenuationModel public: QDeclarativeAttenuationModelInverse(QObject *parent = 0); - void componentComplete(); - qreal referenceDistance() const; void setReferenceDistance(qreal referenceDistance); @@ -114,6 +111,8 @@ public: qreal calculateGain(const QVector3D &listenerPosition, const QVector3D &sourcePosition) const; + void setEngine(QDeclarativeAudioEngine *engine); + private: Q_DISABLE_COPY(QDeclarativeAttenuationModelInverse); qreal m_ref; diff --git a/src/imports/audioengine/qdeclarative_audiocategory_p.cpp b/src/imports/audioengine/qdeclarative_audiocategory_p.cpp index df1a3cec9..847941ca9 100644 --- a/src/imports/audioengine/qdeclarative_audiocategory_p.cpp +++ b/src/imports/audioengine/qdeclarative_audiocategory_p.cpp @@ -51,7 +51,9 @@ QT_USE_NAMESPACE This type is part of the \b{QtAudioEngine 1.0} module. An instance of AudioCategory can be accessed through \l {QtAudioEngine::AudioEngine::categories} - {AudioEngine.categories} with its unique name and must be defined inside AudioEngine. + {AudioEngine.categories} with its unique name and must be defined inside AudioEngine or be added + to it using \l{QtAudioEngine::AudioEngine::addAudioCategory()}{AudioEngine.addAudioCategory()} if + AudioCategory is created dynamically. \qml import QtQuick 2.0 @@ -103,8 +105,8 @@ QT_USE_NAMESPACE */ QDeclarativeAudioCategory::QDeclarativeAudioCategory(QObject *parent) : QObject(parent) - , m_complete(false) , m_volume(1) + , m_engine(0) { } @@ -112,21 +114,9 @@ QDeclarativeAudioCategory::~QDeclarativeAudioCategory() { } -void QDeclarativeAudioCategory::classBegin() +void QDeclarativeAudioCategory::setEngine(QDeclarativeAudioEngine *engine) { - if (!parent() || !parent()->inherits("QDeclarativeAudioEngine")) { - qWarning("AudioCategory must be defined inside AudioEngine!"); - return; - } -} - -void QDeclarativeAudioCategory::componentComplete() -{ - if (m_name.isEmpty()) { - qWarning("AudioCategory must have a name!"); - return; - } - m_complete = true; + m_engine = engine; } /*! @@ -159,7 +149,7 @@ void QDeclarativeAudioCategory::setVolume(qreal volume) */ void QDeclarativeAudioCategory::setName(const QString& name) { - if (m_complete) { + if (m_engine) { qWarning("AudioCategory: you can not change name after initialization."); return; } diff --git a/src/imports/audioengine/qdeclarative_audiocategory_p.h b/src/imports/audioengine/qdeclarative_audiocategory_p.h index a797b2698..0345edfdb 100644 --- a/src/imports/audioengine/qdeclarative_audiocategory_p.h +++ b/src/imports/audioengine/qdeclarative_audiocategory_p.h @@ -35,14 +35,14 @@ #define QDECLARATIVEAUDIOCATEGORY_P_H #include <QtQml/qqml.h> -#include <QtQml/qqmlcomponent.h> QT_BEGIN_NAMESPACE -class QDeclarativeAudioCategory : public QObject, public QQmlParserStatus +class QDeclarativeAudioEngine; + +class QDeclarativeAudioCategory : public QObject { Q_OBJECT - Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged) Q_PROPERTY(QString name READ name WRITE setName) @@ -50,15 +50,14 @@ public: QDeclarativeAudioCategory(QObject *parent = 0); ~QDeclarativeAudioCategory(); - void classBegin(); - void componentComplete(); - qreal volume() const; void setVolume(qreal volume); QString name() const; void setName(const QString& name); + void setEngine(QDeclarativeAudioEngine *engine); + Q_SIGNALS: void volumeChanged(qreal newVolume); void stopped(); @@ -72,9 +71,9 @@ public Q_SLOTS: private: Q_DISABLE_COPY(QDeclarativeAudioCategory); - bool m_complete; QString m_name; qreal m_volume; + QDeclarativeAudioEngine *m_engine; }; QT_END_NAMESPACE diff --git a/src/imports/audioengine/qdeclarative_audioengine_p.cpp b/src/imports/audioengine/qdeclarative_audioengine_p.cpp index cf0a22644..dd80c698d 100644 --- a/src/imports/audioengine/qdeclarative_audioengine_p.cpp +++ b/src/imports/audioengine/qdeclarative_audioengine_p.cpp @@ -221,6 +221,222 @@ void QDeclarativeAudioEngine::releaseSoundInstance(QSoundInstance* instance) emit liveInstanceCountChanged(); } +void QDeclarativeAudioEngine::initAudioSample(QDeclarativeAudioSample *sample) +{ + sample->init(); +} + +void QDeclarativeAudioEngine::initSound(QDeclarativeSound *sound) +{ + QDeclarativeAudioCategory *category = m_defaultCategory; + if (m_categories.contains(sound->category())) { + category = qobject_cast<QDeclarativeAudioCategory*>( + qvariant_cast<QObject*>(m_categories[sound->category()])); + } + sound->setCategoryObject(category); + + QDeclarativeAttenuationModel *attenuationModel = 0; + if (sound->attenuationModel().isEmpty()) { + if (m_defaultAttenuationModel) + attenuationModel = m_defaultAttenuationModel; + } else if (m_attenuationModels.contains(sound->attenuationModel())){ + attenuationModel = m_attenuationModels[sound->attenuationModel()]; + } else { + qWarning() << "Sound[" << sound->name() << "] contains invalid attenuationModel[" + << sound->attenuationModel() << "]"; + } + sound->setAttenuationModelObject(attenuationModel); + + foreach (QDeclarativePlayVariation *playVariation, sound->playlist()) { + if (m_samples.contains(playVariation->sample())) { + playVariation->setSampleObject( + qobject_cast<QDeclarativeAudioSample*>( + qvariant_cast<QObject*>(m_samples[playVariation->sample()]))); + } else { + qWarning() << "Sound[" << sound->name() << "] contains invalid sample[" + << playVariation->sample() << "] for its playVarations"; + } + } +} + +/*! + \qmlmethod QtAudioEngine::AudioEngine::addAudioSample(AudioSample sample) + + Adds the given \a sample to the engine. + This can be used when the AudioSample is created dynamically: + + \qml + import QtAudioEngine 1.1 + + AudioEngine { + id: engine + + Component.onCompleted: { + var sample = Qt.createQmlObject('import QtAudioEngine 1.1; AudioSample {}', engine); + sample.name = "example"; + sample.source = "example.wav"; + engine.addAudioSample(sample); + } + } + \endqml +*/ +void QDeclarativeAudioEngine::addAudioSample(QDeclarativeAudioSample *sample) +{ +#ifdef DEBUG_AUDIOENGINE + qDebug() << "add QDeclarativeAudioSample[" << sample->name() << "]"; +#endif + if (sample->name().isEmpty()) { + qWarning("AudioSample must have a name!"); + return; + } + + if (m_samples.contains(sample->name())) { + qWarning() << "Failed to add AudioSample[" << sample->name() << "], already exists!"; + return; + } + m_samples.insert(sample->name(), QVariant::fromValue(sample)); + sample->setEngine(this); + + if (m_complete) { initAudioSample(sample); } +} + +/*! + \qmlmethod QtAudioEngine::AudioEngine::addSound(Sound sound) + + Adds the given \a sound to the engine. + This can be used when the Sound is created dynamically: + + \qml + import QtAudioEngine 1.1 + + AudioEngine { + id: engine + + Component.onCompleted: { + var sound = Qt.createQmlObject('import QtAudioEngine 1.1; Sound {}', engine); + sound.name = "example"; + engine.addSound(sound); + } + } + \endqml +*/ +void QDeclarativeAudioEngine::addSound(QDeclarativeSound *sound) +{ +#ifdef DEBUG_AUDIOENGINE + qDebug() << "add QDeclarativeSound[" << sound->name() << "]"; +#endif + if (sound->name().isEmpty()) { + qWarning("Sound must have a name!"); + return; + } + + if (m_sounds.contains(sound->name())) { + qWarning() << "Failed to add Sound[" << sound->name() << "], already exists!"; + return; + } + m_sounds.insert(sound->name(), QVariant::fromValue(sound)); + sound->setEngine(this); + + if (m_complete) { initSound(sound); } + +} + +/*! + \qmlmethod QtAudioEngine::AudioEngine::addAudioCategory(AudioCategory category) + + Adds the given \a category to the engine. + This can be used when the AudioCategory is created dynamically: + + \qml + import QtAudioEngine 1.1 + + AudioEngine { + id: engine + + Component.onCompleted: { + var category = Qt.createQmlObject('import QtAudioEngine 1.1; AudioCategory {}', engine); + category.name = "sample"; + category.volume = 0.9; + engine.addAudioCategory(category); + } + } + \endqml +*/ +void QDeclarativeAudioEngine::addAudioCategory(QDeclarativeAudioCategory *category) +{ +#ifdef DEBUG_AUDIOENGINE + qDebug() << "add QDeclarativeAudioCategory[" << category->name() << "]"; +#endif + if (category->name().isEmpty()) { + qWarning("AudioCategory must have a name!"); + return; + } + + if (m_categories.contains(category->name())) { + qWarning() << "Failed to add AudioCategory[" << category->name() << "], already exists!"; + return; + } + m_categories.insert(category->name(), QVariant::fromValue(category)); + if (category->name() == QLatin1String("default")) { + if (!m_complete) { + m_defaultCategory = category; + } else { + qWarning() << "Can not change default category after initializing engine"; + } + } + + category->setEngine(this); +} + +/*! + \qmlmethod QtAudioEngine::AudioEngine::addAttenuationModel(AttenuationModel attenuationModel) + + Adds the given \a attenuationModel to the engine. + This can be used when the AttenuationModelLinear / AttenuationModelInverse is created dynamically: + + \qml + import QtAudioEngine 1.1 + + AudioEngine { + id: engine + + Component.onCompleted: { + var attenuationModel = Qt.createQmlObject('import QtAudioEngine 1.1; AttenuationModelLinear {}', engine); + attenuationModel.name ="linear" + attenuationModel.start = 20 + attenuationModel.end = 180 + engine.addAttenuationModel(attenuationModel); + } + } + \endqml +*/ +void QDeclarativeAudioEngine::addAttenuationModel(QDeclarativeAttenuationModel *attenModel) +{ +#ifdef DEBUG_AUDIOENGINE + qDebug() << "add AttenuationModel[" << attenModel->name() << "]"; +#endif + if (attenModel->name().isEmpty()) { + qWarning("AttenuationModel must have a name!"); + return; + } + + if (m_attenuationModels.contains(attenModel->name())) { + qWarning() << "Failed to add AttenuationModel[" << attenModel->name() << "], already exists!"; + return; + } + m_attenuationModels.insert(attenModel->name(), attenModel); + + if (attenModel->name() == QLatin1String("default")) { + if (!m_complete) { + m_defaultAttenuationModel = attenModel; + } else { + qWarning() << "Can not change default attenuation model after initializing engine"; + } + } + + attenModel->setEngine(this); +} + void QDeclarativeAudioEngine::componentComplete() { #ifdef DEBUG_AUDIOENGINE @@ -231,10 +447,9 @@ void QDeclarativeAudioEngine::componentComplete() qDebug() << "creating default category"; #endif m_defaultCategory = new QDeclarativeAudioCategory(this); - m_defaultCategory->classBegin(); m_defaultCategory->setName(QString::fromLatin1("default")); m_defaultCategory->setVolume(1); - m_defaultCategory->componentComplete(); + m_defaultCategory->setEngine(this); } #ifdef DEBUG_AUDIOENGINE qDebug() << "init samples" << m_samples.keys().count(); @@ -246,7 +461,8 @@ void QDeclarativeAudioEngine::componentComplete() qWarning() << "accessing invalid sample[" << key << "]"; continue; } - sample->init(); + + initAudioSample(sample); } #ifdef DEBUG_AUDIOENGINE @@ -260,35 +476,8 @@ void QDeclarativeAudioEngine::componentComplete() qWarning() << "accessing invalid sound[" << key << "]"; continue; } - QDeclarativeAudioCategory *category = m_defaultCategory; - if (m_categories.contains(sound->category())) { - category = qobject_cast<QDeclarativeAudioCategory*>( - qvariant_cast<QObject*>(m_categories[sound->category()])); - } - sound->setCategoryObject(category); - - QDeclarativeAttenuationModel *attenuationModel = 0; - if (sound->attenuationModel().isEmpty()) { - if (m_defaultAttenuationModel) - attenuationModel = m_defaultAttenuationModel; - } else if (m_attenuationModels.contains(sound->attenuationModel())){ - attenuationModel = m_attenuationModels[sound->attenuationModel()]; - } else { - qWarning() << "Sound[" << sound->name() << "] contains invalid attenuationModel[" - << sound->attenuationModel() << "]"; - } - sound->setAttenuationModelObject(attenuationModel); - - foreach (QDeclarativePlayVariation* playVariation, sound->playlist()) { - if (m_samples.contains(playVariation->sample())) { - playVariation->setSampleObject( - qobject_cast<QDeclarativeAudioSample*>( - qvariant_cast<QObject*>(m_samples[playVariation->sample()]))); - } else { - qWarning() << "Sound[" << sound->name() << "] contains invalid sample[" - << playVariation->sample() << "] for its playVarations"; - } - } + + initSound(sound); } m_complete = true; #ifdef DEBUG_AUDIOENGINE @@ -330,65 +519,30 @@ void QDeclarativeAudioEngine::appendFunction(QQmlListProperty<QObject> *property { QDeclarativeAudioEngine* engine = static_cast<QDeclarativeAudioEngine*>(property->object); if (engine->m_complete) { - qWarning("AudioEngine: cannot add child after initialization!"); return; } QDeclarativeSound *sound = qobject_cast<QDeclarativeSound*>(value); if (sound) { -#ifdef DEBUG_AUDIOENGINE - qDebug() << "add QDeclarativeSound[" << sound->name() << "]"; -#endif - if (engine->m_sounds.contains(sound->name())) { - qWarning() << "Failed to add Sound[" << sound->name() << "], already exists!"; - return; - } - engine->m_sounds.insert(sound->name(), QVariant::fromValue(value)); + engine->addSound(sound); return; } QDeclarativeAudioSample *sample = qobject_cast<QDeclarativeAudioSample*>(value); if (sample) { -#ifdef DEBUG_AUDIOENGINE - qDebug() << "add QDeclarativeAudioSample[" << sample->name() << "]"; -#endif - if (engine->m_samples.contains(sample->name())) { - qWarning() << "Failed to add AudioSample[" << sample->name() << "], already exists!"; - return; - } - engine->m_samples.insert(sample->name(), QVariant::fromValue(value)); + engine->addAudioSample(sample); return; } QDeclarativeAudioCategory *category = qobject_cast<QDeclarativeAudioCategory*>(value); if (category) { -#ifdef DEBUG_AUDIOENGINE - qDebug() << "add QDeclarativeAudioCategory[" << category->name() << "]"; -#endif - if (engine->m_categories.contains(category->name())) { - qWarning() << "Failed to add AudioCategory[" << category->name() << "], already exists!"; - return; - } - engine->m_categories.insert(category->name(), QVariant::fromValue(value)); - if (category->name() == QLatin1String("default")) { - engine->m_defaultCategory = category; - } + engine->addAudioCategory(category); return; } QDeclarativeAttenuationModel *attenModel = qobject_cast<QDeclarativeAttenuationModel*>(value); if (attenModel) { -#ifdef DEBUG_AUDIOENGINE - qDebug() << "add AttenuationModel[" << attenModel->name() << "]"; -#endif - if (attenModel->name() == QLatin1String("default")) { - engine->m_defaultAttenuationModel = attenModel; - } - if (engine->m_attenuationModels.contains(attenModel->name())) { - qWarning() << "Failed to add AttenuationModel[" << attenModel->name() << "], already exists!"; - return; - } - engine->m_attenuationModels.insert(attenModel->name(), attenModel); + engine->addAttenuationModel(attenModel); return; } diff --git a/src/imports/audioengine/qdeclarative_audioengine_p.h b/src/imports/audioengine/qdeclarative_audioengine_p.h index 51090b5dc..7ae6049f0 100644 --- a/src/imports/audioengine/qdeclarative_audioengine_p.h +++ b/src/imports/audioengine/qdeclarative_audioengine_p.h @@ -111,6 +111,11 @@ public: QSoundInstance* newSoundInstance(const QString &name); void releaseSoundInstance(QSoundInstance* instance); + Q_REVISION(1) Q_INVOKABLE void addAudioSample(QDeclarativeAudioSample *); + Q_REVISION(1) Q_INVOKABLE void addSound(QDeclarativeSound *); + Q_REVISION(1) Q_INVOKABLE void addAudioCategory(QDeclarativeAudioCategory *); + Q_REVISION(1) Q_INVOKABLE void addAttenuationModel(QDeclarativeAttenuationModel *); + Q_SIGNALS: void ready(); void liveInstanceCountChanged(); @@ -149,6 +154,9 @@ private: QList<QDeclarativeSoundInstance*> m_managedDeclSoundInstances; QList<QDeclarativeSoundInstance*> m_managedDeclSndInstancePool; void releaseManagedDeclarativeSoundInstance(QDeclarativeSoundInstance* declSndInstance); + + void initAudioSample(QDeclarativeAudioSample *); + void initSound(QDeclarativeSound *); }; QT_END_NAMESPACE diff --git a/src/imports/audioengine/qdeclarative_audiosample_p.cpp b/src/imports/audioengine/qdeclarative_audiosample_p.cpp index 5a9a67b71..297af3b31 100644 --- a/src/imports/audioengine/qdeclarative_audiosample_p.cpp +++ b/src/imports/audioengine/qdeclarative_audiosample_p.cpp @@ -54,7 +54,9 @@ QT_USE_NAMESPACE \c AudioSample is part of the \b{QtAudioEngine 1.0} module. It can be accessed through QtAudioEngine::AudioEngine::samples with its unique - name and must be defined inside AudioEngine. + name and must be defined inside AudioEngine or be added to it using + \l{QtAudioEngine::AudioEngine::addAudioSample()}{AudioEngine.addAudioSample()} + if AudioSample is created dynamically. \qml import QtQuick 2.0 @@ -78,10 +80,10 @@ QT_USE_NAMESPACE */ QDeclarativeAudioSample::QDeclarativeAudioSample(QObject *parent) : QObject(parent) - , m_complete(false) , m_streaming(false) , m_preloaded(false) , m_soundBuffer(0) + , m_engine(0) { } @@ -89,23 +91,6 @@ QDeclarativeAudioSample::~QDeclarativeAudioSample() { } -void QDeclarativeAudioSample::classBegin() -{ - if (!parent() || !parent()->inherits("QDeclarativeAudioEngine")) { - qWarning("AudioSample must be defined inside AudioEngine!"); - return; - } -} - -void QDeclarativeAudioSample::componentComplete() -{ - if (m_name.isEmpty()) { - qWarning("AudioSample must have a name!"); - return; - } - m_complete = true; -} - /*! \qmlproperty url QtAudioEngine::AudioSample::source @@ -118,7 +103,7 @@ QUrl QDeclarativeAudioSample::source() const void QDeclarativeAudioSample::setSource(const QUrl& url) { - if (m_complete) { + if (m_engine) { qWarning("AudioSample: source not changeable after initialization."); return; } @@ -130,6 +115,11 @@ bool QDeclarativeAudioSample::isStreaming() const return m_streaming; } +QDeclarativeAudioEngine *QDeclarativeAudioSample::engine() const +{ + return m_engine; +} + /*! \qmlproperty bool QtAudioEngine::AudioSample::preloaded @@ -173,7 +163,7 @@ void QDeclarativeAudioSample::load() void QDeclarativeAudioSample::setPreloaded(bool preloaded) { - if (m_complete) { + if (m_engine) { qWarning("AudioSample: preloaded not changeable after initialization."); return; } @@ -182,13 +172,22 @@ void QDeclarativeAudioSample::setPreloaded(bool preloaded) void QDeclarativeAudioSample::setStreaming(bool streaming) { - if (m_complete) { + if (m_engine) { qWarning("AudioSample: streaming not changeable after initialization."); return; } m_streaming = streaming; } +void QDeclarativeAudioSample::setEngine(QDeclarativeAudioEngine *engine) +{ + if (m_engine) { + qWarning("AudioSample: engine not changeable after initialization."); + return; + } + m_engine = engine; +} + /*! \qmlproperty string QtAudioEngine::AudioSample::name @@ -202,7 +201,7 @@ QString QDeclarativeAudioSample::name() const void QDeclarativeAudioSample::setName(const QString& name) { - if (m_complete) { + if (m_engine) { qWarning("AudioSample: name not changeable after initialization."); return; } @@ -211,12 +210,13 @@ void QDeclarativeAudioSample::setName(const QString& name) void QDeclarativeAudioSample::init() { + Q_ASSERT(m_engine != 0); + if (m_streaming) { //TODO } else { - m_soundBuffer = - qobject_cast<QDeclarativeAudioEngine*>(parent())->engine()->getStaticSoundBuffer(m_url); + m_soundBuffer = m_engine->engine()->getStaticSoundBuffer(m_url); if (m_soundBuffer->state() == QSoundBuffer::Ready) { emit loadedChanged(); } else { diff --git a/src/imports/audioengine/qdeclarative_audiosample_p.h b/src/imports/audioengine/qdeclarative_audiosample_p.h index 94e1c7e27..5b549e785 100644 --- a/src/imports/audioengine/qdeclarative_audiosample_p.h +++ b/src/imports/audioengine/qdeclarative_audiosample_p.h @@ -35,16 +35,15 @@ #define QDECLARATIVEAUDIOSAMPLE_P_H #include <QtQml/qqml.h> -#include <QtQml/qqmlcomponent.h> QT_BEGIN_NAMESPACE class QSoundBuffer; +class QDeclarativeAudioEngine; -class QDeclarativeAudioSample : public QObject, public QQmlParserStatus +class QDeclarativeAudioSample : public QObject { Q_OBJECT - Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(QString name READ name WRITE setName) Q_PROPERTY(QUrl source READ source WRITE setSource) Q_PROPERTY(bool preloaded READ isPreloaded WRITE setPreloaded) @@ -55,9 +54,6 @@ public: QDeclarativeAudioSample(QObject *parent = 0); ~QDeclarativeAudioSample(); - void classBegin(); - void componentComplete(); - QString name() const; void setName(const QString& name); @@ -70,6 +66,9 @@ public: bool isPreloaded() const; void setPreloaded(bool preloaded); + QDeclarativeAudioEngine *engine() const; + void setEngine(QDeclarativeAudioEngine *); + bool isLoaded() const; QSoundBuffer* soundBuffer() const; @@ -85,13 +84,12 @@ public Q_SLOTS: private: Q_DISABLE_COPY(QDeclarativeAudioSample); - bool m_complete; QString m_name; QUrl m_url; bool m_streaming; bool m_preloaded; - QSoundBuffer *m_soundBuffer; + QDeclarativeAudioEngine *m_engine; }; QT_END_NAMESPACE diff --git a/src/imports/audioengine/qdeclarative_playvariation_p.cpp b/src/imports/audioengine/qdeclarative_playvariation_p.cpp index 38876c227..156f83f68 100644 --- a/src/imports/audioengine/qdeclarative_playvariation_p.cpp +++ b/src/imports/audioengine/qdeclarative_playvariation_p.cpp @@ -55,7 +55,9 @@ QT_USE_NAMESPACE This type is part of the \b{QtAudioEngine 1.0} module. - PlayVariation must be defined inside a \l Sound. + PlayVariation must be defined inside a \l Sound or be added to it using + \l{QtAudioEngine::Sound::addPlayVariation()}{Sound.addPlayVariation()} + if PlayVariation is created dynamically. \qml import QtQuick 2.0 @@ -100,13 +102,13 @@ QT_USE_NAMESPACE */ QDeclarativePlayVariation::QDeclarativePlayVariation(QObject *parent) : QObject(parent) - , m_complete(false) , m_looping(false) , m_maxGain(1) , m_minGain(1) , m_maxPitch(1) , m_minPitch(1) , m_sampleObject(0) + , m_engine(0) { } @@ -114,15 +116,7 @@ QDeclarativePlayVariation::~QDeclarativePlayVariation() { } -void QDeclarativePlayVariation::classBegin() -{ - if (!parent() || !parent()->inherits("QDeclarativeSound")) { - qWarning("PlayVariation must be defined inside Sound!"); - return; - } -} - -void QDeclarativePlayVariation::componentComplete() +void QDeclarativePlayVariation::setEngine(QDeclarativeAudioEngine *engine) { if (m_maxGain < m_minGain) { qWarning("PlayVariation: maxGain must be no less than minGain"); @@ -132,7 +126,7 @@ void QDeclarativePlayVariation::componentComplete() qWarning("PlayVariation: maxPitch must be no less than minPitch"); qSwap(m_minPitch, m_maxPitch); } - m_complete = true; + m_engine = engine; } /*! @@ -147,7 +141,7 @@ QString QDeclarativePlayVariation::sample() const void QDeclarativePlayVariation::setSample(const QString& sample) { - if (m_complete) { + if (m_engine) { qWarning("PlayVariation: cannot change properties after initialization."); return; } @@ -166,7 +160,7 @@ bool QDeclarativePlayVariation::isLooping() const void QDeclarativePlayVariation::setLooping(bool looping) { - if (m_complete) { + if (m_engine) { qWarning("PlayVariation: cannot change properties after initialization."); return; } @@ -185,7 +179,7 @@ qreal QDeclarativePlayVariation::maxGain() const void QDeclarativePlayVariation::setMaxGain(qreal maxGain) { - if (m_complete) { + if (m_engine) { qWarning("PlayVariation: cannot change properties after initialization."); return; } @@ -208,7 +202,7 @@ qreal QDeclarativePlayVariation::minGain() const void QDeclarativePlayVariation::setMinGain(qreal minGain) { - if (m_complete) { + if (m_engine) { qWarning("PlayVariation: cannot change properties after initialization."); return; } @@ -231,7 +225,7 @@ qreal QDeclarativePlayVariation::maxPitch() const void QDeclarativePlayVariation::setMaxPitch(qreal maxPitch) { - if (m_complete) { + if (m_engine) { qWarning("PlayVariation: cannot change properties after initialization."); return; } @@ -254,7 +248,7 @@ qreal QDeclarativePlayVariation::minPitch() const void QDeclarativePlayVariation::setMinPitch(qreal minPitch) { - if (m_complete) { + if (m_engine) { qWarning("PlayVariation: cannot change properties after initialization."); return; } diff --git a/src/imports/audioengine/qdeclarative_playvariation_p.h b/src/imports/audioengine/qdeclarative_playvariation_p.h index dfe690cef..d0eed0d0a 100644 --- a/src/imports/audioengine/qdeclarative_playvariation_p.h +++ b/src/imports/audioengine/qdeclarative_playvariation_p.h @@ -35,17 +35,16 @@ #define QDECLARATIVEPLAYVARIATION_P_H #include <QtQml/qqml.h> -#include <QtQml/qqmlcomponent.h> QT_BEGIN_NAMESPACE class QDeclarativeAudioSample; class QSoundInstance; +class QDeclarativeAudioEngine; -class QDeclarativePlayVariation : public QObject, public QQmlParserStatus +class QDeclarativePlayVariation : public QObject { Q_OBJECT - Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(QString sample READ sample WRITE setSample) Q_PROPERTY(bool looping READ isLooping WRITE setLooping) Q_PROPERTY(qreal maxGain READ maxGain WRITE setMaxGain) @@ -57,9 +56,6 @@ public: QDeclarativePlayVariation(QObject *parent = 0); ~QDeclarativePlayVariation(); - void classBegin(); - void componentComplete(); - QString sample() const; void setSample(const QString& sample); @@ -82,9 +78,10 @@ public: void applyParameters(QSoundInstance *soundInstance); + void setEngine(QDeclarativeAudioEngine *engine); + private: Q_DISABLE_COPY(QDeclarativePlayVariation); - bool m_complete; QString m_sample; bool m_looping; qreal m_maxGain; @@ -92,6 +89,7 @@ private: qreal m_maxPitch; qreal m_minPitch; QDeclarativeAudioSample *m_sampleObject; + QDeclarativeAudioEngine *m_engine; }; QT_END_NAMESPACE diff --git a/src/imports/audioengine/qdeclarative_sound_p.cpp b/src/imports/audioengine/qdeclarative_sound_p.cpp index fcbd76f71..f19b8dbb3 100644 --- a/src/imports/audioengine/qdeclarative_sound_p.cpp +++ b/src/imports/audioengine/qdeclarative_sound_p.cpp @@ -64,8 +64,12 @@ qreal QDeclarativeSoundCone::innerAngle() const void QDeclarativeSoundCone::setInnerAngle(qreal innerAngle) { QDeclarativeSound *s = qobject_cast<QDeclarativeSound*>(parent()); - if (s && s->m_complete) + + if (s && s->m_engine) { + qWarning("SoundCone: innerAngle not changeable after initialization."); return; + } + if (innerAngle < 0 || innerAngle > 360) { qWarning() << "innerAngle should be within[0, 360] degrees"; return; @@ -88,8 +92,12 @@ qreal QDeclarativeSoundCone::outerAngle() const void QDeclarativeSoundCone::setOuterAngle(qreal outerAngle) { QDeclarativeSound *s = qobject_cast<QDeclarativeSound*>(parent()); - if (s && s->m_complete) + + if (s && s->m_engine) { + qWarning("SoundCone: outerAngle not changeable after initialization."); return; + } + if (outerAngle < 0 || outerAngle > 360) { qWarning() << "outerAngle should be within[0, 360] degrees"; return; @@ -112,8 +120,12 @@ qreal QDeclarativeSoundCone::outerGain() const void QDeclarativeSoundCone::setOuterGain(qreal outerGain) { QDeclarativeSound *s = qobject_cast<QDeclarativeSound*>(parent()); - if (s && s->m_complete) + + if (s && s->m_engine) { + qWarning("SoundCone: outerGain not changeable after initialization."); return; + } + if (outerGain < 0 || outerGain > 1) { qWarning() << "outerGain should no less than 0 and no more than 1"; return; @@ -121,11 +133,18 @@ void QDeclarativeSoundCone::setOuterGain(qreal outerGain) m_outerGain = outerGain; } -void QDeclarativeSoundCone::componentComplete() +void QDeclarativeSoundCone::setEngine(QDeclarativeAudioEngine *engine) { + if (m_engine) { + qWarning("SoundCone: engine not changeable after initialization."); + return; + } + if (m_outerAngle < m_innerAngle) { m_outerAngle = m_innerAngle; } + + m_engine = engine; } //////////////////////////////////////////////////////////// @@ -143,7 +162,9 @@ void QDeclarativeSoundCone::componentComplete() This type is part of the \b{QtAudioEngine 1.0} module. Sound can be accessed through QtAudioEngine::AudioEngine::sounds with its unique name - and must be defined inside AudioEngine. + and must be defined inside AudioEngine or be added to it using + \l{QtAudioEngine::AudioEngine::addSound()}{AudioEngine.addSound()} + if \l Sound is created dynamically. \qml import QtQuick 2.0 @@ -194,10 +215,10 @@ void QDeclarativeSoundCone::componentComplete() QDeclarativeSound::QDeclarativeSound(QObject *parent) : QObject(parent) - , m_complete(false) , m_playType(Random) , m_attenuationModelObject(0) , m_categoryObject(0) + , m_engine(0) { m_cone = new QDeclarativeSoundCone(this); } @@ -206,20 +227,6 @@ QDeclarativeSound::~QDeclarativeSound() { } -void QDeclarativeSound::classBegin() -{ - if (!parent() || !parent()->inherits("QDeclarativeAudioEngine")) { - qWarning("Sound must be defined inside AudioEngine!"); - return; - } -} - -void QDeclarativeSound::componentComplete() -{ - m_complete = true; - m_cone->componentComplete(); -} - /*! \qmlproperty enumeration QtAudioEngine::Sound::playType @@ -239,7 +246,7 @@ QDeclarativeSound::PlayType QDeclarativeSound::playType() const void QDeclarativeSound::setPlayType(PlayType playType) { - if (m_complete) { + if (m_engine) { qWarning("Sound: playType not changeable after initialization."); return; } @@ -258,7 +265,7 @@ QString QDeclarativeSound::category() const void QDeclarativeSound::setCategory(const QString& category) { - if (m_complete) { + if (m_engine) { qWarning("Sound: category not changeable after initialization."); return; } @@ -278,7 +285,7 @@ QString QDeclarativeSound::name() const void QDeclarativeSound::setName(const QString& name) { - if (m_complete) { + if (m_engine) { qWarning("Sound: category not changeable after initialization."); return; } @@ -322,13 +329,28 @@ QDeclarativePlayVariation* QDeclarativeSound::getVariation(int index) void QDeclarativeSound::setAttenuationModel(const QString &attenuationModel) { - if (m_complete) { + if (m_engine) { qWarning("Sound: attenuationModel not changeable after initialization."); return; } m_attenuationModel = attenuationModel; } +void QDeclarativeSound::setEngine(QDeclarativeAudioEngine *engine) +{ + if (m_engine) { + qWarning("Sound: engine not changeable after initialization."); + return; + } + m_cone->setEngine(engine); + m_engine = engine; +} + +QDeclarativeAudioEngine *QDeclarativeSound::engine() const +{ + return m_engine; +} + QDeclarativeSoundCone* QDeclarativeSound::cone() const { return m_cone; @@ -367,11 +389,42 @@ QList<QDeclarativePlayVariation*>& QDeclarativeSound::playlist() void QDeclarativeSound::appendFunction(QQmlListProperty<QDeclarativePlayVariation> *property, QDeclarativePlayVariation *value) { QDeclarativeSound *sound = static_cast<QDeclarativeSound*>(property->object); - if (sound->m_complete) { - qWarning("Sound: PlayVariation not addable after initialization."); + if (sound->m_engine) { return; } - sound->m_playlist.append(value); + sound->addPlayVariation(value); +} + +/*! + \qmlmethod QtAudioEngine::Sound::addPlayVariation(PlayVariation playVariation) + + Adds the given \a playVariation to sound. + This can be used when the PlayVariation is created dynamically: + + \qml + import QtAudioEngine 1.1 + + AudioEngine { + id: engine + + Component.onCompleted: { + var playVariation = Qt.createQmlObject('import QtAudioEngine 1.1; PlayVariation {}', engine); + playVariation.sample = "sample"; + playVariation.minPitch = 0.8 + playVariation.maxPitch = 1.1 + + var sound = Qt.createQmlObject('import QtAudioEngine 1.1; Sound {}', engine); + sound.name = "example"; + sound.addPlayVariation(playVariation); + engine.addSound(sound); + } + } + \endqml +*/ +void QDeclarativeSound::addPlayVariation(QDeclarativePlayVariation *value) +{ + m_playlist.append(value); + value->setEngine(m_engine); } /*! @@ -507,7 +560,7 @@ void QDeclarativeSound::play(const QVector3D& position, const QVector3D& velocit */ void QDeclarativeSound::play(const QVector3D& position, const QVector3D& velocity, const QVector3D& direction, qreal gain, qreal pitch) { - if (!m_complete) { + if (!m_engine) { qWarning() << "AudioEngine::play not ready!"; return; } @@ -546,8 +599,13 @@ QDeclarativeSoundInstance* QDeclarativeSound::newInstance() QDeclarativeSoundInstance* QDeclarativeSound::newInstance(bool managed) { + if (!m_engine) { + qWarning("engine attrbiute must be set for Sound object!"); + return NULL; + } + QDeclarativeSoundInstance *instance = - qobject_cast<QDeclarativeAudioEngine*>(this->parent())->newDeclarativeSoundInstance(managed); + m_engine->newDeclarativeSoundInstance(managed); instance->setSound(m_name); return instance; } diff --git a/src/imports/audioengine/qdeclarative_sound_p.h b/src/imports/audioengine/qdeclarative_sound_p.h index 14ebd1039..83b1eb2af 100644 --- a/src/imports/audioengine/qdeclarative_sound_p.h +++ b/src/imports/audioengine/qdeclarative_sound_p.h @@ -35,7 +35,6 @@ #define QDECLARATIVESOUND_P_H #include <QtQml/qqml.h> -#include <QtQml/qqmlcomponent.h> #include <QtCore/qlist.h> #include "qdeclarative_playvariation_p.h" @@ -44,6 +43,7 @@ QT_BEGIN_NAMESPACE class QDeclarativeAudioCategory; class QDeclarativeAttenuationModel; class QDeclarativeSoundInstance; +class QDeclarativeAudioEngine; class QDeclarativeSoundCone : public QObject { @@ -65,21 +65,21 @@ public: qreal outerGain() const; void setOuterGain(qreal outerGain); - void componentComplete(); + void setEngine(QDeclarativeAudioEngine *engine); private: Q_DISABLE_COPY(QDeclarativeSoundCone) qreal m_innerAngle; qreal m_outerAngle; qreal m_outerGain; + QDeclarativeAudioEngine *m_engine; }; -class QDeclarativeSound : public QObject, public QQmlParserStatus +class QDeclarativeSound : public QObject { friend class QDeclarativeSoundCone; Q_OBJECT - Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(QString name READ name WRITE setName) Q_PROPERTY(PlayType playType READ playType WRITE setPlayType) Q_PROPERTY(QString category READ category WRITE setCategory) @@ -99,9 +99,6 @@ public: QDeclarativeSound(QObject *parent = 0); ~QDeclarativeSound(); - void classBegin(); - void componentComplete(); - PlayType playType() const; void setPlayType(PlayType playType); @@ -114,6 +111,9 @@ public: QString attenuationModel() const; void setAttenuationModel(const QString &attenuationModel); + QDeclarativeAudioEngine *engine() const; + void setEngine(QDeclarativeAudioEngine *); + QDeclarativeSoundCone* cone() const; QDeclarativeAttenuationModel* attenuationModelObject() const; @@ -128,6 +128,8 @@ public: QQmlListProperty<QDeclarativePlayVariation> playVariationlist(); QList<QDeclarativePlayVariation*>& playlist(); + Q_INVOKABLE Q_REVISION(1) void addPlayVariation(QDeclarativePlayVariation*); + public Q_SLOTS: void play(); void play(qreal gain); @@ -147,7 +149,6 @@ private: Q_DISABLE_COPY(QDeclarativeSound) QDeclarativeSoundInstance* newInstance(bool managed); static void appendFunction(QQmlListProperty<QDeclarativePlayVariation> *property, QDeclarativePlayVariation *value); - bool m_complete; PlayType m_playType; QString m_name; QString m_category; @@ -157,6 +158,7 @@ private: QDeclarativeAttenuationModel *m_attenuationModelObject; QDeclarativeAudioCategory *m_categoryObject; + QDeclarativeAudioEngine *m_engine; }; QT_END_NAMESPACE diff --git a/src/imports/multimedia/Video.qml b/src/imports/multimedia/Video.qml index 2312924d9..ff7cadc8a 100644 --- a/src/imports/multimedia/Video.qml +++ b/src/imports/multimedia/Video.qml @@ -32,7 +32,7 @@ ****************************************************************************/ import QtQuick 2.0 -import QtMultimedia 5.0 +import QtMultimedia 5.6 /*! \qmltype Video @@ -274,6 +274,35 @@ Item { property alias position: player.position /*! + \qmlproperty enumeration Video::audioRole + + This property holds the role of the audio stream. It can be set to specify the type of audio + being played, allowing the system to make appropriate decisions when it comes to volume, + routing or post-processing. + + The audio role must be set before setting the source property. + + Supported values can be retrieved with supportedAudioRoles(). + + The value can be one of: + \list + \li MediaPlayer.UnknownRole - the role is unknown or undefined. + \li MediaPlayer.MusicRole - music. + \li MediaPlayer.VideoRole - soundtrack from a movie or a video. + \li MediaPlayer.VoiceCommunicationRole - voice communications, such as telephony. + \li MediaPlayer.AlarmRole - alarm. + \li MediaPlayer.NotificationRole - notification, such as an incoming e-mail or a chat request. + \li MediaPlayer.RingtoneRole - ringtone. + \li MediaPlayer.AccessibilityRole - for accessibility, such as with a screen reader. + \li MediaPlayer.SonificationRole - sonification, such as with user interface sounds. + \li MediaPlayer.GameRole - game audio. + \endlist + + \since 5.6 + */ + property alias audioRole: player.audioRole + + /*! \qmlproperty bool Video::seekable This property holds whether the playback position of the video can be @@ -287,10 +316,21 @@ Item { \qmlproperty url Video::source This property holds the source URL of the media. + + Setting the \l source property clears the current \l playlist, if any. */ property alias source: player.source /*! + \qmlproperty Playlist Video::playlist + + This property holds the playlist used by the media player. + + Setting the \l playlist property resets the \l source to an empty string. + */ + property alias playlist: player.playlist + + /*! \qmlproperty enumeration Video::status This property holds the status of media loading. It can be one of: @@ -407,4 +447,18 @@ Item { player.seek(offset); } + /*! + \qmlmethod list<int> Video::supportedAudioRoles() + + Returns a list of supported audio roles. + + If setting the audio role is not supported, an empty list is returned. + + \since 5.6 + \sa audioRole + */ + function supportedAudioRoles() { + return player.supportedAudioRoles(); + } + } diff --git a/src/imports/multimedia/multimedia.cpp b/src/imports/multimedia/multimedia.cpp index 652359f3b..4dcb7c61e 100644 --- a/src/imports/multimedia/multimedia.cpp +++ b/src/imports/multimedia/multimedia.cpp @@ -45,6 +45,7 @@ #include "qdeclarativeaudio_p.h" #include "qdeclarativeradio_p.h" #include "qdeclarativeradiodata_p.h" +#include "qdeclarativeplaylist_p.h" #include "qdeclarativecamera_p.h" #include "qdeclarativecamerapreviewprovider_p.h" #include "qdeclarativecameraexposure_p.h" @@ -114,6 +115,11 @@ public: qmlRegisterUncreatableType<QDeclarativeCameraImageProcessing, 1>(uri, 5, 5, "CameraImageProcessing", trUtf8("CameraImageProcessing is provided by Camera")); qmlRegisterRevision<QDeclarativeCamera, 2>(uri, 5, 5); + // 5.6 types + qmlRegisterRevision<QDeclarativeAudio, 1>(uri, 5, 6); + qmlRegisterType<QDeclarativePlaylist>(uri, 5, 6, "Playlist"); + qmlRegisterType<QDeclarativePlaylistItem>(uri, 5, 6, "PlaylistItem"); + qmlRegisterType<QDeclarativeMediaMetaData>(); qmlRegisterType<QAbstractVideoFilter>(); } diff --git a/src/imports/multimedia/multimedia.pro b/src/imports/multimedia/multimedia.pro index 71358caed..606fb3966 100644 --- a/src/imports/multimedia/multimedia.pro +++ b/src/imports/multimedia/multimedia.pro @@ -3,6 +3,7 @@ QT += qml quick network multimedia-private qtmultimediaquicktools-private HEADERS += \ qdeclarativeaudio_p.h \ qdeclarativemediametadata_p.h \ + qdeclarativeplaylist_p.h \ qdeclarativeradio_p.h \ qdeclarativeradiodata_p.h \ qdeclarativecamera_p.h \ @@ -20,6 +21,7 @@ HEADERS += \ SOURCES += \ multimedia.cpp \ qdeclarativeaudio.cpp \ + qdeclarativeplaylist.cpp \ qdeclarativeradio.cpp \ qdeclarativeradiodata.cpp \ qdeclarativecamera.cpp \ diff --git a/src/imports/multimedia/plugins.qmltypes b/src/imports/multimedia/plugins.qmltypes index d01c988ba..11d423f24 100644 --- a/src/imports/multimedia/plugins.qmltypes +++ b/src/imports/multimedia/plugins.qmltypes @@ -1,12 +1,13 @@ -import QtQuick.tooling 1.1 +import QtQuick.tooling 1.2 // This file describes the plugin-supplied types contained in the library. // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable QtMultimedia 5.5' +// 'qmlplugindump -nonrelocatable QtMultimedia 5.6' Module { + dependencies: ["QtQuick 2.0"] Component { name: "QAbstractVideoFilter" prototype: "QObject" @@ -195,6 +196,21 @@ Module { "ResourceMissing": 3 } } + Enum { + name: "AudioRole" + values: { + "UnknownRole": 0, + "AccessibilityRole": 7, + "AlarmRole": 4, + "GameRole": 9, + "MusicRole": 1, + "NotificationRole": 5, + "RingtoneRole": 6, + "SonificationRole": 8, + "VideoRole": 2, + "VoiceCommunicationRole": 3 + } + } Property { name: "source"; type: "QUrl" } Property { name: "loops"; type: "int" } Property { name: "playbackState"; type: "PlaybackState"; isReadonly: true } @@ -220,10 +236,12 @@ Module { } Property { name: "mediaObject"; type: "QObject"; isReadonly: true; isPointer: true } Property { name: "availability"; type: "Availability"; isReadonly: true } + Property { name: "audioRole"; revision: 1; type: "AudioRole" } Signal { name: "loopCountChanged" } Signal { name: "paused" } Signal { name: "stopped" } Signal { name: "playing" } + Signal { name: "audioRoleChanged"; revision: 1 } Signal { name: "availabilityChanged" Parameter { name: "availability"; type: "Availability" } @@ -240,6 +258,7 @@ Module { name: "seek" Parameter { name: "position"; type: "int" } } + Method { name: "supportedAudioRoles"; revision: 1; type: "QJSValue" } } Component { name: "QDeclarativeCamera" diff --git a/src/imports/multimedia/qdeclarativeaudio.cpp b/src/imports/multimedia/qdeclarativeaudio.cpp index 540ed6464..75ed28b80 100644 --- a/src/imports/multimedia/qdeclarativeaudio.cpp +++ b/src/imports/multimedia/qdeclarativeaudio.cpp @@ -42,9 +42,11 @@ #include <qmetadatareadercontrol.h> #include <qmediaavailabilitycontrol.h> +#include "qdeclarativeplaylist_p.h" #include "qdeclarativemediametadata_p.h" #include <QTimerEvent> +#include <QtQml/qqmlengine.h> QT_BEGIN_NAMESPACE @@ -94,16 +96,19 @@ void QDeclarativeAudio::_q_availabilityChanged(QMultimedia::AvailabilityStatus) QDeclarativeAudio::QDeclarativeAudio(QObject *parent) : QObject(parent) + , m_playlist(0) , m_autoPlay(false) , m_autoLoad(true) , m_loaded(false) , m_muted(false) , m_complete(false) + , m_emitPlaylistChanged(false) , m_loopCount(1) , m_runningCount(0) , m_position(0) , m_vol(1.0) , m_playbackRate(1.0) + , m_audioRole(UnknownRole) , m_playbackState(QMediaPlayer::StoppedState) , m_status(QMediaPlayer::NoMedia) , m_error(QMediaPlayer::ServiceMissingError) @@ -144,11 +149,122 @@ QDeclarativeAudio::Availability QDeclarativeAudio::availability() const return Availability(m_player->availability()); } +/*! + \qmlproperty enumeration QtMultimedia::Audio::audioRole + + This property holds the role of the audio stream. It can be set to specify the type of audio + being played, allowing the system to make appropriate decisions when it comes to volume, + routing or post-processing. + + The audio role must be set before setting the source property. + + Supported values can be retrieved with supportedAudioRoles(). + + The value can be one of: + \list + \li UnknownRole - the role is unknown or undefined. + \li MusicRole - music. + \li VideoRole - soundtrack from a movie or a video. + \li VoiceCommunicationRole - voice communications, such as telephony. + \li AlarmRole - alarm. + \li NotificationRole - notification, such as an incoming e-mail or a chat request. + \li RingtoneRole - ringtone. + \li AccessibilityRole - for accessibility, such as with a screen reader. + \li SonificationRole - sonification, such as with user interface sounds. + \li GameRole - game audio. + \endlist + + \since 5.6 +*/ +QDeclarativeAudio::AudioRole QDeclarativeAudio::audioRole() const +{ + return !m_complete ? m_audioRole : AudioRole(m_player->audioRole()); +} + +void QDeclarativeAudio::setAudioRole(QDeclarativeAudio::AudioRole audioRole) +{ + if (this->audioRole() == audioRole) + return; + + if (m_complete) { + m_player->setAudioRole(QAudio::Role(audioRole)); + } else { + m_audioRole = audioRole; + emit audioRoleChanged(); + } +} + +/*! + \qmlmethod list<int> QtMultimedia::Audio::supportedAudioRoles() + + Returns a list of supported audio roles. + + If setting the audio role is not supported, an empty list is returned. + + \since 5.6 + \sa audioRole +*/ +QJSValue QDeclarativeAudio::supportedAudioRoles() const +{ + QJSEngine *engine = qmlEngine(this); + + if (!m_complete) + return engine->newArray(); + + QList<QAudio::Role> roles = m_player->supportedAudioRoles(); + int size = roles.size(); + + QJSValue result = engine->newArray(size); + for (int i = 0; i < size; ++i) + result.setProperty(i, roles.at(i)); + + return result; +} + QUrl QDeclarativeAudio::source() const { return m_source; } +QDeclarativePlaylist *QDeclarativeAudio::playlist() const +{ + return m_playlist; +} + +void QDeclarativeAudio::setPlaylist(QDeclarativePlaylist *playlist) +{ + if (playlist == m_playlist && m_source.isEmpty()) + return; + + if (!m_source.isEmpty()) { + m_source.clear(); + emit sourceChanged(); + } + + m_playlist = playlist; + m_content = m_playlist ? + QMediaContent(m_playlist->mediaPlaylist(), QUrl(), false) : QMediaContent(); + m_loaded = false; + if (m_complete && (m_autoLoad || m_content.isNull() || m_autoPlay)) { + if (m_error != QMediaPlayer::ServiceMissingError && m_error != QMediaPlayer::NoError) { + m_error = QMediaPlayer::NoError; + m_errorString = QString(); + + emit errorChanged(); + } + + if (!playlist) + m_emitPlaylistChanged = true; + m_player->setMedia(m_content, 0); + m_loaded = true; + } + else + emit playlistChanged(); + + if (m_autoPlay) + m_player->play(); +} + bool QDeclarativeAudio::autoPlay() const { return m_autoPlay; @@ -166,9 +282,14 @@ void QDeclarativeAudio::setAutoPlay(bool autoplay) void QDeclarativeAudio::setSource(const QUrl &url) { - if (url == m_source) + if (url == m_source && m_playlist == NULL) return; + if (m_playlist) { + m_playlist = NULL; + emit playlistChanged(); + } + m_source = url; m_content = m_source.isEmpty() ? QMediaContent() : m_source; m_loaded = false; @@ -425,6 +546,16 @@ void QDeclarativeAudio::seek(int position) \qmlproperty url QtMultimedia::Audio::source This property holds the source URL of the media. + + Setting the \l source property clears the current \l playlist, if any. +*/ + +/*! + \qmlproperty Playlist QtMultimedia::Audio::playlist + + This property holds the playlist used by the media player. + + Setting the \l playlist property resets the \l source to an empty string. */ /*! @@ -650,8 +781,8 @@ void QDeclarativeAudio::classBegin() this, SLOT(_q_statusChanged())); connect(m_player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), this, SLOT(_q_statusChanged())); - connect(m_player, SIGNAL(mediaChanged(QMediaContent)), - this, SIGNAL(sourceChanged())); + connect(m_player, SIGNAL(mediaChanged(const QMediaContent&)), + this, SLOT(_q_mediaChanged(const QMediaContent&))); connect(m_player, SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged())); connect(m_player, SIGNAL(positionChanged(qint64)), @@ -672,6 +803,8 @@ void QDeclarativeAudio::classBegin() this, SIGNAL(hasAudioChanged())); connect(m_player, SIGNAL(videoAvailableChanged(bool)), this, SIGNAL(hasVideoChanged())); + connect(m_player, SIGNAL(audioRoleChanged(QAudio::Role)), + this, SIGNAL(audioRoleChanged())); m_error = m_player->availability() == QMultimedia::ServiceMissing ? QMediaPlayer::ServiceMissingError : QMediaPlayer::NoError; @@ -694,6 +827,8 @@ void QDeclarativeAudio::componentComplete() m_player->setMuted(m_muted); if (!qFuzzyCompare(m_playbackRate, qreal(1.0))) m_player->setPlaybackRate(m_playbackRate); + if (m_audioRole != UnknownRole) + m_player->setAudioRole(QAudio::Role(m_audioRole)); if (!m_content.isNull() && (m_autoLoad || m_autoPlay)) { m_player->setMedia(m_content, 0); @@ -752,6 +887,16 @@ void QDeclarativeAudio::_q_statusChanged() } } +void QDeclarativeAudio::_q_mediaChanged(const QMediaContent &media) +{ + if (!media.playlist() && !m_emitPlaylistChanged) { + emit sourceChanged(); + } else { + m_emitPlaylistChanged = false; + emit playlistChanged(); + } +} + /*! \qmlproperty string QtMultimedia::Audio::errorString @@ -965,6 +1110,45 @@ void QDeclarativeAudio::_q_statusChanged() */ /*! + \qmlproperty enumeration QtMultimedia::MediaPlayer::audioRole + + This property holds the role of the audio stream. It can be set to specify the type of audio + being played, allowing the system to make appropriate decisions when it comes to volume, + routing or post-processing. + + The audio role must be set before setting the source property. + + Supported values can be retrieved with supportedAudioRoles(). + + The value can be one of: + \list + \li UnknownRole - the role is unknown or undefined. + \li MusicRole - music. + \li VideoRole - soundtrack from a movie or a video. + \li VoiceCommunicationRole - voice communications, such as telephony. + \li AlarmRole - alarm. + \li NotificationRole - notification, such as an incoming e-mail or a chat request. + \li RingtoneRole - ringtone. + \li AccessibilityRole - for accessibility, such as with a screen reader. + \li SonificationRole - sonification, such as with user interface sounds. + \li GameRole - game audio. + \endlist + + \since 5.6 +*/ + +/*! + \qmlmethod list<int> QtMultimedia::MediaPlayer::supportedAudioRoles() + + Returns a list of supported audio roles. + + If setting the audio role is not supported, an empty list is returned. + + \since 5.6 + \sa audioRole +*/ + +/*! \qmlmethod QtMultimedia::MediaPlayer::play() Starts playback of the media. @@ -992,6 +1176,16 @@ void QDeclarativeAudio::_q_statusChanged() \qmlproperty url QtMultimedia::MediaPlayer::source This property holds the source URL of the media. + + Setting the \l source property clears the current \l playlist, if any. +*/ + +/*! + \qmlproperty Playlist QtMultimedia::MediaPlayer::playlist + + This property holds the playlist used by the media player. + + Setting the \l playlist property resets the \l source to an empty string. */ /*! diff --git a/src/imports/multimedia/qdeclarativeaudio_p.h b/src/imports/multimedia/qdeclarativeaudio_p.h index d4840f207..d8363969d 100644 --- a/src/imports/multimedia/qdeclarativeaudio_p.h +++ b/src/imports/multimedia/qdeclarativeaudio_p.h @@ -48,6 +48,7 @@ #include <QtCore/qbasictimer.h> #include <QtQml/qqmlparserstatus.h> #include <QtQml/qqml.h> +#include <QtQml/qjsvalue.h> #include <qmediaplayer.h> @@ -58,6 +59,7 @@ class QMediaPlayerControl; class QMediaService; class QMediaServiceProvider; class QMetaDataReaderControl; +class QDeclarativePlaylist; class QDeclarativeMediaBaseAnimation; class QDeclarativeMediaMetaData; class QMediaAvailabilityControl; @@ -66,6 +68,7 @@ class QDeclarativeAudio : public QObject, public QQmlParserStatus { Q_OBJECT Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(QDeclarativePlaylist *playlist READ playlist WRITE setPlaylist NOTIFY playlistChanged REVISION 1) Q_PROPERTY(int loops READ loopCount WRITE setLoopCount NOTIFY loopCountChanged) Q_PROPERTY(PlaybackState playbackState READ playbackState NOTIFY playbackStateChanged) Q_PROPERTY(bool autoPlay READ autoPlay WRITE setAutoPlay NOTIFY autoPlayChanged) @@ -85,11 +88,13 @@ class QDeclarativeAudio : public QObject, public QQmlParserStatus Q_PROPERTY(QDeclarativeMediaMetaData *metaData READ metaData CONSTANT) Q_PROPERTY(QObject *mediaObject READ mediaObject NOTIFY mediaObjectChanged SCRIPTABLE false DESIGNABLE false) Q_PROPERTY(Availability availability READ availability NOTIFY availabilityChanged) + Q_PROPERTY(AudioRole audioRole READ audioRole WRITE setAudioRole NOTIFY audioRoleChanged REVISION 1) Q_ENUMS(Status) Q_ENUMS(Error) Q_ENUMS(Loop) Q_ENUMS(PlaybackState) Q_ENUMS(Availability) + Q_ENUMS(AudioRole) Q_INTERFACES(QQmlParserStatus) public: enum Status @@ -134,6 +139,19 @@ public: ResourceMissing = QMultimedia::ResourceError }; + enum AudioRole { + UnknownRole = QAudio::UnknownRole, + AccessibilityRole = QAudio::AccessibilityRole, + AlarmRole = QAudio::AlarmRole, + GameRole = QAudio::GameRole, + MusicRole = QAudio::MusicRole, + NotificationRole = QAudio::NotificationRole, + RingtoneRole = QAudio::RingtoneRole, + SonificationRole = QAudio::SonificationRole, + VideoRole = QAudio::VideoRole, + VoiceCommunicationRole = QAudio::VoiceCommunicationRole + }; + QDeclarativeAudio(QObject *parent = 0); ~QDeclarativeAudio(); @@ -152,9 +170,15 @@ public: Availability availability() const; + AudioRole audioRole() const; + void setAudioRole(AudioRole audioRole); + QUrl source() const; void setSource(const QUrl &url); + QDeclarativePlaylist *playlist() const; + void setPlaylist(QDeclarativePlaylist *playlist); + int loopCount() const; void setLoopCount(int loopCount); @@ -191,7 +215,11 @@ public Q_SLOTS: void stop(); void seek(int position); + Q_REVISION(1) QJSValue supportedAudioRoles() const; + Q_SIGNALS: + Q_REVISION(1) void playlistChanged(); + void sourceChanged(); void autoLoadChanged(); void loopCountChanged(); @@ -218,6 +246,8 @@ Q_SIGNALS: void seekableChanged(); void playbackRateChanged(); + Q_REVISION(1) void audioRoleChanged(); + void availabilityChanged(Availability availability); void errorChanged(); @@ -229,20 +259,24 @@ private Q_SLOTS: void _q_error(QMediaPlayer::Error); void _q_availabilityChanged(QMultimedia::AvailabilityStatus); void _q_statusChanged(); + void _q_mediaChanged(const QMediaContent&); private: Q_DISABLE_COPY(QDeclarativeAudio) + QDeclarativePlaylist *m_playlist; bool m_autoPlay; bool m_autoLoad; bool m_loaded; bool m_muted; bool m_complete; + bool m_emitPlaylistChanged; int m_loopCount; int m_runningCount; int m_position; qreal m_vol; qreal m_playbackRate; + AudioRole m_audioRole; QMediaPlayer::State m_playbackState; QMediaPlayer::MediaStatus m_status; diff --git a/src/imports/multimedia/qdeclarativeplaylist.cpp b/src/imports/multimedia/qdeclarativeplaylist.cpp new file mode 100644 index 000000000..bb785aa98 --- /dev/null +++ b/src/imports/multimedia/qdeclarativeplaylist.cpp @@ -0,0 +1,583 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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 "qdeclarativeplaylist_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype PlaylistItem + \instantiates QDeclarativePlaylistItem + \since 5.6 + + \inqmlmodule QtMultimedia + \ingroup multimedia_qml + \ingroup multimedia_audio_qml + \ingroup multimedia_video_qml + \brief Defines an item in a Playlist. + + \sa Playlist +*/ + +/*! + \qmlproperty url QtMultimedia::PlaylistItem::source + + This property holds the source URL of the item. + + \sa Playlist +*/ +QDeclarativePlaylistItem::QDeclarativePlaylistItem(QObject *parent) + : QObject(parent) +{ +} + +QUrl QDeclarativePlaylistItem::source() const +{ + return m_source; +} + +void QDeclarativePlaylistItem::setSource(const QUrl &source) +{ + m_source = source; +} + +/*! + \qmltype Playlist + \instantiates QDeclarativePlaylist + \since 5.6 + \brief For specifying a list of media to be played. + + \inqmlmodule QtMultimedia + \ingroup multimedia_qml + \ingroup multimedia_audio_qml + \ingroup multimedia_video_qml + + The Playlist type provides a way to play a list of media with the MediaPlayer, Audio and Video + types. It can be used as a data source for view elements (such as ListView) and other elements + that interact with model data (such as Repeater). When used as a data model, each playlist + item's source URL can be accessed using the \c source role. + + \qml + import QtQuick 2.0 + import QtMultimedia 5.6 + + Item { + width: 400; + height: 300; + + Audio { + id: player; + playlist: Playlist { + id: playlist + PlaylistItem { source: "song1.ogg"; } + PlaylistItem { source: "song2.ogg"; } + PlaylistItem { source: "song3.ogg"; } + } + } + + ListView { + model: playlist; + delegate: Text { + font.pixelSize: 16; + text: source; + } + } + + MouseArea { + anchors.fill: parent; + onPressed: { + if (player.playbackState != Audio.PlayingState) { + player.play(); + } else { + player.pause(); + } + } + } + } + \endqml + + \sa MediaPlayer, Audio, Video +*/ + +void QDeclarativePlaylist::_q_mediaAboutToBeInserted(int start, int end) +{ + emit itemAboutToBeInserted(start, end); + + beginInsertRows(QModelIndex(), start, end); +} + +void QDeclarativePlaylist::_q_mediaInserted(int start, int end) +{ + endInsertRows(); + + emit itemCountChanged(); + emit itemInserted(start, end); +} + +void QDeclarativePlaylist::_q_mediaAboutToBeRemoved(int start, int end) +{ + emit itemAboutToBeRemoved(start, end); + + beginRemoveRows(QModelIndex(), start, end); +} + +void QDeclarativePlaylist::_q_mediaRemoved(int start, int end) +{ + endRemoveRows(); + + emit itemCountChanged(); + emit itemRemoved(start, end); +} + +void QDeclarativePlaylist::_q_mediaChanged(int start, int end) +{ + emit dataChanged(createIndex(start, 0), createIndex(end, 0)); + emit itemChanged(start, end); +} + +void QDeclarativePlaylist::_q_loadFailed() +{ + m_error = m_playlist->error(); + m_errorString = m_playlist->errorString(); + + emit error(Error(m_error), m_errorString); + emit errorChanged(); + emit loadFailed(); +} + +QDeclarativePlaylist::QDeclarativePlaylist(QObject *parent) + : QAbstractListModel(parent) + , m_playlist(0) + , m_error(QMediaPlaylist::NoError) + , m_readOnly(false) +{ +} + +QDeclarativePlaylist::~QDeclarativePlaylist() +{ + delete m_playlist; +} + +/*! + \qmlproperty enumeration QtMultimedia::Playlist::playbackMode + + This property holds the order in which items in the playlist are played. + + \table + \header \li Value \li Description + \row \li CurrentItemOnce + \li The current item is played only once. + \row \li CurrentItemInLoop + \li The current item is played repeatedly in a loop. + \row \li Sequential + \li Playback starts from the current and moves through each successive item until the last + is reached and then stops. The next item is a null item when the last one is currently + playing. + \row \li Loop + \li Playback restarts at the first item after the last has finished playing. + \row \li Random + \li Play items in random order. + \endtable + */ +QDeclarativePlaylist::PlaybackMode QDeclarativePlaylist::playbackMode() const +{ + return PlaybackMode(m_playlist->playbackMode()); +} + +void QDeclarativePlaylist::setPlaybackMode(PlaybackMode mode) +{ + if (playbackMode() == mode) + return; + + m_playlist->setPlaybackMode(QMediaPlaylist::PlaybackMode(mode)); +} + +/*! + \qmlproperty url QtMultimedia::Playlist::currentItemsource + + This property holds the source URL of the current item in the playlist. + */ +QUrl QDeclarativePlaylist::currentItemSource() const +{ + return m_playlist->currentMedia().canonicalUrl(); +} + +/*! + \qmlproperty int QtMultimedia::Playlist::currentIndex + + This property holds the position of the current item in the playlist. + */ +int QDeclarativePlaylist::currentIndex() const +{ + return m_playlist->currentIndex(); +} + +void QDeclarativePlaylist::setCurrentIndex(int index) +{ + if (currentIndex() == index) + return; + + m_playlist->setCurrentIndex(index); +} + +/*! + \qmlproperty int QtMultimedia::Playlist::itemCount + + This property holds the number of items in the playlist. + */ +int QDeclarativePlaylist::itemCount() const +{ + return m_playlist->mediaCount(); +} + +/*! + \qmlproperty bool QtMultimedia::Playlist::readOnly + + This property indicates if the playlist can be modified. + */ +bool QDeclarativePlaylist::readOnly() const +{ + // There's no signal to tell whether or not the read only state changed, so we consider it fixed + // after its initial retrieval in componentComplete(). + return m_readOnly; +} + +/*! + \qmlproperty enumeration QtMultimedia::Playlist::error + + This property holds the error condition of the playlist. + + \table + \header \li Value \li Description + \row \li NoError + \li No errors + \row \li FormatError + \li Format error. + \row \li FormatNotSupportedError + \li Format not supported. + \row \li NetworkError + \li Network error. + \row \li AccessDeniedError + \li Access denied error. + \endtable + */ +QDeclarativePlaylist::Error QDeclarativePlaylist::error() const +{ + return Error(m_error); +} + +/*! + \qmlproperty string QtMultimedia::Playlist::errorString + + This property holds a string describing the current error condition of the playlist. +*/ +QString QDeclarativePlaylist::errorString() const +{ + return m_errorString; +} + +/*! + \qmlmethod url QtMultimedia::Playlist::itemSource(index) + + Returns the source URL of the item at the given \a index in the playlist. +*/ +QUrl QDeclarativePlaylist::itemSource(int index) +{ + return m_playlist->media(index).canonicalUrl(); +} + +/*! + \qmlmethod int QtMultimedia::Playlist::nextIndex(steps) + + Returns the index of the item in the playlist which would be current after calling next() + \a steps times. + + Returned value depends on the size of the playlist, the current position and the playback mode. + + \sa playbackMode, previousIndex() +*/ +int QDeclarativePlaylist::nextIndex(int steps) +{ + return m_playlist->nextIndex(steps); +} + +/*! + \qmlmethod int QtMultimedia::Playlist::previousIndex(steps) + + Returns the index of the item in the playlist which would be current after calling previous() + \a steps times. + + Returned value depends on the size of the playlist, the current position and the playback mode. + + \sa playbackMode, nextIndex() +*/ +int QDeclarativePlaylist::previousIndex(int steps) +{ + return m_playlist->previousIndex(steps); +} + +/*! + \qmlmethod QtMultimedia::Playlist::next() + + Advances to the next item in the playlist. +*/ +void QDeclarativePlaylist::next() +{ + m_playlist->next(); +} + +/*! + \qmlmethod QtMultimedia::Playlist::previous() + + Returns to the previous item in the playlist. +*/ +void QDeclarativePlaylist::previous() +{ + m_playlist->previous(); +} + +/*! + \qmlmethod QtMultimedia::Playlist::shuffle() + + Shuffles items in the playlist. +*/ +void QDeclarativePlaylist::shuffle() +{ + m_playlist->shuffle(); +} + +/*! + \qmlmethod QtMultimedia::Playlist::load(location, format) + + Loads a playlist from the given \a location. If \a format is specified, it is used, otherwise + the format is guessed from the location name and the data. + + New items are appended to the playlist. + + \c onloaded() is emitted if the playlist loads successfully, otherwise \c onLoadFailed() is + emitted with \l error and \l errorString defined accordingly. +*/ +void QDeclarativePlaylist::load(const QUrl &location, const QString &format) +{ + m_error = QMediaPlaylist::NoError; + m_errorString = QString(); + emit errorChanged(); + m_playlist->load(location, format.toLatin1().constData()); +} + +/*! + \qmlmethod bool QtMultimedia::Playlist::save(location, format) + + Saves the playlist to the given \a location. If \a format is specified, it is used, otherwise + the format is guessed from the location name. + + Returns true if the playlist is saved successfully. +*/ +bool QDeclarativePlaylist::save(const QUrl &location, const QString &format) +{ + return m_playlist->save(location, format.toLatin1().constData()); +} + +/*! + \qmlmethod bool QtMultimedia::Playlist::addItem(source) + + Appends the \a source URL to the playlist. + + Returns true if the \a source is added successfully. +*/ +bool QDeclarativePlaylist::addItem(const QUrl &source) +{ + return m_playlist->addMedia(QMediaContent(source)); +} + +/*! + \qmlmethod bool QtMultimedia::Playlist::insertItem(index, source) + + Inserts the \a source URL to the playlist at the given \a index. + + Returns true if the \a source is added successfully. +*/ +bool QDeclarativePlaylist::insertItem(int index, const QUrl &source) +{ + return m_playlist->insertMedia(index, QMediaContent(source)); +} + +/*! + \qmlmethod bool QtMultimedia::Playlist::removeItem(index) + + Removed the item at the given \a index from the playlist. + + Returns true if the \a source is removed successfully. +*/ +bool QDeclarativePlaylist::removeItem(int index) +{ + return m_playlist->removeMedia(index); +} + +/*! + \qmlmethod bool QtMultimedia::Playlist::clear() + + Removes all the items from the playlist. + + Returns true if the operation is successful. +*/ +bool QDeclarativePlaylist::clear() +{ + return m_playlist->clear(); +} + +int QDeclarativePlaylist::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return m_playlist->mediaCount(); +} + +QVariant QDeclarativePlaylist::data(const QModelIndex &index, int role) const +{ + Q_UNUSED(role); + + if (!index.isValid()) + return QVariant(); + + return m_playlist->media(index.row()).canonicalUrl(); +} + +QHash<int, QByteArray> QDeclarativePlaylist::roleNames() const +{ + QHash<int, QByteArray> roleNames; + roleNames[SourceRole] = "source"; + return roleNames; +} + +void QDeclarativePlaylist::classBegin() +{ + m_playlist = new QMediaPlaylist(this); + + connect(m_playlist, SIGNAL(currentIndexChanged(int)), + this, SIGNAL(currentIndexChanged())); + connect(m_playlist, SIGNAL(playbackModeChanged(QMediaPlaylist::PlaybackMode)), + this, SIGNAL(playbackModeChanged())); + connect(m_playlist, SIGNAL(currentMediaChanged(QMediaContent)), + this, SIGNAL(currentItemSourceChanged())); + connect(m_playlist, SIGNAL(mediaAboutToBeInserted(int,int)), + this, SLOT(_q_mediaAboutToBeInserted(int,int))); + connect(m_playlist, SIGNAL(mediaInserted(int,int)), + this, SLOT(_q_mediaInserted(int,int))); + connect(m_playlist, SIGNAL(mediaAboutToBeRemoved(int,int)), + this, SLOT(_q_mediaAboutToBeRemoved(int,int))); + connect(m_playlist, SIGNAL(mediaRemoved(int,int)), + this, SLOT(_q_mediaRemoved(int,int))); + connect(m_playlist, SIGNAL(mediaChanged(int,int)), + this, SLOT(_q_mediaChanged(int,int))); + connect(m_playlist, SIGNAL(loaded()), + this, SIGNAL(loaded())); + connect(m_playlist, SIGNAL(loadFailed()), + this, SLOT(_q_loadFailed())); + + if (m_playlist->isReadOnly()) { + m_readOnly = true; + emit readOnlyChanged(); + } +} + +void QDeclarativePlaylist::componentComplete() +{ +} + +/*! + \qmlsignal QtMultimedia::Audio::itemAboutToBeInserted(start, end) + + This signal is emitted when items are to be inserted into the playlist at \a start and ending at + \a end. + + The corresponding handler is \c onItemAboutToBeInserted. +*/ + +/*! + \qmlsignal QtMultimedia::Audio::itemInserted(start, end) + + This signal is emitted after items have been inserted into the playlist. The new items are those + between \a start and \a end inclusive. + + The corresponding handler is \c onItemInserted. +*/ + +/*! + \qmlsignal QtMultimedia::Audio::itemAboutToBeRemoved(start, end) + + This signal emitted when items are to be deleted from the playlist at \a start and ending at + \a end. + + The corresponding handler is \c onItemAboutToBeRemoved. +*/ + +/*! + \qmlsignal QtMultimedia::Audio::itemRemoved(start, end) + + This signal is emitted after items have been removed from the playlist. The removed items are + those between \a start and \a end inclusive. + + The corresponding handler is \c onMediaRemoved. +*/ + +/*! + \qmlsignal QtMultimedia::Audio::itemChanged(start, end) + + This signal is emitted after items have been changed in the playlist between \a start and + \a end positions inclusive. + + The corresponding handler is \c onItemChanged. +*/ + +/*! + \qmlsignal QtMultimedia::Audio::loaded() + + This signal is emitted when the playlist loading succeeded. + + The corresponding handler is \c onLoaded. +*/ + +/*! + \qmlsignal QtMultimedia::Audio::loadFailed() + + This signal is emitted when the playlist loading failed. \l error and \l errorString can be + checked for more information on the failure. + + The corresponding handler is \c onLoadFailed. +*/ + +QT_END_NAMESPACE + +#include "moc_qdeclarativeplaylist_p.cpp" diff --git a/src/imports/multimedia/qdeclarativeplaylist_p.h b/src/imports/multimedia/qdeclarativeplaylist_p.h new file mode 100644 index 000000000..fd94135e6 --- /dev/null +++ b/src/imports/multimedia/qdeclarativeplaylist_p.h @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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 QDECLARATIVEPLAYLIST_P_H +#define QDECLARATIVEPLAYLIST_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QAbstractListModel> +#include <QtQml/qqmlparserstatus.h> +#include <QtQml/qqml.h> + +#include <qmediaplaylist.h> + +QT_BEGIN_NAMESPACE + +class QDeclarativePlaylistItem : public QObject +{ + Q_OBJECT + Q_PROPERTY(QUrl source READ source WRITE setSource) + +public: + QDeclarativePlaylistItem(QObject *parent = 0); + + QUrl source() const; + void setSource(const QUrl &source); + +private: + QUrl m_source; +}; + +class QDeclarativePlaylist : public QAbstractListModel, public QQmlParserStatus +{ + Q_OBJECT + Q_PROPERTY(PlaybackMode playbackMode READ playbackMode WRITE setPlaybackMode NOTIFY playbackModeChanged) + Q_PROPERTY(QUrl currentItemSource READ currentItemSource NOTIFY currentItemSourceChanged) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + Q_PROPERTY(int itemCount READ itemCount NOTIFY itemCountChanged) + Q_PROPERTY(bool readOnly READ readOnly NOTIFY readOnlyChanged) + Q_PROPERTY(Error error READ error NOTIFY errorChanged) + Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged) + Q_PROPERTY(QQmlListProperty<QDeclarativePlaylistItem> items READ items DESIGNABLE false) + Q_ENUMS(PlaybackMode) + Q_ENUMS(Error) + Q_INTERFACES(QQmlParserStatus) + Q_CLASSINFO("DefaultProperty", "items") + +public: + enum PlaybackMode + { + CurrentItemOnce = QMediaPlaylist::CurrentItemOnce, + CurrentItemInLoop = QMediaPlaylist::CurrentItemInLoop, + Sequential = QMediaPlaylist::Sequential, + Loop = QMediaPlaylist::Loop, + Random = QMediaPlaylist::Random + }; + enum Error + { + NoError = QMediaPlaylist::NoError, + FormatError = QMediaPlaylist::FormatError, + FormatNotSupportedError = QMediaPlaylist::FormatNotSupportedError, + NetworkError = QMediaPlaylist::NetworkError, + AccessDeniedError = QMediaPlaylist::AccessDeniedError + }; + enum Roles + { + SourceRole = Qt::UserRole + 1 + }; + + QDeclarativePlaylist(QObject *parent = 0); + ~QDeclarativePlaylist(); + + PlaybackMode playbackMode() const; + void setPlaybackMode(PlaybackMode playbackMode); + QUrl currentItemSource() const; + int currentIndex() const; + void setCurrentIndex(int currentIndex); + int itemCount() const; + bool readOnly() const; + Error error() const; + QString errorString() const; + QMediaPlaylist *mediaPlaylist() const { return m_playlist; } + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QHash<int, QByteArray> roleNames() const; + + void classBegin(); + void componentComplete(); + + QQmlListProperty<QDeclarativePlaylistItem> items() { + return QQmlListProperty<QDeclarativePlaylistItem>( + this, 0, item_append, item_count, 0, item_clear); + } + static void item_append(QQmlListProperty<QDeclarativePlaylistItem> *list, + QDeclarativePlaylistItem* item) { + static_cast<QDeclarativePlaylist*>(list->object)->addItem(item->source()); + } + static int item_count(QQmlListProperty<QDeclarativePlaylistItem> *list) { + return static_cast<QDeclarativePlaylist*>(list->object)->itemCount(); + } + static void item_clear(QQmlListProperty<QDeclarativePlaylistItem> *list) { + static_cast<QDeclarativePlaylist*>(list->object)->clear(); + } + +public Q_SLOTS: + QUrl itemSource(int index); + int nextIndex(int steps = 1); + int previousIndex(int steps = 1); + void next(); + void previous(); + void shuffle(); + void load(const QUrl &location, const QString &format = QString()); + bool save(const QUrl &location, const QString &format = QString()); + bool addItem(const QUrl &source); + bool insertItem(int index, const QUrl &source); + bool removeItem(int index); + bool clear(); + +Q_SIGNALS: + void playbackModeChanged(); + void currentItemSourceChanged(); + void currentIndexChanged(); + void itemCountChanged(); + void readOnlyChanged(); + void errorChanged(); + + void itemAboutToBeInserted(int start, int end); + void itemInserted(int start, int end); + void itemAboutToBeRemoved(int start, int end); + void itemRemoved(int start, int end); + void itemChanged(int start, int end); + void loaded(); + void loadFailed(); + + void error(QDeclarativePlaylist::Error error, const QString &errorString); + +private Q_SLOTS: + void _q_mediaAboutToBeInserted(int start, int end); + void _q_mediaInserted(int start, int end); + void _q_mediaAboutToBeRemoved(int start, int end); + void _q_mediaRemoved(int start, int end); + void _q_mediaChanged(int start, int end); + void _q_loadFailed(); + +private: + Q_DISABLE_COPY(QDeclarativePlaylist) + + QMediaPlaylist *m_playlist; + QString m_errorString; + QMediaPlaylist::Error m_error; + bool m_readOnly; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QT_PREPEND_NAMESPACE(QDeclarativePlaylistItem)) +QML_DECLARE_TYPE(QT_PREPEND_NAMESPACE(QDeclarativePlaylist)) + +#endif diff --git a/src/multimedia/audio/qaudio.cpp b/src/multimedia/audio/qaudio.cpp index 8b452a118..72c24ad9f 100644 --- a/src/multimedia/audio/qaudio.cpp +++ b/src/multimedia/audio/qaudio.cpp @@ -42,6 +42,7 @@ static void qRegisterAudioMetaTypes() qRegisterMetaType<QAudio::Error>(); qRegisterMetaType<QAudio::State>(); qRegisterMetaType<QAudio::Mode>(); + qRegisterMetaType<QAudio::Role>(); } Q_CONSTRUCTOR_FUNCTION(qRegisterAudioMetaTypes) @@ -83,6 +84,26 @@ Q_CONSTRUCTOR_FUNCTION(qRegisterAudioMetaTypes) \value AudioInput audio input device */ +/*! + \enum QAudio::Role + + This enum describes the role of an audio stream. + + \value UnknownRole The role is unknown or undefined + \value MusicRole Music + \value VideoRole Soundtrack from a movie or a video + \value VoiceCommunicationRole Voice communications, such as telephony + \value AlarmRole Alarm + \value NotificationRole Notification, such as an incoming e-mail or a chat request + \value RingtoneRole Ringtone + \value AccessibilityRole For accessibility, such as with a screen reader + \value SonificationRole Sonification, such as with user interface sounds + \value GameRole Game audio + + \since 5.6 + \sa QMediaPlayer::setAudioRole() +*/ + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, QAudio::Error error) { @@ -143,6 +164,45 @@ QDebug operator<<(QDebug dbg, QAudio::Mode mode) } return dbg; } + +QDebug operator<<(QDebug dbg, QAudio::Role role) +{ + QDebugStateSaver saver(dbg); + dbg.nospace(); + switch (role) { + case QAudio::UnknownRole: + dbg << "UnknownRole"; + break; + case QAudio::AccessibilityRole: + dbg << "AccessibilityRole"; + break; + case QAudio::AlarmRole: + dbg << "AlarmRole"; + break; + case QAudio::GameRole: + dbg << "GameRole"; + break; + case QAudio::MusicRole: + dbg << "MusicRole"; + break; + case QAudio::NotificationRole: + dbg << "NotificationRole"; + break; + case QAudio::RingtoneRole: + dbg << "RingtoneRole"; + break; + case QAudio::SonificationRole: + dbg << "SonificationRole"; + break; + case QAudio::VideoRole: + dbg << "VideoRole"; + break; + case QAudio::VoiceCommunicationRole: + dbg << "VoiceCommunicationRole"; + break; + } + return dbg; +} #endif diff --git a/src/multimedia/audio/qaudio.h b/src/multimedia/audio/qaudio.h index 63953145c..2ee66bcc1 100644 --- a/src/multimedia/audio/qaudio.h +++ b/src/multimedia/audio/qaudio.h @@ -51,12 +51,26 @@ namespace QAudio enum Error { NoError, OpenError, IOError, UnderrunError, FatalError }; enum State { ActiveState, SuspendedState, StoppedState, IdleState }; enum Mode { AudioInput, AudioOutput }; + + enum Role { + UnknownRole, + MusicRole, + VideoRole, + VoiceCommunicationRole, + AlarmRole, + NotificationRole, + RingtoneRole, + AccessibilityRole, + SonificationRole, + GameRole + }; } #ifndef QT_NO_DEBUG_STREAM Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QAudio::Error error); Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QAudio::State state); Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QAudio::Mode mode); +Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QAudio::Role role); #endif QT_END_NAMESPACE @@ -64,5 +78,6 @@ QT_END_NAMESPACE Q_DECLARE_METATYPE(QAudio::Error) Q_DECLARE_METATYPE(QAudio::State) Q_DECLARE_METATYPE(QAudio::Mode) +Q_DECLARE_METATYPE(QAudio::Role) #endif // QAUDIO_H diff --git a/src/multimedia/controls/controls.pri b/src/multimedia/controls/controls.pri index b19532fdc..7ab2e51c4 100644 --- a/src/multimedia/controls/controls.pri +++ b/src/multimedia/controls/controls.pri @@ -36,7 +36,8 @@ PUBLIC_HEADERS += \ controls/qvideowindowcontrol.h \ controls/qmediaaudioprobecontrol.h \ controls/qmediavideoprobecontrol.h \ - controls/qmediaavailabilitycontrol.h + controls/qmediaavailabilitycontrol.h \ + controls/qaudiorolecontrol.h PRIVATE_HEADERS += \ controls/qmediaplaylistcontrol_p.h \ @@ -79,5 +80,6 @@ SOURCES += \ controls/qaudioencodersettingscontrol.cpp \ controls/qaudioinputselectorcontrol.cpp \ controls/qaudiooutputselectorcontrol.cpp \ - controls/qvideodeviceselectorcontrol.cpp + controls/qvideodeviceselectorcontrol.cpp \ + controls/qaudiorolecontrol.cpp diff --git a/src/multimedia/controls/qaudiorolecontrol.cpp b/src/multimedia/controls/qaudiorolecontrol.cpp new file mode 100644 index 000000000..62696f013 --- /dev/null +++ b/src/multimedia/controls/qaudiorolecontrol.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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 "qmediacontrol_p.h" +#include "qaudiorolecontrol.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QAudioRoleControl + \inmodule QtMultimedia + \ingroup multimedia_control + \since 5.6 + + \brief The QAudioRoleControl class provides control over the audio role of a media object. + + If a QMediaService supports audio roles it will implement QAudioRoleControl. + + The functionality provided by this control is exposed to application code through the + QMediaPlayer class. + + The interface name of QAudioRoleControl is \c org.qt-project.qt.audiorolecontrol/5.6 as + defined in QAudioRoleControl_iid. + + \sa QMediaService::requestControl(), QMediaPlayer +*/ + +/*! + \macro QAudioRoleControl_iid + + \c org.qt-project.qt.audiorolecontrol/5.6 + + Defines the interface name of the QAudioRoleControl class. + + \relates QAudioRoleControl +*/ + +/*! + Construct a QAudioRoleControl with the given \a parent. +*/ +QAudioRoleControl::QAudioRoleControl(QObject *parent) + : QMediaControl(*new QMediaControlPrivate, parent) +{ + +} + +/*! + Destroys the audio role control. +*/ +QAudioRoleControl::~QAudioRoleControl() +{ + +} + +/*! + \fn QAudio::Role QAudioRoleControl::audioRole() const + + Returns the audio role of the media played by the media service. +*/ + +/*! + \fn void QAudioRoleControl::setAudioRole(QAudio::Role role) + + Sets the audio \a role of the media played by the media service. +*/ + +/*! + \fn QList<QAudio::Role> QAudioRoleControl::supportedAudioRoles() const + + Returns a list of audio roles that the media service supports. +*/ + +/*! + \fn void QAudioRoleControl::audioRoleChanged(QAudio::Role role) + + Signal emitted when the audio \a role has changed. + */ + + +#include "moc_qaudiorolecontrol.cpp" +QT_END_NAMESPACE diff --git a/src/multimedia/controls/qaudiorolecontrol.h b/src/multimedia/controls/qaudiorolecontrol.h new file mode 100644 index 000000000..983b2aed0 --- /dev/null +++ b/src/multimedia/controls/qaudiorolecontrol.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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 QAUDIOROLECONTROL_H +#define QAUDIOROLECONTROL_H + +#include <QtMultimedia/qmediacontrol.h> +#include <QtMultimedia/qaudio.h> + +QT_BEGIN_NAMESPACE + +// Class forward declaration required for QDoc bug +class QString; + +class Q_MULTIMEDIA_EXPORT QAudioRoleControl : public QMediaControl +{ + Q_OBJECT + +public: + virtual ~QAudioRoleControl(); + + virtual QAudio::Role audioRole() const = 0; + virtual void setAudioRole(QAudio::Role role) = 0; + + virtual QList<QAudio::Role> supportedAudioRoles() const = 0; + +Q_SIGNALS: + void audioRoleChanged(QAudio::Role role); + +protected: + explicit QAudioRoleControl(QObject *parent = 0); +}; + +#define QAudioRoleControl_iid "org.qt-project.qt.audiorolecontrol/5.6" +Q_MEDIA_DECLARE_CONTROL(QAudioRoleControl, QAudioRoleControl_iid) + +QT_END_NAMESPACE + +#endif // QAUDIOROLECONTROL_H diff --git a/src/multimedia/controls/qmediaplayercontrol.cpp b/src/multimedia/controls/qmediaplayercontrol.cpp index 9ea6fde82..0c891a1d4 100644 --- a/src/multimedia/controls/qmediaplayercontrol.cpp +++ b/src/multimedia/controls/qmediaplayercontrol.cpp @@ -111,6 +111,7 @@ QMediaPlayerControl::QMediaPlayerControl(QObject *parent): Returns the status of the current media. */ + /*! \fn QMediaPlayerControl::mediaStatusChanged(QMediaPlayer::MediaStatus status) diff --git a/src/multimedia/doc/src/qtmultimedia-index.qdoc b/src/multimedia/doc/src/qtmultimedia-index.qdoc index 65fcb210b..0e2750872 100644 --- a/src/multimedia/doc/src/qtmultimedia-index.qdoc +++ b/src/multimedia/doc/src/qtmultimedia-index.qdoc @@ -79,6 +79,9 @@ \li \l {QtMultimedia::Audio}{Audio} \li Add audio playback functionality to a scene \row + \li \l {QtMultimedia::Playlist}{Playlist} + \li For specifying a list of media to be played. + \row \li \l {QtMultimedia::Camera}{Camera} \li Access camera viewfinder frames \row @@ -117,6 +120,9 @@ \li QMediaPlayer \li Playback media from a source. \row + \li QMediaPlaylist + \li List of media to be played. + \row \li QRadioTuner \li Access radio device. \row diff --git a/src/multimedia/playback/playlistfileparser.cpp b/src/multimedia/playback/playlistfileparser.cpp index edaf98c52..c7553e16b 100644 --- a/src/multimedia/playback/playlistfileparser.cpp +++ b/src/multimedia/playback/playlistfileparser.cpp @@ -290,7 +290,8 @@ void QPlaylistFileParserPrivate::processLine(int startIndex, int length) switch (m_type) { case QPlaylistFileParser::UNKNOWN: - emit q->error(QPlaylistFileParser::FormatError, QString(QObject::tr("%1 playlist type is unknown")).arg(m_root.toString())); + emit q->error(QPlaylistFileParser::FormatError, + QPlaylistFileParser::tr("%1 playlist type is unknown").arg(m_root.toString())); q->stop(); return; case QPlaylistFileParser::M3U: @@ -350,7 +351,7 @@ void QPlaylistFileParserPrivate::_q_handleData() if (m_buffer.length() - processedBytes >= LINE_LIMIT) { qWarning() << "error parsing playlist["<< m_root << "] with line content >= 4096 bytes."; - emit q->error(QPlaylistFileParser::FormatError, QObject::tr("invalid line in playlist file")); + emit q->error(QPlaylistFileParser::FormatError, QPlaylistFileParser::tr("invalid line in playlist file")); q->stop(); return; } @@ -398,7 +399,7 @@ void QPlaylistFileParserPrivate::_q_handleParserFinished() Q_Q(QPlaylistFileParser); bool isParserValid = (m_currentParser != 0); if (!isParserValid) - emit q->error(QPlaylistFileParser::FormatNotSupportedError, QObject::tr("Empty file provided")); + emit q->error(QPlaylistFileParser::FormatNotSupportedError, QPlaylistFileParser::tr("Empty file provided")); q->stop(); diff --git a/src/multimedia/playback/qmediaplayer.cpp b/src/multimedia/playback/qmediaplayer.cpp index 51be94c43..c1636e8e8 100644 --- a/src/multimedia/playback/qmediaplayer.cpp +++ b/src/multimedia/playback/qmediaplayer.cpp @@ -42,6 +42,7 @@ #include <qmediaplaylistcontrol_p.h> #include <qmediaplaylistsourcecontrol_p.h> #include <qmedianetworkaccesscontrol.h> +#include <qaudiorolecontrol.h> #include <QtCore/qcoreevent.h> #include <QtCore/qmetaobject.h> @@ -104,6 +105,7 @@ public: QMediaPlayerPrivate() : provider(0) , control(0) + , audioRoleControl(0) , state(QMediaPlayer::StoppedState) , status(QMediaPlayer::UnknownMediaStatus) , error(QMediaPlayer::NoError) @@ -116,6 +118,7 @@ public: QMediaServiceProvider *provider; QMediaPlayerControl* control; + QAudioRoleControl *audioRoleControl; QMediaPlayer::State state; QMediaPlayer::MediaStatus status; QMediaPlayer::Error error; @@ -598,6 +601,12 @@ QMediaPlayer::QMediaPlayer(QObject *parent, QMediaPlayer::Flags flags): addPropertyWatch("bufferStatus"); d->hasStreamPlaybackFeature = d->provider->supportedFeatures(d->service).testFlag(QMediaServiceProviderHint::StreamPlayback); + + d->audioRoleControl = qobject_cast<QAudioRoleControl*>(d->service->requestControl(QAudioRoleControl_iid)); + if (d->audioRoleControl) { + connect(d->audioRoleControl, &QAudioRoleControl::audioRoleChanged, + this, &QMediaPlayer::audioRoleChanged); + } } if (d->networkAccessControl != 0) { connect(d->networkAccessControl, SIGNAL(configurationChanged(QNetworkConfiguration)), @@ -620,6 +629,8 @@ QMediaPlayer::~QMediaPlayer() if (d->service) { if (d->control) d->service->releaseControl(d->control); + if (d->audioRoleControl) + d->service->releaseControl(d->audioRoleControl); d->provider->releaseService(d->service); } @@ -1113,6 +1124,41 @@ QMultimedia::AvailabilityStatus QMediaPlayer::availability() const return QMediaObject::availability(); } +QAudio::Role QMediaPlayer::audioRole() const +{ + Q_D(const QMediaPlayer); + + if (d->audioRoleControl != NULL) + return d->audioRoleControl->audioRole(); + + return QAudio::UnknownRole; +} + +void QMediaPlayer::setAudioRole(QAudio::Role audioRole) +{ + Q_D(QMediaPlayer); + + if (d->audioRoleControl) + d->audioRoleControl->setAudioRole(audioRole); +} + +/*! + Returns a list of supported audio roles. + + If setting the audio role is not supported, an empty list is returned. + + \since 5.6 + \sa audioRole +*/ +QList<QAudio::Role> QMediaPlayer::supportedAudioRoles() const +{ + Q_D(const QMediaPlayer); + + if (d->audioRoleControl) + return d->audioRoleControl->supportedAudioRoles(); + + return QList<QAudio::Role>(); +} // Enums /*! @@ -1213,6 +1259,14 @@ QMultimedia::AvailabilityStatus QMediaPlayer::availability() const Signals the \a seekable status of the player object has changed. */ +/*! + \fn void QMediaPlayer::audioRoleChanged(QAudio::Role role) + + Signals that the audio \a role of the media player has changed. + + \since 5.6 +*/ + // Properties /*! \property QMediaPlayer::state @@ -1381,6 +1435,19 @@ QMultimedia::AvailabilityStatus QMediaPlayer::availability() const */ /*! + \property QMediaPlayer::audioRole + \brief the role of the audio stream played by the media player. + + It can be set to specify the type of audio being played, allowing the system to make + appropriate decisions when it comes to volume, routing or post-processing. + + The audio role must be set before calling setMedia(). + + \since 5.6 + \sa supportedAudioRoles() +*/ + +/*! \fn void QMediaPlayer::durationChanged(qint64 duration) Signal the duration of the content has changed to \a duration, expressed in milliseconds. diff --git a/src/multimedia/playback/qmediaplayer.h b/src/multimedia/playback/qmediaplayer.h index 735f11130..437bf58ad 100644 --- a/src/multimedia/playback/qmediaplayer.h +++ b/src/multimedia/playback/qmediaplayer.h @@ -37,6 +37,7 @@ #include <QtMultimedia/qmediaobject.h> #include <QtMultimedia/qmediacontent.h> #include <QtMultimedia/qmediaenumdebug.h> +#include <QtMultimedia/qaudio.h> #include <QtNetwork/qnetworkconfiguration.h> @@ -66,6 +67,7 @@ class Q_MULTIMEDIA_EXPORT QMediaPlayer : public QMediaObject Q_PROPERTY(qreal playbackRate READ playbackRate WRITE setPlaybackRate NOTIFY playbackRateChanged) Q_PROPERTY(State state READ state NOTIFY stateChanged) Q_PROPERTY(MediaStatus mediaStatus READ mediaStatus NOTIFY mediaStatusChanged) + Q_PROPERTY(QAudio::Role audioRole READ audioRole WRITE setAudioRole) Q_PROPERTY(QString error READ errorString) Q_ENUMS(State) Q_ENUMS(MediaStatus) @@ -151,6 +153,10 @@ public: QMultimedia::AvailabilityStatus availability() const; + QAudio::Role audioRole() const; + void setAudioRole(QAudio::Role audioRole); + QList<QAudio::Role> supportedAudioRoles() const; + public Q_SLOTS: void play(); void pause(); @@ -187,6 +193,8 @@ Q_SIGNALS: void seekableChanged(bool seekable); void playbackRateChanged(qreal rate); + void audioRoleChanged(QAudio::Role role); + void error(QMediaPlayer::Error error); void networkConfigurationChanged(const QNetworkConfiguration &configuration); diff --git a/src/plugins/android/src/wrappers/jni/jni.pri b/src/plugins/android/src/wrappers/jni/jni.pri index ba2ad0801..e96baff1c 100644 --- a/src/plugins/android/src/wrappers/jni/jni.pri +++ b/src/plugins/android/src/wrappers/jni/jni.pri @@ -1,4 +1,4 @@ -QT += platformsupport-private +QT += core-private INCLUDEPATH += $$PWD diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp index 7132c29ab..91bfd67f3 100644 --- a/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp +++ b/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp @@ -252,7 +252,7 @@ void QGstreamerRecorderControl::applySettings() bool found = false; foreach (const QString &audioCandidate, audioCandidates) { QSet<QString> audioTypes = audioEncodeControl->supportedStreamTypes(audioCandidate); - if (!audioTypes.intersect(supportedTypes).isEmpty()) { + if (audioTypes.intersects(supportedTypes)) { found = true; audioCodec = audioCandidate; break; @@ -266,7 +266,7 @@ void QGstreamerRecorderControl::applySettings() bool found = false; foreach (const QString &videoCandidate, videoCandidates) { QSet<QString> videoTypes = videoEncodeControl->supportedStreamTypes(videoCandidate); - if (!videoTypes.intersect(supportedTypes).isEmpty()) { + if (videoTypes.intersects(supportedTypes)) { found = true; videoCodec = videoCandidate; break; diff --git a/src/plugins/pulseaudio/qaudioinput_pulse.h b/src/plugins/pulseaudio/qaudioinput_pulse.h index b7f7e7f10..6963a2419 100644 --- a/src/plugins/pulseaudio/qaudioinput_pulse.h +++ b/src/plugins/pulseaudio/qaudioinput_pulse.h @@ -116,8 +116,6 @@ private: void close(); void setPulseVolume(); - static QMap<void *, QPulseAudioInput*> s_inputsMap; - static void sourceInfoCallback(pa_context *c, const pa_source_info *i, int eol, void *userdata); static void inputVolumeCallback(pa_context *context, int success, void *userdata); diff --git a/src/plugins/winrt/qwinrtcameracontrol.cpp b/src/plugins/winrt/qwinrtcameracontrol.cpp index 9013416fe..6abf3a052 100644 --- a/src/plugins/winrt/qwinrtcameracontrol.cpp +++ b/src/plugins/winrt/qwinrtcameracontrol.cpp @@ -39,6 +39,8 @@ #include "qwinrtvideodeviceselectorcontrol.h" #include "qwinrtcameraimagecapturecontrol.h" #include "qwinrtimageencodercontrol.h" +#include "qwinrtcamerafocuscontrol.h" +#include "qwinrtcameralockscontrol.h" #include <QtCore/qfunctions_winrt.h> #include <QtCore/QCoreApplication> @@ -51,6 +53,7 @@ #include <windows.devices.enumeration.h> #include <windows.media.capture.h> #include <windows.storage.streams.h> +#include <windows.media.devices.h> #ifdef Q_OS_WINPHONE #include <Windows.Security.ExchangeActiveSyncProvisioning.h> @@ -76,10 +79,125 @@ QT_BEGIN_NAMESPACE RETURN_VOID_IF_FAILED(msg); \ } -inline uint qHash (const QSize &key) { - return key.width() * key.height(); +#define FOCUS_RECT_SIZE 0.01f +#define FOCUS_RECT_HALF_SIZE 0.005f // FOCUS_RECT_SIZE / 2 +#define FOCUS_RECT_BOUNDARY 1.0f +#define FOCUS_RECT_POSITION_MIN 0.0f +#define FOCUS_RECT_POSITION_MAX 0.995f // FOCUS_RECT_BOUNDARY - FOCUS_RECT_HALF_SIZE +#define ASPECTRATIO_EPSILON 0.01f + +HRESULT getMediaStreamResolutions(IMediaDeviceController *device, + MediaStreamType type, + IVectorView<IMediaEncodingProperties *> **propertiesList, + QVector<QSize> *resolutions) +{ + HRESULT hr; + hr = device->GetAvailableMediaStreamProperties(type, propertiesList); + Q_ASSERT_SUCCEEDED(hr); + quint32 listSize; + hr = (*propertiesList)->get_Size(&listSize); + Q_ASSERT_SUCCEEDED(hr); + resolutions->reserve(listSize); + for (quint32 index = 0; index < listSize; ++index) { + ComPtr<IMediaEncodingProperties> properties; + hr = (*propertiesList)->GetAt(index, &properties); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVideoEncodingProperties> videoProperties; + hr = properties.As(&videoProperties); + Q_ASSERT_SUCCEEDED(hr); + UINT32 width, height; + hr = videoProperties->get_Width(&width); + Q_ASSERT_SUCCEEDED(hr); + hr = videoProperties->get_Height(&height); + Q_ASSERT_SUCCEEDED(hr); + resolutions->append(QSize(width, height)); + } + return resolutions->isEmpty() ? MF_E_INVALID_FORMAT : hr; } +template<typename T, size_t typeSize> struct CustomPropertyValue; + +inline static ComPtr<IPropertyValueStatics> propertyValueStatics() +{ + ComPtr<IPropertyValueStatics> valueStatics; + GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), &valueStatics); + return valueStatics; +} + +template <typename T> +struct CustomPropertyValue<T, 4> +{ + static ComPtr<IReference<T>> create(T value) + { + ComPtr<IInspectable> propertyValueObject; + HRESULT hr = propertyValueStatics()->CreateUInt32(value, &propertyValueObject); + ComPtr<IReference<UINT32>> uint32Object; + Q_ASSERT_SUCCEEDED(hr); + hr = propertyValueObject.As(&uint32Object); + Q_ASSERT_SUCCEEDED(hr); + return reinterpret_cast<IReference<T> *>(uint32Object.Get()); + } +}; + +template <typename T> +struct CustomPropertyValue<T, 8> +{ + static ComPtr<IReference<T>> create(T value) + { + ComPtr<IInspectable> propertyValueObject; + HRESULT hr = propertyValueStatics()->CreateUInt64(value, &propertyValueObject); + ComPtr<IReference<UINT64>> uint64Object; + Q_ASSERT_SUCCEEDED(hr); + hr = propertyValueObject.As(&uint64Object); + Q_ASSERT_SUCCEEDED(hr); + return reinterpret_cast<IReference<T> *>(uint64Object.Get()); + } +}; + +// Required camera point focus +class WindowsRegionOfInterestIterableIterator : public RuntimeClass<IIterator<RegionOfInterest *>> +{ +public: + explicit WindowsRegionOfInterestIterableIterator(const ComPtr<IRegionOfInterest> &item) + { + regionOfInterest = item; + } + HRESULT __stdcall get_Current(IRegionOfInterest **current) + { + *current = regionOfInterest.Detach(); + return S_OK; + } + HRESULT __stdcall get_HasCurrent(boolean *hasCurrent) + { + *hasCurrent = true; + return S_OK; + } + HRESULT __stdcall MoveNext(boolean *hasCurrent) + { + *hasCurrent = false; + return S_OK; + } +private: + ComPtr<IRegionOfInterest> regionOfInterest; +}; + +class WindowsRegionOfInterestIterable : public RuntimeClass<IIterable<RegionOfInterest *>> +{ +public: + explicit WindowsRegionOfInterestIterable(const ComPtr<IRegionOfInterest> &item) + { + regionOfInterest = item; + } + HRESULT __stdcall First(IIterator<RegionOfInterest *> **first) + { + ComPtr<WindowsRegionOfInterestIterableIterator> iterator = Make<WindowsRegionOfInterestIterableIterator>(regionOfInterest); + *first = iterator.Detach(); + return S_OK; + } +private: + ComPtr<IRegionOfInterest> regionOfInterest; +}; + class CriticalSectionLocker { public: @@ -454,12 +572,15 @@ public: ComPtr<IMediaEncodingProfile> encodingProfile; ComPtr<MediaSink> mediaSink; + ComPtr<IFocusControl> focusControl; + ComPtr<IRegionsOfInterestControl> regionsOfInterestControl; - QSize size; QPointer<QWinRTCameraVideoRendererControl> videoRenderer; QPointer<QWinRTVideoDeviceSelectorControl> videoDeviceSelector; QPointer<QWinRTCameraImageCaptureControl> imageCaptureControl; QPointer<QWinRTImageEncoderControl> imageEncoderControl; + QPointer<QWinRTCameraFocusControl> cameraFocusControl; + QPointer<QWinRTCameraLocksControl> cameraLocksControl; }; QWinRTCameraControl::QWinRTCameraControl(QObject *parent) @@ -472,12 +593,14 @@ QWinRTCameraControl::QWinRTCameraControl(QObject *parent) d->captureMode = QCamera::CaptureStillImage; d->captureFailedCookie.value = 0; d->recordLimitationCookie.value = 0; - d->videoRenderer = new QWinRTCameraVideoRendererControl(d->size, this); + d->videoRenderer = new QWinRTCameraVideoRendererControl(QSize(), this); connect(d->videoRenderer, &QWinRTCameraVideoRendererControl::bufferRequested, this, &QWinRTCameraControl::onBufferRequested); d->videoDeviceSelector = new QWinRTVideoDeviceSelectorControl(this); d->imageCaptureControl = new QWinRTCameraImageCaptureControl(this); d->imageEncoderControl = new QWinRTImageEncoderControl(this); + d->cameraFocusControl = new QWinRTCameraFocusControl(this); + d->cameraLocksControl = new QWinRTCameraLocksControl(this); } QWinRTCameraControl::~QWinRTCameraControl() @@ -524,10 +647,15 @@ void QWinRTCameraControl::setState(QCamera::State state) return; } + QCameraFocus::FocusModes focusMode = d->cameraFocusControl->focusMode(); + if (setFocus(focusMode) && focusMode == QCameraFocus::ContinuousFocus) + focus(); + d->state = QCamera::ActiveState; emit stateChanged(d->state); d->status = QCamera::ActiveStatus; emit statusChanged(d->status); + d->mediaSink->RequestSample(); break; } case QCamera::LoadedState: { @@ -668,6 +796,18 @@ QImageEncoderControl *QWinRTCameraControl::imageEncoderControl() const return d->imageEncoderControl; } +QCameraFocusControl *QWinRTCameraControl::cameraFocusControl() const +{ + Q_D(const QWinRTCameraControl); + return d->cameraFocusControl; +} + +QCameraLocksControl *QWinRTCameraControl::cameraLocksControl() const +{ + Q_D(const QWinRTCameraControl); + return d->cameraLocksControl; +} + IMediaCapture *QWinRTCameraControl::handle() const { Q_D(const QWinRTCameraControl); @@ -749,76 +889,96 @@ HRESULT QWinRTCameraControl::initialize() ComPtr<IVideoDeviceController> videoDeviceController; hr = d->capture->get_VideoDeviceController(&videoDeviceController); + ComPtr<IAdvancedVideoCaptureDeviceController2> advancedVideoDeviceController; + hr = videoDeviceController.As(&advancedVideoDeviceController); Q_ASSERT_SUCCEEDED(hr); - ComPtr<IMediaDeviceController> deviceController; - hr = videoDeviceController.As(&deviceController); - Q_ASSERT_SUCCEEDED(hr); - ComPtr<IVectorView<IMediaEncodingProperties *>> encodingPropertiesList; - MediaStreamType mediaStreamType; - switch (d->captureMode) { - default: - case QCamera::CaptureViewfinder: - mediaStreamType = MediaStreamType_VideoPreview; - break; - case QCamera::CaptureStillImage: - mediaStreamType = MediaStreamType_Photo; - break; - case QCamera::CaptureVideo: - mediaStreamType = MediaStreamType_VideoRecord; - break; - } - hr = deviceController->GetAvailableMediaStreamProperties(mediaStreamType, &encodingPropertiesList); + hr = advancedVideoDeviceController->get_FocusControl(&d->focusControl); Q_ASSERT_SUCCEEDED(hr); - d->size = QSize(); - quint32 encodingPropertiesListSize; - hr = encodingPropertiesList->get_Size(&encodingPropertiesListSize); + boolean isFocusSupported; + hr = d->focusControl->get_Supported(&isFocusSupported); Q_ASSERT_SUCCEEDED(hr); - QHash<QSize, ComPtr<IVideoEncodingProperties>> videoEncodingPropertiesList; - int pixelCount = 0; - for (quint32 i = 0; i < encodingPropertiesListSize; ++i) { - ComPtr<IMediaEncodingProperties> properties; - hr = encodingPropertiesList->GetAt(i, &properties); - Q_ASSERT_SUCCEEDED(hr); - ComPtr<IVideoEncodingProperties> videoProperties; - hr = properties.As(&videoProperties); - Q_ASSERT_SUCCEEDED(hr); - UINT32 width, height; - hr = videoProperties->get_Width(&width); + if (isFocusSupported) { + hr = advancedVideoDeviceController->get_RegionsOfInterestControl(&d->regionsOfInterestControl); Q_ASSERT_SUCCEEDED(hr); - hr = videoProperties->get_Height(&height); + hr = initializeFocus(); Q_ASSERT_SUCCEEDED(hr); - if (d->captureMode != QCamera::CaptureStillImage && int(width * height) > pixelCount) { - d->size = QSize(width, height);// Choose the Highest-quality format - pixelCount = d->size.width() * d->size.height(); - } - videoEncodingPropertiesList.insert(QSize(width, height), videoProperties); + } else { + d->cameraFocusControl->setSupportedFocusMode(0); + d->cameraFocusControl->setSupportedFocusPointMode(QSet<QCameraFocus::FocusPointMode>()); } + d->cameraLocksControl->initialize(); - if (videoEncodingPropertiesList.isEmpty()) { - hr = MF_E_INVALID_FORMAT; - RETURN_HR_IF_FAILED("Failed to find a suitable video format"); - } + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IMediaDeviceController> deviceController; + hr = videoDeviceController.As(&deviceController); + Q_ASSERT_SUCCEEDED(hr); - if (d->captureMode == QCamera::CaptureStillImage) { - d->imageEncoderControl->setSupportedResolutionsList(videoEncodingPropertiesList.keys()); - d->size = d->imageEncoderControl->imageSettings().resolution(); + // Get preview stream properties. + ComPtr<IVectorView<IMediaEncodingProperties *>> previewPropertiesList; + QVector<QSize> previewResolutions; + hr = getMediaStreamResolutions(deviceController.Get(), + MediaStreamType_VideoPreview, + &previewPropertiesList, + &previewResolutions); + RETURN_HR_IF_FAILED("Failed to find a suitable video format"); + + MediaStreamType mediaStreamType = + d->captureMode == QCamera::CaptureVideo ? MediaStreamType_VideoRecord : MediaStreamType_Photo; + + // Get capture stream properties. + ComPtr<IVectorView<IMediaEncodingProperties *>> capturePropertiesList; + QVector<QSize> captureResolutions; + hr = getMediaStreamResolutions(deviceController.Get(), + mediaStreamType, + &capturePropertiesList, + &captureResolutions); + RETURN_HR_IF_FAILED("Failed to find a suitable video format"); + + // Set capture resolutions. + d->imageEncoderControl->setSupportedResolutionsList(captureResolutions.toList()); + const QSize captureResolution = d->imageEncoderControl->imageSettings().resolution(); + const quint32 captureResolutionIndex = captureResolutions.indexOf(captureResolution); + ComPtr<IMediaEncodingProperties> captureProperties; + hr = capturePropertiesList->GetAt(captureResolutionIndex, &captureProperties); + Q_ASSERT_SUCCEEDED(hr); + hr = deviceController->SetMediaStreamPropertiesAsync(mediaStreamType, captureProperties.Get(), &op); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(op); + Q_ASSERT_SUCCEEDED(hr); + + // Set preview resolution. + QVector<QSize> filtered; + const float captureAspectRatio = float(captureResolution.width()) / captureResolution.height(); + foreach (const QSize &resolution, previewResolutions) { + const float aspectRatio = float(resolution.width()) / resolution.height(); + if (qAbs(aspectRatio - captureAspectRatio) <= ASPECTRATIO_EPSILON) + filtered.append(resolution); } + qSort(filtered.begin(), + filtered.end(), + [](QSize size1, QSize size2) { return size1.width() * size1.height() < size2.width() * size2.height(); }); + const QSize &viewfinderResolution = filtered.first(); + const quint32 viewfinderResolutionIndex = previewResolutions.indexOf(viewfinderResolution); hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile).Get(), &d->encodingProfile); Q_ASSERT_SUCCEEDED(hr); - - const ComPtr<IVideoEncodingProperties> videoEncodingProperties = videoEncodingPropertiesList[d->size]; - if (!videoEncodingProperties) { - hr = MF_E_INVALID_FORMAT; - RETURN_HR_IF_FAILED("Failed to find a suitable video format properties"); - } - - hr = d->encodingProfile->put_Video(videoEncodingProperties.Get()); + ComPtr<IMediaEncodingProperties> previewProperties; + hr = previewPropertiesList->GetAt(viewfinderResolutionIndex, &previewProperties); Q_ASSERT_SUCCEEDED(hr); + hr = deviceController->SetMediaStreamPropertiesAsync(MediaStreamType_VideoPreview, previewProperties.Get(), &op); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(op); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVideoEncodingProperties> videoPreviewProperties; + hr = previewProperties.As(&videoPreviewProperties); + Q_ASSERT_SUCCEEDED(hr); + hr = d->encodingProfile->put_Video(videoPreviewProperties.Get()); + Q_ASSERT_SUCCEEDED(hr); + if (d->videoRenderer) - d->videoRenderer->setSize(d->size); + d->videoRenderer->setSize(viewfinderResolution); if (SUCCEEDED(hr) && d->state != QCamera::LoadedState) { d->state = QCamera::LoadedState; @@ -831,6 +991,278 @@ HRESULT QWinRTCameraControl::initialize() return hr; } +#ifdef Q_OS_WINPHONE + +HRESULT QWinRTCameraControl::initializeFocus() +{ + Q_D(QWinRTCameraControl); + ComPtr<IFocusControl2> focusControl2; + HRESULT hr = d->focusControl.As(&focusControl2); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVectorView<enum FocusMode>> focusModes; + hr = focusControl2->get_SupportedFocusModes(&focusModes); + if (FAILED(hr)) { + d->cameraFocusControl->setSupportedFocusMode(0); + d->cameraFocusControl->setSupportedFocusPointMode(QSet<QCameraFocus::FocusPointMode>()); + qErrnoWarning(hr, "Failed to get camera supported focus mode list"); + return hr; + } + quint32 size; + hr = focusModes->get_Size(&size); + Q_ASSERT_SUCCEEDED(hr); + QCameraFocus::FocusModes supportedModeFlag = 0; + for (quint32 i = 0; i < size; ++i) { + FocusMode mode; + hr = focusModes->GetAt(i, &mode); + Q_ASSERT_SUCCEEDED(hr); + switch (mode) { + case FocusMode_Continuous: + supportedModeFlag |= QCameraFocus::ContinuousFocus; + break; + case FocusMode_Single: + supportedModeFlag |= QCameraFocus::AutoFocus; + break; + default: + break; + } + } + + ComPtr<IVectorView<enum AutoFocusRange>> focusRange; + hr = focusControl2->get_SupportedFocusRanges(&focusRange); + if (FAILED(hr)) { + qErrnoWarning(hr, "Failed to get camera supported focus range list"); + } else { + hr = focusRange->get_Size(&size); + Q_ASSERT_SUCCEEDED(hr); + for (quint32 i = 0; i < size; ++i) { + AutoFocusRange range; + hr = focusRange->GetAt(i, &range); + Q_ASSERT_SUCCEEDED(hr); + switch (range) { + case AutoFocusRange_Macro: + supportedModeFlag |= QCameraFocus::MacroFocus; + break; + case AutoFocusRange_FullRange: + supportedModeFlag |= QCameraFocus::InfinityFocus; + break; + default: + break; + } + } + } + d->cameraFocusControl->setSupportedFocusMode(supportedModeFlag); + if (!d->regionsOfInterestControl) { + d->cameraFocusControl->setSupportedFocusPointMode(QSet<QCameraFocus::FocusPointMode>()); + return S_OK; + } + boolean isRegionsfocusSupported = false; + hr = d->regionsOfInterestControl->get_AutoFocusSupported(&isRegionsfocusSupported); + Q_ASSERT_SUCCEEDED(hr); + UINT32 maxRegions; + hr = d->regionsOfInterestControl->get_MaxRegions(&maxRegions); + Q_ASSERT_SUCCEEDED(hr); + if (!isRegionsfocusSupported || maxRegions == 0) { + d->cameraFocusControl->setSupportedFocusPointMode(QSet<QCameraFocus::FocusPointMode>()); + return S_OK; + } + QSet<QCameraFocus::FocusPointMode> supportedFocusPointModes; + supportedFocusPointModes << QCameraFocus::FocusPointCustom + << QCameraFocus::FocusPointCenter + << QCameraFocus::FocusPointAuto; + d->cameraFocusControl->setSupportedFocusPointMode(supportedFocusPointModes); + return S_OK; +} + +bool QWinRTCameraControl::setFocus(QCameraFocus::FocusModes modes) +{ + Q_D(QWinRTCameraControl); + if (d->status == QCamera::UnloadedStatus) + return false; + + ComPtr<IFocusSettings> focusSettings; + ComPtr<IInspectable> focusSettingsObject; + HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_Devices_FocusSettings).Get(), &focusSettingsObject); + Q_ASSERT_SUCCEEDED(hr); + hr = focusSettingsObject.As(&focusSettings); + Q_ASSERT_SUCCEEDED(hr); + FocusMode mode; + if (modes.testFlag(QCameraFocus::ContinuousFocus)) { + mode = FocusMode_Continuous; + } else if (modes.testFlag(QCameraFocus::AutoFocus) + || modes.testFlag(QCameraFocus::MacroFocus) + || modes.testFlag(QCameraFocus::InfinityFocus)) { + // The Macro and infinity focus modes are only supported in auto focus mode on WinRT. + // QML camera focus doesn't support combined focus flags settings. In the case of macro + // and infinity Focus modes, the auto focus setting is applied. + mode = FocusMode_Single; + } else { + emit error(QCamera::NotSupportedFeatureError, QStringLiteral("Unsupported camera focus modes.")); + return false; + } + hr = focusSettings->put_Mode(mode); + Q_ASSERT_SUCCEEDED(hr); + AutoFocusRange range = AutoFocusRange_Normal; + if (modes.testFlag(QCameraFocus::MacroFocus)) + range = AutoFocusRange_Macro; + else if (modes.testFlag(QCameraFocus::InfinityFocus)) + range = AutoFocusRange_FullRange; + hr = focusSettings->put_AutoFocusRange(range); + Q_ASSERT_SUCCEEDED(hr); + hr = focusSettings->put_WaitForFocus(true); + Q_ASSERT_SUCCEEDED(hr); + hr = focusSettings->put_DisableDriverFallback(false); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IFocusControl2> focusControl2; + hr = d->focusControl.As(&focusControl2); + Q_ASSERT_SUCCEEDED(hr); + hr = focusControl2->Configure(focusSettings.Get()); + RETURN_FALSE_IF_FAILED("Failed to configure camera focus control"); + return true; +} + +bool QWinRTCameraControl::setFocusPoint(const QPointF &focusPoint) +{ + Q_D(QWinRTCameraControl); + if (focusPoint.x() < FOCUS_RECT_POSITION_MIN || focusPoint.x() > FOCUS_RECT_BOUNDARY) { + emit error(QCamera::CameraError, QStringLiteral("Focus horizontal location should be between 0.0 and 1.0.")); + return false; + } + + if (focusPoint.y() < FOCUS_RECT_POSITION_MIN || focusPoint.y() > FOCUS_RECT_BOUNDARY) { + emit error(QCamera::CameraError, QStringLiteral("Focus vertical location should be between 0.0 and 1.0.")); + return false; + } + + ABI::Windows::Foundation::Rect rect; + rect.X = qBound<float>(FOCUS_RECT_POSITION_MIN, focusPoint.x() - FOCUS_RECT_HALF_SIZE, FOCUS_RECT_POSITION_MAX); + rect.Y = qBound<float>(FOCUS_RECT_POSITION_MIN, focusPoint.y() - FOCUS_RECT_HALF_SIZE, FOCUS_RECT_POSITION_MAX); + rect.Width = (rect.X + FOCUS_RECT_SIZE) < FOCUS_RECT_BOUNDARY ? FOCUS_RECT_SIZE : FOCUS_RECT_BOUNDARY - rect.X; + rect.Height = (rect.Y + FOCUS_RECT_SIZE) < FOCUS_RECT_BOUNDARY ? FOCUS_RECT_SIZE : FOCUS_RECT_BOUNDARY - rect.Y; + + ComPtr<IRegionOfInterest> regionOfInterest; + ComPtr<IInspectable> regionOfInterestObject; + HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_Devices_RegionOfInterest).Get(), ®ionOfInterestObject); + Q_ASSERT_SUCCEEDED(hr); + hr = regionOfInterestObject.As(®ionOfInterest); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IRegionOfInterest2> regionOfInterest2; + hr = regionOfInterestObject.As(®ionOfInterest2); + Q_ASSERT_SUCCEEDED(hr); + hr = regionOfInterest2->put_BoundsNormalized(true); + Q_ASSERT_SUCCEEDED(hr); + hr = regionOfInterest2->put_Weight(1); + Q_ASSERT_SUCCEEDED(hr); + hr = regionOfInterest2->put_Type(RegionOfInterestType_Unknown); + Q_ASSERT_SUCCEEDED(hr); + hr = regionOfInterest->put_AutoFocusEnabled(true); + Q_ASSERT_SUCCEEDED(hr); + hr = regionOfInterest->put_Bounds(rect); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<WindowsRegionOfInterestIterable> regionOfInterestIterable = Make<WindowsRegionOfInterestIterable>(regionOfInterest); + ComPtr<IAsyncAction> op; + hr = d->regionsOfInterestControl->SetRegionsAsync(regionOfInterestIterable.Get(), &op); + Q_ASSERT_SUCCEEDED(hr); + return QWinRTFunctions::await(op) == S_OK; +} + +bool QWinRTCameraControl::focus() +{ + Q_D(QWinRTCameraControl); + if (!d->focusControl) + return false; + ComPtr<IAsyncAction> op; + HRESULT hr = d->focusControl->FocusAsync(&op); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(op); + Q_ASSERT_SUCCEEDED(hr); + return hr == S_OK; +} + +void QWinRTCameraControl::clearFocusPoint() +{ + Q_D(QWinRTCameraControl); + if (!d->focusControl) + return; + ComPtr<IAsyncAction> op; + HRESULT hr = d->regionsOfInterestControl->ClearRegionsAsync(&op); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(op); + Q_ASSERT_SUCCEEDED(hr); +} + +bool QWinRTCameraControl::lockFocus() +{ + Q_D(QWinRTCameraControl); + if (!d->focusControl) + return false; + ComPtr<IFocusControl2> focusControl2; + HRESULT hr = d->focusControl.As(&focusControl2); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IAsyncAction> op; + hr = focusControl2->LockAsync(&op); + Q_ASSERT_SUCCEEDED(hr); + return QWinRTFunctions::await(op) == S_OK; +} + +bool QWinRTCameraControl::unlockFocus() +{ + Q_D(QWinRTCameraControl); + if (!d->focusControl) + return false; + ComPtr<IFocusControl2> focusControl2; + HRESULT hr = d->focusControl.As(&focusControl2); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IAsyncAction> op; + hr = focusControl2->UnlockAsync(&op); + Q_ASSERT_SUCCEEDED(hr); + return QWinRTFunctions::await(op) == S_OK; +} + +#else // Q_OS_WINPHONE + +HRESULT QWinRTCameraControl::initializeFocus() +{ + Q_D(QWinRTCameraControl); + d->cameraFocusControl->setSupportedFocusMode(0); + d->cameraFocusControl->setSupportedFocusPointMode(QSet<QCameraFocus::FocusPointMode>()); + return S_OK; +} + +bool QWinRTCameraControl::setFocus(QCameraFocus::FocusModes modes) +{ + Q_UNUSED(modes) + return false; +} + +bool QWinRTCameraControl::setFocusPoint(const QPointF &focusPoint) +{ + Q_UNUSED(focusPoint) + return false; +} + +bool QWinRTCameraControl::focus() +{ + return false; +} + +void QWinRTCameraControl::clearFocusPoint() +{ +} + +bool QWinRTCameraControl::lockFocus() +{ + return false; +} + +bool QWinRTCameraControl::unlockFocus() +{ + return false; +} + +#endif // !Q_OS_WINPHONE + HRESULT QWinRTCameraControl::onCaptureFailed(IMediaCapture *, IMediaCaptureFailedEventArgs *args) { HRESULT hr; @@ -854,4 +1286,9 @@ HRESULT QWinRTCameraControl::onRecordLimitationExceeded(IMediaCapture *) return S_OK; } +void QWinRTCameraControl::emitError(int errorCode, const QString &errorString) +{ + emit error(errorCode, errorString); +} + QT_END_NAMESPACE diff --git a/src/plugins/winrt/qwinrtcameracontrol.h b/src/plugins/winrt/qwinrtcameracontrol.h index 307667eb1..ac1c922a4 100644 --- a/src/plugins/winrt/qwinrtcameracontrol.h +++ b/src/plugins/winrt/qwinrtcameracontrol.h @@ -61,6 +61,8 @@ class QVideoRendererControl; class QVideoDeviceSelectorControl; class QCameraImageCaptureControl; class QImageEncoderControl; +class QCameraFocusControl; +class QCameraLocksControl; class QWinRTCameraControlPrivate; class QWinRTCameraControl : public QCameraControl @@ -85,15 +87,26 @@ public: QVideoDeviceSelectorControl *videoDeviceSelector() const; QCameraImageCaptureControl *imageCaptureControl() const; QImageEncoderControl *imageEncoderControl() const; + QCameraFocusControl *cameraFocusControl() const; + QCameraLocksControl *cameraLocksControl() const; ABI::Windows::Media::Capture::IMediaCapture *handle() const; + bool setFocus(QCameraFocus::FocusModes mode); + bool setFocusPoint(const QPointF &point); + bool focus(); + void clearFocusPoint(); + void emitError(int errorCode, const QString &errorString); + bool lockFocus(); + bool unlockFocus(); + private slots: void onBufferRequested(); private: HRESULT enumerateDevices(); HRESULT initialize(); + HRESULT initializeFocus(); HRESULT onCaptureFailed(ABI::Windows::Media::Capture::IMediaCapture *, ABI::Windows::Media::Capture::IMediaCaptureFailedEventArgs *); HRESULT onRecordLimitationExceeded(ABI::Windows::Media::Capture::IMediaCapture *); diff --git a/src/plugins/winrt/qwinrtcamerafocuscontrol.cpp b/src/plugins/winrt/qwinrtcamerafocuscontrol.cpp new file mode 100644 index 000000000..7eda823dd --- /dev/null +++ b/src/plugins/winrt/qwinrtcamerafocuscontrol.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwinrtcamerafocuscontrol.h" +#include "qwinrtcameraimagecapturecontrol.h" +#include "qwinrtcameracontrol.h" + +QT_BEGIN_NAMESPACE + +class QWinRTCameraFocusControlPrivate +{ +public: + QCameraFocus::FocusModes focusModes; + QCameraFocus::FocusModes supportedFocusModes; + QCameraFocus::FocusPointMode focusPointMode; + QSet<QCameraFocus::FocusPointMode> supportedFocusPointModes; + QPointF focusPoint; + bool focusModeInitialized; + bool focusPointModeInitialized; + bool imageCaptureIdle; +}; + +QWinRTCameraFocusControl::QWinRTCameraFocusControl(QWinRTCameraControl *parent) + : QCameraFocusControl(parent), d_ptr(new QWinRTCameraFocusControlPrivate) +{ + Q_D(QWinRTCameraFocusControl); + d->focusModeInitialized = false; + d->focusPointModeInitialized = false; + d->focusModes = QCameraFocus::ContinuousFocus; + d->focusPointMode = QCameraFocus::FocusPointAuto; + d->imageCaptureIdle = true; + QWinRTCameraImageCaptureControl *imageCaptureControl = static_cast<QWinRTCameraImageCaptureControl *>(parent->imageCaptureControl()); + Q_ASSERT(imageCaptureControl); + connect(imageCaptureControl, &QWinRTCameraImageCaptureControl::captureQueueChanged, + this, &QWinRTCameraFocusControl::imageCaptureQueueChanged, Qt::QueuedConnection); +} + +QCameraFocus::FocusModes QWinRTCameraFocusControl::focusMode() const +{ + Q_D(const QWinRTCameraFocusControl); + return d->focusModes; +} + +void QWinRTCameraFocusControl::setFocusMode(QCameraFocus::FocusModes modes) +{ + QMetaObject::invokeMethod(this, "applyFocusMode", Qt::QueuedConnection, Q_ARG(QCameraFocus::FocusModes, modes)); +} + +bool QWinRTCameraFocusControl::isFocusModeSupported(QCameraFocus::FocusModes modes) const +{ + Q_D(const QWinRTCameraFocusControl); + return (d->focusModeInitialized && modes) ? !((d->supportedFocusModes & modes) ^ modes) : false; +} + +QCameraFocus::FocusPointMode QWinRTCameraFocusControl::focusPointMode() const +{ + Q_D(const QWinRTCameraFocusControl); + return d->focusPointMode; +} + +void QWinRTCameraFocusControl::setFocusPointMode(QCameraFocus::FocusPointMode mode) +{ + QMetaObject::invokeMethod(this, "applyFocusPointMode", Qt::QueuedConnection, Q_ARG(QCameraFocus::FocusPointMode, mode)); +} + +bool QWinRTCameraFocusControl::isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const +{ + Q_D(const QWinRTCameraFocusControl); + return d->supportedFocusPointModes.contains(mode); +} + +QPointF QWinRTCameraFocusControl::customFocusPoint() const +{ + Q_D(const QWinRTCameraFocusControl); + return d->focusPoint; +} + +void QWinRTCameraFocusControl::setCustomFocusPoint(const QPointF &point) +{ + QMetaObject::invokeMethod(this, "applyFocusCustomPoint", Qt::QueuedConnection, Q_ARG(const QPointF, point)); +} + +QCameraFocusZoneList QWinRTCameraFocusControl::focusZones() const +{ + return QCameraFocusZoneList(); +} + +void QWinRTCameraFocusControl::setSupportedFocusMode(QCameraFocus::FocusModes modes) +{ + Q_D(QWinRTCameraFocusControl); + d->supportedFocusModes = modes; + d->focusModeInitialized = true; + if (isFocusModeSupported(d->focusModes)) + return; + d->focusModes = 0; + if (!modes) { + emit focusModeChanged(d->focusModes); + return; + } + if (isFocusModeSupported(QCameraFocus::ContinuousFocus)) + d->focusModes = QCameraFocus::ContinuousFocus; + else if (isFocusModeSupported(QCameraFocus::AutoFocus)) + d->focusModes = QCameraFocus::AutoFocus; + else if (isFocusModeSupported(QCameraFocus::ManualFocus)) + d->focusModes = QCameraFocus::ManualFocus; + emit focusModeChanged(d->focusModes); +} + +void QWinRTCameraFocusControl::setSupportedFocusPointMode(const QSet<QCameraFocus::FocusPointMode> &supportedFocusPointModes) +{ + Q_D(QWinRTCameraFocusControl); + d->supportedFocusPointModes = supportedFocusPointModes; + d->focusPointModeInitialized = true; + + if (supportedFocusPointModes.isEmpty()) { + if (d->focusPointMode != QCameraFocus::FocusPointAuto) { + d->focusPointMode = QCameraFocus::FocusPointAuto; + emit focusPointModeChanged(d->focusPointMode); + } + return; + } + + if (isFocusPointModeSupported(d->focusPointMode)) + return; + + if (isFocusPointModeSupported(QCameraFocus::FocusPointCenter)) + d->focusPointMode = QCameraFocus::FocusPointCenter; + else if (isFocusPointModeSupported(QCameraFocus::FocusPointAuto)) + d->focusPointMode = QCameraFocus::FocusPointAuto; + else if (isFocusPointModeSupported(QCameraFocus::FocusPointCustom)) + d->focusPointMode = QCameraFocus::FocusPointCustom; + else if (isFocusPointModeSupported(QCameraFocus::FocusPointFaceDetection)) + d->focusPointMode = QCameraFocus::FocusPointFaceDetection; + emit focusPointModeChanged(d->focusPointMode); +} + +void QWinRTCameraFocusControl::imageCaptureQueueChanged(bool isEmpty) +{ + Q_D(QWinRTCameraFocusControl); + d->imageCaptureIdle = isEmpty; +} + +void QWinRTCameraFocusControl::applyFocusCustomPoint(const QPointF &point) +{ + Q_D(QWinRTCameraFocusControl); + if (d->focusPointMode != QCameraFocus::FocusPointCustom) { + QWinRTCameraControl *cameraControl = static_cast<QWinRTCameraControl *>(parent()); + Q_ASSERT(cameraControl); + cameraControl->emitError(QCamera::InvalidRequestError, QStringLiteral("Custom focus point can be set only in FocusPointCustom focus mode.")); + return; + } + if (d->focusPoint == point) + return; + if (changeFocusCustomPoint(point)) { + d->focusPoint = point; + emit customFocusPointChanged(point); + } +} + +void QWinRTCameraFocusControl::applyFocusMode(QCameraFocus::FocusModes modes) +{ + Q_D(QWinRTCameraFocusControl); + if (d->focusModes == modes) + return; + QWinRTCameraControl *cameraControl = static_cast<QWinRTCameraControl *>(parent()); + Q_ASSERT(cameraControl); + if (!modes) { + cameraControl->emitError(QCamera::InvalidRequestError, QStringLiteral("Can't set empty camera focus modes.")); + return; + } + if (!d->focusModeInitialized) { + d->focusModes = modes; + emit focusModeChanged(modes); + return; + } + if (!isFocusModeSupported(modes)) { + cameraControl->emitError(QCamera::NotSupportedFeatureError, QStringLiteral("Unsupported camera focus modes.")); + return; + } + if (modes.testFlag(QCameraFocus::ContinuousFocus)) { + if (QCameraFocus::FocusPointCustom == d->focusPointMode) { + cameraControl->emitError(QCamera::NotSupportedFeatureError, + QStringLiteral("Unsupported camera focus modes: ContinuousFocus with FocusPointCustom.")); + return; + } else if (!d->imageCaptureIdle) { + cameraControl->emitError(QCamera::NotSupportedFeatureError, + QStringLiteral("Can't set ContinuousFocus camera focus mode while capturing image.")); + return; + } + } + if (!cameraControl->setFocus(modes)) + return; + if (modes.testFlag(QCameraFocus::ContinuousFocus) || d->focusModes.testFlag(QCameraFocus::ContinuousFocus)) + cameraControl->focus(); + d->focusModes = modes; + emit focusModeChanged(modes); +} + +void QWinRTCameraFocusControl::applyFocusPointMode(QCameraFocus::FocusPointMode mode) +{ + Q_D(QWinRTCameraFocusControl); + if (d->focusPointMode == mode) + return; + + if (!d->focusModeInitialized) { + d->focusPointMode = mode; + emit focusPointModeChanged(mode); + return; + } + QWinRTCameraControl *cameraControl = static_cast<QWinRTCameraControl *>(parent()); + Q_ASSERT(cameraControl); + if (!d->supportedFocusPointModes.contains(mode)) { + cameraControl->emitError(QCamera::NotSupportedFeatureError, QStringLiteral("Unsupported camera point focus mode.")); + return; + } + if (QCameraFocus::FocusPointCenter == mode || QCameraFocus::FocusPointAuto == mode) + d->focusPoint = QPointF(0.5, 0.5); + // Don't apply focus point focus settings if camera is in continuous focus mode + if (!d->focusModes.testFlag(QCameraFocus::ContinuousFocus)) { + changeFocusCustomPoint(d->focusPoint); + } else if (QCameraFocus::FocusPointCustom == mode) { + cameraControl->emitError(QCamera::NotSupportedFeatureError, QStringLiteral("Unsupported camera focus modes: ContinuousFocus with FocusPointCustom.")); + return; + } + d->focusPointMode = mode; + emit focusPointModeChanged(mode); +} + +bool QWinRTCameraFocusControl::changeFocusCustomPoint(const QPointF &point) +{ + Q_D(QWinRTCameraFocusControl); + if (!d->focusPointModeInitialized || point.isNull()) + return true; + QWinRTCameraControl *cameraControl = static_cast<QWinRTCameraControl *>(parent()); + Q_ASSERT(cameraControl); + cameraControl->clearFocusPoint(); + return cameraControl->setFocusPoint(point); +} + +QT_END_NAMESPACE diff --git a/src/plugins/winrt/qwinrtcamerafocuscontrol.h b/src/plugins/winrt/qwinrtcamerafocuscontrol.h new file mode 100644 index 000000000..4862c0115 --- /dev/null +++ b/src/plugins/winrt/qwinrtcamerafocuscontrol.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINRTCAMERAFOCUSCONTROL_H +#define QWINRTCAMERAFOCUSCONTROL_H +#include <qcamerafocuscontrol.h> + +QT_BEGIN_NAMESPACE + +class QWinRTCameraControl; +class QWinRTCameraFocusControlPrivate; +class QWinRTCameraFocusControl : public QCameraFocusControl +{ + Q_OBJECT +public: + explicit QWinRTCameraFocusControl(QWinRTCameraControl *parent); + + QCameraFocus::FocusModes focusMode() const Q_DECL_OVERRIDE; + void setFocusMode(QCameraFocus::FocusModes mode) Q_DECL_OVERRIDE; + bool isFocusModeSupported(QCameraFocus::FocusModes mode) const Q_DECL_OVERRIDE; + QCameraFocus::FocusPointMode focusPointMode() const Q_DECL_OVERRIDE; + void setFocusPointMode(QCameraFocus::FocusPointMode mode) Q_DECL_OVERRIDE; + bool isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const Q_DECL_OVERRIDE; + QPointF customFocusPoint() const Q_DECL_OVERRIDE; + void setCustomFocusPoint(const QPointF &point) Q_DECL_OVERRIDE; + QCameraFocusZoneList focusZones() const Q_DECL_OVERRIDE; + + void setSupportedFocusMode(QCameraFocus::FocusModes flag); + void setSupportedFocusPointMode(const QSet<QCameraFocus::FocusPointMode> &supportedFocusPointModes); + +private slots: + void imageCaptureQueueChanged(bool isEmpty); + +private: + Q_INVOKABLE void applyFocusCustomPoint(const QPointF &point); + Q_INVOKABLE void applyFocusMode(QCameraFocus::FocusModes modes); + Q_INVOKABLE void applyFocusPointMode(QCameraFocus::FocusPointMode mode); + bool changeFocusCustomPoint(const QPointF &point); + + QScopedPointer<QWinRTCameraFocusControlPrivate> d_ptr; + Q_DECLARE_PRIVATE(QWinRTCameraFocusControl) +}; + +#endif // QWINRTCAMERAFOCUSCONTROL_H diff --git a/src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp index 55f553778..ae67e33f4 100644 --- a/src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp +++ b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp @@ -179,6 +179,7 @@ int QWinRTCameraImageCaptureControl::capture(const QString &fileName) qErrnoWarning("Camera photo capture failed."); return -1; } + emit captureQueueChanged(false); d->requests.insert(request.op.Get(), request); hr = request.op->put_Completed(Callback<IAsyncActionCompletedHandler>( @@ -199,6 +200,7 @@ void QWinRTCameraImageCaptureControl::cancelCapture() info->Cancel(); it = d->requests.erase(it); } + emit captureQueueChanged(true); } HRESULT QWinRTCameraImageCaptureControl::onCaptureCompleted(IAsyncAction *asyncInfo, AsyncStatus status) @@ -209,7 +211,7 @@ HRESULT QWinRTCameraImageCaptureControl::onCaptureCompleted(IAsyncAction *asyncI return S_OK; CaptureRequest request = d->requests.take(asyncInfo); - + emit captureQueueChanged(d->requests.isEmpty()); HRESULT hr; if (status == Error) { hr = asyncInfo->GetResults(); diff --git a/src/plugins/winrt/qwinrtcameraimagecapturecontrol.h b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.h index dc8802552..1fbdccf6b 100644 --- a/src/plugins/winrt/qwinrtcameraimagecapturecontrol.h +++ b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.h @@ -68,6 +68,9 @@ public: int capture(const QString &fileName) Q_DECL_OVERRIDE; void cancelCapture() Q_DECL_OVERRIDE; +signals: + void captureQueueChanged(bool isEmpty); + private: HRESULT onCaptureCompleted(ABI::Windows::Foundation::IAsyncAction *, ABI::Windows::Foundation::AsyncStatus); diff --git a/src/plugins/winrt/qwinrtcameralockscontrol.cpp b/src/plugins/winrt/qwinrtcameralockscontrol.cpp new file mode 100644 index 000000000..e429c5ff1 --- /dev/null +++ b/src/plugins/winrt/qwinrtcameralockscontrol.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QCameraFocusControl> + +#include "qwinrtcameralockscontrol.h" +#include "qwinrtcameracontrol.h" + +QT_BEGIN_NAMESPACE + +QWinRTCameraLocksControl::QWinRTCameraLocksControl(QObject *parent) + : QCameraLocksControl(parent) + , m_supportedLocks(QCamera::NoLock) + , m_focusLockStatus(QCamera::Unlocked) +{ +} + +QCamera::LockTypes QWinRTCameraLocksControl::supportedLocks() const +{ + return m_supportedLocks; +} + +QCamera::LockStatus QWinRTCameraLocksControl::lockStatus(QCamera::LockType lock) const +{ + switch (lock) { + case QCamera::LockFocus: + return m_focusLockStatus; + case QCamera::LockExposure: + case QCamera::LockWhiteBalance: + default: + return QCamera::Unlocked; + } +} + +void QWinRTCameraLocksControl::searchAndLock(QCamera::LockTypes locks) +{ + if (locks.testFlag(QCamera::LockFocus)) { + QMetaObject::invokeMethod(this, "searchAndLockFocus", Qt::QueuedConnection); + } else { + QWinRTCameraControl *cameraControl = qobject_cast<QWinRTCameraControl *>(parent()); + Q_ASSERT(cameraControl); + cameraControl->emitError(QCamera::InvalidRequestError, QStringLiteral("Unsupported camera lock type.")); + } +} + +void QWinRTCameraLocksControl::unlock(QCamera::LockTypes locks) +{ + if (locks.testFlag(QCamera::LockFocus)) + QMetaObject::invokeMethod(this, "unlockFocus", Qt::QueuedConnection); +} + +void QWinRTCameraLocksControl::initialize() +{ + QWinRTCameraControl *cameraControl = qobject_cast<QWinRTCameraControl *>(parent()); + Q_ASSERT(cameraControl); + QCameraFocusControl *focusControl = cameraControl->cameraFocusControl(); + Q_ASSERT(focusControl); + if (focusControl->isFocusModeSupported(QCameraFocus::AutoFocus)) + m_supportedLocks |= QCamera::LockFocus; +} + +void QWinRTCameraLocksControl::searchAndLockFocus() +{ + if (QCamera::Locked == m_focusLockStatus) + unlockFocus(); + QWinRTCameraControl *cameraControl = qobject_cast<QWinRTCameraControl *>(parent()); + Q_ASSERT(cameraControl); + QCameraFocusControl *focusControl = cameraControl->cameraFocusControl(); + Q_ASSERT(focusControl); + if (focusControl->focusMode().testFlag(QCameraFocus::ContinuousFocus)) { + cameraControl->emitError(QCamera::NotSupportedFeatureError, QStringLiteral("Camera can't lock focus in continuous focus mode.")); + } else { + m_focusLockStatus = QCamera::Searching; + emit lockStatusChanged(QCamera::LockFocus, m_focusLockStatus, QCamera::LockAcquired); + cameraControl->focus(); + cameraControl->lockFocus(); + m_focusLockStatus = QCamera::Locked; + emit lockStatusChanged(QCamera::LockFocus, m_focusLockStatus, QCamera::LockAcquired); + } +} + +void QWinRTCameraLocksControl::unlockFocus() +{ + if (QCamera::Unlocked == m_focusLockStatus) + return; + QWinRTCameraControl *cameraControl = qobject_cast<QWinRTCameraControl *>(parent()); + Q_ASSERT(cameraControl); + cameraControl->unlockFocus(); + m_focusLockStatus = QCamera::Unlocked; + emit lockStatusChanged(QCamera::LockFocus, m_focusLockStatus, QCamera::UserRequest); +} + +QT_END_NAMESPACE diff --git a/src/plugins/winrt/qwinrtcameralockscontrol.h b/src/plugins/winrt/qwinrtcameralockscontrol.h new file mode 100644 index 000000000..5a7b57c11 --- /dev/null +++ b/src/plugins/winrt/qwinrtcameralockscontrol.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINRTCAMERALOCKSCONTROL_H +#define QWINRTCAMERALOCKSCONTROL_H + +#include <QCameraLocksControl> + +QT_BEGIN_NAMESPACE + +class QWinRTCameraControl; +class QWinRTCameraLocksControl : public QCameraLocksControl +{ + Q_OBJECT +public: + explicit QWinRTCameraLocksControl(QObject *parent); + + QCamera::LockTypes supportedLocks() const Q_DECL_OVERRIDE; + QCamera::LockStatus lockStatus(QCamera::LockType lock) const Q_DECL_OVERRIDE; + void searchAndLock(QCamera::LockTypes locks) Q_DECL_OVERRIDE; + void unlock(QCamera::LockTypes locks) Q_DECL_OVERRIDE; + void initialize(); + +private: + Q_INVOKABLE void searchAndLockFocus(); + Q_INVOKABLE void unlockFocus(); + QCamera::LockTypes m_supportedLocks; + QCamera::LockStatus m_focusLockStatus; +}; + +QT_END_NAMESPACE + +#endif // QWINRTCAMERALOCKSCONTROL_H diff --git a/src/plugins/winrt/qwinrtcameraservice.cpp b/src/plugins/winrt/qwinrtcameraservice.cpp index be67b4742..977acdcab 100644 --- a/src/plugins/winrt/qwinrtcameraservice.cpp +++ b/src/plugins/winrt/qwinrtcameraservice.cpp @@ -37,6 +37,8 @@ #include "qwinrtcameraservice.h" #include "qwinrtcameracontrol.h" #include "qwinrtcamerainfocontrol.h" +#include "qwinrtvideoprobecontrol.h" +#include "qwinrtcameravideorenderercontrol.h" #include <QtCore/QCoreApplication> #include <QtCore/qfunctions_winrt.h> @@ -45,6 +47,9 @@ #include <QtMultimedia/QVideoRendererControl> #include <QtMultimedia/QVideoDeviceSelectorControl> #include <QtMultimedia/QImageEncoderControl> +#include <QtMultimedia/QCameraFocusControl> +#include <QtMultimedia/QCameraLocksControl> +#include <QtMultimedia/QMediaVideoProbeControl> QT_BEGIN_NAMESPACE @@ -69,39 +74,44 @@ QMediaControl *QWinRTCameraService::requestControl(const char *name) d->cameraControl = new QWinRTCameraControl(this); return d->cameraControl; } - - if (qstrcmp(name, QVideoRendererControl_iid) == 0) { - if (d->cameraControl) - return d->cameraControl->videoRenderer(); - } - - if (qstrcmp(name, QVideoDeviceSelectorControl_iid) == 0) { - if (d->cameraControl) - return d->cameraControl->videoDeviceSelector(); - } - if (qstrcmp(name, QCameraInfoControl_iid) == 0) { if (!d->cameraInfoControl) d->cameraInfoControl = new QWinRTCameraInfoControl(this); return d->cameraInfoControl; } - if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0) { - if (d->cameraControl) - return d->cameraControl->imageCaptureControl(); - } + if (!d->cameraControl) + return nullptr; - if (qstrcmp(name, QImageEncoderControl_iid) == 0) { - if (d->cameraControl) - return d->cameraControl->imageEncoderControl(); - } + if (qstrcmp(name, QVideoRendererControl_iid) == 0) + return d->cameraControl->videoRenderer(); + + if (qstrcmp(name, QVideoDeviceSelectorControl_iid) == 0) + return d->cameraControl->videoDeviceSelector(); + + if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0) + return d->cameraControl->imageCaptureControl(); + + if (qstrcmp(name, QImageEncoderControl_iid) == 0) + return d->cameraControl->imageEncoderControl(); + + if (qstrcmp(name, QCameraFocusControl_iid) == 0) + return d->cameraControl->cameraFocusControl(); + + if (qstrcmp(name, QCameraLocksControl_iid) == 0) + return d->cameraControl->cameraLocksControl(); + + if (qstrcmp(name, QMediaVideoProbeControl_iid) == 0) + return new QWinRTVideoProbeControl(qobject_cast<QWinRTCameraVideoRendererControl *>(d->cameraControl->videoRenderer())); - return Q_NULLPTR; + return nullptr; } void QWinRTCameraService::releaseControl(QMediaControl *control) { - Q_UNUSED(control); + Q_ASSERT(control); + if (QWinRTVideoProbeControl *videoProbe = qobject_cast<QWinRTVideoProbeControl *>(control)) + videoProbe->deleteLater(); } QT_END_NAMESPACE diff --git a/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp b/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp index 4878c55c9..6c5575a17 100644 --- a/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp +++ b/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp @@ -39,6 +39,7 @@ #include <QtCore/qfunctions_winrt.h> #include <QtCore/QSize> #include <QtCore/QVector> +#include <QVideoFrame> #include <d3d11.h> #include <mfapi.h> @@ -47,6 +48,60 @@ using namespace Microsoft::WRL; QT_BEGIN_NAMESPACE +class QWinRTCameraVideoBuffer : public QAbstractVideoBuffer +{ +public: + QWinRTCameraVideoBuffer(IMF2DBuffer *buffer, int size) + : QAbstractVideoBuffer(NoHandle) + , currentMode(NotMapped) + , buffer(buffer) + , size(size) + { + } + + ~QWinRTCameraVideoBuffer() + { + unmap(); + } + + MapMode mapMode() const Q_DECL_OVERRIDE + { + return currentMode; + } + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) Q_DECL_OVERRIDE + { + if (currentMode != NotMapped || mode == NotMapped) + return nullptr; + + BYTE *bytes; + LONG stride; + HRESULT hr = buffer->Lock2D(&bytes, &stride); + RETURN_IF_FAILED("Failed to lock camera frame buffer", nullptr); + + if (bytesPerLine) + *bytesPerLine = stride; + if (numBytes) + *numBytes = size; + currentMode = mode; + return bytes; + } + + void unmap() Q_DECL_OVERRIDE + { + if (currentMode == NotMapped) + return; + HRESULT hr = buffer->Unlock2D(); + RETURN_VOID_IF_FAILED("Failed to unlock camera frame buffer"); + currentMode = NotMapped; + } + +private: + ComPtr<IMF2DBuffer> buffer; + MapMode currentMode; + int size; +}; + class D3DVideoBlitter { public: @@ -143,11 +198,52 @@ public: ComPtr<IMF2DBuffer> buffers[CAMERA_SAMPLE_QUEUE_SIZE]; QAtomicInteger<quint16> writeIndex; QAtomicInteger<quint16> readIndex; + QVideoFrame::PixelFormat cameraSampleformat; + int cameraSampleSize; + uint videoProbesCounter; + bool getCameraSampleInfo(const ComPtr<IMF2DBuffer> &buffer); + ComPtr<IMF2DBuffer> dequeueBuffer(); }; +bool QWinRTCameraVideoRendererControlPrivate::getCameraSampleInfo(const ComPtr<IMF2DBuffer> &buffer) +{ + ComPtr<ID3D11Texture2D> sourceTexture; + ComPtr<IMFDXGIBuffer> dxgiBuffer; + HRESULT hr = buffer.As(&dxgiBuffer); + Q_ASSERT_SUCCEEDED(hr); + hr = dxgiBuffer->GetResource(IID_PPV_ARGS(&sourceTexture)); + if (FAILED(hr)) { + qErrnoWarning(hr, "The video frame does not support texture output"); + cameraSampleformat = QVideoFrame::Format_Invalid; + return false; + } + D3D11_TEXTURE2D_DESC desc; + sourceTexture->GetDesc(&desc); + switch (desc.Format) { + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + cameraSampleformat = QVideoFrame::Format_ARGB32; + break; + case DXGI_FORMAT_NV12: + cameraSampleformat = QVideoFrame::Format_NV12; + break; + default: + cameraSampleformat = QVideoFrame::Format_Invalid; + qErrnoWarning("Unsupported camera probe format."); + return false; + } + DWORD pcbLength; + hr = buffer->GetContiguousLength(&pcbLength); + Q_ASSERT_SUCCEEDED(hr); + cameraSampleSize = pcbLength; + return true; +} + QWinRTCameraVideoRendererControl::QWinRTCameraVideoRendererControl(const QSize &size, QObject *parent) : QWinRTAbstractVideoRendererControl(size, parent), d_ptr(new QWinRTCameraVideoRendererControlPrivate) { + Q_D(QWinRTCameraVideoRendererControl); + d->cameraSampleformat = QVideoFrame::Format_User; + d->videoProbesCounter = 0; } QWinRTCameraVideoRendererControl::~QWinRTCameraVideoRendererControl() @@ -158,22 +254,15 @@ QWinRTCameraVideoRendererControl::~QWinRTCameraVideoRendererControl() bool QWinRTCameraVideoRendererControl::render(ID3D11Texture2D *target) { Q_D(QWinRTCameraVideoRendererControl); - - const quint16 readIndex = d->readIndex; - if (readIndex == d->writeIndex) { + ComPtr<IMF2DBuffer> buffer = d->dequeueBuffer(); + if (!buffer) { emit bufferRequested(); return false; } - HRESULT hr; - ComPtr<IMF2DBuffer> buffer = d->buffers[readIndex]; - Q_ASSERT(buffer); - d->buffers[readIndex].Reset(); - d->readIndex = (readIndex + 1) % CAMERA_SAMPLE_QUEUE_SIZE; - ComPtr<ID3D11Texture2D> sourceTexture; ComPtr<IMFDXGIBuffer> dxgiBuffer; - hr = buffer.As(&dxgiBuffer); + HRESULT hr = buffer.As(&dxgiBuffer); Q_ASSERT_SUCCEEDED(hr); hr = dxgiBuffer->GetResource(IID_PPV_ARGS(&sourceTexture)); if (FAILED(hr)) { @@ -196,11 +285,41 @@ void QWinRTCameraVideoRendererControl::queueBuffer(IMF2DBuffer *buffer) { Q_D(QWinRTCameraVideoRendererControl); Q_ASSERT(buffer); + + if (d->videoProbesCounter > 0) { + if (d->cameraSampleformat == QVideoFrame::Format_User) + d->getCameraSampleInfo(buffer); + + if (d->cameraSampleformat != QVideoFrame::Format_Invalid) { + QWinRTCameraVideoBuffer *videoBuffer = new QWinRTCameraVideoBuffer(buffer, d->cameraSampleSize); + QVideoFrame frame(videoBuffer, size(), d->cameraSampleformat); + emit videoFrameProbed(frame); + } + } + const quint16 writeIndex = (d->writeIndex + 1) % CAMERA_SAMPLE_QUEUE_SIZE; if (d->readIndex == writeIndex) // Drop new sample if queue is full return; d->buffers[d->writeIndex] = buffer; d->writeIndex = writeIndex; + + if (!surface()) { + d->dequeueBuffer(); + emit bufferRequested(); + } +} + +ComPtr<IMF2DBuffer> QWinRTCameraVideoRendererControlPrivate::dequeueBuffer() +{ + const quint16 currentReadIndex = readIndex; + if (currentReadIndex == writeIndex) + return nullptr; + + ComPtr<IMF2DBuffer> buffer = buffers[currentReadIndex]; + Q_ASSERT(buffer); + buffers[currentReadIndex].Reset(); + readIndex = (currentReadIndex + 1) % CAMERA_SAMPLE_QUEUE_SIZE; + return buffer; } void QWinRTCameraVideoRendererControl::discardBuffers() @@ -211,4 +330,17 @@ void QWinRTCameraVideoRendererControl::discardBuffers() buffer.Reset(); } +void QWinRTCameraVideoRendererControl::incrementProbe() +{ + Q_D(QWinRTCameraVideoRendererControl); + ++d->videoProbesCounter; +} + +void QWinRTCameraVideoRendererControl::decrementProbe() +{ + Q_D(QWinRTCameraVideoRendererControl); + Q_ASSERT(d->videoProbesCounter > 0); + --d->videoProbesCounter; +} + QT_END_NAMESPACE diff --git a/src/plugins/winrt/qwinrtcameravideorenderercontrol.h b/src/plugins/winrt/qwinrtcameravideorenderercontrol.h index 68e9bd9f8..122418de3 100644 --- a/src/plugins/winrt/qwinrtcameravideorenderercontrol.h +++ b/src/plugins/winrt/qwinrtcameravideorenderercontrol.h @@ -39,10 +39,13 @@ #include "qwinrtabstractvideorenderercontrol.h" +#include <QVideoFrame> + struct IMF2DBuffer; QT_BEGIN_NAMESPACE +class QWinRTVideoProbeControl; class QVideoSurfaceFormat; class QWinRTCameraVideoRendererControlPrivate; class QWinRTCameraVideoRendererControl : public QWinRTAbstractVideoRendererControl @@ -55,9 +58,12 @@ public: bool render(ID3D11Texture2D *texture) Q_DECL_OVERRIDE; void queueBuffer(IMF2DBuffer *buffer); void discardBuffers(); + void incrementProbe(); + void decrementProbe(); signals: void bufferRequested(); + void videoFrameProbed(const QVideoFrame &frame); private: QScopedPointer<QWinRTCameraVideoRendererControlPrivate> d_ptr; diff --git a/src/plugins/winrt/qwinrtimageencodercontrol.cpp b/src/plugins/winrt/qwinrtimageencodercontrol.cpp index 0ab7a7cfb..6260a1d66 100644 --- a/src/plugins/winrt/qwinrtimageencodercontrol.cpp +++ b/src/plugins/winrt/qwinrtimageencodercontrol.cpp @@ -99,23 +99,23 @@ void QWinRTImageEncoderControl::setSupportedResolutionsList(const QList<QSize> r void QWinRTImageEncoderControl::applySettings() { Q_D(QWinRTImageEncoderControl); - d->imageEncoderSetting.setCodec(QStringLiteral("jpeg")); + if (d->imageEncoderSetting.codec().isEmpty()) + d->imageEncoderSetting.setCodec(QStringLiteral("jpeg")); QSize requestResolution = d->imageEncoderSetting.resolution(); if (d->supportedResolutions.isEmpty() || d->supportedResolutions.contains(requestResolution)) return; - if (!requestResolution.isValid()) - requestResolution = QSize(0, 0);// Find the minimal available resolution - // Find closest resolution from the list const int pixelCount = requestResolution.width() * requestResolution.height(); - int minPixelCountGap = -1; - for (int i = 0; i < d->supportedResolutions.size(); ++i) { - int gap = qAbs(pixelCount - d->supportedResolutions.at(i).width() * d->supportedResolutions.at(i).height()); - if (gap < minPixelCountGap || minPixelCountGap < 0) { - minPixelCountGap = gap; - requestResolution = d->supportedResolutions.at(i); + int minimumGap = std::numeric_limits<int>::max(); + foreach (const QSize &size, d->supportedResolutions) { + int gap = qAbs(pixelCount - size.width() * size.height()); + if (gap < minimumGap) { + minimumGap = gap; + requestResolution = size; + if (gap == 0) + break; } } d->imageEncoderSetting.setResolution(requestResolution); diff --git a/src/plugins/winrt/qwinrtimageencodercontrol.h b/src/plugins/winrt/qwinrtimageencodercontrol.h index 7682e7626..10a09916e 100644 --- a/src/plugins/winrt/qwinrtimageencodercontrol.h +++ b/src/plugins/winrt/qwinrtimageencodercontrol.h @@ -44,6 +44,7 @@ QT_BEGIN_NAMESPACE class QWinRTImageEncoderControlPrivate; class QWinRTImageEncoderControl : public QImageEncoderControl { + Q_OBJECT public: explicit QWinRTImageEncoderControl(QObject *parent = 0); diff --git a/src/plugins/winrt/qwinrtvideoprobecontrol.cpp b/src/plugins/winrt/qwinrtvideoprobecontrol.cpp new file mode 100644 index 000000000..7242efe60 --- /dev/null +++ b/src/plugins/winrt/qwinrtvideoprobecontrol.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qwinrtvideoprobecontrol.h" +#include "qwinrtcameravideorenderercontrol.h" + +QT_BEGIN_NAMESPACE + +QWinRTVideoProbeControl::QWinRTVideoProbeControl(QWinRTCameraVideoRendererControl *parent) + : QMediaVideoProbeControl(parent) +{ + QObject::connect(parent, &QWinRTCameraVideoRendererControl::videoFrameProbed, + this, &QMediaVideoProbeControl::videoFrameProbed, Qt::QueuedConnection); + parent->incrementProbe(); +} + +QWinRTVideoProbeControl::~QWinRTVideoProbeControl() +{ + if (QWinRTCameraVideoRendererControl *renderer = qobject_cast<QWinRTCameraVideoRendererControl *>(parent())) + renderer->decrementProbe(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/winrt/qwinrtvideoprobecontrol.h b/src/plugins/winrt/qwinrtvideoprobecontrol.h new file mode 100644 index 000000000..dc9a8392e --- /dev/null +++ b/src/plugins/winrt/qwinrtvideoprobecontrol.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QWINRTVIDEOPROBECONTROL_H +#define QWINRTVIDEOPROBECONTROL_H + +#include <qmediavideoprobecontrol.h> + +QT_BEGIN_NAMESPACE + +class QWinRTCameraVideoRendererControl; +class QWinRTVideoProbeControl : public QMediaVideoProbeControl +{ + Q_OBJECT +public: + explicit QWinRTVideoProbeControl(QWinRTCameraVideoRendererControl *parent); + ~QWinRTVideoProbeControl(); +}; + +QT_END_NAMESPACE + +#endif // QWINRTVIDEOPROBECONTROL_H diff --git a/src/plugins/winrt/winrt.pro b/src/plugins/winrt/winrt.pro index 58c2371f9..2f87ea8ff 100644 --- a/src/plugins/winrt/winrt.pro +++ b/src/plugins/winrt/winrt.pro @@ -10,30 +10,36 @@ LIBS += -lmfplat -lmfuuid -loleaut32 -ld3d11 -lruntimeobject HEADERS += \ qwinrtabstractvideorenderercontrol.h \ qwinrtcameracontrol.h \ - qwinrtcamerainfocontrol.h \ + qwinrtcamerafocuscontrol.h \ qwinrtcameraimagecapturecontrol.h \ + qwinrtcamerainfocontrol.h \ + qwinrtcameralockscontrol.h \ qwinrtcameraservice.h \ qwinrtcameravideorenderercontrol.h \ + qwinrtimageencodercontrol.h \ qwinrtmediaplayercontrol.h \ qwinrtmediaplayerservice.h \ qwinrtplayerrenderercontrol.h \ qwinrtserviceplugin.h \ qwinrtvideodeviceselectorcontrol.h \ - qwinrtimageencodercontrol.h + qwinrtvideoprobecontrol.h SOURCES += \ qwinrtabstractvideorenderercontrol.cpp \ qwinrtcameracontrol.cpp \ - qwinrtcamerainfocontrol.cpp \ + qwinrtcamerafocuscontrol.cpp \ qwinrtcameraimagecapturecontrol.cpp \ + qwinrtcamerainfocontrol.cpp \ + qwinrtcameralockscontrol.cpp \ qwinrtcameraservice.cpp \ qwinrtcameravideorenderercontrol.cpp \ + qwinrtimageencodercontrol.cpp \ qwinrtmediaplayercontrol.cpp \ qwinrtmediaplayerservice.cpp \ qwinrtplayerrenderercontrol.cpp \ qwinrtserviceplugin.cpp \ qwinrtvideodeviceselectorcontrol.cpp \ - qwinrtimageencodercontrol.cpp + qwinrtvideoprobecontrol.cpp OTHER_FILES += \ winrt.json diff --git a/tests/auto/integration/qaudiodecoderbackend/BLACKLIST b/tests/auto/integration/qaudiodecoderbackend/BLACKLIST new file mode 100644 index 000000000..bd2349568 --- /dev/null +++ b/tests/auto/integration/qaudiodecoderbackend/BLACKLIST @@ -0,0 +1,37 @@ +# QTBUG-46331 + +[fileTest] +opensuse-13.1 64bit +redhatenterpriselinuxworkstation-6.6 +osx-10.8 +osx-10.9 +osx-10.10 +windows 32bit developer-build +windows 64bit developer-build + +[unsupportedFileTest] +opensuse-13.1 64bit +redhatenterpriselinuxworkstation-6.6 +osx-10.8 +osx-10.9 +osx-10.10 +windows 32bit developer-build +windows 64bit developer-build + +[corruptedFileTest] +opensuse-13.1 64bit +redhatenterpriselinuxworkstation-6.6 +osx-10.8 +osx-10.9 +osx-10.10 +windows 32bit developer-build +windows 64bit developer-build + +[deviceTest] +opensuse-13.1 64bit +redhatenterpriselinuxworkstation-6.6 +osx-10.8 +osx-10.9 +osx-10.10 +windows 32bit developer-build +windows 64bit developer-build diff --git a/tests/auto/integration/qaudiodecoderbackend/qaudiodecoderbackend.pro b/tests/auto/integration/qaudiodecoderbackend/qaudiodecoderbackend.pro index 80ce0c112..c9f5a089c 100644 --- a/tests/auto/integration/qaudiodecoderbackend/qaudiodecoderbackend.pro +++ b/tests/auto/integration/qaudiodecoderbackend/qaudiodecoderbackend.pro @@ -3,7 +3,7 @@ TARGET = tst_qaudiodecoderbackend QT += multimedia multimedia-private testlib # This is more of a system test -CONFIG += testcase insignificant_test +CONFIG += testcase TESTDATA += testdata/* INCLUDEPATH += \ diff --git a/tests/auto/integration/qaudiodeviceinfo/BLACKLIST b/tests/auto/integration/qaudiodeviceinfo/BLACKLIST new file mode 100644 index 000000000..40dc63a09 --- /dev/null +++ b/tests/auto/integration/qaudiodeviceinfo/BLACKLIST @@ -0,0 +1,4 @@ +# QTBUG-46409 + +[deviceName] +redhatenterpriselinuxworkstation-6.6 diff --git a/tests/auto/integration/qaudiodeviceinfo/qaudiodeviceinfo.pro b/tests/auto/integration/qaudiodeviceinfo/qaudiodeviceinfo.pro index 69bd792bc..98ed7e69c 100644 --- a/tests/auto/integration/qaudiodeviceinfo/qaudiodeviceinfo.pro +++ b/tests/auto/integration/qaudiodeviceinfo/qaudiodeviceinfo.pro @@ -3,7 +3,7 @@ TARGET = tst_qaudiodeviceinfo QT += core multimedia-private testlib # This is more of a system test -CONFIG += testcase insignificant_test +CONFIG += testcase SOURCES += tst_qaudiodeviceinfo.cpp diff --git a/tests/auto/integration/qaudiooutput/BLACKLIST b/tests/auto/integration/qaudiooutput/BLACKLIST new file mode 100644 index 000000000..e640ef926 --- /dev/null +++ b/tests/auto/integration/qaudiooutput/BLACKLIST @@ -0,0 +1,4 @@ +[pullSuspendResume] +redhatenterpriselinuxworkstation-6.6 +ubuntu-14.04 +opensuse-13.1 64bit diff --git a/tests/auto/integration/qmediaplayerbackend/BLACKLIST b/tests/auto/integration/qmediaplayerbackend/BLACKLIST new file mode 100644 index 000000000..0d2fe2872 --- /dev/null +++ b/tests/auto/integration/qmediaplayerbackend/BLACKLIST @@ -0,0 +1,53 @@ +# QTBUG-46368 + +osx-10.8 +osx-10.9 +osx-10.10 +windows 32bit developer-build + +[construction] +opensuse-13.1 64bit +redhatenterpriselinuxworkstation-6.6 + +[loadMedia] +opensuse-13.1 64bit +redhatenterpriselinuxworkstation-6.6 +windows 64bit developer-build + +[unloadMedia] +opensuse-13.1 64bit +redhatenterpriselinuxworkstation-6.6 +windows 64bit developer-build + +[playPauseStop] +opensuse-13.1 64bit +redhatenterpriselinuxworkstation-6.6 +ubuntu-14.04 64bit +windows 64bit developer-build + +[processEOS] +opensuse-13.1 64bit +redhatenterpriselinuxworkstation-6.6 +windows 64bit developer-build + +[deleteLaterAtEOS] +opensuse-13.1 64bit +redhatenterpriselinuxworkstation-6.6 +windows 64bit developer-build + +[volumeAndMuted] +opensuse-13.1 64bit +redhatenterpriselinuxworkstation-6.6 + +[volumeAcrossFiles] +opensuse-13.1 64bit +redhatenterpriselinuxworkstation-6.6 + +[initialVolume] +opensuse-13.1 64bit +redhatenterpriselinuxworkstation-6.6 +windows 64bit developer-build + +[playlist] +opensuse-13.1 64bit +redhatenterpriselinuxworkstation-6.6 diff --git a/tests/auto/integration/qmediaplayerbackend/qmediaplayerbackend.pro b/tests/auto/integration/qmediaplayerbackend/qmediaplayerbackend.pro index 79028d885..a2c1bdf87 100644 --- a/tests/auto/integration/qmediaplayerbackend/qmediaplayerbackend.pro +++ b/tests/auto/integration/qmediaplayerbackend/qmediaplayerbackend.pro @@ -3,7 +3,7 @@ TARGET = tst_qmediaplayerbackend QT += multimedia-private testlib # This is more of a system test -CONFIG += testcase insignificant_test +CONFIG += testcase SOURCES += \ diff --git a/tests/auto/integration/qsoundeffect/BLACKLIST b/tests/auto/integration/qsoundeffect/BLACKLIST new file mode 100644 index 000000000..f290cc8ff --- /dev/null +++ b/tests/auto/integration/qsoundeffect/BLACKLIST @@ -0,0 +1,6 @@ +# QTBUG-46689 + +[testLooping] +ubuntu-14.04 64bit +redhatenterpriselinuxworkstation-6.6 +opensuse-13.1 diff --git a/tests/auto/integration/qsoundeffect/qsoundeffect.pro b/tests/auto/integration/qsoundeffect/qsoundeffect.pro index cb186db20..bce5fc77a 100644 --- a/tests/auto/integration/qsoundeffect/qsoundeffect.pro +++ b/tests/auto/integration/qsoundeffect/qsoundeffect.pro @@ -15,6 +15,6 @@ unix:!mac { TESTDATA += test.wav -win32:CONFIG += insignificant_test # QTBUG-26509 -linux-*:CONFIG += insignificant_test # QTBUG-26748 DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 + +config_pulseaudio: CONFIG += insignificant_testcase # Crashes in QSoundEffectPrivate::sampleReady with bufferAttr == 0 diff --git a/tests/auto/unit/qaudiodecoder/tst_qaudiodecoder.cpp b/tests/auto/unit/qaudiodecoder/tst_qaudiodecoder.cpp index 72ba0484c..5d1e045d0 100644 --- a/tests/auto/unit/qaudiodecoder/tst_qaudiodecoder.cpp +++ b/tests/auto/unit/qaudiodecoder/tst_qaudiodecoder.cpp @@ -128,7 +128,7 @@ void tst_QAudioDecoder::read() QVERIFY(!b.isValid()); // Wait a while - QTRY_COMPARE(d.bufferAvailable(), 1); + QTRY_VERIFY(d.bufferAvailable()); QVERIFY(d.bufferAvailable()); @@ -189,7 +189,7 @@ void tst_QAudioDecoder::stop() QVERIFY(!b.isValid()); // Wait a while - QTRY_COMPARE(d.bufferAvailable(), 1); + QTRY_VERIFY(d.bufferAvailable()); QVERIFY(d.bufferAvailable()); @@ -231,7 +231,7 @@ void tst_QAudioDecoder::format() QVERIFY(!b.isValid()); // Wait a while - QTRY_COMPARE(d.bufferAvailable(), 1); + QTRY_VERIFY(d.bufferAvailable()); b = d.read(); QVERIFY(d.audioFormat() == b.format()); @@ -251,7 +251,7 @@ void tst_QAudioDecoder::format() // Decode again d.start(); - QTRY_COMPARE(d.bufferAvailable(), 1); + QTRY_VERIFY(d.bufferAvailable()); b = d.read(); QVERIFY(d.audioFormat() == f); diff --git a/tests/auto/unit/qdeclarativeaudio/qdeclarativeaudio.pro b/tests/auto/unit/qdeclarativeaudio/qdeclarativeaudio.pro index 6471f7b2a..e36c7dc1f 100644 --- a/tests/auto/unit/qdeclarativeaudio/qdeclarativeaudio.pro +++ b/tests/auto/unit/qdeclarativeaudio/qdeclarativeaudio.pro @@ -13,3 +13,7 @@ SOURCES += \ INCLUDEPATH += ../../../../src/imports/multimedia DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 + +include (../qmultimedia_common/mock.pri) +include (../qmultimedia_common/mockplayer.pri) + diff --git a/tests/auto/unit/qdeclarativeaudio/tst_qdeclarativeaudio.cpp b/tests/auto/unit/qdeclarativeaudio/tst_qdeclarativeaudio.cpp index a257ee7b6..355e25331 100644 --- a/tests/auto/unit/qdeclarativeaudio/tst_qdeclarativeaudio.cpp +++ b/tests/auto/unit/qdeclarativeaudio/tst_qdeclarativeaudio.cpp @@ -38,6 +38,9 @@ #include "qdeclarativeaudio_p.h" #include "qdeclarativemediametadata_p.h" +#include "mockmediaserviceprovider.h" +#include "mockmediaplayerservice.h" + #include <QtMultimedia/qmediametadata.h> #include <qmediaplayercontrol.h> #include <qmediaservice.h> @@ -45,6 +48,8 @@ #include <qmetadatareadercontrol.h> #include <QtGui/qguiapplication.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcomponent.h> class tst_QDeclarativeAudio : public QObject { @@ -73,9 +78,11 @@ private slots: void metaData(); void error(); void loops(); + void audioRole(); }; Q_DECLARE_METATYPE(QDeclarativeAudio::Error); +Q_DECLARE_METATYPE(QDeclarativeAudio::AudioRole); class QtTestMediaPlayerControl : public QMediaPlayerControl { @@ -285,6 +292,7 @@ public: void tst_QDeclarativeAudio::initTestCase() { qRegisterMetaType<QDeclarativeAudio::Error>(); + qRegisterMetaType<QDeclarativeAudio::AudioRole>(); } void tst_QDeclarativeAudio::nullPlayerControl() @@ -1007,6 +1015,47 @@ void tst_QDeclarativeAudio::loops() qDebug() << "Testing version 5"; } +void tst_QDeclarativeAudio::audioRole() +{ + MockMediaPlayerService mockService; + MockMediaServiceProvider mockProvider(&mockService); + QMediaServiceProvider::setDefaultServiceProvider(&mockProvider); + + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0 \n import QtMultimedia 5.6 \n Audio { }", QUrl()); + + { + mockService.setHasAudioRole(false); + QDeclarativeAudio *audio = static_cast<QDeclarativeAudio*>(component.create()); + + QCOMPARE(audio->audioRole(), QDeclarativeAudio::UnknownRole); + QVERIFY(audio->supportedAudioRoles().isArray()); + QVERIFY(audio->supportedAudioRoles().toVariant().toList().isEmpty()); + + QSignalSpy spy(audio, SIGNAL(audioRoleChanged())); + audio->setAudioRole(QDeclarativeAudio::MusicRole); + QCOMPARE(audio->audioRole(), QDeclarativeAudio::UnknownRole); + QCOMPARE(spy.count(), 0); + } + + { + mockService.reset(); + mockService.setHasAudioRole(true); + QDeclarativeAudio *audio = static_cast<QDeclarativeAudio*>(component.create()); + QSignalSpy spy(audio, SIGNAL(audioRoleChanged())); + + QCOMPARE(audio->audioRole(), QDeclarativeAudio::UnknownRole); + QVERIFY(audio->supportedAudioRoles().isArray()); + QVERIFY(!audio->supportedAudioRoles().toVariant().toList().isEmpty()); + + audio->setAudioRole(QDeclarativeAudio::MusicRole); + QCOMPARE(audio->audioRole(), QDeclarativeAudio::MusicRole); + QCOMPARE(mockService.mockAudioRoleControl->audioRole(), QAudio::MusicRole); + QCOMPARE(spy.count(), 1); + } +} + QTEST_MAIN(tst_QDeclarativeAudio) #include "tst_qdeclarativeaudio.moc" diff --git a/tests/auto/unit/qmediaplayer/tst_qmediaplayer.cpp b/tests/auto/unit/qmediaplayer/tst_qmediaplayer.cpp index 0271f1a8f..84248cd46 100644 --- a/tests/auto/unit/qmediaplayer/tst_qmediaplayer.cpp +++ b/tests/auto/unit/qmediaplayer/tst_qmediaplayer.cpp @@ -135,6 +135,7 @@ private slots: void testSupportedMimeTypes(); void testQrc_data(); void testQrc(); + void testAudioRole(); private: void setupCommonTestData(); @@ -1296,5 +1297,45 @@ void tst_QMediaPlayer::testQrc() QCOMPARE(bool(mockService->mockControl->mediaStream()), backendHasStream); } +void tst_QMediaPlayer::testAudioRole() +{ + { + mockService->setHasAudioRole(false); + QMediaPlayer player; + + QCOMPARE(player.audioRole(), QAudio::UnknownRole); + QVERIFY(player.supportedAudioRoles().isEmpty()); + + QSignalSpy spy(&player, SIGNAL(audioRoleChanged(QAudio::Role))); + player.setAudioRole(QAudio::MusicRole); + QCOMPARE(player.audioRole(), QAudio::UnknownRole); + QCOMPARE(spy.count(), 0); + } + + { + mockService->reset(); + mockService->setHasAudioRole(true); + QMediaPlayer player; + QSignalSpy spy(&player, SIGNAL(audioRoleChanged(QAudio::Role))); + + QCOMPARE(player.audioRole(), QAudio::UnknownRole); + QVERIFY(!player.supportedAudioRoles().isEmpty()); + + player.setAudioRole(QAudio::MusicRole); + QCOMPARE(player.audioRole(), QAudio::MusicRole); + QCOMPARE(mockService->mockAudioRoleControl->audioRole(), QAudio::MusicRole); + QCOMPARE(spy.count(), 1); + QCOMPARE(qvariant_cast<QAudio::Role>(spy.last().value(0)), QAudio::MusicRole); + + spy.clear(); + + player.setProperty("audioRole", qVariantFromValue(QAudio::AlarmRole)); + QCOMPARE(qvariant_cast<QAudio::Role>(player.property("audioRole")), QAudio::AlarmRole); + QCOMPARE(mockService->mockAudioRoleControl->audioRole(), QAudio::AlarmRole); + QCOMPARE(spy.count(), 1); + QCOMPARE(qvariant_cast<QAudio::Role>(spy.last().value(0)), QAudio::AlarmRole); + } +} + QTEST_GUILESS_MAIN(tst_QMediaPlayer) #include "tst_qmediaplayer.moc" diff --git a/tests/auto/unit/qmultimedia_common/mockaudiorolecontrol.h b/tests/auto/unit/qmultimedia_common/mockaudiorolecontrol.h new file mode 100644 index 000000000..6ba2328b9 --- /dev/null +++ b/tests/auto/unit/qmultimedia_common/mockaudiorolecontrol.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite 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 MOCKAUDIOROLECONTROL_H +#define MOCKAUDIOROLECONTROL_H + +#include <qaudiorolecontrol.h> + +class MockAudioRoleControl : public QAudioRoleControl +{ + friend class MockMediaPlayerService; + +public: + MockAudioRoleControl() + : QAudioRoleControl() + , m_audioRole(QAudio::UnknownRole) + { + } + + QAudio::Role audioRole() const + { + return m_audioRole; + } + + void setAudioRole(QAudio::Role role) + { + if (role != m_audioRole) + emit audioRoleChanged(m_audioRole = role); + } + + QList<QAudio::Role> supportedAudioRoles() const + { + return QList<QAudio::Role>() << QAudio::MusicRole + << QAudio::AlarmRole + << QAudio::NotificationRole; + } + + QAudio::Role m_audioRole; +}; + +#endif // MOCKAUDIOROLECONTROL_H + diff --git a/tests/auto/unit/qmultimedia_common/mockmediaplayerservice.h b/tests/auto/unit/qmultimedia_common/mockmediaplayerservice.h index d5c6d2e9f..398f92ac2 100644 --- a/tests/auto/unit/qmultimedia_common/mockmediaplayerservice.h +++ b/tests/auto/unit/qmultimedia_common/mockmediaplayerservice.h @@ -42,6 +42,7 @@ #include "mockvideorenderercontrol.h" #include "mockvideoprobecontrol.h" #include "mockvideowindowcontrol.h" +#include "mockaudiorolecontrol.h" class MockMediaPlayerService : public QMediaService { @@ -51,6 +52,7 @@ public: MockMediaPlayerService():QMediaService(0) { mockControl = new MockMediaPlayerControl; + mockAudioRoleControl = new MockAudioRoleControl; mockStreamsControl = new MockStreamsControl; mockNetworkControl = new MockNetworkAccessControl; rendererControl = new MockVideoRendererControl; @@ -58,11 +60,13 @@ public: mockVideoProbeControl = new MockVideoProbeControl; windowControl = new MockVideoWindowControl; windowRef = 0; + enableAudioRole = true; } ~MockMediaPlayerService() { delete mockControl; + delete mockAudioRoleControl; delete mockStreamsControl; delete mockNetworkControl; delete rendererControl; @@ -87,6 +91,8 @@ public: windowRef += 1; return windowControl; } + } else if (enableAudioRole && qstrcmp(iid, QAudioRoleControl_iid) == 0) { + return mockAudioRoleControl; } if (qstrcmp(iid, QMediaNetworkAccessControl_iid) == 0) @@ -125,6 +131,8 @@ public: void selectCurrentConfiguration(QNetworkConfiguration config) { mockNetworkControl->setCurrentConfiguration(config); } + void setHasAudioRole(bool enable) { enableAudioRole = enable; } + void reset() { mockControl->_state = QMediaPlayer::StoppedState; @@ -143,11 +151,15 @@ public: mockControl->_isValid = false; mockControl->_errorString = QString(); + enableAudioRole = true; + mockAudioRoleControl->m_audioRole = QAudio::UnknownRole; + mockNetworkControl->_current = QNetworkConfiguration(); mockNetworkControl->_configurations = QList<QNetworkConfiguration>(); } MockMediaPlayerControl *mockControl; + MockAudioRoleControl *mockAudioRoleControl; MockStreamsControl *mockStreamsControl; MockNetworkAccessControl *mockNetworkControl; MockVideoRendererControl *rendererControl; @@ -155,6 +167,7 @@ public: MockVideoWindowControl *windowControl; int windowRef; int rendererRef; + bool enableAudioRole; }; diff --git a/tests/auto/unit/qmultimedia_common/mockplayer.pri b/tests/auto/unit/qmultimedia_common/mockplayer.pri index 74f289d47..c43fb31e5 100644 --- a/tests/auto/unit/qmultimedia_common/mockplayer.pri +++ b/tests/auto/unit/qmultimedia_common/mockplayer.pri @@ -8,6 +8,7 @@ HEADERS *= \ ../qmultimedia_common/mockmediaplayercontrol.h \ ../qmultimedia_common/mockmediastreamscontrol.h \ ../qmultimedia_common/mockmedianetworkaccesscontrol.h \ - ../qmultimedia_common/mockvideoprobecontrol.h + ../qmultimedia_common/mockvideoprobecontrol.h \ + ../qmultimedia_common/mockaudiorolecontrol.h include(mockvideo.pri) |