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
qgstappsource.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 <QDebug>
5
6#include "qgstappsource_p.h"
8#include "qnetworkreply.h"
9#include "qloggingcategory.h"
10
11static Q_LOGGING_CATEGORY(qLcAppSrc, "qt.multimedia.appsrc")
12
14
16{
17 QGstAppSrc appsrc = QGstAppSrc::create("appsrc");
18 if (!appsrc)
19 return errorMessageCannotFindElement("appsrc");
20
21 return new QGstAppSource(appsrc, parent);
22}
23
24QGstAppSource::QGstAppSource(QGstAppSrc appsrc, QObject *parent)
25 : QObject(parent), m_appSrc(std::move(appsrc))
26{
27 m_appSrc.set("emit-signals", false);
28}
29
31{
32 m_appSrc.setStateSync(GST_STATE_NULL);
33 streamDestroyed();
34 qCDebug(qLcAppSrc) << "~QGstAppSrc";
35}
36
38{
39 QMutexLocker locker(&m_mutex);
40
41 if (m_appSrc.isNull())
42 return false;
43
44 if (!setStream(stream, offset))
45 return false;
46
47 GstAppSrcCallbacks callbacks{};
48 callbacks.need_data = QGstAppSource::on_need_data;
49 callbacks.enough_data = QGstAppSource::on_enough_data;
50 callbacks.seek_data = QGstAppSource::on_seek_data;
51
52 m_appSrc.setCallbacks(callbacks, this, nullptr);
53
54 GstAppSrc *appSrc = m_appSrc.appSrc();
55 m_maxBytes = gst_app_src_get_max_bytes(appSrc);
56 m_suspended = false;
57
58 if (m_sequential)
59 m_streamType = GST_APP_STREAM_TYPE_STREAM;
60 else
61 m_streamType = GST_APP_STREAM_TYPE_RANDOM_ACCESS;
62 gst_app_src_set_stream_type(appSrc, m_streamType);
63 gst_app_src_set_size(appSrc, m_sequential ? -1 : m_stream->size() - m_offset);
64
65 m_noMoreData = true;
66
67 return true;
68}
69
71{
72 QMutexLocker locker(&m_mutex);
73
74 m_format = f;
75 if (!m_format.isValid())
76 return;
77
78 auto caps = QGstUtils::capsForAudioFormat(m_format);
79 Q_ASSERT(!caps.isNull());
80 m_appSrc.set("caps", caps);
81 m_appSrc.set("format", GST_FORMAT_TIME);
82}
83
84void QGstAppSource::setExternalAppSrc(QGstAppSrc appsrc)
85{
86 QMutexLocker locker(&m_mutex);
87 m_appSrc = std::move(appsrc);
88}
89
90bool QGstAppSource::setStream(QIODevice *stream, qint64 offset)
91{
92 if (m_stream) {
93 disconnect(m_stream, &QIODevice::readyRead, this, &QGstAppSource::onDataReady);
94 disconnect(m_stream, &QIODevice::destroyed, this, &QGstAppSource::streamDestroyed);
95 m_stream = nullptr;
96 }
97
98 m_dataRequestSize = 0;
99 m_sequential = true;
100 m_maxBytes = 0;
101 streamedSamples = 0;
102
103 if (stream) {
104 if (!stream->isOpen() && !stream->open(QIODevice::ReadOnly))
105 return false;
106 m_stream = stream;
107 connect(m_stream, &QIODevice::destroyed, this, &QGstAppSource::streamDestroyed);
108 connect(m_stream, &QIODevice::readyRead, this, &QGstAppSource::onDataReady);
109 m_sequential = m_stream->isSequential();
110 m_offset = offset;
111 }
112 return true;
113}
114
115bool QGstAppSource::isStreamValid() const
116{
117 return m_stream != nullptr && m_stream->isOpen();
118}
119
121{
122 return m_appSrc;
123}
124
126{
127 QMutexLocker locker(&m_mutex);
128
129 qCDebug(qLcAppSrc) << "write" << size << m_noMoreData << m_dataRequestSize;
130 if (!size)
131 return;
132 Q_ASSERT(!m_stream);
133 m_buffer.append(data, size);
134 m_noMoreData = false;
135 pushData();
136}
137
139{
140 QMutexLocker locker(&m_mutex);
141 return m_noMoreData || m_dataRequestSize != 0;
142}
143
145{
146 QMutexLocker locker(&m_mutex);
147 m_suspended = true;
148}
149
151{
152 QMutexLocker locker(&m_mutex);
153 m_suspended = false;
154 m_noMoreData = true;
155}
156
157void QGstAppSource::onDataReady()
158{
159 qCDebug(qLcAppSrc) << "onDataReady" << m_stream->bytesAvailable() << m_stream->size();
160 pushData();
161}
162
163void QGstAppSource::streamDestroyed()
164{
165 qCDebug(qLcAppSrc) << "stream destroyed";
166 m_stream = nullptr;
167 m_dataRequestSize = 0;
168 streamedSamples = 0;
169 sendEOS();
170}
171
172void QGstAppSource::pushData()
173{
174 if (m_appSrc.isNull() || !m_dataRequestSize || m_suspended) {
175 qCDebug(qLcAppSrc) << "push data: return immediately" << m_appSrc.isNull() << m_dataRequestSize << m_suspended;
176 return;
177 }
178
179 qCDebug(qLcAppSrc) << "pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size();
180 if ((m_stream && m_stream->atEnd())) {
181 eosOrIdle();
182 qCDebug(qLcAppSrc) << "end pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size();
183 return;
184 }
185
186 qint64 size;
187 if (m_stream)
188 size = m_stream->bytesAvailable();
189 else
190 size = m_buffer.size();
191
192 if (!m_dataRequestSize)
193 m_dataRequestSize = m_maxBytes;
194 size = qMin(size, (qint64)m_dataRequestSize);
195 qCDebug(qLcAppSrc) << " reading" << size << "bytes" << size << m_dataRequestSize;
196
197 GstBuffer* buffer = gst_buffer_new_and_alloc(size);
198
199 if (m_sequential || !m_stream)
200 buffer->offset = bytesReadSoFar;
201 else
202 buffer->offset = m_stream->pos();
203
204 if (m_format.isValid()) {
205 // timestamp raw audio data
206 uint nSamples = size/m_format.bytesPerFrame();
207
208 GST_BUFFER_TIMESTAMP(buffer) = gst_util_uint64_scale(streamedSamples, GST_SECOND, m_format.sampleRate());
209 GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale(nSamples, GST_SECOND, m_format.sampleRate());
210 streamedSamples += nSamples;
211 }
212
213 GstMapInfo mapInfo;
214 gst_buffer_map(buffer, &mapInfo, GST_MAP_WRITE);
215 void* bufferData = mapInfo.data;
216
217 qint64 bytesRead;
218 if (m_stream)
219 bytesRead = m_stream->read((char*)bufferData, size);
220 else
221 bytesRead = m_buffer.read((char*)bufferData, size);
222 buffer->offset_end = buffer->offset + bytesRead - 1;
223 bytesReadSoFar += bytesRead;
224
225 gst_buffer_unmap(buffer, &mapInfo);
226 qCDebug(qLcAppSrc) << "pushing bytes into gstreamer" << buffer->offset << bytesRead;
227 if (bytesRead == 0) {
228 gst_buffer_unref(buffer);
229 eosOrIdle();
230 qCDebug(qLcAppSrc) << "end pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size();
231 return;
232 }
233 m_noMoreData = false;
234 emit bytesProcessed(bytesRead);
235
236 GstFlowReturn ret = m_appSrc.pushBuffer(buffer);
237 if (ret == GST_FLOW_ERROR) {
238 qWarning() << "QGstAppSrc: push buffer error";
239 } else if (ret == GST_FLOW_FLUSHING) {
240 qWarning() << "QGstAppSrc: push buffer wrong state";
241 }
242 qCDebug(qLcAppSrc) << "end pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size();
243
244}
245
246bool QGstAppSource::doSeek(qint64 value)
247{
248 if (isStreamValid())
249 return m_stream->seek(value + m_offset);
250 return false;
251}
252
253gboolean QGstAppSource::on_seek_data(GstAppSrc *, guint64 arg0, gpointer userdata)
254{
255 // we do get some spurious seeks to INT_MAX, ignore those
256 if (arg0 == std::numeric_limits<quint64>::max())
257 return true;
258
259 QGstAppSource *self = reinterpret_cast<QGstAppSource *>(userdata);
260 Q_ASSERT(self);
261
262 QMutexLocker locker(&self->m_mutex);
263
264 if (self->m_sequential)
265 return false;
266
267 self->doSeek(arg0);
268 return true;
269}
270
271void QGstAppSource::on_enough_data(GstAppSrc *, gpointer userdata)
272{
273 qCDebug(qLcAppSrc) << "on_enough_data";
274 QGstAppSource *self = static_cast<QGstAppSource *>(userdata);
275 Q_ASSERT(self);
276 QMutexLocker locker(&self->m_mutex);
277 self->m_dataRequestSize = 0;
278}
279
280void QGstAppSource::on_need_data(GstAppSrc *, guint arg0, gpointer userdata)
281{
282 qCDebug(qLcAppSrc) << "on_need_data requesting bytes" << arg0;
283 QGstAppSource *self = static_cast<QGstAppSource *>(userdata);
284 Q_ASSERT(self);
285 QMutexLocker locker(&self->m_mutex);
286 self->m_dataRequestSize = arg0;
287 self->pushData();
288 qCDebug(qLcAppSrc) << "done on_need_data";
289}
290
291void QGstAppSource::sendEOS()
292{
293 qCDebug(qLcAppSrc) << "sending EOS";
294 if (m_appSrc.isNull())
295 return;
296
297 gst_app_src_end_of_stream(GST_APP_SRC(m_appSrc.element()));
298}
299
300void QGstAppSource::eosOrIdle()
301{
302 qCDebug(qLcAppSrc) << "eosOrIdle";
303 if (m_appSrc.isNull())
304 return;
305
306 if (!m_sequential) {
307 sendEOS();
308 return;
309 }
310 if (m_noMoreData)
311 return;
312 qCDebug(qLcAppSrc) << " idle!";
313 m_noMoreData = true;
315}
316
318
319#include "moc_qgstappsource_p.cpp"
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 bytesProcessed(int bytes)
bool setup(QIODevice *stream=nullptr, qint64 offset=0)
void setAudioFormat(const QAudioFormat &f)
QGstElement element() const
void noMoreData()
void write(const char *data, qsizetype size)
void setExternalAppSrc(QGstAppSrc)
bool canAcceptMoreData() const
\inmodule QtCore \reentrant
Definition qiodevice.h:34
void readyRead()
This signal is emitted once every time new data is available for reading from the device's current re...
virtual qint64 size() const
For open random-access devices, this function returns the size of the device.
virtual qint64 pos() const
For random-access devices, this function returns the position that data is written to or read from.
virtual bool isSequential() const
Returns true if this device is sequential; otherwise returns false.
bool isOpen() const
Returns true if the device is open; otherwise returns false.
virtual qint64 bytesAvailable() const
Returns the number of bytes that are available for reading.
virtual bool seek(qint64 pos)
For random-access devices, this function sets the current position to pos, returning true on success,...
virtual bool atEnd() const
Returns true if the current read and write position is at the end of the device (i....
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qobject.h:103
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
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
qint64 size() const
Q_CORE_EXPORT void append(const char *data, qint64 size)
Q_CORE_EXPORT qint64 read(char *data, qint64 maxLength)
QGstCaps capsForAudioFormat(const QAudioFormat &format)
Definition qgstutils.cpp:83
Combined button and popup list for selecting options.
QString self
Definition language.cpp:58
EGLStreamKHR stream
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
QString errorMessageCannotFindElement(std::string_view element)
Definition qgst_p.h:804
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
return ret
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum GLuint buffer
GLenum GLuint GLintptr offset
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
PromiseCallbacks callbacks
Definition qstdweb.cpp:275
#define emit
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
myObject disconnect()
[26]
view create()