Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qgstappsrc.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 "qgstappsrc_p.h"
7#include "qgstutils_p.h"
8#include "qnetworkreply.h"
9#include "qloggingcategory.h"
10
11static Q_LOGGING_CATEGORY(qLcAppSrc, "qt.multimedia.appsrc")
12
14
16{
17 QGstElement appsrc("appsrc", "appsrc");
18 if (!appsrc)
19 return errorMessageCannotFindElement("appsrc");
20
21 return new QGstAppSrc(appsrc, parent);
22}
23
24QGstAppSrc::QGstAppSrc(QGstElement appsrc, QObject *parent)
25 : QObject(parent), m_appSrc(std::move(appsrc))
26{
27}
28
30{
31 m_appSrc.setStateSync(GST_STATE_NULL);
32 streamDestroyed();
33 qCDebug(qLcAppSrc) << "~QGstAppSrc";
34}
35
37{
38 if (m_appSrc.isNull())
39 return false;
40
41 if (!setStream(stream, offset))
42 return false;
43
44 auto *appSrc = GST_APP_SRC(m_appSrc.element());
45 GstAppSrcCallbacks m_callbacks;
46 memset(&m_callbacks, 0, sizeof(GstAppSrcCallbacks));
47 m_callbacks.need_data = &QGstAppSrc::on_need_data;
48 m_callbacks.enough_data = &QGstAppSrc::on_enough_data;
49 m_callbacks.seek_data = &QGstAppSrc::on_seek_data;
50 gst_app_src_set_callbacks(appSrc, (GstAppSrcCallbacks*)&m_callbacks, this, nullptr);
51
52 m_maxBytes = gst_app_src_get_max_bytes(appSrc);
53 m_suspended = false;
54
55 if (m_sequential)
56 m_streamType = GST_APP_STREAM_TYPE_STREAM;
57 else
58 m_streamType = GST_APP_STREAM_TYPE_RANDOM_ACCESS;
59 gst_app_src_set_stream_type(appSrc, m_streamType);
60 gst_app_src_set_size(appSrc, m_sequential ? -1 : m_stream->size() - m_offset);
61
62 m_networkReply = qobject_cast<QNetworkReply *>(m_stream);
63 m_noMoreData = true;
64
65 return true;
66}
67
69{
70 m_format = f;
71 if (!m_format.isValid())
72 return;
73
74 auto caps = QGstUtils::capsForAudioFormat(m_format);
75 Q_ASSERT(!caps.isNull());
76 m_appSrc.set("caps", caps);
77 m_appSrc.set("format", GST_FORMAT_TIME);
78}
79
81{
82 m_appSrc = appsrc;
83}
84
85bool QGstAppSrc::setStream(QIODevice *stream, qint64 offset)
86{
87 if (m_stream) {
88 disconnect(m_stream, SIGNAL(readyRead()), this, SLOT(onDataReady()));
89 disconnect(m_stream, SIGNAL(destroyed()), this, SLOT(streamDestroyed()));
90 m_stream = nullptr;
91 }
92
93 m_dataRequestSize = 0;
94 m_sequential = true;
95 m_maxBytes = 0;
96 streamedSamples = 0;
97
98 if (stream) {
99 if (!stream->isOpen() && !stream->open(QIODevice::ReadOnly))
100 return false;
101 m_stream = stream;
102 connect(m_stream, SIGNAL(destroyed()), SLOT(streamDestroyed()));
103 connect(m_stream, SIGNAL(readyRead()), this, SLOT(onDataReady()));
104 m_sequential = m_stream->isSequential();
105 m_offset = offset;
106 }
107 return true;
108}
109
111{
112 return m_appSrc;
113}
114
116{
117 qCDebug(qLcAppSrc) << "write" << size << m_noMoreData << m_dataRequestSize;
118 if (!size)
119 return;
120 Q_ASSERT(!m_stream);
121 m_buffer.append(data, size);
122 m_noMoreData = false;
123 pushData();
124}
125
126void QGstAppSrc::onDataReady()
127{
128 qCDebug(qLcAppSrc) << "onDataReady" << m_stream->bytesAvailable() << m_stream->size();
129 pushData();
130}
131
132void QGstAppSrc::streamDestroyed()
133{
134 qCDebug(qLcAppSrc) << "stream destroyed";
135 m_stream = nullptr;
136 m_dataRequestSize = 0;
137 streamedSamples = 0;
138 sendEOS();
139}
140
141void QGstAppSrc::pushData()
142{
143 if (m_appSrc.isNull() || !m_dataRequestSize || m_suspended) {
144 qCDebug(qLcAppSrc) << "push data: return immediately" << m_appSrc.isNull() << m_dataRequestSize << m_suspended;
145 return;
146 }
147
148 qCDebug(qLcAppSrc) << "pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size();
149 if ((m_stream && m_stream->atEnd())) {
150 eosOrIdle();
151 qCDebug(qLcAppSrc) << "end pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size();
152 return;
153 }
154
155 qint64 size;
156 if (m_stream)
157 size = m_stream->bytesAvailable();
158 else
159 size = m_buffer.size();
160
161 if (!m_dataRequestSize)
162 m_dataRequestSize = m_maxBytes;
163 size = qMin(size, (qint64)m_dataRequestSize);
164 qCDebug(qLcAppSrc) << " reading" << size << "bytes" << size << m_dataRequestSize;
165
166 GstBuffer* buffer = gst_buffer_new_and_alloc(size);
167
168 if (m_sequential || !m_stream)
169 buffer->offset = bytesReadSoFar;
170 else
171 buffer->offset = m_stream->pos();
172
173 if (m_format.isValid()) {
174 // timestamp raw audio data
175 uint nSamples = size/m_format.bytesPerFrame();
176
177 GST_BUFFER_TIMESTAMP(buffer) = gst_util_uint64_scale(streamedSamples, GST_SECOND, m_format.sampleRate());
178 GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale(nSamples, GST_SECOND, m_format.sampleRate());
179 streamedSamples += nSamples;
180 }
181
182 GstMapInfo mapInfo;
183 gst_buffer_map(buffer, &mapInfo, GST_MAP_WRITE);
184 void* bufferData = mapInfo.data;
185
186 qint64 bytesRead;
187 if (m_stream)
188 bytesRead = m_stream->read((char*)bufferData, size);
189 else
190 bytesRead = m_buffer.read((char*)bufferData, size);
191 buffer->offset_end = buffer->offset + bytesRead - 1;
192 bytesReadSoFar += bytesRead;
193
194 gst_buffer_unmap(buffer, &mapInfo);
195 qCDebug(qLcAppSrc) << "pushing bytes into gstreamer" << buffer->offset << bytesRead;
196 if (bytesRead == 0) {
197 gst_buffer_unref(buffer);
198 eosOrIdle();
199 qCDebug(qLcAppSrc) << "end pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size();
200 return;
201 }
202 m_noMoreData = false;
203 emit bytesProcessed(bytesRead);
204
205 GstFlowReturn ret = gst_app_src_push_buffer(GST_APP_SRC(m_appSrc.element()), buffer);
206 if (ret == GST_FLOW_ERROR) {
207 qWarning() << "QGstAppSrc: push buffer error";
208 } else if (ret == GST_FLOW_FLUSHING) {
209 qWarning() << "QGstAppSrc: push buffer wrong state";
210 }
211 qCDebug(qLcAppSrc) << "end pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size();
212
213}
214
215bool QGstAppSrc::doSeek(qint64 value)
216{
217 if (isStreamValid())
218 return m_stream->seek(value + m_offset);
219 return false;
220}
221
222
223gboolean QGstAppSrc::on_seek_data(GstAppSrc *, guint64 arg0, gpointer userdata)
224{
225 // we do get some spurious seeks to INT_MAX, ignore those
226 if (arg0 == std::numeric_limits<quint64>::max())
227 return true;
228
229 QGstAppSrc *self = reinterpret_cast<QGstAppSrc*>(userdata);
230 Q_ASSERT(self);
231 if (self->m_sequential)
232 return false;
233
235 return true;
236}
237
238void QGstAppSrc::on_enough_data(GstAppSrc *, gpointer userdata)
239{
240 qCDebug(qLcAppSrc) << "on_enough_data";
241 QGstAppSrc *self = static_cast<QGstAppSrc*>(userdata);
242 Q_ASSERT(self);
243 self->m_dataRequestSize = 0;
244}
245
246void QGstAppSrc::on_need_data(GstAppSrc *, guint arg0, gpointer userdata)
247{
248 qCDebug(qLcAppSrc) << "on_need_data requesting bytes" << arg0;
249 QGstAppSrc *self = static_cast<QGstAppSrc*>(userdata);
250 Q_ASSERT(self);
251 self->m_dataRequestSize = arg0;
253 qCDebug(qLcAppSrc) << "done on_need_data";
254}
255
256void QGstAppSrc::sendEOS()
257{
258 qCDebug(qLcAppSrc) << "sending EOS";
259 if (m_appSrc.isNull())
260 return;
261
262 gst_app_src_end_of_stream(GST_APP_SRC(m_appSrc.element()));
263}
264
265void QGstAppSrc::eosOrIdle()
266{
267 qCDebug(qLcAppSrc) << "eosOrIdle";
268 if (m_appSrc.isNull())
269 return;
270
271 if (!m_sequential) {
272 sendEOS();
273 return;
274 }
275 if (m_noMoreData)
276 return;
277 qCDebug(qLcAppSrc) << " idle!";
278 m_noMoreData = true;
280}
281
283
284#include "moc_qgstappsrc_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 setAudioFormat(const QAudioFormat &f)
void setExternalAppSrc(const QGstElement &appsrc)
void bytesProcessed(int bytes)
QGstElement element()
void noMoreData()
bool setup(QIODevice *stream=nullptr, qint64 offset=0)
void write(const char *data, qsizetype size)
GstElement * element() const
Definition qgst_p.h:526
bool setStateSync(GstState state)
Definition qgst_p.h:463
bool isNull() const
Definition qgst_p.h:288
void set(const char *property, const char *str)
Definition qgst_p.h:290
\inmodule QtCore \reentrant
Definition qiodevice.h:34
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.
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 qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
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)
Q_MULTIMEDIA_EXPORT QGstCaps capsForAudioFormat(const QAudioFormat &format)
Definition qgstutils.cpp:99
Combined button and popup list for selecting options.
@ AutoConnection
QString self
Definition language.cpp:57
EGLStreamKHR stream
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
QString errorMessageCannotFindElement(std::string_view element)
Definition qgst_p.h:590
#define qWarning
Definition qlogging.h:162
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
return ret
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
#define SLOT(a)
Definition qobjectdefs.h:51
#define Q_ARG(Type, data)
Definition qobjectdefs.h:62
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLfloat GLfloat f
GLenum GLuint buffer
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
myObject disconnect()
[26]
view create()
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