diff options
29 files changed, 2437 insertions, 157 deletions
diff --git a/.qmake.conf b/.qmake.conf index a7e09bffc..4baafa83d 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,4 +1,4 @@ load(qt_build_config) CONFIG += qt_example_installs -MODULE_VERSION = 5.1.1 +MODULE_VERSION = 5.2.0 diff --git a/config.tests/opensles/main.cpp b/config.tests/opensles/main.cpp new file mode 100644 index 000000000..45fd2f05d --- /dev/null +++ b/config.tests/opensles/main.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <SLES/OpenSLES.h> + +int main() +{ + return 0; +} diff --git a/config.tests/opensles/opensles.pro b/config.tests/opensles/opensles.pro new file mode 100644 index 000000000..def49c7ac --- /dev/null +++ b/config.tests/opensles/opensles.pro @@ -0,0 +1,2 @@ +LIBS += -lOpenSLES +SOURCES += main.cpp diff --git a/qtmultimedia.pro b/qtmultimedia.pro index ed4a1bfc9..593dff24f 100644 --- a/qtmultimedia.pro +++ b/qtmultimedia.pro @@ -2,6 +2,7 @@ requires(qtHaveModule(gui)) load(configure) qtCompileTest(openal) +qtCompileTest(opensles) win32 { qtCompileTest(directshow) qtCompileTest(wmsdk) diff --git a/src/imports/multimedia/multimedia.cpp b/src/imports/multimedia/multimedia.cpp index 92c9b257d..d2bc829db 100644 --- a/src/imports/multimedia/multimedia.cpp +++ b/src/imports/multimedia/multimedia.cpp @@ -76,6 +76,7 @@ public: qmlRegisterType<QDeclarativeAudio>(uri, 5, 0, "Audio"); qmlRegisterType<QDeclarativeAudio>(uri, 5, 0, "MediaPlayer"); qmlRegisterType<QDeclarativeVideoOutput>(uri, 5, 0, "VideoOutput"); + qmlRegisterType<QDeclarativeVideoOutput, 1>(uri, 5, 1, "VideoOutput"); qmlRegisterType<QDeclarativeRadio>(uri, 5, 0, "Radio"); qmlRegisterType<QDeclarativeRadioData>(uri, 5, 0, "RadioData"); qmlRegisterType<QDeclarativeCamera>(uri, 5, 0, "Camera"); diff --git a/src/imports/multimedia/qdeclarativevideooutput.cpp b/src/imports/multimedia/qdeclarativevideooutput.cpp index 653d45b6d..53c810403 100644 --- a/src/imports/multimedia/qdeclarativevideooutput.cpp +++ b/src/imports/multimedia/qdeclarativevideooutput.cpp @@ -43,6 +43,7 @@ #include "qdeclarativevideooutput_render_p.h" #include "qdeclarativevideooutput_window_p.h" +#include <private/qvideooutputorientationhandler_p.h> #include <QtMultimedia/qmediaobject.h> #include <QtMultimedia/qmediaservice.h> @@ -127,7 +128,9 @@ QDeclarativeVideoOutput::QDeclarativeVideoOutput(QQuickItem *parent) : m_sourceType(NoSource), m_fillMode(PreserveAspectFit), m_geometryDirty(true), - m_orientation(0) + m_orientation(0), + m_autoOrientation(false), + m_screenOrientationHandler(0) { setFlag(ItemHasContents, true); } @@ -349,6 +352,12 @@ void QDeclarativeVideoOutput::_q_updateGeometry() if (m_contentRect != oldContentRect) emit contentRectChanged(); } + +void QDeclarativeVideoOutput::_q_screenOrientationChanged(int orientation) +{ + setOrientation(orientation); +} + /*! \qmlproperty int QtMultimedia5::VideoOutput::orientation @@ -411,6 +420,45 @@ void QDeclarativeVideoOutput::setOrientation(int orientation) } /*! + \qmlproperty int QtMultimedia5::VideoOutput::autoOrientation + + This property allows you to enable and disable auto orientation + of the video stream, so that its orientation always matches + the orientation of the screen. If \c autoOrientation is enabled, + the \c orientation property is overwritten. + + By default \c autoOrientation is disabled. + + \since QtMultimedia 5.1 +*/ +bool QDeclarativeVideoOutput::autoOrientation() const +{ + return m_autoOrientation; +} + +void QDeclarativeVideoOutput::setAutoOrientation(bool autoOrientation) +{ + if (autoOrientation == m_autoOrientation) + return; + + m_autoOrientation = autoOrientation; + if (m_autoOrientation) { + m_screenOrientationHandler = new QVideoOutputOrientationHandler(this); + connect(m_screenOrientationHandler, SIGNAL(orientationChanged(int)), + this, SLOT(_q_screenOrientationChanged(int))); + + _q_screenOrientationChanged(m_screenOrientationHandler->currentOrientation()); + } else { + disconnect(m_screenOrientationHandler, SIGNAL(orientationChanged(int)), + this, SLOT(_q_screenOrientationChanged(int))); + m_screenOrientationHandler->deleteLater(); + m_screenOrientationHandler = 0; + } + + emit autoOrientationChanged(); +} + +/*! \qmlproperty rectangle QtMultimedia5::VideoOutput::contentRect This property holds the item coordinates of the area that diff --git a/src/imports/multimedia/qdeclarativevideooutput_p.h b/src/imports/multimedia/qdeclarativevideooutput_p.h index 51ceff9eb..1de1fccbc 100644 --- a/src/imports/multimedia/qdeclarativevideooutput_p.h +++ b/src/imports/multimedia/qdeclarativevideooutput_p.h @@ -53,6 +53,7 @@ QT_BEGIN_NAMESPACE class QMediaObject; class QMediaService; class QDeclarativeVideoBackend; +class QVideoOutputOrientationHandler; class QDeclarativeVideoOutput : public QQuickItem { @@ -61,6 +62,7 @@ class QDeclarativeVideoOutput : public QQuickItem Q_PROPERTY(QObject* source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) Q_PROPERTY(int orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) + Q_PROPERTY(bool autoOrientation READ autoOrientation WRITE setAutoOrientation NOTIFY autoOrientationChanged REVISION 1) Q_PROPERTY(QRectF sourceRect READ sourceRect NOTIFY sourceRectChanged) Q_PROPERTY(QRectF contentRect READ contentRect NOTIFY contentRectChanged) Q_ENUMS(FillMode) @@ -85,6 +87,9 @@ public: int orientation() const; void setOrientation(int); + bool autoOrientation() const; + void setAutoOrientation(bool); + QRectF sourceRect() const; QRectF contentRect() const; @@ -108,6 +113,7 @@ Q_SIGNALS: void sourceChanged(); void fillModeChanged(QDeclarativeVideoOutput::FillMode); void orientationChanged(); + void autoOrientationChanged(); void sourceRectChanged(); void contentRectChanged(); @@ -120,6 +126,7 @@ private Q_SLOTS: void _q_updateMediaObject(); void _q_updateNativeSize(); void _q_updateGeometry(); + void _q_screenOrientationChanged(int); private: bool createBackend(QMediaService *service); @@ -137,6 +144,8 @@ private: QRectF m_lastRect; // Cache of last rect to avoid recalculating geometry QRectF m_contentRect; // Destination pixel coordinates, unclipped int m_orientation; + bool m_autoOrientation; + QVideoOutputOrientationHandler *m_screenOrientationHandler; QScopedPointer<QDeclarativeVideoBackend> m_backend; }; diff --git a/src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp b/src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp index 9b9c3fb25..d2a4eea3a 100644 --- a/src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp +++ b/src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp @@ -197,23 +197,22 @@ bool QAudioDeviceInfoInternal::testSettings(const QAudioFormat& format) const { // Set nearest to closest settings that do work. // See if what is in settings will work (return value). - int err = 0; - snd_pcm_t* handle; + int err = -1; + snd_pcm_t* pcmHandle; snd_pcm_hw_params_t *params; - QString dev = device; - - QList<QByteArray> devices = QAudioDeviceInfoInternal::availableDevices(QAudio::AudioOutput); + QString dev; - if(dev.compare(QLatin1String("default")) == 0) { #if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) - dev = QLatin1String(devices.first().constData()); + dev = device; + if (dev.compare(QLatin1String("default")) == 0) { + QList<QByteArray> devices = availableDevices(QAudio::AudioOutput); + if (!devices.isEmpty()) + dev = QLatin1String(devices.first().constData()); + } #else + if (dev.compare(QLatin1String("default")) == 0) { dev = QLatin1String("hw:0,0"); -#endif } else { -#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) - dev = device; -#else int idx = 0; char *name; @@ -225,156 +224,81 @@ bool QAudioDeviceInfoInternal::testSettings(const QAudioFormat& format) const idx++; } dev = QString(QLatin1String("hw:%1,0")).arg(idx); -#endif - } - if(mode == QAudio::AudioOutput) { - err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0); - } else { - err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0); - } - if(err < 0) { - handle = 0; - return false; } +#endif - bool testChannel = false; - bool testCodec = false; - bool testSampleRate = false; - bool testType = false; - bool testSize = false; + snd_pcm_stream_t stream = mode == QAudio::AudioOutput + ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE; - int dir = 0; + if (snd_pcm_open(&pcmHandle, dev.toLocal8Bit().constData(), stream, 0) < 0) + return false; - snd_pcm_nonblock( handle, 0 ); - snd_pcm_hw_params_alloca( ¶ms ); - snd_pcm_hw_params_any( handle, params ); + snd_pcm_nonblock(pcmHandle, 0); + snd_pcm_hw_params_alloca(¶ms); + snd_pcm_hw_params_any(pcmHandle, params); // set the values! - snd_pcm_hw_params_set_channels(handle,params,format.channelCount()); - snd_pcm_hw_params_set_rate(handle,params,format.sampleRate(),dir); - - err = -1; - - switch(format.sampleSize()) { - case 8: - if(format.sampleType() == QAudioFormat::SignedInt) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8); - else if(format.sampleType() == QAudioFormat::UnSignedInt) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8); - break; - case 16: - if(format.sampleType() == QAudioFormat::SignedInt) { - if(format.byteOrder() == QAudioFormat::LittleEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE); - else if(format.byteOrder() == QAudioFormat::BigEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE); - } else if(format.sampleType() == QAudioFormat::UnSignedInt) { - if(format.byteOrder() == QAudioFormat::LittleEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE); - else if(format.byteOrder() == QAudioFormat::BigEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE); - } - break; - case 32: - if(format.sampleType() == QAudioFormat::SignedInt) { - if(format.byteOrder() == QAudioFormat::LittleEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE); - else if(format.byteOrder() == QAudioFormat::BigEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE); - } else if(format.sampleType() == QAudioFormat::UnSignedInt) { - if(format.byteOrder() == QAudioFormat::LittleEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE); - else if(format.byteOrder() == QAudioFormat::BigEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE); - } else if (format.sampleType() == QAudioFormat::Float) { - if (format.byteOrder() == QAudioFormat::LittleEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_FLOAT_LE); - else if (format.byteOrder() == QAudioFormat::BigEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_FLOAT_BE); - } + snd_pcm_hw_params_set_channels(pcmHandle, params, format.channelCount()); + snd_pcm_hw_params_set_rate(pcmHandle, params, format.sampleRate(), 0); + + snd_pcm_format_t pcmFormat = SND_PCM_FORMAT_UNKNOWN; + switch (format.sampleSize()) { + case 8: + if (format.sampleType() == QAudioFormat::SignedInt) + pcmFormat = SND_PCM_FORMAT_S8; + else if (format.sampleType() == QAudioFormat::UnSignedInt) + pcmFormat = SND_PCM_FORMAT_U8; + break; + case 16: + if (format.sampleType() == QAudioFormat::SignedInt) { + pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian + ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_S16_BE; + } else if (format.sampleType() == QAudioFormat::UnSignedInt) { + pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian + ? SND_PCM_FORMAT_U16_LE : SND_PCM_FORMAT_U16_BE; + } + break; + case 32: + if (format.sampleType() == QAudioFormat::SignedInt) { + pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian + ? SND_PCM_FORMAT_S32_LE : SND_PCM_FORMAT_S32_BE; + } else if (format.sampleType() == QAudioFormat::UnSignedInt) { + pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian + ? SND_PCM_FORMAT_U32_LE : SND_PCM_FORMAT_U32_BE; + } else if (format.sampleType() == QAudioFormat::Float) { + pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian + ? SND_PCM_FORMAT_FLOAT_LE : SND_PCM_FORMAT_FLOAT_BE; + } } + if (pcmFormat != SND_PCM_FORMAT_UNKNOWN) + err = snd_pcm_hw_params_set_format(pcmHandle, params, pcmFormat); + // For now, just accept only audio/pcm codec - if(!format.codec().startsWith(QLatin1String("audio/pcm"))) { - err=-1; - } else - testCodec = true; - - if (err>=0 && format.channelCount() != -1) { - err = snd_pcm_hw_params_test_channels(handle,params,format.channelCount()); - if(err>=0) - err = snd_pcm_hw_params_set_channels(handle,params,format.channelCount()); - if(err>=0) - testChannel = true; - } + if (!format.codec().startsWith(QLatin1String("audio/pcm"))) + err = -1; - if (err>=0 && format.sampleRate() != -1) { - err = snd_pcm_hw_params_test_rate(handle,params,format.sampleRate(),0); - if(err>=0) - err = snd_pcm_hw_params_set_rate(handle,params,format.sampleRate(),dir); - if(err>=0) - testSampleRate = true; + if (err >= 0 && format.channelCount() != -1) { + err = snd_pcm_hw_params_test_channels(pcmHandle, params, format.channelCount()); + if (err >= 0) + err = snd_pcm_hw_params_set_channels(pcmHandle, params, format.channelCount()); } - if((err>=0 && format.sampleSize() != -1) && - (format.sampleType() != QAudioFormat::Unknown)) { - switch(format.sampleSize()) { - case 8: - if(format.sampleType() == QAudioFormat::SignedInt) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8); - else if(format.sampleType() == QAudioFormat::UnSignedInt) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8); - break; - case 16: - if(format.sampleType() == QAudioFormat::SignedInt) { - if(format.byteOrder() == QAudioFormat::LittleEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE); - else if(format.byteOrder() == QAudioFormat::BigEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE); - } else if(format.sampleType() == QAudioFormat::UnSignedInt) { - if(format.byteOrder() == QAudioFormat::LittleEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE); - else if(format.byteOrder() == QAudioFormat::BigEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE); - } - break; - case 32: - if(format.sampleType() == QAudioFormat::SignedInt) { - if(format.byteOrder() == QAudioFormat::LittleEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE); - else if(format.byteOrder() == QAudioFormat::BigEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE); - } else if(format.sampleType() == QAudioFormat::UnSignedInt) { - if(format.byteOrder() == QAudioFormat::LittleEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE); - else if(format.byteOrder() == QAudioFormat::BigEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE); - } else if (format.sampleType() == QAudioFormat::Float) { - if (format.byteOrder() == QAudioFormat::LittleEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_FLOAT_LE); - else if (format.byteOrder() == QAudioFormat::BigEndian) - err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_FLOAT_BE); - } - } - if(err>=0) { - testSize = true; - testType = true; - } - } - if(err>=0) - err = snd_pcm_hw_params(handle, params); - - if(err == 0) { - // settings work - // close() - if(handle) - snd_pcm_close(handle); - return true; + if (err >= 0 && format.sampleRate() != -1) { + err = snd_pcm_hw_params_test_rate(pcmHandle, params, format.sampleRate(), 0); + if (err >= 0) + err = snd_pcm_hw_params_set_rate(pcmHandle, params, format.sampleRate(), 0); } - if(handle) - snd_pcm_close(handle); - return false; + if (err >= 0 && pcmFormat != SND_PCM_FORMAT_UNKNOWN) + err = snd_pcm_hw_params_set_format(pcmHandle, params, pcmFormat); + + if (err >= 0) + err = snd_pcm_hw_params(pcmHandle, params); + + snd_pcm_close(pcmHandle); + + return (err == 0); } void QAudioDeviceInfoInternal::updateLists() @@ -451,12 +375,10 @@ QList<QByteArray> QAudioDeviceInfoInternal::availableDevices(QAudio::Mode mode) devices.append(deviceName.toLocal8Bit().constData()); } - free(name); - if (descr != NULL) - free(descr); - if (io != NULL) - free(io); + free(descr); + free(io); } + free(name); ++n; } snd_device_name_free_hint(hints); diff --git a/src/multimedia/audio/qaudiooutput_alsa_p.cpp b/src/multimedia/audio/qaudiooutput_alsa_p.cpp index 1384dfdc5..3a779f322 100644 --- a/src/multimedia/audio/qaudiooutput_alsa_p.cpp +++ b/src/multimedia/audio/qaudiooutput_alsa_p.cpp @@ -125,7 +125,7 @@ void QAudioOutputPrivate::async_callback(snd_async_handler_t *ahandler) audioOut = static_cast<QAudioOutputPrivate*> (snd_async_handler_get_callback_private(ahandler)); - if((audioOut->deviceState==QAudio::ActiveState)||(audioOut->resuming)) + if (audioOut && (audioOut->deviceState == QAudio::ActiveState || audioOut->resuming)) audioOut->feedback(); } diff --git a/src/multimedia/audio/qsamplecache_p.cpp b/src/multimedia/audio/qsamplecache_p.cpp index 03ed5710a..39e924f08 100644 --- a/src/multimedia/audio/qsamplecache_p.cpp +++ b/src/multimedia/audio/qsamplecache_p.cpp @@ -129,7 +129,7 @@ QSampleCache::~QSampleCache() foreach (QSample* sample, m_staleSamples) delete sample; // deleting a sample does affect the m_staleSamples list, but foreach copies it - delete m_networkAccessManager; + m_networkAccessManager->deleteLater(); } void QSampleCache::loadingRelease() diff --git a/src/multimedia/doc/qtmultimedia.qdocconf b/src/multimedia/doc/qtmultimedia.qdocconf index b78c91989..5f82a330b 100644 --- a/src/multimedia/doc/qtmultimedia.qdocconf +++ b/src/multimedia/doc/qtmultimedia.qdocconf @@ -44,3 +44,7 @@ sourcedirs += ../.. excludedirs += ../../multimediawidgets depends += qtcore qtdoc qtquick qtqml qtmultimediawidgets + +navigation.landingpage = "Qt Multimedia" +navigation.cppclassespage = "Qt Multimedia C++ Classes" +navigation.qmltypespage = "Qt Multimedia QML Types" diff --git a/src/multimedia/multimedia.pro b/src/multimedia/multimedia.pro index 96b9e79ec..265728552 100644 --- a/src/multimedia/multimedia.pro +++ b/src/multimedia/multimedia.pro @@ -1,5 +1,5 @@ TARGET = QtMultimedia -QT = core-private network gui +QT = core-private network gui-private QMAKE_DOCS = $$PWD/doc/qtmultimedia.qdocconf diff --git a/src/multimedia/video/qvideooutputorientationhandler.cpp b/src/multimedia/video/qvideooutputorientationhandler.cpp new file mode 100644 index 000000000..4c966c02d --- /dev/null +++ b/src/multimedia/video/qvideooutputorientationhandler.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvideooutputorientationhandler_p.h" + +#include <QGuiApplication> +#include <QScreen> +#include <qpa/qplatformscreen.h> + +QT_BEGIN_NAMESPACE + +QVideoOutputOrientationHandler::QVideoOutputOrientationHandler(QObject *parent) + : QObject(parent) + , m_currentOrientation(0) +{ + QScreen *screen = QGuiApplication::primaryScreen(); + + // we want to be informed about all orientation changes + screen->setOrientationUpdateMask(Qt::PortraitOrientation|Qt::LandscapeOrientation + |Qt::InvertedPortraitOrientation|Qt::InvertedLandscapeOrientation); + + connect(screen, SIGNAL(orientationChanged(Qt::ScreenOrientation)), + this, SLOT(screenOrientationChanged(Qt::ScreenOrientation))); + + screenOrientationChanged(screen->orientation()); +} + +int QVideoOutputOrientationHandler::currentOrientation() const +{ + return m_currentOrientation; +} + +void QVideoOutputOrientationHandler::screenOrientationChanged(Qt::ScreenOrientation orientation) +{ + const QScreen *screen = QGuiApplication::primaryScreen(); + const QPlatformScreen *platformScreen = screen->handle(); + + const int angle = (360 - screen->angleBetween(platformScreen->nativeOrientation(), orientation)); + + if (angle == m_currentOrientation) + return; + + m_currentOrientation = angle; + emit orientationChanged(m_currentOrientation); +} + +QT_END_NAMESPACE diff --git a/src/multimedia/video/qvideooutputorientationhandler_p.h b/src/multimedia/video/qvideooutputorientationhandler_p.h new file mode 100644 index 000000000..08a45c646 --- /dev/null +++ b/src/multimedia/video/qvideooutputorientationhandler_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVIDEOOUTPUTORIENTATIONHANDLER_P_H +#define QVIDEOOUTPUTORIENTATIONHANDLER_P_H + +#include <qtmultimediadefs.h> + +#include <QObject> + +QT_BEGIN_NAMESPACE + +class Q_MULTIMEDIA_EXPORT QVideoOutputOrientationHandler : public QObject +{ + Q_OBJECT +public: + explicit QVideoOutputOrientationHandler(QObject *parent = 0); + + int currentOrientation() const; + +signals: + void orientationChanged(int angle); + +private slots: + void screenOrientationChanged(Qt::ScreenOrientation orientation); + +private: + int m_currentOrientation; +}; + +QT_END_NAMESPACE + + +#endif diff --git a/src/multimedia/video/video.pri b/src/multimedia/video/video.pri index 1eaed32c1..161163783 100644 --- a/src/multimedia/video/video.pri +++ b/src/multimedia/video/video.pri @@ -12,6 +12,7 @@ PRIVATE_HEADERS += \ video/qabstractvideobuffer_p.h \ video/qimagevideobuffer_p.h \ video/qmemoryvideobuffer_p.h \ + video/qvideooutputorientationhandler_p.h \ video/qvideosurfaceoutput_p.h SOURCES += \ @@ -20,6 +21,7 @@ SOURCES += \ video/qimagevideobuffer.cpp \ video/qmemoryvideobuffer.cpp \ video/qvideoframe.cpp \ + video/qvideooutputorientationhandler.cpp \ video/qvideosurfaceformat.cpp \ video/qvideosurfaceoutput.cpp \ video/qvideoprobe.cpp diff --git a/src/multimediawidgets/doc/qtmultimediawidgets.qdocconf b/src/multimediawidgets/doc/qtmultimediawidgets.qdocconf index e44a73d77..5648041ad 100644 --- a/src/multimediawidgets/doc/qtmultimediawidgets.qdocconf +++ b/src/multimediawidgets/doc/qtmultimediawidgets.qdocconf @@ -40,3 +40,6 @@ sourcedirs += ../ excludedirs += depends += qtcore qtdoc qtquick qtqml qtmultimedia qtwidgets qtgui + +navigation.landingpage = "Qt Multimedia Widgets" +navigation.cppclassespage = "Qt Multimedia Widgets C++ Classes" diff --git a/src/plugins/opensles/opensles.json b/src/plugins/opensles/opensles.json new file mode 100644 index 000000000..a31d52107 --- /dev/null +++ b/src/plugins/opensles/opensles.json @@ -0,0 +1,3 @@ +{ + "Keys": ["default"] +} diff --git a/src/plugins/opensles/opensles.pro b/src/plugins/opensles/opensles.pro new file mode 100644 index 000000000..53c5a120b --- /dev/null +++ b/src/plugins/opensles/opensles.pro @@ -0,0 +1,25 @@ +TARGET = qtaudio_opensles +QT += multimedia-private + +PLUGIN_TYPE = audio +PLUGIN_CLASS_NAME = QOpenSLESPlugin +load(qt_plugin) + +LIBS += -lOpenSLES + +HEADERS += \ + qopenslesplugin.h \ + qopenslesengine.h \ + qopenslesdeviceinfo.h \ + qopenslesaudioinput.h \ + qopenslesaudiooutput.h + +SOURCES += \ + qopenslesplugin.cpp \ + qopenslesengine.cpp \ + qopenslesdeviceinfo.cpp \ + qopenslesaudioinput.cpp \ + qopenslesaudiooutput.cpp + +OTHER_FILES += \ + opensles.json diff --git a/src/plugins/opensles/qopenslesaudioinput.cpp b/src/plugins/opensles/qopenslesaudioinput.cpp new file mode 100644 index 000000000..d07421f0e --- /dev/null +++ b/src/plugins/opensles/qopenslesaudioinput.cpp @@ -0,0 +1,505 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopenslesaudioinput.h" + +#include "qopenslesengine.h" +#include <qbuffer.h> +#include <qdebug.h> + +#ifdef ANDROID +#include <SLES/OpenSLES_AndroidConfiguration.h> +#endif + +QT_BEGIN_NAMESPACE + +#define NUM_BUFFERS 2 +#define DEFAULT_PERIOD_TIME_MS 50 +#define MINIMUM_PERIOD_TIME_MS 5 + +#ifdef ANDROID +static void bufferQueueCallback(SLAndroidSimpleBufferQueueItf, void *context) +#else +static void bufferQueueCallback(SLBufferQueueItf, void *context) +#endif +{ + // Process buffer in main thread + QMetaObject::invokeMethod(reinterpret_cast<QOpenSLESAudioInput*>(context), "processBuffer"); +} + +QOpenSLESAudioInput::QOpenSLESAudioInput(const QByteArray &device) + : m_device(device) + , m_engine(QOpenSLESEngine::instance()) + , m_recorderObject(0) + , m_recorder(0) + , m_bufferQueue(0) + , m_pullMode(true) + , m_processedBytes(0) + , m_audioSource(0) + , m_bufferIODevice(0) + , m_errorState(QAudio::NoError) + , m_deviceState(QAudio::StoppedState) + , m_lastNotifyTime(0) + , m_volume(1) + , m_bufferSize(0) + , m_periodSize(0) + , m_intervalTime(1000) + , m_buffers(new QByteArray[NUM_BUFFERS]) + , m_currentBuffer(0) +{ +#ifdef ANDROID + if (qstrcmp(device, QT_ANDROID_PRESET_CAMCORDER) == 0) + m_recorderPreset = SL_ANDROID_RECORDING_PRESET_CAMCORDER; + else if (qstrcmp(device, QT_ANDROID_PRESET_VOICE_RECOGNITION) == 0) + m_recorderPreset = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; + else + m_recorderPreset = SL_ANDROID_RECORDING_PRESET_GENERIC; +#endif +} + +QOpenSLESAudioInput::~QOpenSLESAudioInput() +{ + if (m_recorderObject) + (*m_recorderObject)->Destroy(m_recorderObject); + delete[] m_buffers; +} + +QAudio::Error QOpenSLESAudioInput::error() const +{ + return m_errorState; +} + +QAudio::State QOpenSLESAudioInput::state() const +{ + return m_deviceState; +} + +void QOpenSLESAudioInput::setFormat(const QAudioFormat &format) +{ + if (m_deviceState == QAudio::StoppedState) + m_format = format; +} + +QAudioFormat QOpenSLESAudioInput::format() const +{ + return m_format; +} + +void QOpenSLESAudioInput::start(QIODevice *device) +{ + if (m_deviceState != QAudio::StoppedState) + stopRecording(); + + if (!m_pullMode && m_bufferIODevice) { + m_bufferIODevice->close(); + delete m_bufferIODevice; + m_bufferIODevice = 0; + } + + m_pullMode = true; + m_audioSource = device; + + if (startRecording()) { + m_deviceState = QAudio::ActiveState; + } else { + m_deviceState = QAudio::StoppedState; + Q_EMIT errorChanged(m_errorState); + } + + Q_EMIT stateChanged(m_deviceState); +} + +QIODevice *QOpenSLESAudioInput::start() +{ + if (m_deviceState != QAudio::StoppedState) + stopRecording(); + + m_audioSource = 0; + + if (!m_pullMode && m_bufferIODevice) { + m_bufferIODevice->close(); + delete m_bufferIODevice; + } + + m_pullMode = false; + m_pushBuffer.clear(); + m_bufferIODevice = new QBuffer(&m_pushBuffer); + m_bufferIODevice->open(QIODevice::ReadOnly); + + if (startRecording()) { + m_deviceState = QAudio::IdleState; + } else { + m_deviceState = QAudio::StoppedState; + Q_EMIT errorChanged(m_errorState); + m_bufferIODevice->close(); + delete m_bufferIODevice; + m_bufferIODevice = 0; + } + + Q_EMIT stateChanged(m_deviceState); + return m_bufferIODevice; +} + +bool QOpenSLESAudioInput::startRecording() +{ + m_processedBytes = 0; + m_clockStamp.restart(); + m_lastNotifyTime = 0; + + SLresult result; + + // configure audio source + SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, + SL_DEFAULTDEVICEID_AUDIOINPUT, NULL }; + SLDataSource audioSrc = { &loc_dev, NULL }; + + // configure audio sink +#ifdef ANDROID + SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, + NUM_BUFFERS }; +#else + SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, NUM_BUFFERS }; +#endif + + SLDataFormat_PCM format_pcm = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format); + SLDataSink audioSnk = { &loc_bq, &format_pcm }; + + // create audio recorder + // (requires the RECORD_AUDIO permission) +#ifdef ANDROID + const SLInterfaceID id[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }; + const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; +#else + const SLInterfaceID id[1] = { SL_IID_BUFFERQUEUE }; + const SLboolean req[1] = { SL_BOOLEAN_TRUE }; +#endif + + result = (*m_engine->slEngine())->CreateAudioRecorder(m_engine->slEngine(), &m_recorderObject, + &audioSrc, &audioSnk, + sizeof(req) / sizeof(SLboolean), id, req); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::OpenError; + return false; + } + +#ifdef ANDROID + // configure recorder source + SLAndroidConfigurationItf configItf; + result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_ANDROIDCONFIGURATION, + &configItf); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::OpenError; + return false; + } + + result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET, + &m_recorderPreset, sizeof(SLuint32)); + + SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_NONE; + SLuint32 presetSize = 2*sizeof(SLuint32); // intentionally too big + result = (*configItf)->GetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET, + &presetSize, (void*)&presetValue); + + if (result != SL_RESULT_SUCCESS || presetValue == SL_ANDROID_RECORDING_PRESET_NONE) { + m_errorState = QAudio::OpenError; + return false; + } +#endif + + // realize the audio recorder + result = (*m_recorderObject)->Realize(m_recorderObject, SL_BOOLEAN_FALSE); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::OpenError; + return false; + } + + // get the record interface + result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_RECORD, &m_recorder); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::FatalError; + return false; + } + + // get the buffer queue interface +#ifdef ANDROID + SLInterfaceID bufferqueueItfID = SL_IID_ANDROIDSIMPLEBUFFERQUEUE; +#else + SLInterfaceID bufferqueueItfID = SL_IID_BUFFERQUEUE; +#endif + result = (*m_recorderObject)->GetInterface(m_recorderObject, bufferqueueItfID, &m_bufferQueue); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::FatalError; + return false; + } + + // register callback on the buffer queue + result = (*m_bufferQueue)->RegisterCallback(m_bufferQueue, bufferQueueCallback, this); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::FatalError; + return false; + } + + if (m_bufferSize <= 0) { + m_bufferSize = m_format.bytesForDuration(DEFAULT_PERIOD_TIME_MS * 1000); + } else { + int minimumBufSize = m_format.bytesForDuration(MINIMUM_PERIOD_TIME_MS * 1000); + if (m_bufferSize < minimumBufSize) + m_bufferSize = minimumBufSize; + } + + m_periodSize = m_bufferSize; + + // enqueue empty buffers to be filled by the recorder + for (int i = 0; i < NUM_BUFFERS; ++i) { + m_buffers[i].resize(m_periodSize); + + result = (*m_bufferQueue)->Enqueue(m_bufferQueue, m_buffers[i].data(), m_periodSize); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::FatalError; + return false; + } + } + + // start recording + result = (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_RECORDING); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::FatalError; + return false; + } + + m_errorState = QAudio::NoError; + + return true; +} + +void QOpenSLESAudioInput::stop() +{ + if (m_deviceState == QAudio::StoppedState) + return; + + m_deviceState = QAudio::StoppedState; + + stopRecording(); + + m_errorState = QAudio::NoError; + Q_EMIT stateChanged(m_deviceState); +} + +void QOpenSLESAudioInput::stopRecording() +{ + flushBuffers(); + + SLresult result; + result = (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_STOPPED); + result = (*m_bufferQueue)->Clear(m_bufferQueue); + + (*m_recorderObject)->Destroy(m_recorderObject); + m_recorderObject = 0; + + for (int i = 0; i < NUM_BUFFERS; ++i) + m_buffers[i].clear(); + m_currentBuffer = 0; + + if (!m_pullMode && m_bufferIODevice) { + m_bufferIODevice->close(); + delete m_bufferIODevice; + m_bufferIODevice = 0; + m_pushBuffer.clear(); + } +} + +void QOpenSLESAudioInput::suspend() +{ + if (m_deviceState == QAudio::ActiveState) { + m_deviceState = QAudio::SuspendedState; + emit stateChanged(m_deviceState); + + (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_PAUSED); + } +} + +void QOpenSLESAudioInput::resume() +{ + if (m_deviceState == QAudio::SuspendedState || m_deviceState == QAudio::IdleState) { + (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_RECORDING); + + m_deviceState = QAudio::ActiveState; + emit stateChanged(m_deviceState); + } +} + +void QOpenSLESAudioInput::processBuffer() +{ + if (m_deviceState == QAudio::StoppedState || m_deviceState == QAudio::SuspendedState) + return; + + if (m_deviceState != QAudio::ActiveState) { + m_errorState = QAudio::NoError; + m_deviceState = QAudio::ActiveState; + emit stateChanged(m_deviceState); + } + + QByteArray *processedBuffer = &m_buffers[m_currentBuffer]; + writeDataToDevice(processedBuffer->constData(), processedBuffer->size()); + + // Re-enqueue the buffer + SLresult result = (*m_bufferQueue)->Enqueue(m_bufferQueue, + processedBuffer->data(), + processedBuffer->size()); + + m_currentBuffer = (m_currentBuffer + 1) % NUM_BUFFERS; + + // If the buffer queue is empty (shouldn't happen), stop recording. +#ifdef ANDROID + SLAndroidSimpleBufferQueueState state; +#else + SLBufferQueueState state; +#endif + result = (*m_bufferQueue)->GetState(m_bufferQueue, &state); + if (result != SL_RESULT_SUCCESS || state.count == 0) { + stop(); + m_errorState = QAudio::FatalError; + Q_EMIT errorChanged(m_errorState); + } +} + +void QOpenSLESAudioInput::writeDataToDevice(const char *data, int size) +{ + m_processedBytes += size; + + if (m_pullMode) { + // write buffer to the QIODevice + if (m_audioSource->write(data, size) < 0) { + stop(); + m_errorState = QAudio::IOError; + Q_EMIT errorChanged(m_errorState); + } + } else { + // emits readyRead() so user will call read() on QIODevice to get some audio data + if (m_bufferIODevice != 0) { + m_pushBuffer.append(data, size); + Q_EMIT m_bufferIODevice->readyRead(); + } + } + + // Send notify signal if needed + qint64 processedMsecs = processedUSecs() / 1000; + if (m_intervalTime && (processedMsecs - m_lastNotifyTime) >= m_intervalTime) { + Q_EMIT notify(); + m_lastNotifyTime = processedMsecs; + } +} + +void QOpenSLESAudioInput::flushBuffers() +{ + SLmillisecond recorderPos; + (*m_recorder)->GetPosition(m_recorder, &recorderPos); + qint64 devicePos = processedUSecs(); + + qint64 delta = recorderPos * 1000 - devicePos; + + if (delta > 0) + writeDataToDevice(m_buffers[m_currentBuffer].constData(), m_format.bytesForDuration(delta)); +} + +int QOpenSLESAudioInput::bytesReady() const +{ + if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::SuspendedState) + return m_bufferIODevice ? m_bufferIODevice->bytesAvailable() : m_periodSize; + + return 0; +} + +void QOpenSLESAudioInput::setBufferSize(int value) +{ + m_bufferSize = value; +} + +int QOpenSLESAudioInput::bufferSize() const +{ + return m_bufferSize; +} + +int QOpenSLESAudioInput::periodSize() const +{ + return m_periodSize; +} + +void QOpenSLESAudioInput::setNotifyInterval(int ms) +{ + m_intervalTime = qMax(0, ms); +} + +int QOpenSLESAudioInput::notifyInterval() const +{ + return m_intervalTime; +} + +qint64 QOpenSLESAudioInput::processedUSecs() const +{ + return m_format.durationForBytes(m_processedBytes); +} + +qint64 QOpenSLESAudioInput::elapsedUSecs() const +{ + if (m_deviceState == QAudio::StoppedState) + return 0; + + return m_clockStamp.elapsed() * 1000; +} + +void QOpenSLESAudioInput::setVolume(qreal vol) +{ + // Volume interface is not available for the recorder on Android + m_volume = vol; +} + +qreal QOpenSLESAudioInput::volume() const +{ + return m_volume; +} + +void QOpenSLESAudioInput::reset() +{ + stop(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/opensles/qopenslesaudioinput.h b/src/plugins/opensles/qopenslesaudioinput.h new file mode 100644 index 000000000..c71963087 --- /dev/null +++ b/src/plugins/opensles/qopenslesaudioinput.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENSLESAUDIOINPUT_H +#define QOPENSLESAUDIOINPUT_H + +#include <qaudiosystem.h> +#include <QTime> +#include <SLES/OpenSLES.h> + +#ifdef ANDROID +#include <SLES/OpenSLES_Android.h> + +#define QT_ANDROID_PRESET_MIC "mic" +#define QT_ANDROID_PRESET_CAMCORDER "camcorder" +#define QT_ANDROID_PRESET_VOICE_RECOGNITION "voicerecognition" + +#endif + +QT_BEGIN_NAMESPACE + +class QOpenSLESEngine; +class QIODevice; +class QBuffer; + +class QOpenSLESAudioInput : public QAbstractAudioInput +{ + Q_OBJECT + +public: + QOpenSLESAudioInput(const QByteArray &device); + ~QOpenSLESAudioInput(); + + void start(QIODevice *device); + QIODevice *start(); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesReady() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + QAudio::Error error() const; + QAudio::State state() const; + void setFormat(const QAudioFormat &format); + QAudioFormat format() const; + + void setVolume(qreal volume); + qreal volume() const; + +public Q_SLOTS: + void processBuffer(); + +private: + bool startRecording(); + void stopRecording(); + void writeDataToDevice(const char *data, int size); + void flushBuffers(); + + QByteArray m_device; + QOpenSLESEngine *m_engine; + SLObjectItf m_recorderObject; + SLRecordItf m_recorder; +#ifdef ANDROID + SLuint32 m_recorderPreset; + SLAndroidSimpleBufferQueueItf m_bufferQueue; +#else + SLBufferQueueItf m_bufferQueue; +#endif + + bool m_pullMode; + qint64 m_processedBytes; + QIODevice *m_audioSource; + QBuffer *m_bufferIODevice; + QByteArray m_pushBuffer; + QAudioFormat m_format; + QAudio::Error m_errorState; + QAudio::State m_deviceState; + QTime m_clockStamp; + qint64 m_lastNotifyTime; + qreal m_volume; + int m_bufferSize; + int m_periodSize; + int m_intervalTime; + QByteArray *m_buffers; + int m_currentBuffer; +}; + +QT_END_NAMESPACE + +#endif // QOPENSLESAUDIOINPUT_H diff --git a/src/plugins/opensles/qopenslesaudiooutput.cpp b/src/plugins/opensles/qopenslesaudiooutput.cpp new file mode 100644 index 000000000..908e299c1 --- /dev/null +++ b/src/plugins/opensles/qopenslesaudiooutput.cpp @@ -0,0 +1,628 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopenslesaudiooutput.h" +#include "qopenslesengine.h" +#include <QDebug> +#include <qmath.h> + +#ifdef ANDROID +#include <SLES/OpenSLES_Android.h> +#include <SLES/OpenSLES_AndroidConfiguration.h> +#endif // ANDROID + +#define BUFFER_COUNT 2 +#define DEFAULT_PERIOD_TIME_MS 50 +#define MINIMUM_PERIOD_TIME_MS 5 + +QT_BEGIN_NAMESPACE + +QMap<QString, qint32> QOpenSLESAudioOutput::m_categories; + +QOpenSLESAudioOutput::QOpenSLESAudioOutput(const QByteArray &device) + : m_deviceName(device), + m_state(QAudio::StoppedState), + m_error(QAudio::NoError), + m_outputMixObject(Q_NULLPTR), + m_playerObject(Q_NULLPTR), + m_playItf(Q_NULLPTR), + m_volumeItf(Q_NULLPTR), + m_bufferQueueItf(Q_NULLPTR), + m_audioSource(Q_NULLPTR), + m_buffers(Q_NULLPTR), + m_volume(1.0), + m_pullMode(false), + m_nextBuffer(0), + m_bufferSize(0), + m_notifyInterval(1000), + m_periodSize(0), + m_elapsedTime(0), + m_processedBytes(0), + m_availableBuffers(BUFFER_COUNT) +{ +#ifndef ANDROID + m_streamType = -1; +#else + m_streamType = SL_ANDROID_STREAM_MEDIA; + m_category = QLatin1String("media"); +#endif // ANDROID +} + +QOpenSLESAudioOutput::~QOpenSLESAudioOutput() +{ + destroyPlayer(); +} + +QAudio::Error QOpenSLESAudioOutput::error() const +{ + return m_error; +} + +QAudio::State QOpenSLESAudioOutput::state() const +{ + return m_state; +} + +void QOpenSLESAudioOutput::start(QIODevice *device) +{ + Q_ASSERT(device); + destroyPlayer(); + + m_pullMode = true; + + if (!preparePlayer()) + return; + + m_audioSource = device; + setState(QAudio::ActiveState); + setError(QAudio::NoError); + + // Attempt to fill buffers first. + for (int i = 0; i != BUFFER_COUNT; ++i) { + const int index = i * m_bufferSize; + const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize); + if (readSize && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf, + m_buffers + index, + readSize)) { + setError(QAudio::FatalError); + destroyPlayer(); + return; + } + m_processedBytes += readSize; + } + + // Change to state to playing. + // We need to do this after filling the buffers or processedBytes might get corrupted. + if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) { + setError(QAudio::FatalError); + destroyPlayer(); + } +} + +QIODevice *QOpenSLESAudioOutput::start() +{ + destroyPlayer(); + + m_pullMode = false; + + if (!preparePlayer()) + return Q_NULLPTR; + + m_audioSource = new SLIODevicePrivate(this); + m_audioSource->open(QIODevice::WriteOnly | QIODevice::Unbuffered); + + // Change to state to playing + if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) { + setError(QAudio::FatalError); + destroyPlayer(); + } + + setState(QAudio::IdleState); + return m_audioSource; +} + +void QOpenSLESAudioOutput::stop() +{ + if (m_state == QAudio::StoppedState) + return; + + destroyPlayer(); + setError(QAudio::NoError); +} + +int QOpenSLESAudioOutput::bytesFree() const +{ + if (m_state != QAudio::ActiveState && m_state != QAudio::IdleState) + return 0; + + return m_availableBuffers.load() ? m_bufferSize : 0; +} + +int QOpenSLESAudioOutput::periodSize() const +{ + return m_periodSize; +} + +void QOpenSLESAudioOutput::setBufferSize(int value) +{ + if (m_state != QAudio::StoppedState) + return; + + m_bufferSize = value; +} + +int QOpenSLESAudioOutput::bufferSize() const +{ + return m_bufferSize; +} + +void QOpenSLESAudioOutput::setNotifyInterval(int ms) +{ + m_notifyInterval = ms > 0 ? ms : 0; +} + +int QOpenSLESAudioOutput::notifyInterval() const +{ + return m_notifyInterval; +} + +qint64 QOpenSLESAudioOutput::processedUSecs() const +{ + if (m_state == QAudio::IdleState || m_state == QAudio::SuspendedState) + return m_format.durationForBytes(m_processedBytes); + + SLmillisecond processMSec = 0; + if (m_playItf) + (*m_playItf)->GetPosition(m_playItf, &processMSec); + + return processMSec * 1000; +} + +void QOpenSLESAudioOutput::resume() +{ + if (m_state != QAudio::SuspendedState) + return; + + if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) { + setError(QAudio::FatalError); + destroyPlayer(); + return; + } + + setState(QAudio::ActiveState); + setError(QAudio::NoError); +} + +void QOpenSLESAudioOutput::setFormat(const QAudioFormat &format) +{ + m_format = format; +} + +QAudioFormat QOpenSLESAudioOutput::format() const +{ + return m_format; +} + +void QOpenSLESAudioOutput::suspend() +{ + if (m_state != QAudio::ActiveState && m_state != QAudio::IdleState) + return; + + if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PAUSED)) { + setError(QAudio::FatalError); + destroyPlayer(); + return; + } + + setState(QAudio::SuspendedState); + setError(QAudio::NoError); +} + +qint64 QOpenSLESAudioOutput::elapsedUSecs() const +{ + if (m_state == QAudio::StoppedState) + return 0; + + return m_clockStamp.elapsed() * 1000; +} + +void QOpenSLESAudioOutput::reset() +{ + destroyPlayer(); +} + +void QOpenSLESAudioOutput::setVolume(qreal vol) +{ + m_volume = qBound(qreal(0.0), vol, qreal(1.0)); + const SLmillibel newVolume = adjustVolume(m_volume); + if (m_volumeItf && SL_RESULT_SUCCESS != (*m_volumeItf)->SetVolumeLevel(m_volumeItf, newVolume)) + qWarning() << "Unable to change volume"; +} + +qreal QOpenSLESAudioOutput::volume() const +{ + return m_volume; +} + +void QOpenSLESAudioOutput::setCategory(const QString &category) +{ +#ifndef ANDROID + Q_UNUSED(category); +#else + if (m_categories.isEmpty()) { + m_categories.insert(QLatin1String("voice"), SL_ANDROID_STREAM_VOICE); + m_categories.insert(QLatin1String("system"), SL_ANDROID_STREAM_SYSTEM); + m_categories.insert(QLatin1String("ring"), SL_ANDROID_STREAM_RING); + m_categories.insert(QLatin1String("media"), SL_ANDROID_STREAM_MEDIA); + m_categories.insert(QLatin1String("alarm"), SL_ANDROID_STREAM_ALARM); + m_categories.insert(QLatin1String("notification"), SL_ANDROID_STREAM_NOTIFICATION); + } + + const SLint32 streamType = m_categories.value(category, -1); + if (streamType == -1) { + qWarning() << "Unknown category" << category + << ", available categories are:" << m_categories.keys() + << ". Defaulting to category \"media\""; + return; + } + + m_streamType = streamType; + m_category = category; +#endif // ANDROID +} + +QString QOpenSLESAudioOutput::category() const +{ + return m_category; +} + +void QOpenSLESAudioOutput::onEOSEvent() +{ + if (m_state != QAudio::ActiveState) + return; + + SLBufferQueueState state; + if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->GetState(m_bufferQueueItf, &state)) + return; + + if (state.count > 0) + return; + + setState(QAudio::IdleState); + setError(QAudio::UnderrunError); +} + +void QOpenSLESAudioOutput::bufferAvailable(quint32 count, quint32 playIndex) +{ + Q_UNUSED(count); + Q_UNUSED(playIndex); + + if (m_state == QAudio::StoppedState) + return; + + if (!m_pullMode) { + m_availableBuffers.fetchAndAddRelaxed(1); + return; + } + + const int index = m_nextBuffer * m_bufferSize; + const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize); + + if (1 > readSize) + return; + + if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf, + m_buffers + index, + readSize)) { + setError(QAudio::FatalError); + destroyPlayer(); + return; + } + + m_processedBytes += readSize; + m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT; +} + +void QOpenSLESAudioOutput::playCallback(SLPlayItf player, void *ctx, SLuint32 event) +{ + Q_UNUSED(player); + QOpenSLESAudioOutput *audioOutput = reinterpret_cast<QOpenSLESAudioOutput *>(ctx); + if (event & SL_PLAYEVENT_HEADATEND) + QMetaObject::invokeMethod(audioOutput, "onEOSEvent", Qt::QueuedConnection); + if (event & SL_PLAYEVENT_HEADATNEWPOS) + Q_EMIT audioOutput->notify(); + +} + +void QOpenSLESAudioOutput::bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx) +{ + SLBufferQueueState state; + (*bufferQueue)->GetState(bufferQueue, &state); + QOpenSLESAudioOutput *audioOutput = reinterpret_cast<QOpenSLESAudioOutput *>(ctx); + audioOutput->bufferAvailable(state.count, state.playIndex); +} + +bool QOpenSLESAudioOutput::preparePlayer() +{ + SLEngineItf engine = QOpenSLESEngine::instance()->slEngine(); + if (!engine) { + qWarning() << "No engine"; + setError(QAudio::FatalError); + return false; + } + + SLDataLocator_BufferQueue bufferQueueLocator = { SL_DATALOCATOR_BUFFERQUEUE, BUFFER_COUNT }; + SLDataFormat_PCM pcmFormat = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format); + + SLDataSource audioSrc = { &bufferQueueLocator, &pcmFormat }; + + // OutputMix + if (SL_RESULT_SUCCESS != (*engine)->CreateOutputMix(engine, + &m_outputMixObject, + 0, + Q_NULLPTR, + Q_NULLPTR)) { + qWarning() << "Unable to create output mix"; + setError(QAudio::FatalError); + return false; + } + + if (SL_RESULT_SUCCESS != (*m_outputMixObject)->Realize(m_outputMixObject, SL_BOOLEAN_FALSE)) { + qWarning() << "Unable to initialize output mix"; + setError(QAudio::FatalError); + return false; + } + + SLDataLocator_OutputMix outputMixLocator = { SL_DATALOCATOR_OUTPUTMIX, m_outputMixObject }; + SLDataSink audioSink = { &outputMixLocator, Q_NULLPTR }; + +#ifndef ANDROID + const int iids = 2; + const SLInterfaceID ids[iids] = { SL_IID_BUFFERQUEUE, SL_IID_VOLUME }; + const SLboolean req[iids] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; +#else + const int iids = 3; + const SLInterfaceID ids[iids] = { SL_IID_BUFFERQUEUE, + SL_IID_VOLUME, + SL_IID_ANDROIDCONFIGURATION }; + const SLboolean req[iids] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; +#endif // ANDROID + + // AudioPlayer + if (SL_RESULT_SUCCESS != (*engine)->CreateAudioPlayer(engine, + &m_playerObject, + &audioSrc, + &audioSink, + iids, + ids, + req)) { + qWarning() << "Unable to create AudioPlayer"; + setError(QAudio::OpenError); + return false; + } + +#ifdef ANDROID + // Set profile/category + SLAndroidConfigurationItf playerConfig; + if (SL_RESULT_SUCCESS == (*m_playerObject)->GetInterface(m_playerObject, + SL_IID_ANDROIDCONFIGURATION, + &playerConfig)) { + (*playerConfig)->SetConfiguration(playerConfig, + SL_ANDROID_KEY_STREAM_TYPE, + &m_streamType, + sizeof(SLint32)); + } +#endif // ANDROID + + if (SL_RESULT_SUCCESS != (*m_playerObject)->Realize(m_playerObject, SL_BOOLEAN_FALSE)) { + qWarning() << "Unable to initialize AudioPlayer"; + setError(QAudio::OpenError); + return false; + } + + // Buffer interface + if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject, + SL_IID_BUFFERQUEUE, + &m_bufferQueueItf)) { + setError(QAudio::FatalError); + return false; + } + + if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->RegisterCallback(m_bufferQueueItf, + bufferQueueCallback, + this)) { + setError(QAudio::FatalError); + return false; + } + + // Play interface + if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject, + SL_IID_PLAY, + &m_playItf)) { + setError(QAudio::FatalError); + return false; + } + + if (SL_RESULT_SUCCESS != (*m_playItf)->RegisterCallback(m_playItf, playCallback, this)) { + setError(QAudio::FatalError); + return false; + } + + SLuint32 mask = SL_PLAYEVENT_HEADATEND; + if (m_notifyInterval && SL_RESULT_SUCCESS == (*m_playItf)->SetPositionUpdatePeriod(m_playItf, + m_notifyInterval)) { + mask |= SL_PLAYEVENT_HEADATNEWPOS; + } + + if (SL_RESULT_SUCCESS != (*m_playItf)->SetCallbackEventsMask(m_playItf, mask)) { + setError(QAudio::FatalError); + return false; + } + + // Volume interface + if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject, + SL_IID_VOLUME, + &m_volumeItf)) { + setError(QAudio::FatalError); + return false; + } + + setVolume(m_volume); + + // Buffer size + if (m_bufferSize <= 0) { + m_bufferSize = m_format.bytesForDuration(DEFAULT_PERIOD_TIME_MS * 1000); + } else { + const int minimumBufSize = m_format.bytesForDuration(MINIMUM_PERIOD_TIME_MS * 1000); + if (m_bufferSize < minimumBufSize) + m_bufferSize = minimumBufSize; + } + + m_periodSize = m_bufferSize; + + if (!m_buffers) + m_buffers = new char[BUFFER_COUNT * m_bufferSize]; + + m_clockStamp.restart(); + setError(QAudio::NoError); + + return true; +} + +void QOpenSLESAudioOutput::destroyPlayer() +{ + setState(QAudio::StoppedState); + + // We need to change the state manually... + if (m_playItf) + (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_STOPPED); + + if (m_bufferQueueItf && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Clear(m_bufferQueueItf)) + qWarning() << "Unable to clear buffer"; + + if (m_playerObject) { + (*m_playerObject)->Destroy(m_playerObject); + m_playerObject = Q_NULLPTR; + } + + if (m_outputMixObject) { + (*m_outputMixObject)->Destroy(m_outputMixObject); + m_outputMixObject = Q_NULLPTR; + } + + if (!m_pullMode && m_audioSource) { + m_audioSource->close(); + delete m_audioSource; + m_audioSource = Q_NULLPTR; + } + + delete [] m_buffers; + m_buffers = Q_NULLPTR; + m_processedBytes = 0; + m_nextBuffer = 0; + m_availableBuffers = BUFFER_COUNT; + m_playItf = Q_NULLPTR; + m_volumeItf = Q_NULLPTR; + m_bufferQueueItf = Q_NULLPTR; +} + +qint64 QOpenSLESAudioOutput::writeData(const char *data, qint64 len) +{ + if (!len) + return 0; + + if (len > m_bufferSize) + len = m_bufferSize; + + const int index = m_nextBuffer * m_bufferSize; + ::memcpy(m_buffers + index, data, len); + const SLuint32 res = (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf, + m_buffers + index, + len); + + if (res == SL_RESULT_BUFFER_INSUFFICIENT) + return 0; + + if (res != SL_RESULT_SUCCESS) { + setError(QAudio::FatalError); + destroyPlayer(); + return -1; + } + + m_processedBytes += len; + m_availableBuffers.fetchAndAddRelaxed(-1); + setState(QAudio::ActiveState); + setError(QAudio::NoError); + m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT; + + return len; +} + +inline void QOpenSLESAudioOutput::setState(QAudio::State state) +{ + if (m_state == state) + return; + + m_state = state; + Q_EMIT stateChanged(m_state); +} + +inline void QOpenSLESAudioOutput::setError(QAudio::Error error) +{ + if (m_error == error) + return; + + m_error = error; + Q_EMIT errorChanged(m_error); +} + +inline SLmillibel QOpenSLESAudioOutput::adjustVolume(qreal vol) +{ + if (qFuzzyIsNull(vol)) + return SL_MILLIBEL_MIN; + + if (qFuzzyCompare(vol, qreal(1.0))) + return 0; + + return SL_MILLIBEL_MIN + ((1 - (qLn(10 - (vol * 10)) / qLn(10))) * SL_MILLIBEL_MAX); +} + +QT_END_NAMESPACE diff --git a/src/plugins/opensles/qopenslesaudiooutput.h b/src/plugins/opensles/qopenslesaudiooutput.h new file mode 100644 index 000000000..b0f01fa22 --- /dev/null +++ b/src/plugins/opensles/qopenslesaudiooutput.h @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENSLESAUDIOOUTPUT_H +#define QOPENSLESAUDIOOUTPUT_H + +#include <qaudiosystem.h> +#include <SLES/OpenSLES.h> +#include <qbytearray.h> +#include <qmap.h> +#include <QTime> + +QT_BEGIN_NAMESPACE + +class QOpenSLESAudioOutput : public QAbstractAudioOutput +{ + Q_OBJECT + +public: + QOpenSLESAudioOutput(const QByteArray &device); + ~QOpenSLESAudioOutput(); + + void start(QIODevice *device) Q_DECL_OVERRIDE; + QIODevice *start() Q_DECL_OVERRIDE; + void stop() Q_DECL_OVERRIDE; + void reset() Q_DECL_OVERRIDE; + void suspend() Q_DECL_OVERRIDE; + void resume() Q_DECL_OVERRIDE; + int bytesFree() const Q_DECL_OVERRIDE; + int periodSize() const Q_DECL_OVERRIDE; + void setBufferSize(int value) Q_DECL_OVERRIDE; + int bufferSize() const Q_DECL_OVERRIDE; + void setNotifyInterval(int milliSeconds) Q_DECL_OVERRIDE; + int notifyInterval() const Q_DECL_OVERRIDE; + qint64 processedUSecs() const Q_DECL_OVERRIDE; + qint64 elapsedUSecs() const Q_DECL_OVERRIDE; + QAudio::Error error() const Q_DECL_OVERRIDE; + QAudio::State state() const Q_DECL_OVERRIDE; + void setFormat(const QAudioFormat &format) Q_DECL_OVERRIDE; + QAudioFormat format() const Q_DECL_OVERRIDE; + + void setVolume(qreal volume) Q_DECL_OVERRIDE; + qreal volume() const Q_DECL_OVERRIDE; + + void setCategory(const QString &category) Q_DECL_OVERRIDE; + QString category() const Q_DECL_OVERRIDE; + +private: + friend class SLIODevicePrivate; + + Q_INVOKABLE void onEOSEvent(); + void bufferAvailable(quint32 count, quint32 playIndex); + + static void playCallback(SLPlayItf playItf, void *ctx, SLuint32 event); + static void bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx); + + bool preparePlayer(); + void destroyPlayer(); + qint64 writeData(const char *data, qint64 len); + + void setState(QAudio::State state); + void setError(QAudio::Error error); + + SLmillibel adjustVolume(qreal vol); + + QByteArray m_deviceName; + QAudio::State m_state; + QAudio::Error m_error; + SLObjectItf m_outputMixObject; + SLObjectItf m_playerObject; + SLPlayItf m_playItf; + SLVolumeItf m_volumeItf; + SLBufferQueueItf m_bufferQueueItf; + QIODevice *m_audioSource; + char *m_buffers; + qreal m_volume; + bool m_pullMode; + int m_nextBuffer; + int m_bufferSize; + int m_notifyInterval; + int m_periodSize; + qint64 m_elapsedTime; + qint64 m_processedBytes; + QAtomicInt m_availableBuffers; + + qint32 m_streamType; + QTime m_clockStamp; + QAudioFormat m_format; + QString m_category; + static QMap<QString, qint32> m_categories; +}; + +class SLIODevicePrivate : public QIODevice +{ + Q_OBJECT + +public: + inline SLIODevicePrivate(QOpenSLESAudioOutput *audio) : m_audioDevice(audio) {} + inline ~SLIODevicePrivate() Q_DECL_OVERRIDE {} + +protected: + inline qint64 readData(char *, qint64) Q_DECL_OVERRIDE { return 0; } + inline qint64 writeData(const char *data, qint64 len) Q_DECL_OVERRIDE; + +private: + QOpenSLESAudioOutput *m_audioDevice; +}; + +qint64 SLIODevicePrivate::writeData(const char *data, qint64 len) +{ + Q_ASSERT(m_audioDevice); + return m_audioDevice->writeData(data, len); +} + +QT_END_NAMESPACE + +#endif // QOPENSLESAUDIOOUTPUT_H diff --git a/src/plugins/opensles/qopenslesdeviceinfo.cpp b/src/plugins/opensles/qopenslesdeviceinfo.cpp new file mode 100644 index 000000000..8301beaa5 --- /dev/null +++ b/src/plugins/opensles/qopenslesdeviceinfo.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopenslesdeviceinfo.h" + +#include "qopenslesengine.h" + +QT_BEGIN_NAMESPACE + +QOpenSLESDeviceInfo::QOpenSLESDeviceInfo(const QByteArray &device, QAudio::Mode mode) + : m_engine(QOpenSLESEngine::instance()) + , m_device(device) + , m_mode(mode) +{ +} + +bool QOpenSLESDeviceInfo::isFormatSupported(const QAudioFormat &format) const +{ + QOpenSLESDeviceInfo *that = const_cast<QOpenSLESDeviceInfo*>(this); + return that->supportedCodecs().contains(format.codec()) + && that->supportedSampleRates().contains(format.sampleRate()) + && that->supportedChannelCounts().contains(format.channelCount()) + && that->supportedSampleSizes().contains(format.sampleSize()) + && that->supportedByteOrders().contains(format.byteOrder()) + && that->supportedSampleTypes().contains(format.sampleType()); +} + +QAudioFormat QOpenSLESDeviceInfo::preferredFormat() const +{ + QAudioFormat format; + format.setCodec(QStringLiteral("audio/pcm")); + format.setSampleSize(16); + format.setSampleType(QAudioFormat::SignedInt); + format.setSampleRate(44100); + format.setChannelCount(m_mode == QAudio::AudioInput ? 1 : 2); + return format; +} + +QString QOpenSLESDeviceInfo::deviceName() const +{ + return m_device; +} + +QStringList QOpenSLESDeviceInfo::supportedCodecs() +{ + return QStringList() << QStringLiteral("audio/pcm"); +} + +QList<int> QOpenSLESDeviceInfo::supportedSampleRates() +{ + return m_engine->supportedSampleRates(m_mode); +} + +QList<int> QOpenSLESDeviceInfo::supportedChannelCounts() +{ + return m_engine->supportedChannelCounts(m_mode); +} + +QList<int> QOpenSLESDeviceInfo::supportedSampleSizes() +{ + if (m_mode == QAudio::AudioInput) + return QList<int>() << 16; + else + return QList<int>() << 8 << 16; +} + +QList<QAudioFormat::Endian> QOpenSLESDeviceInfo::supportedByteOrders() +{ + return QList<QAudioFormat::Endian>() << QAudioFormat::LittleEndian; +} + +QList<QAudioFormat::SampleType> QOpenSLESDeviceInfo::supportedSampleTypes() +{ + return QList<QAudioFormat::SampleType>() << QAudioFormat::SignedInt; +} + +QT_END_NAMESPACE diff --git a/src/plugins/opensles/qopenslesdeviceinfo.h b/src/plugins/opensles/qopenslesdeviceinfo.h new file mode 100644 index 000000000..15a5c52f3 --- /dev/null +++ b/src/plugins/opensles/qopenslesdeviceinfo.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENSLESDEVICEINFO_H +#define QOPENSLESDEVICEINFO_H + +#include <qaudiosystem.h> + +QT_BEGIN_NAMESPACE + +class QOpenSLESEngine; + +class QOpenSLESDeviceInfo : public QAbstractAudioDeviceInfo +{ + Q_OBJECT + +public: + QOpenSLESDeviceInfo(const QByteArray &device, QAudio::Mode mode); + ~QOpenSLESDeviceInfo() {} + + QAudioFormat preferredFormat() const; + bool isFormatSupported(const QAudioFormat &format) const; + QString deviceName() const; + QStringList supportedCodecs(); + QList<int> supportedSampleRates(); + QList<int> supportedChannelCounts(); + QList<int> supportedSampleSizes(); + QList<QAudioFormat::Endian> supportedByteOrders(); + QList<QAudioFormat::SampleType> supportedSampleTypes(); + +private: + QOpenSLESEngine *m_engine; + QByteArray m_device; + QAudio::Mode m_mode; +}; + +QT_END_NAMESPACE + +#endif // QOPENSLESDEVICEINFO_H diff --git a/src/plugins/opensles/qopenslesengine.cpp b/src/plugins/opensles/qopenslesengine.cpp new file mode 100644 index 000000000..056b51e26 --- /dev/null +++ b/src/plugins/opensles/qopenslesengine.cpp @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopenslesengine.h" + +#include "qopenslesaudioinput.h" +#include <qdebug.h> + +#ifdef ANDROID +#include <SLES/OpenSLES_Android.h> +#endif + +#define CheckError(message) if (result != SL_RESULT_SUCCESS) { qWarning(message); return; } + +Q_GLOBAL_STATIC(QOpenSLESEngine, openslesEngine); + +QOpenSLESEngine::QOpenSLESEngine() + : m_engineObject(0) + , m_engine(0) +{ + SLresult result; + + result = slCreateEngine(&m_engineObject, 0, 0, 0, 0, 0); + CheckError("Failed to create engine"); + + result = (*m_engineObject)->Realize(m_engineObject, SL_BOOLEAN_FALSE); + CheckError("Failed to realize engine"); + + result = (*m_engineObject)->GetInterface(m_engineObject, SL_IID_ENGINE, &m_engine); + CheckError("Failed to get engine interface"); + + checkSupportedInputFormats(); +} + +QOpenSLESEngine::~QOpenSLESEngine() +{ + if (m_engineObject) + (*m_engineObject)->Destroy(m_engineObject); +} + +QOpenSLESEngine *QOpenSLESEngine::instance() +{ + return openslesEngine(); +} + +SLDataFormat_PCM QOpenSLESEngine::audioFormatToSLFormatPCM(const QAudioFormat &format) +{ + SLDataFormat_PCM format_pcm; + format_pcm.formatType = SL_DATAFORMAT_PCM; + format_pcm.numChannels = format.channelCount(); + format_pcm.samplesPerSec = format.sampleRate() * 1000; + format_pcm.bitsPerSample = format.sampleSize(); + format_pcm.containerSize = format.sampleSize(); + format_pcm.channelMask = (format.channelCount() == 1 ? + SL_SPEAKER_FRONT_CENTER : + SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT); + format_pcm.endianness = (format.byteOrder() == QAudioFormat::LittleEndian ? + SL_BYTEORDER_LITTLEENDIAN : + SL_BYTEORDER_BIGENDIAN); + return format_pcm; + +} + +QList<QByteArray> QOpenSLESEngine::availableDevices(QAudio::Mode mode) const +{ + QList<QByteArray> devices; + if (mode == QAudio::AudioInput) { +#ifdef ANDROID + devices << QT_ANDROID_PRESET_MIC + << QT_ANDROID_PRESET_CAMCORDER + << QT_ANDROID_PRESET_VOICE_RECOGNITION; +#else + devices << "default"; +#endif + } else { + devices << "default"; + } + return devices; +} + +QList<int> QOpenSLESEngine::supportedChannelCounts(QAudio::Mode mode) const +{ + if (mode == QAudio::AudioInput) + return m_supportedInputChannelCounts; + else + return QList<int>() << 1 << 2; +} + +QList<int> QOpenSLESEngine::supportedSampleRates(QAudio::Mode mode) const +{ + if (mode == QAudio::AudioInput) { + return m_supportedInputSampleRates; + } else { + return QList<int>() << 8000 << 11025 << 12000 << 16000 << 22050 + << 24000 << 32000 << 44100 << 48000; + } +} + +void QOpenSLESEngine::checkSupportedInputFormats() +{ + m_supportedInputChannelCounts = QList<int>() << 1; + m_supportedInputSampleRates.clear(); + + SLDataFormat_PCM defaultFormat; + defaultFormat.formatType = SL_DATAFORMAT_PCM; + defaultFormat.numChannels = 1; + defaultFormat.samplesPerSec = SL_SAMPLINGRATE_44_1; + defaultFormat.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; + defaultFormat.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; + defaultFormat.channelMask = SL_SPEAKER_FRONT_CENTER; + defaultFormat.endianness = SL_BYTEORDER_LITTLEENDIAN; + + const SLuint32 rates[9] = { SL_SAMPLINGRATE_8, + SL_SAMPLINGRATE_11_025, + SL_SAMPLINGRATE_12, + SL_SAMPLINGRATE_16, + SL_SAMPLINGRATE_22_05, + SL_SAMPLINGRATE_24, + SL_SAMPLINGRATE_32, + SL_SAMPLINGRATE_44_1, + SL_SAMPLINGRATE_48 }; + + + // Test sampling rates + for (int i = 0 ; i < 9; ++i) { + SLDataFormat_PCM format = defaultFormat; + format.samplesPerSec = rates[i]; + + if (inputFormatIsSupported(format)) + m_supportedInputSampleRates.append(rates[i] / 1000); + + } + + // Test if stereo is supported + { + SLDataFormat_PCM format = defaultFormat; + format.numChannels = 2; + format.channelMask = 0; + if (inputFormatIsSupported(format)) + m_supportedInputChannelCounts.append(2); + } +} + +bool QOpenSLESEngine::inputFormatIsSupported(SLDataFormat_PCM format) +{ + SLresult result; + SLObjectItf recorder = 0; + SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, + SL_DEFAULTDEVICEID_AUDIOINPUT, NULL }; + SLDataSource audioSrc = { &loc_dev, NULL }; + +#ifdef ANDROID + SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 }; +#else + SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, 1 }; +#endif + SLDataSink audioSnk = { &loc_bq, &format }; + + result = (*m_engine)->CreateAudioRecorder(m_engine, &recorder, &audioSrc, &audioSnk, 0, 0, 0); + if (result == SL_RESULT_SUCCESS) + result = (*recorder)->Realize(recorder, false); + + if (result == SL_RESULT_SUCCESS) { + (*recorder)->Destroy(recorder); + return true; + } + + return false; +} diff --git a/src/plugins/opensles/qopenslesengine.h b/src/plugins/opensles/qopenslesengine.h new file mode 100644 index 000000000..9f12ac65d --- /dev/null +++ b/src/plugins/opensles/qopenslesengine.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENSLESENGINE_H +#define QOPENSLESENGINE_H + +#include <qglobal.h> +#include <qaudio.h> +#include <qlist.h> +#include <qaudioformat.h> +#include <SLES/OpenSLES.h> + +QT_BEGIN_NAMESPACE + +class QOpenSLESEngine +{ +public: + QOpenSLESEngine(); + ~QOpenSLESEngine(); + + static QOpenSLESEngine *instance(); + + SLEngineItf slEngine() const { return m_engine; } + + static SLDataFormat_PCM audioFormatToSLFormatPCM(const QAudioFormat &format); + + QList<QByteArray> availableDevices(QAudio::Mode mode) const; + QList<int> supportedChannelCounts(QAudio::Mode mode) const; + QList<int> supportedSampleRates(QAudio::Mode mode) const; + +private: + void checkSupportedInputFormats(); + bool inputFormatIsSupported(SLDataFormat_PCM format); + + SLObjectItf m_engineObject; + SLEngineItf m_engine; + + QList<int> m_supportedInputChannelCounts; + QList<int> m_supportedInputSampleRates; +}; + +QT_END_NAMESPACE + +#endif // QOPENSLESENGINE_H diff --git a/src/plugins/opensles/qopenslesplugin.cpp b/src/plugins/opensles/qopenslesplugin.cpp new file mode 100644 index 000000000..a7fdb9bd4 --- /dev/null +++ b/src/plugins/opensles/qopenslesplugin.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopenslesplugin.h" + +#include "qopenslesengine.h" +#include "qopenslesdeviceinfo.h" +#include "qopenslesaudioinput.h" +#include "qopenslesaudiooutput.h" + +QT_BEGIN_NAMESPACE + +QOpenSLESPlugin::QOpenSLESPlugin(QObject *parent) + : QAudioSystemPlugin(parent) + , m_engine(QOpenSLESEngine::instance()) +{ +} + +QList<QByteArray> QOpenSLESPlugin::availableDevices(QAudio::Mode mode) const +{ + return m_engine->availableDevices(mode); +} + +QAbstractAudioInput *QOpenSLESPlugin::createInput(const QByteArray &device) +{ + return new QOpenSLESAudioInput(device); +} + +QAbstractAudioOutput *QOpenSLESPlugin::createOutput(const QByteArray &device) +{ + return new QOpenSLESAudioOutput(device); +} + +QAbstractAudioDeviceInfo *QOpenSLESPlugin::createDeviceInfo(const QByteArray &device, QAudio::Mode mode) +{ + return new QOpenSLESDeviceInfo(device, mode); +} + +QT_END_NAMESPACE + diff --git a/src/plugins/opensles/qopenslesplugin.h b/src/plugins/opensles/qopenslesplugin.h new file mode 100644 index 000000000..46144abe2 --- /dev/null +++ b/src/plugins/opensles/qopenslesplugin.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENSLESPLUGIN_H +#define QOPENSLESPLUGIN_H + +#include <qaudiosystemplugin.h> + +QT_BEGIN_NAMESPACE + +class QOpenSLESEngine; + +class QOpenSLESPlugin : public QAudioSystemPlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "org.qt-project.qt.audiosystemfactory/5.0" FILE "opensles.json") + +public: + QOpenSLESPlugin(QObject *parent = 0); + ~QOpenSLESPlugin() {} + + QList<QByteArray> availableDevices(QAudio::Mode mode) const; + QAbstractAudioInput *createInput(const QByteArray &device); + QAbstractAudioOutput *createOutput(const QByteArray &device); + QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode); + +private: + QOpenSLESEngine *m_engine; +}; + +QT_END_NAMESPACE + +#endif // QOPENSLESPLUGIN_H diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 8f751ae3b..4ef472dcd 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -53,3 +53,6 @@ mac:!simulator { } } +config_opensles { + SUBDIRS += opensles +} |