summaryrefslogtreecommitdiffstats
path: root/src/multimedia/audio
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2021-01-05 16:52:36 +0100
committerLars Knoll <lars.knoll@qt.io>2021-01-22 07:01:12 +0000
commit7b616ac0c4b3a3564eef87bd05524a4be7ea2842 (patch)
tree9c9dfcef848b8f636dd0c2e18710ceac1808c4a6 /src/multimedia/audio
parent4741716b2fccb11f0fab81e25d4ffc9ff1110c1e (diff)
Use gstreamer for audio
This is a basic implementation of the audio classes using gstreamer. The code still has bugs and needs further work, but this is good enough for now. With this change, we can remove the alsa/pulseaudio code paths and start getting rid of the plugin architecture as a whole. Change-Id: I5469d13991071ef28d35f63aa3c15539334db15e Reviewed-by: Doris Verria <doris.verria@qt.io> Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/multimedia/audio')
-rw-r--r--src/multimedia/audio/audio.pri1
-rw-r--r--src/multimedia/audio/gstreamer/gstreamer.pri17
-rw-r--r--src/multimedia/audio/gstreamer/qaudiodeviceinfo_gstreamer.cpp127
-rw-r--r--src/multimedia/audio/gstreamer/qaudiodeviceinfo_gstreamer_p.h92
-rw-r--r--src/multimedia/audio/gstreamer/qaudioengine_gstreamer.cpp108
-rw-r--r--src/multimedia/audio/gstreamer/qaudioengine_gstreamer_p.h85
-rw-r--r--src/multimedia/audio/gstreamer/qaudioinput_gstreamer.cpp424
-rw-r--r--src/multimedia/audio/gstreamer/qaudioinput_gstreamer_p.h159
-rw-r--r--src/multimedia/audio/gstreamer/qaudiointerface_gstreamer.cpp88
-rw-r--r--src/multimedia/audio/gstreamer/qaudiointerface_gstreamer_p.h78
-rw-r--r--src/multimedia/audio/gstreamer/qaudiooutput_gstreamer.cpp414
-rw-r--r--src/multimedia/audio/gstreamer/qaudiooutput_gstreamer_p.h164
-rw-r--r--src/multimedia/audio/qaudiosystem.cpp8
13 files changed, 1763 insertions, 2 deletions
diff --git a/src/multimedia/audio/audio.pri b/src/multimedia/audio/audio.pri
index aeb204481..75738abd6 100644
--- a/src/multimedia/audio/audio.pri
+++ b/src/multimedia/audio/audio.pri
@@ -41,4 +41,5 @@ darwin:!watchos: include(coreaudio/coreaudio.pri)
qnx: include(qnx/qnx.pri)
qtConfig(pulseaudio): include(pulseaudio/pulseaudio.pri)
qtConfig(alsa): include(alsa/alsa.pri)
+qtConfig(gstreamer): include(gstreamer/gstreamer.pri)
diff --git a/src/multimedia/audio/gstreamer/gstreamer.pri b/src/multimedia/audio/gstreamer/gstreamer.pri
new file mode 100644
index 000000000..eae5ebb06
--- /dev/null
+++ b/src/multimedia/audio/gstreamer/gstreamer.pri
@@ -0,0 +1,17 @@
+HEADERS += audio/gstreamer/qaudiointerface_gstreamer_p.h \
+ audio/gstreamer/qaudiodeviceinfo_gstreamer_p.h \
+ audio/gstreamer/qaudiooutput_gstreamer_p.h \
+ audio/gstreamer/qaudioinput_gstreamer_p.h \
+ audio/gstreamer/qaudioengine_gstreamer_p.h \
+
+SOURCES += audio/gstreamer/qaudiointerface_gstreamer.cpp \
+ audio/gstreamer/qaudiodeviceinfo_gstreamer.cpp \
+ audio/gstreamer/qaudiooutput_gstreamer.cpp \
+ audio/gstreamer/qaudioinput_gstreamer.cpp \
+ audio/gstreamer/qaudioengine_gstreamer.cpp \
+
+QMAKE_USE_PRIVATE += gstreamer
+
+qtConfig(gstreamer_app): \
+ QMAKE_USE += gstreamer_app
+
diff --git a/src/multimedia/audio/gstreamer/qaudiodeviceinfo_gstreamer.cpp b/src/multimedia/audio/gstreamer/qaudiodeviceinfo_gstreamer.cpp
new file mode 100644
index 000000000..e4182c1df
--- /dev/null
+++ b/src/multimedia/audio/gstreamer/qaudiodeviceinfo_gstreamer.cpp
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qaudiodeviceinfo_gstreamer_p.h"
+#include "qaudioengine_gstreamer_p.h"
+
+#include <private/qgstutils_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QGStreamerAudioDeviceInfo::QGStreamerAudioDeviceInfo(const QByteArray &device, QAudio::Mode mode)
+ : m_device(device)
+ , m_mode(mode)
+{
+ const auto devices = (mode == QAudio::AudioOutput) ? QGstUtils::audioSinks() : QGstUtils::audioSources();
+
+ for (auto *d : devices) {
+ auto *properties = gst_device_get_properties(d);
+ if (properties) {
+ auto *name = gst_structure_get_string(properties, "sysfs.path");
+ if (device == name) {
+ gstDevice = d;
+ gst_object_ref(gstDevice);
+ }
+ }
+
+ gst_structure_free(properties);
+ if (gstDevice)
+ return;
+ }
+}
+
+QGStreamerAudioDeviceInfo::~QGStreamerAudioDeviceInfo()
+{
+ if (gstDevice)
+ gst_object_unref(gstDevice);
+}
+
+bool QGStreamerAudioDeviceInfo::isFormatSupported(const QAudioFormat &) const
+{
+ // ####
+ return true;
+}
+
+QAudioFormat QGStreamerAudioDeviceInfo::preferredFormat() const
+{
+ QAudioFormat format;
+ format.setByteOrder(QAudioFormat::LittleEndian);
+ format.setChannelCount(2);
+ format.setSampleRate(48000);
+ format.setSampleSize(16);
+ return format;
+}
+
+QString QGStreamerAudioDeviceInfo::deviceName() const
+{
+ // ### no readable name available!
+ return QString::fromUtf8(m_device);
+}
+
+QStringList QGStreamerAudioDeviceInfo::supportedCodecs()
+{
+ return QStringList() << QString::fromLatin1("audio/pcm");
+}
+
+QList<int> QGStreamerAudioDeviceInfo::supportedSampleRates()
+{
+ return QList<int>() << 8000 << 11025 << 22050 << 44100 << 48000;
+}
+
+QList<int> QGStreamerAudioDeviceInfo::supportedChannelCounts()
+{
+ return QList<int>() << 1 << 2 << 4 << 6 << 8;
+}
+
+QList<int> QGStreamerAudioDeviceInfo::supportedSampleSizes()
+{
+ return QList<int>() << 8 << 16 << 24 << 32;
+}
+
+QList<QAudioFormat::Endian> QGStreamerAudioDeviceInfo::supportedByteOrders()
+{
+ return QList<QAudioFormat::Endian>() << QAudioFormat::BigEndian << QAudioFormat::LittleEndian;
+}
+
+QList<QAudioFormat::SampleType> QGStreamerAudioDeviceInfo::supportedSampleTypes()
+{
+ return QList<QAudioFormat::SampleType>() << QAudioFormat::SignedInt << QAudioFormat::UnSignedInt << QAudioFormat::Float;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/audio/gstreamer/qaudiodeviceinfo_gstreamer_p.h b/src/multimedia/audio/gstreamer/qaudiodeviceinfo_gstreamer_p.h
new file mode 100644
index 000000000..45ee4e9f8
--- /dev/null
+++ b/src/multimedia/audio/gstreamer/qaudiodeviceinfo_gstreamer_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGSTREAMERAUDIODEVICEINFO_H
+#define QGSTREAMERAUDIODEVICEINFO_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qlist.h>
+
+#include "qaudio.h"
+#include "qaudiodeviceinfo.h"
+#include <private/qaudiosystem_p.h>
+
+#include <gst/gst.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGStreamerAudioDeviceInfo : public QAbstractAudioDeviceInfo
+{
+ Q_OBJECT
+
+public:
+ QGStreamerAudioDeviceInfo(const QByteArray &device, QAudio::Mode mode);
+ ~QGStreamerAudioDeviceInfo();
+
+ QAudioFormat preferredFormat() const override;
+ bool isFormatSupported(const QAudioFormat &format) const override;
+ QString deviceName() const override;
+ QStringList supportedCodecs() override;
+ QList<int> supportedSampleRates() override;
+ QList<int> supportedChannelCounts() override;
+ QList<int> supportedSampleSizes() override;
+ QList<QAudioFormat::Endian> supportedByteOrders() override;
+ QList<QAudioFormat::SampleType> supportedSampleTypes() override;
+
+ QByteArray m_device;
+ QAudio::Mode m_mode;
+ GstDevice *gstDevice = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/multimedia/audio/gstreamer/qaudioengine_gstreamer.cpp b/src/multimedia/audio/gstreamer/qaudioengine_gstreamer.cpp
new file mode 100644
index 000000000..1108fa7b5
--- /dev/null
+++ b/src/multimedia/audio/gstreamer/qaudioengine_gstreamer.cpp
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qdebug.h>
+
+#include <qaudiodeviceinfo.h>
+#include "qaudioengine_gstreamer_p.h"
+#include "qaudiodeviceinfo_gstreamer_p.h"
+#include "qaudiooutput_gstreamer_p.h"
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <private/qgstutils_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(QGStreamerAudioEngine, gstreamerEngine);
+
+QGStreamerAudioEngine::QGStreamerAudioEngine(QObject *parent)
+ : QObject(parent)
+{
+ updateDevices();
+}
+
+void QGStreamerAudioEngine::updateDevices()
+{
+ QGstUtils::initializeGst();
+
+ const auto sources = QGstUtils::audioSources();
+
+ auto deviceList = [](const auto &deviceSet) {
+ QList<QByteArray> devices;
+ for (auto *d : deviceSet) {
+ auto *properties = gst_device_get_properties(d);
+ if (properties) {
+ auto *klass = gst_structure_get_string(properties, "device.class");
+ if (strcmp(klass, "monitor")) {
+ auto *name = gst_structure_get_string(properties, "sysfs.path");
+ gboolean def;
+ if (gst_structure_get_boolean(properties, "is-default", &def) && def)
+ devices.prepend(name);
+ else
+ devices.append(name);
+ }
+
+ gst_structure_free(properties);
+ }
+ }
+ return devices;
+ };
+
+ m_sources = deviceList(QGstUtils::audioSources());
+ m_sinks = deviceList(QGstUtils::audioSinks());
+}
+
+QGStreamerAudioEngine *QGStreamerAudioEngine::instance()
+{
+ return gstreamerEngine();
+}
+
+QList<QByteArray> QGStreamerAudioEngine::availableDevices(QAudio::Mode mode) const
+{
+ return (mode == QAudio::AudioOutput) ? m_sinks : m_sources;
+}
+
+QByteArray QGStreamerAudioEngine::defaultDevice(QAudio::Mode mode) const
+{
+ const QList<QByteArray> &devices = availableDevices(mode);
+ return devices.size() ? devices.at(0) : QByteArray();
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/audio/gstreamer/qaudioengine_gstreamer_p.h b/src/multimedia/audio/gstreamer/qaudioengine_gstreamer_p.h
new file mode 100644
index 000000000..e7043c3f8
--- /dev/null
+++ b/src/multimedia/audio/gstreamer/qaudioengine_gstreamer_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGSTREAMERAUDIOENGINE_H
+#define QGSTREAMERAUDIOENGINE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qmap.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qreadwritelock.h>
+#include <qaudioformat.h>
+#include <qaudio.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGStreamerAudioEngine : public QObject
+{
+ Q_OBJECT
+
+public:
+ QGStreamerAudioEngine(QObject *parent = 0);
+
+ static QGStreamerAudioEngine *instance();
+
+ QList<QByteArray> availableDevices(QAudio::Mode mode) const;
+ QByteArray defaultDevice(QAudio::Mode mode) const;
+
+private:
+ void updateDevices();
+
+public:
+ QList<QByteArray> m_sinks;
+ QList<QByteArray> m_sources;
+ QMap<QByteArray, QAudioFormat> m_preferredFormats;
+ };
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/audio/gstreamer/qaudioinput_gstreamer.cpp b/src/multimedia/audio/gstreamer/qaudioinput_gstreamer.cpp
new file mode 100644
index 000000000..60b2a6b83
--- /dev/null
+++ b/src/multimedia/audio/gstreamer/qaudioinput_gstreamer.cpp
@@ -0,0 +1,424 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qmath.h>
+#include <private/qaudiohelpers_p.h>
+
+#include "qaudioinput_gstreamer_p.h"
+#include "qaudioengine_gstreamer_p.h"
+#include "qaudiodeviceinfo_gstreamer_p.h"
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <gst/gst.h>
+
+QT_BEGIN_NAMESPACE
+
+QGStreamerAudioInput::QGStreamerAudioInput(const QByteArray &device)
+ : m_device(device)
+{
+}
+
+QGStreamerAudioInput::~QGStreamerAudioInput()
+{
+ close();
+}
+
+void QGStreamerAudioInput::setError(QAudio::Error error)
+{
+ if (m_errorState == error)
+ return;
+
+ m_errorState = error;
+ emit errorChanged(error);
+}
+
+QAudio::Error QGStreamerAudioInput::error() const
+{
+ return m_errorState;
+}
+
+void QGStreamerAudioInput::setState(QAudio::State state)
+{
+ if (m_deviceState == state)
+ return;
+
+ m_deviceState = state;
+ emit stateChanged(state);
+}
+
+QAudio::State QGStreamerAudioInput::state() const
+{
+ return m_deviceState;
+}
+
+void QGStreamerAudioInput::setFormat(const QAudioFormat &format)
+{
+ if (m_deviceState == QAudio::StoppedState)
+ m_format = format;
+}
+
+QAudioFormat QGStreamerAudioInput::format() const
+{
+ return m_format;
+}
+
+void QGStreamerAudioInput::start(QIODevice *device)
+{
+ setState(QAudio::StoppedState);
+ setError(QAudio::NoError);
+
+ close();
+
+ if (!open())
+ return;
+
+ m_pullMode = true;
+ m_audioSink = device;
+
+ setState(QAudio::ActiveState);
+}
+
+QIODevice *QGStreamerAudioInput::start()
+{
+ setState(QAudio::StoppedState);
+ setError(QAudio::NoError);
+
+ close();
+
+ if (!open())
+ return nullptr;
+
+ m_pullMode = false;
+ m_audioSink = new GStreamerInputPrivate(this);
+ m_audioSink->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
+
+ setState(QAudio::IdleState);
+
+ return m_audioSink;
+}
+
+void QGStreamerAudioInput::stop()
+{
+ if (m_deviceState == QAudio::StoppedState)
+ return;
+
+ close();
+
+ setError(QAudio::NoError);
+ setState(QAudio::StoppedState);
+}
+
+bool QGStreamerAudioInput::open()
+{
+ if (m_opened)
+ return true;
+
+ QGStreamerAudioDeviceInfo deviceInfo(m_device, QAudio::AudioInput);
+ if (!deviceInfo.gstDevice) {
+ setError(QAudio::OpenError);
+ setState(QAudio::StoppedState);
+ return false;
+ }
+
+ gstInput = gst_device_create_element(deviceInfo.gstDevice, NULL);
+ if (!gstInput) {
+ setError(QAudio::OpenError);
+ setState(QAudio::StoppedState);
+ return false;
+ }
+
+ auto *gstCaps = QGstUtils::capsForAudioFormat(m_format);
+
+ if (!gstCaps) {
+ setError(QAudio::OpenError);
+ setState(QAudio::StoppedState);
+ return false;
+ }
+
+
+#ifdef DEBUG_AUDIO
+ qDebug() << "Opening input" << QTime::currentTime();
+ qDebug() << "Caps: " << gst_caps_to_string(gstCaps);
+#endif
+
+ gstPipeline = gst_pipeline_new ("pipeline");
+
+ auto *gstBus = gst_pipeline_get_bus(GST_PIPELINE(gstPipeline));
+ gst_bus_add_watch(gstBus, &QGStreamerAudioInput::busMessage, this);
+ gst_object_unref (gstBus);
+
+#if 1
+ gstAppSink = createAppSink();
+ g_object_set(gstAppSink, "caps", gstCaps, nullptr);
+#else
+ gstAppSrc = gst_element_factory_make ("filesink", "file");
+ g_object_set (G_OBJECT (gstAppSrc), "location", "/home/lars/out.pcm", NULL);
+#endif
+
+ GstElement *conv = gst_element_factory_make ("audioconvert", "conv");
+ gstVolume = gst_element_factory_make ("volume", "volume");
+ if (m_volume != 1.)
+ g_object_set(gstVolume, "volume", m_volume, nullptr);
+
+ gst_bin_add_many(GST_BIN (gstPipeline), gstInput, gstVolume, conv, gstAppSink, nullptr);
+
+ gst_element_link(gstInput, gstVolume);
+ gst_element_link(gstVolume, conv);
+ gst_element_link(conv, gstAppSink);
+
+ /* run */
+ gst_element_set_state (gstPipeline, GST_STATE_PLAYING);
+
+ m_opened = true;
+
+ m_clockStamp.restart();
+ m_timeStamp.restart();
+ m_elapsedTimeOffset = 0;
+ m_totalTimeValue = 0;
+
+ return true;
+}
+
+void QGStreamerAudioInput::close()
+{
+ if (!m_opened)
+ return;
+
+ gst_element_set_state(gstPipeline, GST_STATE_NULL);
+ gst_object_unref(gstPipeline);
+ gstVolume = nullptr;
+ gstAppSink = nullptr;
+ gstInput = nullptr;
+
+ if (!m_pullMode && m_audioSink) {
+ delete m_audioSink;
+ }
+ m_audioSink = nullptr;
+ m_opened = false;
+}
+
+gboolean QGStreamerAudioInput::busMessage(GstBus *, GstMessage *msg, gpointer user_data)
+{
+ QGStreamerAudioInput *input = static_cast<QGStreamerAudioInput *>(user_data);
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_EOS:
+ input->stop();
+ break;
+ case GST_MESSAGE_ERROR: {
+ input->setError(QAudio::IOError);
+ gchar *debug;
+ GError *error;
+
+ gst_message_parse_error (msg, &error, &debug);
+ g_free (debug);
+
+ qDebug("Error: %s\n", error->message);
+ g_error_free (error);
+
+ break;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+int QGStreamerAudioInput::bytesReady() const
+{
+ return m_buffer.size();
+}
+
+void QGStreamerAudioInput::resume()
+{
+ if (m_deviceState == QAudio::SuspendedState || m_deviceState == QAudio::IdleState) {
+ gst_element_set_state (gstPipeline, GST_STATE_PLAYING);
+
+ setState(QAudio::ActiveState);
+ setError(QAudio::NoError);
+ }
+}
+
+void QGStreamerAudioInput::setVolume(qreal vol)
+{
+ if (m_volume == vol)
+ return;
+
+ m_volume = vol;
+ if (gstVolume)
+ g_object_set(gstVolume, "volume", vol, nullptr);
+}
+
+qreal QGStreamerAudioInput::volume() const
+{
+ return m_volume;
+}
+
+void QGStreamerAudioInput::setBufferSize(int value)
+{
+ m_bufferSize = value;
+}
+
+int QGStreamerAudioInput::bufferSize() const
+{
+ return m_bufferSize;
+}
+
+int QGStreamerAudioInput::periodSize() const
+{
+ return m_periodSize;
+}
+
+void QGStreamerAudioInput::setNotifyInterval(int ms)
+{
+ m_intervalTime = qMax(0, ms);
+}
+
+int QGStreamerAudioInput::notifyInterval() const
+{
+ return m_intervalTime;
+}
+
+qint64 QGStreamerAudioInput::processedUSecs() const
+{
+ return 0;
+}
+
+void QGStreamerAudioInput::suspend()
+{
+ if (m_deviceState == QAudio::ActiveState) {
+ setError(QAudio::NoError);
+ setState(QAudio::SuspendedState);
+
+ gst_element_set_state (gstPipeline, GST_STATE_PLAYING);
+ }
+}
+
+qint64 QGStreamerAudioInput::elapsedUSecs() const
+{
+ if (m_deviceState == QAudio::StoppedState)
+ return 0;
+
+ return m_clockStamp.elapsed() * qint64(1000);
+}
+
+void QGStreamerAudioInput::reset()
+{
+ stop();
+ m_bytesAvailable = 0;
+}
+
+//#define MAX_BUFFERS_IN_QUEUE 4
+
+GstElement *QGStreamerAudioInput::createAppSink()
+{
+ GstElement *sink = gst_element_factory_make("appsink", NULL);
+ GstAppSink *appSink = reinterpret_cast<GstAppSink *>(sink);
+
+ GstAppSinkCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.eos = &eos;
+ callbacks.new_sample = &new_sample;
+ gst_app_sink_set_callbacks(appSink, &callbacks, this, NULL);
+// gst_app_sink_set_max_buffers(appSink, MAX_BUFFERS_IN_QUEUE);
+ gst_base_sink_set_sync(GST_BASE_SINK(appSink), FALSE);
+
+ return sink;
+}
+
+GstFlowReturn QGStreamerAudioInput::new_sample(GstAppSink *sink, gpointer user_data)
+{
+ // "Note that the preroll buffer will also be returned as the first buffer when calling gst_app_sink_pull_buffer()."
+ QGStreamerAudioInput *control = static_cast<QGStreamerAudioInput*>(user_data);
+
+ GstSample *sample = gst_app_sink_pull_sample(sink);
+ GstBuffer *buffer = gst_sample_get_buffer(sample);
+ GstMapInfo mapInfo;
+ gst_buffer_map(buffer, &mapInfo, GST_MAP_READ);
+ const char *bufferData = (const char*)mapInfo.data;
+ gsize bufferSize = mapInfo.size;
+
+ if (!control->m_pullMode) {
+ // need to store that data in the QBuffer
+ control->m_buffer.append(bufferData, bufferSize);
+ control->m_audioSink->readyRead();
+ } else {
+ control->m_audioSink->write(bufferData, bufferSize);
+ }
+
+ gst_buffer_unmap(buffer, &mapInfo);
+ gst_sample_unref(sample);
+
+ return GST_FLOW_OK;
+}
+
+void QGStreamerAudioInput::eos(GstAppSink *, gpointer user_data)
+{
+ QGStreamerAudioInput *control = static_cast<QGStreamerAudioInput*>(user_data);
+ control->setState(QAudio::StoppedState);
+}
+
+GStreamerInputPrivate::GStreamerInputPrivate(QGStreamerAudioInput *audio)
+{
+ m_audioDevice = qobject_cast<QGStreamerAudioInput*>(audio);
+}
+
+qint64 GStreamerInputPrivate::readData(char *data, qint64 len)
+{
+ return m_audioDevice->m_buffer.read(data, len);
+}
+
+qint64 GStreamerInputPrivate::writeData(const char *data, qint64 len)
+{
+ Q_UNUSED(data);
+ Q_UNUSED(len);
+ return 0;
+}
+
+qint64 GStreamerInputPrivate::bytesAvailable() const
+{
+ return m_audioDevice->m_buffer.size();
+}
+
+
+QT_END_NAMESPACE
+
+#include "moc_qaudioinput_gstreamer_p.cpp"
diff --git a/src/multimedia/audio/gstreamer/qaudioinput_gstreamer_p.h b/src/multimedia/audio/gstreamer/qaudioinput_gstreamer_p.h
new file mode 100644
index 000000000..ceff047c0
--- /dev/null
+++ b/src/multimedia/audio/gstreamer/qaudioinput_gstreamer_p.h
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QAUDIOINPUTPULSE_H
+#define QAUDIOINPUTPULSE_H
+
+#include <QtCore/qfile.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/private/qringbuffer_p.h>
+
+#include "qaudio.h"
+#include "qaudiodeviceinfo.h"
+#include <private/qaudiosystem_p.h>
+
+#include <private/qgstutils_p.h>
+#include <gst/app/gstappsink.h>
+
+QT_BEGIN_NAMESPACE
+
+class GStreamerInputPrivate;
+
+class QGStreamerAudioInput
+ : public QAbstractAudioInput
+{
+ Q_OBJECT
+ friend class GStreamerInputPrivate;
+public:
+ QGStreamerAudioInput(const QByteArray &device);
+ ~QGStreamerAudioInput();
+
+ void start(QIODevice *device) override;
+ QIODevice *start() override;
+ void stop() override;
+ void reset() override;
+ void suspend() override;
+ void resume() override;
+ int bytesReady() const override;
+ int periodSize() const override;
+ void setBufferSize(int value) override;
+ int bufferSize() const override;
+ void setNotifyInterval(int milliSeconds) override;
+ int notifyInterval() const override;
+ qint64 processedUSecs() const override;
+ qint64 elapsedUSecs() const override;
+ QAudio::Error error() const override;
+ QAudio::State state() const override;
+ void setFormat(const QAudioFormat &format) override;
+ QAudioFormat format() const override;
+
+ void setVolume(qreal volume) override;
+ qreal volume() const override;
+
+private:
+ void setState(QAudio::State state);
+ void setError(QAudio::Error error);
+
+ GstElement *createAppSink();
+ static GstFlowReturn new_sample(GstAppSink *, gpointer user_data);
+ static void eos(GstAppSink *, gpointer user_data);
+
+ bool open();
+ void close();
+
+ static gboolean busMessage(GstBus *bus, GstMessage *msg, gpointer user_data);
+
+ qint64 m_totalTimeValue = 0;
+ QIODevice *m_audioSink = nullptr;
+ QAudioFormat m_format;
+ QAudio::Error m_errorState = QAudio::NoError;
+ QAudio::State m_deviceState = QAudio::StoppedState;
+ qreal m_volume = 1.;
+
+ QRingBuffer m_buffer;
+ bool m_pullMode = true;
+ bool m_opened = false;
+ int m_bytesAvailable = 0;
+ int m_bufferSize = 0;
+ int m_periodSize = 0;
+ int m_intervalTime = 1000;
+ qint64 m_elapsedTimeOffset = 0;
+ QElapsedTimer m_timeStamp;
+ QElapsedTimer m_clockStamp;
+ QByteArray m_device;
+ QByteArray m_tempBuffer;
+ GstElement *gstInput = nullptr;
+ GstElement *gstPipeline = nullptr;
+ GstElement *gstVolume = nullptr;
+ GstElement *gstAppSink = nullptr;
+};
+
+class GStreamerInputPrivate : public QIODevice
+{
+ Q_OBJECT
+public:
+ GStreamerInputPrivate(QGStreamerAudioInput *audio);
+ ~GStreamerInputPrivate() {};
+
+ qint64 readData(char *data, qint64 len) override;
+ qint64 writeData(const char *data, qint64 len) override;
+ qint64 bytesAvailable() const override;
+ bool isSequential() const override { return true; }
+private:
+ QGStreamerAudioInput *m_audioDevice;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/audio/gstreamer/qaudiointerface_gstreamer.cpp b/src/multimedia/audio/gstreamer/qaudiointerface_gstreamer.cpp
new file mode 100644
index 000000000..b449a989a
--- /dev/null
+++ b/src/multimedia/audio/gstreamer/qaudiointerface_gstreamer.cpp
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qaudiodeviceinfo.h>
+
+#include "qaudiointerface_gstreamer_p.h"
+#include "qaudiodeviceinfo_gstreamer_p.h"
+#include "qaudiooutput_gstreamer_p.h"
+#include "qaudioinput_gstreamer_p.h"
+#include "qaudioengine_gstreamer_p.h"
+
+#include "private/qgstreameraudioinputselector_p.h"
+#include "private/qgstutils_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QGStreamerAudioInterface::QGStreamerAudioInterface()
+ : m_gstreamerEngine(QGStreamerAudioEngine::instance())
+{
+ QGstUtils::initializeGst();
+}
+
+QByteArray QGStreamerAudioInterface::defaultDevice(QAudio::Mode mode) const
+{
+ return m_gstreamerEngine->defaultDevice(mode);
+}
+
+QList<QByteArray> QGStreamerAudioInterface::availableDevices(QAudio::Mode mode) const
+{
+ return m_gstreamerEngine->availableDevices(mode);
+}
+
+QAbstractAudioInput *QGStreamerAudioInterface::createInput(const QByteArray &device)
+{
+ QGStreamerAudioInput *input = new QGStreamerAudioInput(device);
+ return input;
+}
+
+QAbstractAudioOutput *QGStreamerAudioInterface::createOutput(const QByteArray &device)
+{
+
+ QGStreamerAudioOutput *output = new QGStreamerAudioOutput(device);
+ return output;
+}
+
+QAbstractAudioDeviceInfo *QGStreamerAudioInterface::createDeviceInfo(const QByteArray &device, QAudio::Mode mode)
+{
+ QGStreamerAudioDeviceInfo *deviceInfo = new QGStreamerAudioDeviceInfo(device, mode);
+ return deviceInfo;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/audio/gstreamer/qaudiointerface_gstreamer_p.h b/src/multimedia/audio/gstreamer/qaudiointerface_gstreamer_p.h
new file mode 100644
index 000000000..7a79bd186
--- /dev/null
+++ b/src/multimedia/audio/gstreamer/qaudiointerface_gstreamer_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGSTREAMERAUDIOPLUGIN_H
+#define QGSTREAMERAUDIOPLUGIN_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qaudiosystem_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGStreamerAudioEngine;
+
+// #### merge with QGstreamerAudioInputSelector
+class QGStreamerAudioInterface : public QAudioSystemInterface
+{
+public:
+ QGStreamerAudioInterface();
+
+ QByteArray defaultDevice(QAudio::Mode mode) const override;
+ QList<QByteArray> availableDevices(QAudio::Mode mode) const override;
+ QAbstractAudioInput *createInput(const QByteArray &device) override;
+ QAbstractAudioOutput *createOutput(const QByteArray &device) override;
+ QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode) override;
+
+private:
+ QGStreamerAudioEngine *m_gstreamerEngine;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/audio/gstreamer/qaudiooutput_gstreamer.cpp b/src/multimedia/audio/gstreamer/qaudiooutput_gstreamer.cpp
new file mode 100644
index 000000000..eaa4c52f2
--- /dev/null
+++ b/src/multimedia/audio/gstreamer/qaudiooutput_gstreamer.cpp
@@ -0,0 +1,414 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qmath.h>
+#include <private/qaudiohelpers_p.h>
+
+#include "qaudiooutput_gstreamer_p.h"
+#include "qaudiodeviceinfo_gstreamer_p.h"
+#include "qaudioengine_gstreamer_p.h"
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <private/qgstreamerbushelper_p.h>
+#include <private/qgstappsrc_p.h>
+
+#include <private/qgstutils_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QGStreamerAudioOutput::QGStreamerAudioOutput(const QByteArray &device)
+ : m_device(device)
+{
+ QGStreamerAudioDeviceInfo audioInfo(device, QAudio::AudioOutput);
+ gstOutput = gst_device_create_element(audioInfo.gstDevice, nullptr);
+ gst_object_ref(gstOutput);
+}
+
+QGStreamerAudioOutput::~QGStreamerAudioOutput()
+{
+ if (gstOutput)
+ gst_object_unref(gstOutput);
+ close();
+ QCoreApplication::processEvents();
+}
+
+void QGStreamerAudioOutput::setError(QAudio::Error error)
+{
+ if (m_errorState == error)
+ return;
+
+ m_errorState = error;
+ emit errorChanged(error);
+}
+
+QAudio::Error QGStreamerAudioOutput::error() const
+{
+ return m_errorState;
+}
+
+void QGStreamerAudioOutput::setState(QAudio::State state)
+{
+ if (m_deviceState == state)
+ return;
+
+ m_deviceState = state;
+ emit stateChanged(state);
+}
+
+QAudio::State QGStreamerAudioOutput::state() const
+{
+ return m_deviceState;
+}
+
+void QGStreamerAudioOutput::start(QIODevice *device)
+{
+ setState(QAudio::StoppedState);
+ setError(QAudio::NoError);
+
+ close();
+
+ m_pullMode = true;
+ m_audioSource = device;
+
+ if (!open()) {
+ m_audioSource = nullptr;
+ return;
+ }
+
+ setState(QAudio::ActiveState);
+}
+
+QIODevice *QGStreamerAudioOutput::start()
+{
+ setState(QAudio::StoppedState);
+ setError(QAudio::NoError);
+
+ close();
+
+ m_pullMode = false;
+
+ if (!open())
+ return nullptr;
+
+ m_audioSource = new GStreamerOutputPrivate(this);
+ m_audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
+
+ setState(QAudio::IdleState);
+
+ return m_audioSource;
+}
+
+#if 0
+static void padAdded(GstElement *element, GstPad *pad, gpointer data)
+{
+ GstElement *other = static_cast<GstElement *>(data);
+
+ gchar *name = gst_pad_get_name(pad);
+ qDebug("A new pad %s was created for %s\n", name, gst_element_get_name(element));
+ g_free(name);
+
+ qDebug("element %s will be linked to %s\n",
+ gst_element_get_name(element),
+ gst_element_get_name(other));
+ gst_element_link(element, other);
+}
+#endif
+
+gboolean QGStreamerAudioOutput::busMessage(GstBus *, GstMessage *msg, gpointer user_data)
+{
+ QGStreamerAudioOutput *output = static_cast<QGStreamerAudioOutput *>(user_data);
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_EOS:
+ output->stop();
+ break;
+ case GST_MESSAGE_ERROR: {
+ output->setError(QAudio::IOError);
+ gchar *debug;
+ GError *error;
+
+ gst_message_parse_error (msg, &error, &debug);
+ g_free (debug);
+
+ qDebug("Error: %s\n", error->message);
+ g_error_free (error);
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool QGStreamerAudioOutput::open()
+{
+ if (m_opened)
+ return true;
+
+ if (!gstOutput) {
+ setError(QAudio::OpenError);
+ setState(QAudio::StoppedState);
+ return false;
+ }
+
+ gstPipeline = gst_pipeline_new ("pipeline");
+
+ auto *gstBus = gst_pipeline_get_bus(GST_PIPELINE(gstPipeline));
+ gst_bus_add_watch(gstBus, &QGStreamerAudioOutput::busMessage, this);
+ gst_object_unref (gstBus);
+
+ gstAppSrc = gst_element_factory_make("appsrc", "appsrc");
+
+ m_bufferSize = gst_app_src_get_max_bytes(GST_APP_SRC(gstAppSrc));
+
+// qDebug() << "GST caps:" << gst_caps_to_string(caps);
+ m_appSrc = new QGstAppSrc;
+ if (m_audioSource) {
+ m_appSrc->setStream(m_audioSource);
+ } else {
+ m_appSrc->setBuffer(&m_buffer);
+ }
+ m_appSrc->setAudioFormat(m_format);
+ m_appSrc->setup(gstAppSrc);
+
+// gstDecodeBin = gst_element_factory_make ("decodebin", "dec");
+ GstElement *conv = gst_element_factory_make("audioconvert", "conv");
+ gstVolume = gst_element_factory_make ("volume", "volume");
+ if (m_volume != 1.)
+ g_object_set(gstVolume, "volume", m_volume, nullptr);
+
+ gst_bin_add_many(GST_BIN (gstPipeline), gstAppSrc, /*gstDecodeBin, */ conv, gstVolume, gstOutput, nullptr);
+
+ gst_element_link(gstAppSrc, conv);
+ gst_element_link(conv, gstVolume);
+ gst_element_link(gstVolume, gstOutput);
+// gst_element_link(gstVolume, gstOutput);
+
+ // link decodeBin to audioconvert in a callback once we get a pad from the decoder
+// g_signal_connect (gstDecodeBin, "pad-added", (GCallback) padAdded, conv);
+
+ /* run */
+ gst_element_set_state(gstPipeline, GST_STATE_PLAYING);
+
+ m_opened = true;
+
+ m_elapsedTimeOffset = 0;
+ m_timeStamp.restart();
+ m_clockStamp.restart();
+
+ return true;
+}
+
+void QGStreamerAudioOutput::close()
+{
+ if (!m_opened)
+ return;
+
+ gst_element_set_state(gstPipeline, GST_STATE_NULL);
+ gst_object_unref (gstPipeline);
+ gstVolume = nullptr;
+ gstAppSrc = nullptr;
+
+ if (!m_pullMode && m_audioSource) {
+ delete m_audioSource;
+ m_audioSource = nullptr;
+ }
+ m_opened = false;
+ m_buffer.clear();
+}
+
+qint64 QGStreamerAudioOutput::write(const char *data, qint64 len)
+{
+ m_buffer.append(data, len);
+ m_totalTimeValue += len;
+ return len;
+}
+
+void QGStreamerAudioOutput::stop()
+{
+ if (m_deviceState == QAudio::StoppedState)
+ return;
+
+ close();
+
+ setError(QAudio::NoError);
+ setState(QAudio::StoppedState);
+}
+
+int QGStreamerAudioOutput::bytesFree() const
+{
+ if (m_deviceState != QAudio::ActiveState && m_deviceState != QAudio::IdleState)
+ return 0;
+
+ return m_bufferSize; // ### correct????
+}
+
+int QGStreamerAudioOutput::periodSize() const
+{
+ // max 5ms periods. Gstreamer itself will ask for 4k data at a time
+ return qMin(4096, 5*m_format.sampleRate()*m_format.sampleSize()*m_format.channelCount()/8/1000);
+}
+
+void QGStreamerAudioOutput::setBufferSize(int value)
+{
+ m_bufferSize = value;
+ if (gstAppSrc)
+ gst_app_src_set_max_bytes(GST_APP_SRC(gstAppSrc), value);
+}
+
+int QGStreamerAudioOutput::bufferSize() const
+{
+ return m_bufferSize;
+}
+
+void QGStreamerAudioOutput::setNotifyInterval(int ms)
+{
+ m_notifyInterval = qMax(0, ms);
+}
+
+int QGStreamerAudioOutput::notifyInterval() const
+{
+ return m_notifyInterval;
+}
+
+qint64 QGStreamerAudioOutput::processedUSecs() const
+{
+ qint64 result = qint64(1000000) * m_totalTimeValue /
+ (m_format.channelCount() * (m_format.sampleSize() / 8)) /
+ m_format.sampleRate();
+
+ return result;
+}
+
+void QGStreamerAudioOutput::resume()
+{
+ if (m_deviceState == QAudio::SuspendedState) {
+ gst_element_set_state (gstPipeline, GST_STATE_PLAYING);
+
+ setState(m_pullMode ? QAudio::ActiveState : QAudio::IdleState);
+ setError(QAudio::NoError);
+ }
+}
+
+void QGStreamerAudioOutput::setFormat(const QAudioFormat &format)
+{
+ m_format = format;
+}
+
+QAudioFormat QGStreamerAudioOutput::format() const
+{
+ return m_format;
+}
+
+void QGStreamerAudioOutput::suspend()
+{
+ if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::IdleState) {
+ setError(QAudio::NoError);
+ setState(QAudio::SuspendedState);
+
+ gst_element_set_state (gstPipeline, GST_STATE_PAUSED);
+ // ### elapsed time
+ }
+}
+
+qint64 QGStreamerAudioOutput::elapsedUSecs() const
+{
+ if (m_deviceState == QAudio::StoppedState)
+ return 0;
+
+ return m_clockStamp.elapsed() * qint64(1000);
+}
+
+void QGStreamerAudioOutput::reset()
+{
+ stop();
+}
+
+GStreamerOutputPrivate::GStreamerOutputPrivate(QGStreamerAudioOutput *audio)
+{
+ m_audioDevice = audio;
+}
+
+qint64 GStreamerOutputPrivate::readData(char *data, qint64 len)
+{
+ Q_UNUSED(data);
+ Q_UNUSED(len);
+
+ return 0;
+}
+
+qint64 GStreamerOutputPrivate::writeData(const char *data, qint64 len)
+{
+ return m_audioDevice->write(data, len);
+}
+
+void QGStreamerAudioOutput::setVolume(qreal vol)
+{
+ if (m_volume == vol)
+ return;
+
+ m_volume = vol;
+ if (gstVolume)
+ g_object_set(gstVolume, "volume", vol, nullptr);
+}
+
+qreal QGStreamerAudioOutput::volume() const
+{
+ return m_volume;
+}
+
+void QGStreamerAudioOutput::setCategory(const QString &category)
+{
+ if (m_category != category) {
+ m_category = category;
+ }
+}
+
+QString QGStreamerAudioOutput::category() const
+{
+ return m_category;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qaudiooutput_gstreamer_p.cpp"
diff --git a/src/multimedia/audio/gstreamer/qaudiooutput_gstreamer_p.h b/src/multimedia/audio/gstreamer/qaudiooutput_gstreamer_p.h
new file mode 100644
index 000000000..685685b61
--- /dev/null
+++ b/src/multimedia/audio/gstreamer/qaudiooutput_gstreamer_p.h
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QAUDIOOUTPUTPULSE_H
+#define QAUDIOOUTPUTPULSE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qfile.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/private/qringbuffer_p.h>
+
+#include "qaudio.h"
+#include "qaudiodeviceinfo.h"
+#include <private/qaudiosystem_p.h>
+
+#include <gst/gst.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerBusHelper;
+class QGstAppSrc;
+
+class QGStreamerAudioOutput
+ : public QAbstractAudioOutput
+{
+ friend class GStreamerOutputPrivate;
+ Q_OBJECT
+
+public:
+ QGStreamerAudioOutput(const QByteArray &device);
+ ~QGStreamerAudioOutput();
+
+ void start(QIODevice *device) override;
+ QIODevice *start() override;
+ void stop() override;
+ void reset() override;
+ void suspend() override;
+ void resume() override;
+ int bytesFree() const override;
+ int periodSize() const override;
+ void setBufferSize(int value) override;
+ int bufferSize() const override;
+ void setNotifyInterval(int milliSeconds) override;
+ int notifyInterval() const override;
+ qint64 processedUSecs() const override;
+ qint64 elapsedUSecs() const override;
+ QAudio::Error error() const override;
+ QAudio::State state() const override;
+ void setFormat(const QAudioFormat &format) override;
+ QAudioFormat format() const override;
+
+ void setVolume(qreal volume) override;
+ qreal volume() const override;
+
+ void setCategory(const QString &category) override;
+ QString category() const override;
+
+private:
+ void setState(QAudio::State state);
+ void setError(QAudio::Error error);
+
+ static gboolean busMessage(GstBus *bus, GstMessage *msg, gpointer user_data);
+
+ bool open();
+ void close();
+ qint64 write(const char *data, qint64 len);
+
+private:
+ QByteArray m_device;
+ QAudioFormat m_format;
+ QAudio::Error m_errorState = QAudio::NoError;
+ QAudio::State m_deviceState = QAudio::StoppedState;
+ bool m_pullMode = true;
+ bool m_opened = false;
+ QIODevice *m_audioSource = nullptr;
+ QRingBuffer m_buffer;
+ QTimer m_periodTimer;
+ int m_notifyInterval = 1000;
+ int m_bufferSize = 0;
+ QElapsedTimer m_clockStamp;
+ qint64 m_totalTimeValue = 0;
+ QElapsedTimer m_timeStamp;
+ qint64 m_elapsedTimeOffset = 0;
+ QString m_category;
+ qreal m_volume = 1.;
+ QByteArray pushData;
+
+ GstElement *gstPipeline = nullptr;
+ GstElement *gstOutput = nullptr;
+ GstElement *gstVolume = nullptr;
+ GstElement *gstAppSrc = nullptr;
+ QGstAppSrc *m_appSrc = nullptr;
+};
+
+class GStreamerOutputPrivate : public QIODevice
+{
+ friend class QGStreamerAudioOutput;
+ Q_OBJECT
+
+public:
+ GStreamerOutputPrivate(QGStreamerAudioOutput *audio);
+ virtual ~GStreamerOutputPrivate() {}
+
+protected:
+ qint64 readData(char *data, qint64 len) override;
+ qint64 writeData(const char *data, qint64 len) override;
+
+private:
+ QGStreamerAudioOutput *m_audioDevice;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/audio/qaudiosystem.cpp b/src/multimedia/audio/qaudiosystem.cpp
index ce6d7f656..64002fe11 100644
--- a/src/multimedia/audio/qaudiosystem.cpp
+++ b/src/multimedia/audio/qaudiosystem.cpp
@@ -40,7 +40,9 @@
#include <private/qtmultimediaglobal_p.h>
#include "qaudiosystem_p.h"
-#if QT_CONFIG(pulseaudio)
+#if QT_CONFIG(gstreamer)
+#include <private/qaudiointerface_gstreamer_p.h>
+#elif QT_CONFIG(pulseaudio)
#include <private/qaudiointerface_pulse_p.h>
#elif QT_CONFIG(alsa)
#include <private/qalsainterface_p.h>
@@ -429,7 +431,9 @@ QAudioSystemInterface *QAudioSystemInterface::instance()
{
static QAudioSystemInterface *system = nullptr;
if (!system) {
-#if QT_CONFIG(pulseaudio)
+#if QT_CONFIG(gstreamer)
+ system = new QGStreamerAudioInterface();
+#elif QT_CONFIG(pulseaudio)
system = new QPulseAudioInterface();
#elif QT_CONFIG(alsa)
system = new QAlsaInterface();