Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qgstreameraudiosource.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 <private/qaudiohelpers_p.h>
8
11#include <sys/types.h>
12#include <unistd.h>
13
14#include <gst/gst.h>
16Q_DECLARE_METATYPE(GstSample *);
17
19
22 m_info(device),
23 m_device(device.id())
24{
25 qRegisterMetaType<GstSample *>();
26}
27
29{
30 close();
31}
32
33void QGStreamerAudioSource::setError(QAudio::Error error)
34{
35 if (m_errorState == error)
36 return;
37
38 m_errorState = error;
40}
41
43{
44 return m_errorState;
45}
46
47void QGStreamerAudioSource::setState(QAudio::State state)
48{
49 if (m_deviceState == state)
50 return;
51
52 m_deviceState = state;
54}
55
57{
58 return m_deviceState;
59}
60
62{
63 if (m_deviceState == QAudio::StoppedState)
64 m_format = format;
65}
66
68{
69 return m_format;
70}
71
73{
74 setState(QAudio::StoppedState);
75 setError(QAudio::NoError);
76
77 close();
78
79 if (!open())
80 return;
81
82 m_pullMode = true;
83 m_audioSink = device;
84
85 setState(QAudio::ActiveState);
86}
87
89{
90 setState(QAudio::StoppedState);
91 setError(QAudio::NoError);
92
93 close();
94
95 if (!open())
96 return nullptr;
97
98 m_pullMode = false;
99 m_audioSink = new GStreamerInputPrivate(this);
101
102 setState(QAudio::IdleState);
103
104 return m_audioSink;
105}
106
108{
109 if (m_deviceState == QAudio::StoppedState)
110 return;
111
112 close();
113
114 setError(QAudio::NoError);
115 setState(QAudio::StoppedState);
116}
117
118bool QGStreamerAudioSource::open()
119{
120 if (m_opened)
121 return true;
122
123 const auto *deviceInfo = static_cast<const QGStreamerAudioDeviceInfo *>(m_info.handle());
124 if (!deviceInfo->gstDevice) {
125 setError(QAudio::OpenError);
126 setState(QAudio::StoppedState);
127 return false;
128 }
129
130 gstInput = QGstElement(gst_device_create_element(deviceInfo->gstDevice, nullptr));
131 if (gstInput.isNull()) {
132 setError(QAudio::OpenError);
133 setState(QAudio::StoppedState);
134 return false;
135 }
136
137 auto gstCaps = QGstUtils::capsForAudioFormat(m_format);
138
139 if (gstCaps.isNull()) {
140 setError(QAudio::OpenError);
141 setState(QAudio::StoppedState);
142 return false;
143 }
144
145
146#ifdef DEBUG_AUDIO
147 qDebug() << "Opening input" << QTime::currentTime();
148 qDebug() << "Caps: " << gst_caps_to_string(gstCaps);
149#endif
150
151 gstPipeline = QGstPipeline("pipeline");
152
153 auto *gstBus = gst_pipeline_get_bus(gstPipeline.pipeline());
154 gst_bus_add_watch(gstBus, &QGStreamerAudioSource::busMessage, this);
155 gst_object_unref (gstBus);
156
157 gstAppSink = createAppSink();
158 gstAppSink.set("caps", gstCaps);
159
160 QGstElement conv("audioconvert", "conv");
161 gstVolume = QGstElement("volume", "volume");
162 Q_ASSERT(gstVolume);
163 if (m_volume != 1.)
164 gstVolume.set("volume", m_volume);
165
166 gstPipeline.add(gstInput, gstVolume, conv, gstAppSink);
167 gstInput.link(gstVolume, conv, gstAppSink);
168
169 gstPipeline.setState(GST_STATE_PLAYING);
170
171 m_opened = true;
172
173 m_timeStamp.restart();
174 m_elapsedTimeOffset = 0;
175 m_bytesWritten = 0;
176
177 return true;
178}
179
180void QGStreamerAudioSource::close()
181{
182 if (!m_opened)
183 return;
184
185 gstPipeline.setState(GST_STATE_NULL);
186 gstPipeline = {};
187 gstVolume = {};
188 gstAppSink = {};
189 gstInput = {};
190
191 if (!m_pullMode && m_audioSink) {
192 delete m_audioSink;
193 }
194 m_audioSink = nullptr;
195 m_opened = false;
196}
197
198gboolean QGStreamerAudioSource::busMessage(GstBus *, GstMessage *msg, gpointer user_data)
199{
201 switch (GST_MESSAGE_TYPE (msg)) {
202 case GST_MESSAGE_EOS:
203 input->stop();
204 break;
205 case GST_MESSAGE_ERROR: {
206 input->setError(QAudio::IOError);
207 gchar *debug;
208 GError *error;
209
210 gst_message_parse_error (msg, &error, &debug);
211 g_free (debug);
212
213 qDebug("Error: %s\n", error->message);
214 g_error_free (error);
215
216 break;
217 }
218 default:
219 break;
220 }
221 return false;
222}
223
225{
226 return m_buffer.size();
227}
228
230{
231 if (m_deviceState == QAudio::SuspendedState || m_deviceState == QAudio::IdleState) {
232 gstPipeline.setState(GST_STATE_PLAYING);
233 setState(QAudio::ActiveState);
234 setError(QAudio::NoError);
235 }
236}
237
239{
240 if (m_volume == vol)
241 return;
242
243 m_volume = vol;
244 if (!gstVolume.isNull())
245 gstVolume.set("volume", vol);
246}
247
249{
250 return m_volume;
251}
252
254{
255 m_bufferSize = value;
256}
257
259{
260 return m_bufferSize;
261}
262
264{
265 return m_format.durationForBytes(m_bytesWritten);
266}
267
269{
270 if (m_deviceState == QAudio::ActiveState) {
271 setError(QAudio::NoError);
272 setState(QAudio::SuspendedState);
273
274 gstPipeline.setState(GST_STATE_PAUSED);
275 }
276}
277
279{
280 stop();
281 m_buffer.clear();
282}
283
284//#define MAX_BUFFERS_IN_QUEUE 4
285
286QGstElement QGStreamerAudioSource::createAppSink()
287{
288 QGstElement sink("appsink", "appsink");
289 GstAppSink *appSink = reinterpret_cast<GstAppSink *>(sink.element());
290
291 GstAppSinkCallbacks callbacks;
292 memset(&callbacks, 0, sizeof(callbacks));
293 callbacks.eos = &eos;
294 callbacks.new_sample = &new_sample;
295 gst_app_sink_set_callbacks(appSink, &callbacks, this, nullptr);
296// gst_app_sink_set_max_buffers(appSink, MAX_BUFFERS_IN_QUEUE);
297 gst_base_sink_set_sync(GST_BASE_SINK(appSink), FALSE);
298
299 return sink;
300}
301
302void QGStreamerAudioSource::newDataAvailable(GstSample *sample)
303{
304 if (m_audioSink) {
305 GstBuffer *buffer = gst_sample_get_buffer(sample);
306 GstMapInfo mapInfo;
307 gst_buffer_map(buffer, &mapInfo, GST_MAP_READ);
308 const char *bufferData = (const char*)mapInfo.data;
309 gsize bufferSize = mapInfo.size;
310
311 if (!m_pullMode) {
312 // need to store that data in the QBuffer
313 m_buffer.append(bufferData, bufferSize);
314 m_audioSink->readyRead();
315 } else {
316 m_bytesWritten += bufferSize;
317 m_audioSink->write(bufferData, bufferSize);
318 }
319
320 gst_buffer_unmap(buffer, &mapInfo);
321 }
322
323 gst_sample_unref(sample);
324}
325
326GstFlowReturn QGStreamerAudioSource::new_sample(GstAppSink *sink, gpointer user_data)
327{
328 // "Note that the preroll buffer will also be returned as the first buffer when calling gst_app_sink_pull_buffer()."
329 QGStreamerAudioSource *control = static_cast<QGStreamerAudioSource*>(user_data);
330
331 GstSample *sample = gst_app_sink_pull_sample(sink);
332 QMetaObject::invokeMethod(control, "newDataAvailable", Qt::AutoConnection, Q_ARG(GstSample *, sample));
333
334 return GST_FLOW_OK;
335}
336
337void QGStreamerAudioSource::eos(GstAppSink *, gpointer user_data)
338{
339 QGStreamerAudioSource *control = static_cast<QGStreamerAudioSource*>(user_data);
340 control->setState(QAudio::StoppedState);
341}
342
344{
345 m_audioDevice = qobject_cast<QGStreamerAudioSource*>(audio);
346}
347
349{
350 if (m_audioDevice->state() == QAudio::IdleState)
351 m_audioDevice->setState(QAudio::ActiveState);
352 qint64 bytes = m_audioDevice->m_buffer.read(data, len);
353 m_audioDevice->m_bytesWritten += bytes;
354 return bytes;
355}
356
358{
359 Q_UNUSED(data);
360 Q_UNUSED(len);
361 return 0;
362}
363
365{
366 return m_audioDevice->m_buffer.size();
367}
368
369
371
372#include "moc_qgstreameraudiosource_p.cpp"
IOBluetoothDevice * device
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...
qint64 bytesAvailable() const override
Returns the number of bytes that are available for reading.
GStreamerInputPrivate(QGStreamerAudioSource *audio)
qint64 writeData(const char *data, qint64 len) override
Writes up to maxSize bytes from data to the device.
The QAudioDevice class provides an information about audio devices and their functionality.
const QAudioDevicePrivate * handle() const
The QAudioFormat class stores audio stream parameter information.
Q_MULTIMEDIA_EXPORT qint64 durationForBytes(qint32 byteCount) const
Returns the number of microseconds represented by bytes in this format.
qint64 restart() noexcept
Restarts the timer and returns the number of milliseconds elapsed since the previous start.
qsizetype bufferSize() const override
void setFormat(const QAudioFormat &format) override
void setBufferSize(qsizetype value) override
qsizetype bytesReady() const override
QAudioFormat format() const override
qreal volume() const override
qint64 processedUSecs() const override
void setVolume(qreal volume) override
QAudio::State state() const override
QIODevice * start() override
QGStreamerAudioSource(const QAudioDevice &device, QObject *parent)
QAudio::Error error() const override
void add(const QGstElement &element)
Definition qgst_p.h:549
bool link(const QGstElement &next)
Definition qgst_p.h:429
bool isNull() const
Definition qgst_p.h:288
void set(const char *property, const char *str)
Definition qgst_p.h:290
GstStateChangeReturn setState(GstState state)
GstPipeline * pipeline() const
\inmodule QtCore \reentrant
Definition qiodevice.h:34
virtual bool open(QIODeviceBase::OpenMode mode)
Opens the device and sets its OpenMode to mode.
void readyRead()
This signal is emitted once every time new data is available for reading from the device's current re...
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
\inmodule QtCore
Definition qobject.h:90
void errorChanged(QAudio::Error error)
void stateChanged(QAudio::State state)
qint64 size() const
Q_CORE_EXPORT void append(const char *data, qint64 size)
Q_CORE_EXPORT void clear()
Q_CORE_EXPORT qint64 read(char *data, qint64 maxLength)
static QTime currentTime()
Returns the current time as reported by the system clock.
else opt state
[0]
State
\value ActiveState Audio data is being processed, this state is set after start() is called and while...
Definition qaudio.h:25
@ StoppedState
Definition qaudio.h:25
@ SuspendedState
Definition qaudio.h:25
@ IdleState
Definition qaudio.h:25
@ ActiveState
Definition qaudio.h:25
Error
\value NoError No errors have occurred \value OpenError An error occurred opening the audio device \v...
Definition qaudio.h:24
@ OpenError
Definition qaudio.h:24
@ NoError
Definition qaudio.h:24
@ IOError
Definition qaudio.h:24
Q_MULTIMEDIA_EXPORT QGstCaps capsForAudioFormat(const QAudioFormat &format)
Definition qgstutils.cpp:99
Combined button and popup list for selecting options.
@ AutoConnection
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void * user_data
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qDebug
[1]
Definition qlogging.h:160
#define Q_DECLARE_OPAQUE_POINTER(POINTER)
Definition qmetatype.h:1496
#define Q_DECLARE_METATYPE(TYPE)
Definition qmetatype.h:1504
#define Q_ARG(Type, data)
Definition qobjectdefs.h:62
GLenum GLuint id
[7]
GLenum GLuint buffer
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLsizei GLsizei GLenum format
GLenum GLsizei len
GLsizei GLenum GLboolean sink
GLenum GLenum GLenum input
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
PromiseCallbacks callbacks
Definition qstdweb.cpp:270
#define emit
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:70
long long qint64
Definition qtypes.h:55
double qreal
Definition qtypes.h:92
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent