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
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 <QtMultimedia/private/qaudiohelpers_p.h>
8
11#include "common/qgst_p.h"
12#include "common/qgst_debug_p.h"
13
14#include <sys/types.h>
15#include <unistd.h>
16
18Q_DECLARE_METATYPE(GstSample *);
19
21
23 : QPlatformAudioSource(parent),
24 m_info(device),
25 m_device(device.id())
26{
27 qRegisterMetaType<GstSample *>();
28}
29
34
35void QGStreamerAudioSource::setError(QAudio::Error error)
36{
37 if (m_errorState == error)
38 return;
39
40 m_errorState = error;
42}
43
45{
46 return m_errorState;
47}
48
49void QGStreamerAudioSource::setState(QAudio::State state)
50{
51 if (m_deviceState == state)
52 return;
53
54 m_deviceState = state;
56}
57
59{
60 return m_deviceState;
61}
62
64{
65 if (m_deviceState == QAudio::StoppedState)
66 m_format = format;
67}
68
70{
71 return m_format;
72}
73
75{
76 setState(QAudio::StoppedState);
77 setError(QAudio::NoError);
78
79 close();
80
81 if (!open())
82 return;
83
84 m_pullMode = true;
85 m_audioSink = device;
86
87 setState(QAudio::ActiveState);
88}
89
91{
92 setState(QAudio::StoppedState);
93 setError(QAudio::NoError);
94
95 close();
96
97 if (!open())
98 return nullptr;
99
100 m_pullMode = false;
101 m_audioSink = new GStreamerInputPrivate(this);
103
104 setState(QAudio::IdleState);
105
106 return m_audioSink;
107}
108
110{
111 if (m_deviceState == QAudio::StoppedState)
112 return;
113
114 close();
115
116 setError(QAudio::NoError);
117 setState(QAudio::StoppedState);
118}
119
120bool QGStreamerAudioSource::open()
121{
122 if (m_opened)
123 return true;
124
125 const auto *deviceInfo = static_cast<const QGStreamerAudioDeviceInfo *>(m_info.handle());
126 if (!deviceInfo->gstDevice) {
127 setError(QAudio::OpenError);
128 setState(QAudio::StoppedState);
129 return false;
130 }
131
132 gstInput = QGstElement::createFromDevice(deviceInfo->gstDevice);
133 if (gstInput.isNull()) {
134 setError(QAudio::OpenError);
135 setState(QAudio::StoppedState);
136 return false;
137 }
138
139 auto gstCaps = QGstUtils::capsForAudioFormat(m_format);
140
141 if (gstCaps.isNull()) {
142 setError(QAudio::OpenError);
143 setState(QAudio::StoppedState);
144 return false;
145 }
146
147
148#ifdef DEBUG_AUDIO
149 qDebug() << "Opening input" << QTime::currentTime();
150 qDebug() << "Caps: " << gst_caps_to_string(gstCaps);
151#endif
152
153 gstPipeline = QGstPipeline::create("audioSourcePipeline");
154
155 auto *gstBus = gst_pipeline_get_bus(gstPipeline.pipeline());
156 gst_bus_add_watch(gstBus, &QGStreamerAudioSource::busMessage, this);
157 gst_object_unref (gstBus);
158
159 gstAppSink = createAppSink();
160 gstAppSink.set("caps", gstCaps);
161
162 QGstElement conv = QGstElement::createFromFactory("audioconvert", "conv");
163 gstVolume = QGstElement::createFromFactory("volume", "volume");
164 Q_ASSERT(gstVolume);
165 if (m_volume != 1.)
166 gstVolume.set("volume", m_volume);
167
168 gstPipeline.add(gstInput, gstVolume, conv, gstAppSink);
169 qLinkGstElements(gstInput, gstVolume, conv, gstAppSink);
170
171 gstPipeline.setState(GST_STATE_PLAYING);
172
173 m_opened = true;
174
175 m_timeStamp.restart();
176 m_elapsedTimeOffset = 0;
177 m_bytesWritten = 0;
178
179 return true;
180}
181
182void QGStreamerAudioSource::close()
183{
184 if (!m_opened)
185 return;
186
187 gstPipeline.setState(GST_STATE_NULL);
188 gstPipeline = {};
189 gstVolume = {};
190 gstAppSink = {};
191 gstInput = {};
192
193 if (!m_pullMode && m_audioSink) {
194 delete m_audioSink;
195 }
196 m_audioSink = nullptr;
197 m_opened = false;
198}
199
200gboolean QGStreamerAudioSource::busMessage(GstBus *, GstMessage *msg, gpointer user_data)
201{
203 switch (GST_MESSAGE_TYPE (msg)) {
204 case GST_MESSAGE_EOS:
205 input->stop();
206 break;
207 case GST_MESSAGE_ERROR: {
208 input->setError(QAudio::IOError);
209 qDebug() << "Error:" << QCompactGstMessageAdaptor(msg);
210 break;
211 }
212 default:
213 break;
214 }
215 return false;
216}
217
219{
220 return m_buffer.size();
221}
222
224{
225 if (m_deviceState == QAudio::SuspendedState || m_deviceState == QAudio::IdleState) {
226 gstPipeline.setState(GST_STATE_PLAYING);
227 setState(QAudio::ActiveState);
228 setError(QAudio::NoError);
229 }
230}
231
233{
234 if (m_volume == vol)
235 return;
236
237 m_volume = vol;
238 if (!gstVolume.isNull())
239 gstVolume.set("volume", vol);
240}
241
243{
244 return m_volume;
245}
246
248{
249 m_bufferSize = value;
250}
251
253{
254 return m_bufferSize;
255}
256
258{
259 return m_format.durationForBytes(m_bytesWritten);
260}
261
263{
264 if (m_deviceState == QAudio::ActiveState) {
265 setError(QAudio::NoError);
266 setState(QAudio::SuspendedState);
267
268 gstPipeline.setState(GST_STATE_PAUSED);
269 }
270}
271
273{
274 stop();
275 m_buffer.clear();
276}
277
278//#define MAX_BUFFERS_IN_QUEUE 4
279
280QGstAppSink QGStreamerAudioSource::createAppSink()
281{
282 QGstAppSink sink = QGstAppSink::create("appsink");
283
284 GstAppSinkCallbacks callbacks{};
285 callbacks.eos = eos;
286 callbacks.new_sample = new_sample;
287 sink.setCallbacks(callbacks, this, nullptr);
288 // gst_app_sink_set_max_buffers(sink.appSink(), MAX_BUFFERS_IN_QUEUE);
289 gst_base_sink_set_sync(sink.baseSink(), FALSE);
290
291 return sink;
292}
293
294void QGStreamerAudioSource::newDataAvailable(QGstSampleHandle sample)
295{
296 if (m_audioSink) {
297 GstBuffer *buffer = gst_sample_get_buffer(sample.get());
298 GstMapInfo mapInfo;
299 gst_buffer_map(buffer, &mapInfo, GST_MAP_READ);
300 const char *bufferData = (const char*)mapInfo.data;
301 gsize bufferSize = mapInfo.size;
302
303 if (!m_pullMode) {
304 // need to store that data in the QBuffer
305 m_buffer.append(bufferData, bufferSize);
306 m_audioSink->readyRead();
307 } else {
308 m_bytesWritten += bufferSize;
309 m_audioSink->write(bufferData, bufferSize);
310 }
311
312 gst_buffer_unmap(buffer, &mapInfo);
313 }
314}
315
316GstFlowReturn QGStreamerAudioSource::new_sample(GstAppSink *sink, gpointer user_data)
317{
318 // "Note that the preroll buffer will also be returned as the first buffer when calling gst_app_sink_pull_buffer()."
319 QGStreamerAudioSource *control = static_cast<QGStreamerAudioSource*>(user_data);
320
321 QGstSampleHandle sample{
322 gst_app_sink_pull_sample(sink),
324 };
325
326 QMetaObject::invokeMethod(control, [control, sample = std::move(sample)]() mutable {
327 control->newDataAvailable(std::move(sample));
328 });
329
330 return GST_FLOW_OK;
331}
332
333void QGStreamerAudioSource::eos(GstAppSink *, gpointer user_data)
334{
335 QGStreamerAudioSource *control = static_cast<QGStreamerAudioSource*>(user_data);
336 control->setState(QAudio::StoppedState);
337}
338
340{
341 m_audioDevice = audio;
342}
343
345{
346 if (m_audioDevice->state() == QAudio::IdleState)
347 m_audioDevice->setState(QAudio::ActiveState);
348 qint64 bytes = m_audioDevice->m_buffer.read(data, len);
349 m_audioDevice->m_bytesWritten += bytes;
350 return bytes;
351}
352
354{
355 Q_UNUSED(data);
356 Q_UNUSED(len);
357 return 0;
358}
359
361{
362 return m_audioDevice->m_buffer.size();
363}
364
365
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.
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.
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
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
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)
GstPipeline * pipeline() const
static QGstPipeline create(const char *name)
\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:103
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
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
@ OpenError
Definition qaudio.h:28
@ NoError
Definition qaudio.h:28
@ IOError
Definition qaudio.h:28
QGstCaps capsForAudioFormat(const QAudioFormat &format)
Definition qgstutils.cpp:83
Combined button and popup list for selecting options.
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void * user_data
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
#define qDebug
[1]
Definition qlogging.h:164
#define Q_DECLARE_OPAQUE_POINTER(POINTER)
Definition qmetatype.h:1517
#define Q_DECLARE_METATYPE(TYPE)
Definition qmetatype.h:1525
GLenum GLuint id
[7]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint buffer
GLint GLsizei GLsizei GLenum format
GLsizei GLenum GLboolean sink
GLenum GLsizei len
GLenum GLenum GLenum input
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
PromiseCallbacks callbacks
Definition qstdweb.cpp:275
#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
Type get() const noexcept
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...