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#ifndef QGST_P_H
5#define QGST_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <private/qtmultimediaglobal_p.h>
19
20#include <QSemaphore>
21#include <QtCore/qlist.h>
22
23#include <QtMultimedia/qaudioformat.h>
24#include <QtMultimedia/qvideoframe.h>
25
26#include <gst/gst.h>
27#include <gst/video/video-info.h>
28
29#include <functional>
30
31#if QT_CONFIG(gstreamer_photography)
32#define GST_USE_UNSTABLE_API
33#include <gst/interfaces/photography.h>
34#undef GST_USE_UNSTABLE_API
35#endif
36#ifndef QT_NO_DEBUG
37#include <qdebug.h>
38#endif
39
40QT_BEGIN_NAMESPACE
41
42class QSize;
43class QGstStructure;
44class QGstCaps;
45class QGstPipelinePrivate;
46class QCameraFormat;
47
48template <typename T> struct QGRange
49{
50 T min;
51 T max;
52};
53
54class QGString
55{
56 char *str;
57public:
58 QGString(char *string) : str(string) {}
59 ~QGString() { g_free(mem: str); }
60 operator QByteArray() { return QByteArray(str); }
61 operator const char *() { return str; }
62};
63
64class QGValue
65{
66public:
67 QGValue(const GValue *v) : value(v) {}
68 const GValue *value;
69
70 bool isNull() const { return !value; }
71
72 std::optional<bool> toBool() const
73 {
74 if (!G_VALUE_HOLDS_BOOLEAN(value))
75 return std::nullopt;
76 return g_value_get_boolean(value);
77 }
78 std::optional<int> toInt() const
79 {
80 if (!G_VALUE_HOLDS_INT(value))
81 return std::nullopt;
82 return g_value_get_int(value);
83 }
84 std::optional<int> toInt64() const
85 {
86 if (!G_VALUE_HOLDS_INT64(value))
87 return std::nullopt;
88 return g_value_get_int64(value);
89 }
90 template<typename T>
91 T *getPointer() const
92 {
93 return value ? static_cast<T *>(g_value_get_pointer(value)) : nullptr;
94 }
95
96 const char *toString() const
97 {
98 return value ? g_value_get_string(value) : nullptr;
99 }
100 std::optional<float> getFraction() const
101 {
102 if (!GST_VALUE_HOLDS_FRACTION(value))
103 return std::nullopt;
104 return (float)gst_value_get_fraction_numerator(value)/(float)gst_value_get_fraction_denominator(value);
105 }
106
107 std::optional<QGRange<float>> getFractionRange() const
108 {
109 if (!GST_VALUE_HOLDS_FRACTION_RANGE(value))
110 return std::nullopt;
111 QGValue min = gst_value_get_fraction_range_min(value);
112 QGValue max = gst_value_get_fraction_range_max(value);
113 return QGRange<float>{ .min: *min.getFraction(), .max: *max.getFraction() };
114 }
115
116 std::optional<QGRange<int>> toIntRange() const
117 {
118 if (!GST_VALUE_HOLDS_INT_RANGE(value))
119 return std::nullopt;
120 return QGRange<int>{ .min: gst_value_get_int_range_min(value), .max: gst_value_get_int_range_max(value) };
121 }
122
123 inline QGstStructure toStructure() const;
124 inline QGstCaps toCaps() const;
125
126 inline bool isList() const { return value && GST_VALUE_HOLDS_LIST(value); }
127 inline int listSize() const { return gst_value_list_get_size(value); }
128 inline QGValue at(int index) const { return gst_value_list_get_value(value, index); }
129
130 Q_MULTIMEDIA_EXPORT QList<QAudioFormat::SampleFormat> getSampleFormats() const;
131};
132
133class QGstStructure {
134public:
135 const GstStructure *structure = nullptr;
136 QGstStructure() = default;
137 QGstStructure(const GstStructure *s) : structure(s) {}
138 void free() { if (structure) gst_structure_free(structure: const_cast<GstStructure *>(structure)); structure = nullptr; }
139
140 bool isNull() const { return !structure; }
141
142 QByteArrayView name() const { return gst_structure_get_name(structure); }
143
144 QGValue operator[](const char *name) const { return gst_structure_get_value(structure, fieldname: name); }
145
146 Q_MULTIMEDIA_EXPORT QSize resolution() const;
147 Q_MULTIMEDIA_EXPORT QVideoFrameFormat::PixelFormat pixelFormat() const;
148 Q_MULTIMEDIA_EXPORT QGRange<float> frameRateRange() const;
149
150 QByteArray toString() const
151 {
152 char *s = gst_structure_to_string(structure);
153 QByteArray str(s);
154 g_free(mem: s);
155 return str;
156 }
157 QGstStructure copy() const { return gst_structure_copy(structure); }
158};
159
160class QGstCaps {
161 GstCaps *caps = nullptr;
162public:
163 enum RefMode { HasRef, NeedsRef };
164 enum MemoryFormat { CpuMemory, GLTexture, DMABuf };
165
166 QGstCaps() = default;
167
168 explicit QGstCaps(GstCaps *c, RefMode mode) : caps(c)
169 {
170 if (mode == NeedsRef)
171 gst_caps_ref(caps);
172 }
173
174 QGstCaps(const QGstCaps &other) : caps(other.caps)
175 {
176 if (caps)
177 gst_caps_ref(caps);
178 }
179
180 ~QGstCaps() {
181 if (caps)
182 gst_caps_unref(caps);
183 }
184
185 QGstCaps &operator=(const QGstCaps &other)
186 {
187 if (this != &other) {
188 if (other.caps)
189 gst_caps_ref(caps: other.caps);
190 if (caps)
191 gst_caps_unref(caps);
192 caps = other.caps;
193 }
194 return *this;
195 }
196
197 bool isNull() const { return !caps; }
198
199 QByteArray toString() const { return toString(caps); }
200 int size() const { return int(gst_caps_get_size(caps)); }
201 QGstStructure at(int index) const { return gst_caps_get_structure(caps, index); }
202 GstCaps *get() const { return caps; }
203 MemoryFormat memoryFormat() const {
204 auto *features = gst_caps_get_features(caps, index: 0);
205 if (gst_caps_features_contains(features, feature: "memory:GLMemory"))
206 return GLTexture;
207 else if (gst_caps_features_contains(features, feature: "memory:DMABuf"))
208 return DMABuf;
209 return CpuMemory;
210 }
211 QVideoFrameFormat formatForCaps(GstVideoInfo *info) const;
212
213 void addPixelFormats(const QList<QVideoFrameFormat::PixelFormat> &formats, const char *modifier = nullptr);
214
215 static QGstCaps create() {
216 return QGstCaps(gst_caps_new_empty(), HasRef);
217 }
218
219 static QByteArray toString(const GstCaps *caps)
220 {
221 gchar *c = gst_caps_to_string(caps);
222 QByteArray b(c);
223 g_free(mem: c);
224 return b;
225 }
226
227 static QGstCaps fromCameraFormat(const QCameraFormat &format);
228};
229
230class QGstObject
231{
232protected:
233 GstObject *m_object = nullptr;
234public:
235 enum RefMode { HasRef, NeedsRef };
236
237 QGstObject() = default;
238 explicit QGstObject(GstObject *o, RefMode mode = HasRef)
239 : m_object(o)
240 {
241 if (o && mode == NeedsRef)
242 // Use ref_sink to remove any floating references
243 gst_object_ref_sink(object: m_object);
244 }
245 QGstObject(const QGstObject &other)
246 : m_object(other.m_object)
247 {
248 if (m_object)
249 gst_object_ref(object: m_object);
250 }
251 QGstObject &operator=(const QGstObject &other)
252 {
253 if (this == &other)
254 return *this;
255 if (other.m_object)
256 gst_object_ref(object: other.m_object);
257 if (m_object)
258 gst_object_unref(object: m_object);
259 m_object = other.m_object;
260 return *this;
261 }
262
263 QGstObject(QGstObject &&other) noexcept
264 : m_object(std::exchange(obj&: other.m_object, new_val: nullptr))
265 {}
266 QGstObject &operator=(QGstObject &&other)
267 {
268 if (this != &other) {
269 if (m_object)
270 gst_object_unref(object: m_object);
271 m_object = std::exchange(obj&: other.m_object, new_val: nullptr);
272 }
273 return *this;
274 }
275
276 virtual ~QGstObject() {
277 if (m_object)
278 gst_object_unref(object: m_object);
279 }
280
281 explicit operator bool() const { return bool(m_object); }
282
283 friend bool operator==(const QGstObject &a, const QGstObject &b)
284 { return a.m_object == b.m_object; }
285 friend bool operator!=(const QGstObject &a, const QGstObject &b)
286 { return a.m_object != b.m_object; }
287
288 bool isNull() const { return !m_object; }
289
290 void set(const char *property, const char *str) { g_object_set(object: m_object, first_property_name: property, str, nullptr); }
291 void set(const char *property, bool b) { g_object_set(object: m_object, first_property_name: property, gboolean(b), nullptr); }
292 void set(const char *property, uint i) { g_object_set(object: m_object, first_property_name: property, guint(i), nullptr); }
293 void set(const char *property, int i) { g_object_set(object: m_object, first_property_name: property, gint(i), nullptr); }
294 void set(const char *property, qint64 i) { g_object_set(object: m_object, first_property_name: property, gint64(i), nullptr); }
295 void set(const char *property, quint64 i) { g_object_set(object: m_object, first_property_name: property, guint64(i), nullptr); }
296 void set(const char *property, double d) { g_object_set(object: m_object, first_property_name: property, gdouble(d), nullptr); }
297 void set(const char *property, const QGstObject &o) { g_object_set(object: m_object, first_property_name: property, o.object(), nullptr); }
298 void set(const char *property, const QGstCaps &c) { g_object_set(object: m_object, first_property_name: property, c.get(), nullptr); }
299
300 QGString getString(const char *property) const
301 { char *s = nullptr; g_object_get(object: m_object, first_property_name: property, &s, nullptr); return s; }
302 QGstStructure getStructure(const char *property) const
303 { GstStructure *s = nullptr; g_object_get(object: m_object, first_property_name: property, &s, nullptr); return QGstStructure(s); }
304 bool getBool(const char *property) const { gboolean b = false; g_object_get(object: m_object, first_property_name: property, &b, nullptr); return b; }
305 uint getUInt(const char *property) const { guint i = 0; g_object_get(object: m_object, first_property_name: property, &i, nullptr); return i; }
306 int getInt(const char *property) const { gint i = 0; g_object_get(object: m_object, first_property_name: property, &i, nullptr); return i; }
307 quint64 getUInt64(const char *property) const { guint64 i = 0; g_object_get(object: m_object, first_property_name: property, &i, nullptr); return i; }
308 qint64 getInt64(const char *property) const { gint64 i = 0; g_object_get(object: m_object, first_property_name: property, &i, nullptr); return i; }
309 float getFloat(const char *property) const { gfloat d = 0; g_object_get(object: m_object, first_property_name: property, &d, nullptr); return d; }
310 double getDouble(const char *property) const { gdouble d = 0; g_object_get(object: m_object, first_property_name: property, &d, nullptr); return d; }
311 QGstObject getObject(const char *property) const { GstObject *o = nullptr; g_object_get(object: m_object, first_property_name: property, &o, nullptr); return QGstObject(o, HasRef); }
312
313 void connect(const char *name, GCallback callback, gpointer userData) { g_signal_connect(m_object, name, callback, userData); }
314
315 GstObject *object() const { return m_object; }
316 const char *name() const { return m_object ? GST_OBJECT_NAME(m_object) : "(null)"; }
317};
318
319class QGstElement;
320
321class QGstPad : public QGstObject
322{
323public:
324 QGstPad() = default;
325 QGstPad(const QGstObject &o)
326 : QGstPad(GST_PAD(o.object()), NeedsRef)
327 {}
328 QGstPad(GstPad *pad, RefMode mode = NeedsRef)
329 : QGstObject(&pad->object, mode)
330 {}
331
332 QGstCaps currentCaps() const
333 { return QGstCaps(gst_pad_get_current_caps(pad: pad()), QGstCaps::HasRef); }
334 QGstCaps queryCaps() const
335 { return QGstCaps(gst_pad_query_caps(pad: pad(), filter: nullptr), QGstCaps::HasRef); }
336
337 bool isLinked() const { return gst_pad_is_linked(pad: pad()); }
338 bool link(const QGstPad &sink) const { return gst_pad_link(srcpad: pad(), sinkpad: sink.pad()) == GST_PAD_LINK_OK; }
339 bool unlink(const QGstPad &sink) const { return gst_pad_unlink(srcpad: pad(), sinkpad: sink.pad()); }
340 bool unlinkPeer() const { return unlink(sink: peer()); }
341 QGstPad peer() const { return QGstPad(gst_pad_get_peer(pad: pad()), HasRef); }
342 inline QGstElement parent() const;
343
344 GstPad *pad() const { return GST_PAD_CAST(object()); }
345
346 GstEvent *stickyEvent(GstEventType type) { return gst_pad_get_sticky_event(pad: pad(), event_type: type, idx: 0); }
347 bool sendEvent(GstEvent *event) { return gst_pad_send_event (pad: pad(), event); }
348
349 template<auto Member, typename T>
350 void addProbe(T *instance, GstPadProbeType type) {
351 struct Impl {
352 static GstPadProbeReturn callback(GstPad *pad, GstPadProbeInfo *info, gpointer userData) {
353 return (static_cast<T *>(userData)->*Member)(QGstPad(pad, NeedsRef), info);
354 };
355 };
356
357 gst_pad_add_probe (pad(), type, Impl::callback, instance, nullptr);
358 }
359
360 void doInIdleProbe(std::function<void()> work) {
361 struct CallbackData {
362 QSemaphore waitDone;
363 std::function<void()> work;
364 } cd;
365 cd.work = work;
366
367 auto callback= [](GstPad *, GstPadProbeInfo *, gpointer p) {
368 auto cd = reinterpret_cast<CallbackData*>(p);
369 cd->work();
370 cd->waitDone.release();
371 return GST_PAD_PROBE_REMOVE;
372 };
373
374 gst_pad_add_probe(pad: pad(), mask: GST_PAD_PROBE_TYPE_IDLE, callback, user_data: &cd, destroy_data: nullptr);
375 cd.waitDone.acquire();
376 }
377
378 template<auto Member, typename T>
379 void addEosProbe(T *instance) {
380 struct Impl {
381 static GstPadProbeReturn callback(GstPad */*pad*/, GstPadProbeInfo *info, gpointer userData) {
382 if (GST_EVENT_TYPE(GST_PAD_PROBE_INFO_DATA(info)) != GST_EVENT_EOS)
383 return GST_PAD_PROBE_PASS;
384 (static_cast<T *>(userData)->*Member)();
385 return GST_PAD_PROBE_REMOVE;
386 };
387 };
388
389 gst_pad_add_probe (pad(), GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, Impl::callback, instance, nullptr);
390 }
391};
392
393class QGstClock : public QGstObject
394{
395public:
396 QGstClock() = default;
397 QGstClock(const QGstObject &o)
398 : QGstClock(GST_CLOCK(o.object()))
399 {}
400 QGstClock(GstClock *clock, RefMode mode = NeedsRef)
401 : QGstObject(&clock->object, mode)
402 {}
403
404 GstClock *clock() const { return GST_CLOCK_CAST(object()); }
405
406 GstClockTime time() const { return gst_clock_get_time(clock: clock()); }
407};
408
409class QGstElement : public QGstObject
410{
411public:
412 QGstElement() = default;
413 QGstElement(const QGstObject &o)
414 : QGstElement(GST_ELEMENT(o.object()), NeedsRef)
415 {}
416 QGstElement(GstElement *element, RefMode mode = NeedsRef)
417 : QGstObject(&element->object, mode)
418 {}
419
420 QGstElement(const char *factory, const char *name = nullptr)
421 : QGstElement(gst_element_factory_make(factoryname: factory, name), NeedsRef)
422 {
423#ifndef QT_NO_DEBUG
424 if (!m_object)
425 qWarning() << "Failed to make element" << name << "from factory" << factory;
426#endif
427 }
428
429 bool link(const QGstElement &next)
430 { return gst_element_link(src: element(), dest: next.element()); }
431 bool link(const QGstElement &n1, const QGstElement &n2)
432 { return gst_element_link_many(element_1: element(), element_2: n1.element(), n2.element(), nullptr); }
433 bool link(const QGstElement &n1, const QGstElement &n2, const QGstElement &n3)
434 { return gst_element_link_many(element_1: element(), element_2: n1.element(), n2.element(), n3.element(), nullptr); }
435 bool link(const QGstElement &n1, const QGstElement &n2, const QGstElement &n3, const QGstElement &n4)
436 { return gst_element_link_many(element_1: element(), element_2: n1.element(), n2.element(), n3.element(), n4.element(), nullptr); }
437 bool link(const QGstElement &n1, const QGstElement &n2, const QGstElement &n3, const QGstElement &n4, const QGstElement &n5)
438 { return gst_element_link_many(element_1: element(), element_2: n1.element(), n2.element(), n3.element(), n4.element(), n5.element(), nullptr); }
439
440 void unlink(const QGstElement &next)
441 { gst_element_unlink(src: element(), dest: next.element()); }
442
443 QGstPad staticPad(const char *name) const { return QGstPad(gst_element_get_static_pad(element: element(), name), HasRef); }
444 QGstPad src() const { return staticPad(name: "src"); }
445 QGstPad sink() const { return staticPad(name: "sink"); }
446 QGstPad getRequestPad(const char *name) const
447 {
448#if GST_CHECK_VERSION(1,19,1)
449 return QGstPad(gst_element_request_pad_simple(element: element(), name), HasRef);
450#else
451 return QGstPad(gst_element_get_request_pad(element(), name), HasRef);
452#endif
453 }
454 void releaseRequestPad(const QGstPad &pad) const { return gst_element_release_request_pad(element: element(), pad: pad.pad()); }
455
456 GstState state() const
457 {
458 GstState state;
459 gst_element_get_state(element: element(), state: &state, pending: nullptr, timeout: 0);
460 return state;
461 }
462 GstStateChangeReturn setState(GstState state) { return gst_element_set_state(element: element(), state); }
463 bool setStateSync(GstState state)
464 {
465 auto change = gst_element_set_state(element: element(), state);
466 if (change == GST_STATE_CHANGE_ASYNC) {
467 change = gst_element_get_state(element: element(), state: nullptr, pending: &state, timeout: 1000*1e6 /*nano seconds*/);
468 }
469#ifndef QT_NO_DEBUG
470 if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL)
471 qWarning() << "Could not change state of" << name() << "to" << state << change;
472#endif
473 return change == GST_STATE_CHANGE_SUCCESS;
474 }
475 bool syncStateWithParent() { return gst_element_sync_state_with_parent(element: element()) == TRUE; }
476 bool finishStateChange()
477 {
478 auto change = gst_element_get_state(element: element(), state: nullptr, pending: nullptr, timeout: 1000*1e6 /*nano seconds*/);
479#ifndef QT_NO_DEBUG
480 if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL)
481 qWarning() << "Could finish change state of" << name();
482#endif
483 return change == GST_STATE_CHANGE_SUCCESS;
484 }
485
486 void lockState(bool locked) { gst_element_set_locked_state(element: element(), locked_state: locked); }
487 bool isStateLocked() const { return gst_element_is_locked_state(element: element()); }
488
489 void sendEvent(GstEvent *event) const { gst_element_send_event(element: element(), event); }
490 void sendEos() const { sendEvent(event: gst_event_new_eos()); }
491
492 template<auto Member, typename T>
493 void onPadAdded(T *instance) {
494 struct Impl {
495 static void callback(GstElement *e, GstPad *pad, gpointer userData) {
496 (static_cast<T *>(userData)->*Member)(QGstElement(e), QGstPad(pad, NeedsRef));
497 };
498 };
499
500 connect(name: "pad-added", G_CALLBACK(Impl::callback), userData: instance);
501 }
502 template<auto Member, typename T>
503 void onPadRemoved(T *instance) {
504 struct Impl {
505 static void callback(GstElement *e, GstPad *pad, gpointer userData) {
506 (static_cast<T *>(userData)->*Member)(QGstElement(e), QGstPad(pad, NeedsRef));
507 };
508 };
509
510 connect(name: "pad-removed", G_CALLBACK(Impl::callback), userData: instance);
511 }
512 template<auto Member, typename T>
513 void onNoMorePads(T *instance) {
514 struct Impl {
515 static void callback(GstElement *e, gpointer userData) {
516 (static_cast<T *>(userData)->*Member)(QGstElement(e));
517 };
518 };
519
520 connect(name: "no-more-pads", G_CALLBACK(Impl::callback), userData: instance);
521 }
522
523 GstClockTime baseTime() const { return gst_element_get_base_time(element: element()); }
524 void setBaseTime(GstClockTime time) const { gst_element_set_base_time(element: element(), time); }
525
526 GstElement *element() const { return GST_ELEMENT_CAST(m_object); }
527};
528
529inline QGstElement QGstPad::parent() const
530{
531 return QGstElement(gst_pad_get_parent_element(pad: pad()), HasRef);
532}
533
534class QGstBin : public QGstElement
535{
536public:
537 QGstBin() = default;
538 QGstBin(const QGstObject &o)
539 : QGstBin(GST_BIN(o.object()), NeedsRef)
540 {}
541 QGstBin(const char *name)
542 : QGstElement(gst_bin_new(name), NeedsRef)
543 {
544 }
545 QGstBin(GstBin *bin, RefMode mode = NeedsRef)
546 : QGstElement(&bin->element, mode)
547 {}
548
549 void add(const QGstElement &element)
550 { gst_bin_add(bin: bin(), element: element.element()); }
551 void add(const QGstElement &e1, const QGstElement &e2)
552 { gst_bin_add_many(bin: bin(), element_1: e1.element(), e2.element(), nullptr); }
553 void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3)
554 { gst_bin_add_many(bin: bin(), element_1: e1.element(), e2.element(), e3.element(), nullptr); }
555 void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3, const QGstElement &e4)
556 { gst_bin_add_many(bin: bin(), element_1: e1.element(), e2.element(), e3.element(), e4.element(), nullptr); }
557 void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3, const QGstElement &e4, const QGstElement &e5)
558 { gst_bin_add_many(bin: bin(), element_1: e1.element(), e2.element(), e3.element(), e4.element(), e5.element(), nullptr); }
559 void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3, const QGstElement &e4, const QGstElement &e5, const QGstElement &e6)
560 { gst_bin_add_many(bin: bin(), element_1: e1.element(), e2.element(), e3.element(), e4.element(), e5.element(), e6.element(), nullptr); }
561 void remove(const QGstElement &element)
562 { gst_bin_remove(bin: bin(), element: element.element()); }
563
564 GstBin *bin() const { return GST_BIN_CAST(m_object); }
565
566 void addGhostPad(const QGstElement &child, const char *name)
567 {
568 addGhostPad(name, pad: child.staticPad(name));
569 }
570 void addGhostPad(const char *name, const QGstPad &pad)
571 {
572 gst_element_add_pad(element: element(), pad: gst_ghost_pad_new(name, target: pad.pad()));
573 }
574};
575
576inline QGstStructure QGValue::toStructure() const
577{
578 if (!value || !GST_VALUE_HOLDS_STRUCTURE(value))
579 return QGstStructure();
580 return QGstStructure(gst_value_get_structure(value));
581}
582
583inline QGstCaps QGValue::toCaps() const
584{
585 if (!value || !GST_VALUE_HOLDS_CAPS(value))
586 return {};
587 return QGstCaps(gst_caps_copy(gst_value_get_caps(value)), QGstCaps::HasRef);
588}
589
590inline QString errorMessageCannotFindElement(std::string_view element)
591{
592 return QStringLiteral("Could not find the %1 GStreamer element").arg(a: element.data());
593}
594
595QT_END_NAMESPACE
596
597#endif
598

source code of qtmultimedia/src/plugins/multimedia/gstreamer/common/qgst_p.h