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
qgstpipeline.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/qloggingcategory.h>
5
6#include "qgstpipeline_p.h"
7#include "qgst_bus_p.h"
8
10
11Q_STATIC_LOGGING_CATEGORY(qLcGstPipeline, "qt.multimedia.gstpipeline");
12
13static constexpr GstSeekFlags rateChangeSeekFlags =
14#if GST_CHECK_VERSION(1, 18, 0)
15 GST_SEEK_FLAG_INSTANT_RATE_CHANGE;
16#else
17 GST_SEEK_FLAG_FLUSH;
18#endif
19
21{
22public:
23 mutable std::chrono::nanoseconds m_position{};
24
25 double m_rate = 1.;
27 GstState m_savedState = GST_STATE_NULL;
28
30};
31
38
39// QGstPipeline
40
42{
43 GstPipeline *pipeline = qGstCheckedCast<GstPipeline>(gst_pipeline_new(name));
44 return adopt(pipeline);
45}
46
48{
50 GstPipeline *pipeline = qGstCheckedCast<GstPipeline>(playbin3.element());
51
52 return QGstPipeline::adopt(pipeline);
53}
54
55QGstPipeline QGstPipeline::adopt(GstPipeline *pipeline)
56{
57 QGstBusHandle bus{
58 gst_pipeline_get_bus(pipeline),
60 };
61 QGstPipelinePrivate *d = new QGstPipelinePrivate(std::move(bus));
62 g_object_set_data_full(qGstCheckedCast<GObject>(pipeline), "pipeline-private", d,
63 [](gpointer ptr) {
64 delete reinterpret_cast<QGstPipelinePrivate *>(ptr);
65 return;
66 });
67
68 return QGstPipeline{
71 };
72}
73
77
79
81{
82 QGstPipelinePrivate *d = getPrivate();
83 d->installMessageFilter(filter);
84}
85
87{
88 QGstPipelinePrivate *d = getPrivate();
89 d->removeMessageFilter(filter);
90}
91
93{
94 QGstPipelinePrivate *d = getPrivate();
95 d->installMessageFilter(filter);
96}
97
99{
100 QGstPipelinePrivate *d = getPrivate();
101 d->removeMessageFilter(filter);
102}
103
104GstStateChangeReturn QGstPipeline::setState(GstState state)
105{
106 return gst_element_set_state(element(), state);
107}
108
110{
111 QGstPipelinePrivate *d = getPrivate();
112 d->processPendingMessage(types, std::chrono::nanoseconds{ 0 });
113}
114
115void QGstPipeline::beginConfig()
116{
117 QGstPipelinePrivate *d = getPrivate();
118 Q_ASSERT(!isNull());
119
120 ++d->m_configCounter;
121 if (d->m_configCounter > 1)
122 return;
123
124 GstState state;
125 GstState pending;
126 GstStateChangeReturn stateChangeReturn = gst_element_get_state(element(), &state, &pending, 0);
127 switch (stateChangeReturn) {
128 case GST_STATE_CHANGE_ASYNC: {
129 if (state == GST_STATE_PLAYING) {
130 // playing->paused transition in progress. wait for it to finish
131 bool stateChangeSuccessful = this->finishStateChange();
132 if (!stateChangeSuccessful)
133 qWarning() << "QGstPipeline::beginConfig: timeout when waiting for state change";
134 }
135
136 state = pending;
137 break;
138 }
139 case GST_STATE_CHANGE_FAILURE: {
140 qDebug() << "QGstPipeline::beginConfig: state change failure";
141 dumpGraph("beginConfigFailure");
142 break;
143 }
144
145 case GST_STATE_CHANGE_NO_PREROLL:
146 case GST_STATE_CHANGE_SUCCESS:
147 break;
148 }
149
150 d->m_savedState = state;
151 if (d->m_savedState == GST_STATE_PLAYING)
152 setStateSync(GST_STATE_PAUSED);
153}
154
155void QGstPipeline::endConfig()
156{
157 QGstPipelinePrivate *d = getPrivate();
158 Q_ASSERT(!isNull());
159
160 --d->m_configCounter;
161 if (d->m_configCounter)
162 return;
163
164 if (d->m_savedState == GST_STATE_PLAYING)
165 setState(GST_STATE_PLAYING);
166 d->m_savedState = GST_STATE_NULL;
167}
168
170{
171 seek(position());
172}
173
174void QGstPipeline::seek(std::chrono::nanoseconds pos, double rate)
175{
176 using namespace std::chrono_literals;
177
178 QGstPipelinePrivate *d = getPrivate();
179 // always adjust the rate, so it can be set before playback starts
180 // setting position needs a loaded media file that's seekable
181
182 qCDebug(qLcGstPipeline) << "QGstPipeline::seek to" << pos << "rate:" << rate;
183
184 bool success = (rate > 0)
185 ? gst_element_seek(element(), rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
186 GST_SEEK_TYPE_SET, pos.count(), GST_SEEK_TYPE_END, 0)
187 : gst_element_seek(element(), rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
188 GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, pos.count());
189
190 if (!success) {
191 qDebug() << "seek: gst_element_seek failed" << pos;
192 return;
193 }
194
195 d->m_position = pos;
196}
197
198void QGstPipeline::seek(std::chrono::nanoseconds pos)
199{
200 qCDebug(qLcGstPipeline) << "QGstPipeline::seek to" << pos;
201 seek(pos, getPrivate()->m_rate);
202}
203
205{
206 QGstPipelinePrivate *d = getPrivate();
207 if (rate == d->m_rate)
208 return;
209
210 d->m_rate = rate;
211
212 qCDebug(qLcGstPipeline) << "QGstPipeline::setPlaybackRate to" << rate;
213
214 applyPlaybackRate(/*instantRateChange =*/true);
215}
216
218{
219 QGstPipelinePrivate *d = getPrivate();
220 return d->m_rate;
221}
222
223void QGstPipeline::applyPlaybackRate(bool instantRateChange)
224{
225 QGstPipelinePrivate *d = getPrivate();
226
227 // do not GST_SEEK_FLAG_FLUSH with GST_SEEK_TYPE_NONE
228 // https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3604
229 if (instantRateChange && GST_CHECK_VERSION(1, 18, 0)) {
230 qCDebug(qLcGstPipeline) << "QGstPipeline::applyPlaybackRate instantly";
231 bool success = gst_element_seek(
232 element(), d->m_rate, GST_FORMAT_UNDEFINED, rateChangeSeekFlags, GST_SEEK_TYPE_NONE,
233 GST_CLOCK_TIME_NONE, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
234 if (!success)
235 qDebug() << "setPlaybackRate: gst_element_seek failed";
236 } else {
237 seek(position(), d->m_rate);
238 }
239}
240
241void QGstPipeline::setPosition(std::chrono::nanoseconds pos)
242{
243 seek(pos);
244}
245
246std::chrono::nanoseconds QGstPipeline::position() const
247{
248 QGstPipelinePrivate *d = getPrivate();
249 std::optional<std::chrono::nanoseconds> pos = QGstElement::position();
250 if (pos) {
251 d->m_position = *pos;
252 qCDebug(qLcGstPipeline) << "QGstPipeline::position:"
253 << std::chrono::round<std::chrono::milliseconds>(*pos);
254 } else {
255 qDebug() << "QGstPipeline: failed to query position, using previous position";
256 }
257
258 return d->m_position;
259}
260
261std::chrono::milliseconds QGstPipeline::positionInMs() const
262{
263 using namespace std::chrono;
264 return round<milliseconds>(position());
265}
266
267QGstPipelinePrivate *QGstPipeline::getPrivate() const
268{
269 gpointer p = g_object_get_data(qGstCheckedCast<GObject>(object()), "pipeline-private");
270 auto *d = reinterpret_cast<QGstPipelinePrivate *>(p);
271 Q_ASSERT(d);
272 return d;
273}
274
void dumpGraph(const char *fileNamePrefix) const
Definition qgst.cpp:1257
std::optional< std::chrono::nanoseconds > position() const
Definition qgst.cpp:1082
GstElement * element() const
Definition qgst.cpp:1131
bool finishStateChange(std::chrono::nanoseconds timeout=std::chrono::seconds(5))
Definition qgst.cpp:1030
bool setStateSync(GstState state, std::chrono::nanoseconds timeout=std::chrono::seconds(1))
Definition qgst.cpp:1002
static QGstElement createFromFactory(const char *factory, const char *name=nullptr)
Definition qgst.cpp:877
std::chrono::nanoseconds m_position
QGstPipelinePrivate(QGstBusHandle)
static QGstPipeline createFromFactory(const char *factory, const char *name)
GstStateChangeReturn setState(GstState state)
GstPipeline * pipeline() const
void installMessageFilter(QGstreamerSyncMessageFilter *filter)
void removeMessageFilter(QGstreamerSyncMessageFilter *filter)
constexpr QGstPipeline()=default
void applyPlaybackRate(bool instantRateChange)
double playbackRate() const
void setPosition(std::chrono::nanoseconds pos)
std::chrono::nanoseconds position() const
void processMessages(GstMessageType=GST_MESSAGE_ANY)
static QGstPipeline create(const char *name)
std::chrono::milliseconds positionInMs() const
void setPlaybackRate(double rate)
else opt state
[0]
Combined button and popup list for selecting options.
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage return DBusPendingCall * pending
DestinationType * qGstCheckedCast(SourceType *arg)
Definition qgst_p.h:161
static constexpr GstSeekFlags rateChangeSeekFlags
struct _GstPipeline GstPipeline
#define qDebug
[1]
Definition qlogging.h:165
#define qWarning
Definition qlogging.h:167
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)
static ControlElement< T > * ptr(QWidget *widget)
GLenum mode
GLsizei GLenum GLenum * types
GLenum GLenum GLsizei count
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLuint name
GLuint GLenum * rate
GLfloat GLfloat p
[1]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QItemEditorFactory * factory