Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qgstreameraudiosink.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtCore/qcoreapplication.h>
5#include <QtCore/qdebug.h>
6#include <QtCore/qmath.h>
7#include <QtMultimedia/private/qaudiohelpers_p.h>
8
9#include <sys/types.h>
10#include <unistd.h>
11
14#include <common/qgst_debug_p.h>
18#include <common/qgstutils_p.h>
19
20#include <utility>
21
23
24QMaybe<QPlatformAudioSink *> QGStreamerAudioSink::create(const QAudioDevice &device, QObject *parent)
25{
26 auto maybeAppSrc = QGstAppSource::create();
27 if (!maybeAppSrc)
28 return maybeAppSrc.error();
29
30 QGstElement audioconvert = QGstElement::createFromFactory("audioconvert", "conv");
31 if (!audioconvert)
32 return errorMessageCannotFindElement("audioconvert");
33
35 if (!volume)
36 return errorMessageCannotFindElement("volume");
37
38 return new QGStreamerAudioSink(device, maybeAppSrc.value(), audioconvert, volume, parent);
39}
40
41QGStreamerAudioSink::QGStreamerAudioSink(const QAudioDevice &device, QGstAppSource *appsrc,
42 QGstElement audioconvert, QGstElement volume,
43 QObject *parent)
44 : QPlatformAudioSink(parent),
45 m_device(device.id()),
46 gstPipeline(QGstPipeline::create("audioSinkPipeline")),
47 gstVolume(std::move(volume)),
48 m_appSrc(appsrc)
49{
50 gstPipeline.installMessageFilter(this);
51
52 connect(m_appSrc, &QGstAppSource::bytesProcessed, this, &QGStreamerAudioSink::bytesProcessedByAppSrc);
53 connect(m_appSrc, &QGstAppSource::noMoreData, this, &QGStreamerAudioSink::needData);
54 gstAppSrc = m_appSrc->element();
55
56 QGstElement queue = QGstElement::createFromFactory("queue", "audioSinkQueue");
57
58 if (m_volume != 1.)
59 gstVolume.set("volume", m_volume);
60
61 // link decodeBin to audioconvert in a callback once we get a pad from the decoder
62 // g_signal_connect (gstDecodeBin, "pad-added", (GCallback) padAdded, conv);
63
64 const auto *audioInfo = static_cast<const QGStreamerAudioDeviceInfo *>(device.handle());
65 gstOutput = QGstElement::createFromDevice(audioInfo->gstDevice, nullptr);
66
67 gstPipeline.add(gstAppSrc, queue, /*gstDecodeBin, */ audioconvert, gstVolume, gstOutput);
68 qLinkGstElements(gstAppSrc, queue, audioconvert, gstVolume, gstOutput);
69}
70
72{
73 close();
74 gstPipeline.removeMessageFilter(this);
75
76 gstPipeline = {};
77 gstVolume = {};
78 gstAppSrc = {};
79 delete m_appSrc;
80 m_appSrc = nullptr;
81}
82
83void QGStreamerAudioSink::setError(QAudio::Error error)
84{
85 if (m_errorState == error)
86 return;
87
88 m_errorState = error;
90}
91
93{
94 return m_errorState;
95}
96
97void QGStreamerAudioSink::setState(QAudio::State state)
98{
99 if (m_deviceState == state)
100 return;
101
102 m_deviceState = state;
104}
105
107{
108 return m_deviceState;
109}
110
112{
113 setState(QAudio::StoppedState);
114 setError(QAudio::NoError);
115
116 close();
117
118 if (!m_format.isValid()) {
119 setError(QAudio::OpenError);
120 return;
121 }
122
123 m_pullMode = true;
124 m_audioSource = device;
125
126 if (!open()) {
127 m_audioSource = nullptr;
128 setError(QAudio::OpenError);
129 return;
130 }
131
132 setState(QAudio::ActiveState);
133}
134
136{
137 setState(QAudio::StoppedState);
138 setError(QAudio::NoError);
139
140 close();
141
142 if (!m_format.isValid()) {
143 setError(QAudio::OpenError);
144 return nullptr;
145 }
146
147 m_pullMode = false;
148
149 if (!open())
150 return nullptr;
151
152 m_audioSource = new GStreamerOutputPrivate(this);
154
155 setState(QAudio::IdleState);
156
157 return m_audioSource;
158}
159
160#if 0
161static void padAdded(GstElement *element, GstPad *pad, gpointer data)
162{
163 GstElement *other = static_cast<GstElement *>(data);
164
165 QGString name { gst_pad_get_name(pad)};
166 qDebug("A new pad %s was created for %s\n", name, gst_element_get_name(element));
167
168 qDebug("element %s will be linked to %s\n",
169 gst_element_get_name(element),
170 gst_element_get_name(other));
171 gst_element_link(element, other);
172}
173#endif
174
176{
177 auto *msg = message.message();
178 switch (GST_MESSAGE_TYPE (msg)) {
179 case GST_MESSAGE_EOS:
180 setState(QAudio::IdleState);
181 break;
182 case GST_MESSAGE_ERROR: {
183 setError(QAudio::IOError);
184 qDebug() << "Error:" << QCompactGstMessageAdaptor(message);
185 break;
186 }
187 default:
188 break;
189 }
190
191 return true;
192}
193
194bool QGStreamerAudioSink::open()
195{
196 if (m_opened)
197 return true;
198
199 if (gstOutput.isNull()) {
200 setError(QAudio::OpenError);
201 setState(QAudio::StoppedState);
202 return false;
203 }
204
205// qDebug() << "GST caps:" << gst_caps_to_string(caps);
206 m_appSrc->setup(m_audioSource, m_audioSource ? m_audioSource->pos() : 0);
207 m_appSrc->setAudioFormat(m_format);
208
209 /* run */
210 gstPipeline.setState(GST_STATE_PLAYING);
211
212 m_opened = true;
213
214 m_timeStamp.restart();
215 m_bytesProcessed = 0;
216
217 return true;
218}
219
220void QGStreamerAudioSink::close()
221{
222 if (!m_opened)
223 return;
224
225 if (!gstPipeline.setStateSync(GST_STATE_NULL))
226 qWarning() << "failed to close the audio output stream";
227
228 if (!m_pullMode && m_audioSource)
229 delete m_audioSource;
230 m_audioSource = nullptr;
231 m_opened = false;
232}
233
234qint64 QGStreamerAudioSink::write(const char *data, qint64 len)
235{
236 if (!len)
237 return 0;
238 if (m_errorState == QAudio::UnderrunError)
239 m_errorState = QAudio::NoError;
240
241 m_appSrc->write(data, len);
242 return len;
243}
244
246{
247 if (m_deviceState == QAudio::StoppedState)
248 return;
249
250 close();
251
252 setError(QAudio::NoError);
253 setState(QAudio::StoppedState);
254}
255
257{
258 if (m_deviceState != QAudio::ActiveState && m_deviceState != QAudio::IdleState)
259 return 0;
260
261 return m_appSrc->canAcceptMoreData() ? 4096*4 : 0;
262}
263
265{
266 m_bufferSize = value;
267 if (!gstAppSrc.isNull())
268 gst_app_src_set_max_bytes(GST_APP_SRC(gstAppSrc.element()), value);
269}
270
272{
273 return m_bufferSize;
274}
275
277{
278 qint64 result = qint64(1000000) * m_bytesProcessed /
279 m_format.bytesPerFrame() /
280 m_format.sampleRate();
281
282 return result;
283}
284
286{
287 if (m_deviceState == QAudio::SuspendedState) {
288 m_appSrc->resume();
289 gstPipeline.setState(GST_STATE_PLAYING);
290
291 setState(m_suspendedInState);
292 setError(QAudio::NoError);
293 }
294}
295
297{
298 m_format = format;
299}
300
302{
303 return m_format;
304}
305
307{
308 if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::IdleState) {
309 m_suspendedInState = m_deviceState;
310 setError(QAudio::NoError);
311 setState(QAudio::SuspendedState);
312
313 gstPipeline.setState(GST_STATE_PAUSED);
314 m_appSrc->suspend();
315 // ### elapsed time
316 }
317}
318
320{
321 stop();
322}
323
325{
326 m_audioDevice = audio;
327}
328
330{
331 Q_UNUSED(data);
332 Q_UNUSED(len);
333
334 return 0;
335}
336
338{
339 if (m_audioDevice->state() == QAudio::IdleState)
340 m_audioDevice->setState(QAudio::ActiveState);
341 return m_audioDevice->write(data, len);
342}
343
345{
346 if (m_volume == vol)
347 return;
348
349 m_volume = vol;
350 if (!gstVolume.isNull())
351 gstVolume.set("volume", vol);
352}
353
355{
356 return m_volume;
357}
358
359void QGStreamerAudioSink::bytesProcessedByAppSrc(int bytes)
360{
361 m_bytesProcessed += bytes;
362 setState(QAudio::ActiveState);
363 setError(QAudio::NoError);
364}
365
366void QGStreamerAudioSink::needData()
367{
369 setState(QAudio::IdleState);
370 setError(m_audioSource && m_audioSource->atEnd() ? QAudio::NoError : QAudio::UnderrunError);
371 }
372}
373
375
376#include "moc_qgstreameraudiosink_p.cpp"
IOBluetoothDevice * device
qint64 writeData(const char *data, qint64 len) override
Writes up to maxSize bytes from data to the device.
GStreamerOutputPrivate(QGStreamerAudioSink *audio)
qint64 readData(char *data, qint64 len) override
Reads up to maxSize bytes from the device into data, and returns the number of bytes read or -1 if an...
The QAudioDevice class provides an information about audio devices and their functionality.
The QAudioFormat class stores audio stream parameter information.
constexpr int sampleRate() const noexcept
Returns the current sample rate in Hertz.
constexpr int bytesPerFrame() const
Returns the number of bytes required to represent one frame (a sample in each channel) in this format...
constexpr bool isValid() const noexcept
Returns true if all of the parameters are valid.
void stateChanged(QAudio::State state)
void errorChanged(QAudio::Error error)
qint64 restart() noexcept
Restarts the timer and returns the number of milliseconds elapsed since the previous start.
static QMaybe< QPlatformAudioSink * > create(const QAudioDevice &device, QObject *parent)
void setFormat(const QAudioFormat &format) override
friend class GStreamerOutputPrivate
QIODevice * start() override
QAudioFormat format() const override
void setVolume(qreal volume) override
QAudio::State state() const override
bool processBusMessage(const QGstreamerMessage &message) override
qsizetype bufferSize() const override
qsizetype bytesFree() const override
qint64 processedUSecs() const override
qreal volume() const override
void setBufferSize(qsizetype value) override
QAudio::Error error() const override
void bytesProcessed(int bytes)
bool setup(QIODevice *stream=nullptr, qint64 offset=0)
void setAudioFormat(const QAudioFormat &f)
static QMaybe< QGstAppSource * > create(QObject *parent=nullptr)
QGstElement element() const
void noMoreData()
void write(const char *data, qsizetype size)
bool canAcceptMoreData() const
std::enable_if_t<(std::is_base_of_v< QGstElement, Ts > &&...), void add)(const Ts &...ts)
Definition qgst_p.h:690
static QGstElement createFromDevice(const QGstDeviceHandle &, const char *name=nullptr)
Definition qgst.cpp:866
GstElement * element() const
Definition qgst.cpp:1026
bool setStateSync(GstState state, std::chrono::nanoseconds timeout=std::chrono::seconds(1))
Definition qgst.cpp:947
static QGstElement createFromFactory(const char *factory, const char *name=nullptr)
Definition qgst.cpp:835
void set(const char *property, const char *str)
Definition qgst.cpp:538
GstStateChangeReturn setState(GstState state)
void installMessageFilter(QGstreamerSyncMessageFilter *filter)
void removeMessageFilter(QGstreamerSyncMessageFilter *filter)
\inmodule QtCore \reentrant
Definition qiodevice.h:34
virtual bool open(QIODeviceBase::OpenMode mode)
Opens the device and sets its OpenMode to mode.
virtual qint64 pos() const
For random-access devices, this function returns the position that data is written to or read from.
virtual bool atEnd() const
Returns true if the current read and write position is at the end of the device (i....
\inmodule QtCore
Definition qobject.h:103
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
else opt state
[0]
State
Definition qaudio.h:29
@ StoppedState
Definition qaudio.h:29
@ SuspendedState
Definition qaudio.h:29
@ IdleState
Definition qaudio.h:29
@ ActiveState
Definition qaudio.h:29
Error
Definition qaudio.h:28
@ UnderrunError
Definition qaudio.h:28
@ OpenError
Definition qaudio.h:28
@ NoError
Definition qaudio.h:28
@ IOError
Definition qaudio.h:28
Combined button and popup list for selecting options.
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
std::enable_if_t<(std::is_base_of_v< QGstElement, Ts > &&...), void qLinkGstElements)(const Ts &...ts)
Definition qgst_p.h:643
QString errorMessageCannotFindElement(std::string_view element)
Definition qgst_p.h:804
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
GLenum GLuint id
[7]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint GLsizei const GLchar * message
GLuint name
GLint GLsizei GLsizei GLenum format
GLuint64EXT * result
[6]
GLenum GLsizei len
#define emit
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
QQueue< int > queue
[0]
QSharedPointer< T > other(t)
[5]
view create()