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 <QtCore/qdebug.h>
19#include <QtCore/qlist.h>
20#include <QtCore/qmutex.h>
21#include <QtCore/qsemaphore.h>
22
23#include <QtMultimedia/qaudioformat.h>
24#include <QtMultimedia/qvideoframe.h>
25#include <QtMultimedia/private/qtmultimediaglobal_p.h>
26#include <QtMultimedia/private/qmultimediautils_p.h>
27#include <QtMultimedia/private/qplatformmediaplayer_p.h>
28#include <QtMultimedia/private/qsharedhandle_p.h>
29
30#include <gst/gst.h>
31#include <gst/app/gstappsink.h>
32#include <gst/app/gstappsrc.h>
33#include <gst/video/video-info.h>
34
35#include "qgst_handle_types_p.h"
36
37#include <type_traits>
38#ifdef __cpp_lib_three_way_comparison
39# include <compare>
40#endif
41
42#if QT_CONFIG(gstreamer_photography)
43# define GST_USE_UNSTABLE_API
44# include <gst/interfaces/photography.h>
45# undef GST_USE_UNSTABLE_API
46#endif
47
48
49QT_BEGIN_NAMESPACE
50
51namespace QGstImpl {
52
53template <typename T>
54struct GstObjectTraits
55{
56 // using Type = T;
57 // template <typename U>
58 // static bool isObjectOfType(U *);
59 // template <typename U>
60 // static T *cast(U *);
61};
62
63#define QGST_DEFINE_CAST_TRAITS(ClassName, MACRO_LABEL) \
64 template <> \
65 struct GstObjectTraits<ClassName> \
66 { \
67 using Type = ClassName; \
68 template <typename U> \
69 static bool isObjectOfType(U *arg) \
70 { \
71 return GST_IS_##MACRO_LABEL(arg); \
72 } \
73 template <typename U> \
74 static Type *cast(U *arg) \
75 { \
76 return GST_##MACRO_LABEL##_CAST(arg); \
77 } \
78 template <typename U> \
79 static Type *checked_cast(U *arg) \
80 { \
81 return GST_##MACRO_LABEL(arg); \
82 } \
83 }; \
84 static_assert(true, "ensure semicolon")
85
86#define QGST_DEFINE_CAST_TRAITS_FOR_INTERFACE(ClassName, MACRO_LABEL) \
87 template <> \
88 struct GstObjectTraits<ClassName> \
89 { \
90 using Type = ClassName; \
91 template <typename U> \
92 static bool isObjectOfType(U *arg) \
93 { \
94 return GST_IS_##MACRO_LABEL(arg); \
95 } \
96 template <typename U> \
97 static Type *cast(U *arg) \
98 { \
99 return checked_cast(arg); \
100 } \
101 template <typename U> \
102 static Type *checked_cast(U *arg) \
103 { \
104 return GST_##MACRO_LABEL(arg); \
105 } \
106 }; \
107 static_assert(true, "ensure semicolon")
108
109QGST_DEFINE_CAST_TRAITS(GstBin, BIN);
110QGST_DEFINE_CAST_TRAITS(GstClock, CLOCK);
111QGST_DEFINE_CAST_TRAITS(GstElement, ELEMENT);
112QGST_DEFINE_CAST_TRAITS(GstObject, OBJECT);
113QGST_DEFINE_CAST_TRAITS(GstPad, PAD);
114QGST_DEFINE_CAST_TRAITS(GstPipeline, PIPELINE);
115QGST_DEFINE_CAST_TRAITS(GstBaseSink, BASE_SINK);
116QGST_DEFINE_CAST_TRAITS(GstBaseSrc, BASE_SRC);
117QGST_DEFINE_CAST_TRAITS(GstAppSink, APP_SINK);
118QGST_DEFINE_CAST_TRAITS(GstAppSrc, APP_SRC);
119
120QGST_DEFINE_CAST_TRAITS_FOR_INTERFACE(GstTagSetter, TAG_SETTER);
121
122
123template <>
124struct GstObjectTraits<GObject>
125{
126 using Type = GObject;
127 template <typename U>
128 static bool isObjectOfType(U *arg)
129 {
130 return G_IS_OBJECT(arg);
131 }
132 template <typename U>
133 static Type *cast(U *arg)
134 {
135 return G_OBJECT(arg);
136 }
137 template <typename U>
138 static Type *checked_cast(U *arg)
139 {
140 return G_OBJECT(arg);
141 }
142};
143
144#undef QGST_DEFINE_CAST_TRAITS
145#undef QGST_DEFINE_CAST_TRAITS_FOR_INTERFACE
146
147} // namespace QGstImpl
148
149template <typename DestinationType, typename SourceType>
150bool qIsGstObjectOfType(SourceType *arg)
151{
152 using Traits = QGstImpl::GstObjectTraits<DestinationType>;
153 return arg && Traits::isObjectOfType(arg);
154}
155
156template <typename DestinationType, typename SourceType>
157DestinationType *qGstSafeCast(SourceType *arg)
158{
159 using Traits = QGstImpl::GstObjectTraits<DestinationType>;
160 if (arg && Traits::isObjectOfType(arg))
161 return Traits::cast(arg);
162 return nullptr;
163}
164
165template <typename DestinationType, typename SourceType>
166DestinationType *qGstCheckedCast(SourceType *arg)
167{
168 using Traits = QGstImpl::GstObjectTraits<DestinationType>;
169 if (arg)
170 Q_ASSERT(Traits::isObjectOfType(arg));
171 return Traits::cast(arg);
172}
173
174class QSize;
175class QGstStructureView;
176class QGstCaps;
177class QCameraFormat;
178
179template <typename T> struct QGRange
180{
181 T min;
182 T max;
183
184#ifdef __cpp_impl_three_way_comparison
185 auto operator<=> (const QGRange &) const = default;
186#else
187 bool operator==(const QGRange &rhs) const
188 {
189 return std::tie(min, max) == std::tie(rhs.min, rhs.max);
190 }
191#endif
192};
193
194struct QGString : QUniqueGStringHandle
195{
196 using QUniqueGStringHandle::QUniqueGStringHandle;
197
198 QLatin1StringView asStringView() const { return QLatin1StringView{ get() }; }
199 QByteArrayView asByteArrayView() const { return QByteArrayView{ get() }; }
200 QString toQString() const { return QString::fromUtf8(utf8: get()); }
201
202#ifdef __cpp_lib_three_way_comparison
203 // clang-format off
204 friend auto operator<=>(const QGString &lhs, const QGString &rhs)
205 {
206 return lhs.asStringView() <=> rhs.asStringView();
207 }
208 friend auto operator<=>(const QGString &lhs, const QLatin1StringView rhs)
209 {
210 return lhs.asStringView() <=> rhs;
211 }
212 friend auto operator<=>(const QGString &lhs, const QByteArrayView rhs)
213 {
214 return lhs.asByteArrayView() <=> rhs;
215 }
216 friend auto operator<=>(const QLatin1StringView lhs, const QGString &rhs)
217 {
218 return lhs <=> rhs.asStringView();
219 }
220 friend auto operator<=>(const QByteArrayView lhs, const QGString &rhs)
221 {
222 return lhs <=> rhs.asByteArrayView();
223 }
224 // clang-format on
225#else
226 // remove once we're on c++20
227 bool operator==(const QGString &str) const { return asStringView() == str.asStringView(); }
228 bool operator==(const QLatin1StringView str) const { return asStringView() == str; }
229 bool operator==(const QByteArrayView str) const { return asByteArrayView() == str; }
230
231 bool operator!=(const QGString &str) const { return asStringView() != str.asStringView(); }
232 bool operator!=(const QLatin1StringView str) const { return asStringView() != str; }
233 bool operator!=(const QByteArrayView str) const { return asByteArrayView() != str; }
234
235 friend bool operator<(const QGString &lhs, const QGString &rhs)
236 {
237 return lhs.asStringView() < rhs.asStringView();
238 }
239 friend bool operator<(const QGString &lhs, const QLatin1StringView rhs)
240 {
241 return lhs.asStringView() < rhs;
242 }
243 friend bool operator<(const QGString &lhs, const QByteArrayView rhs)
244 {
245 return lhs.asByteArrayView() < rhs;
246 }
247 friend bool operator<(const QLatin1StringView lhs, const QGString &rhs)
248 {
249 return lhs < rhs.asStringView();
250 }
251 friend bool operator<(const QByteArrayView lhs, const QGString &rhs)
252 {
253 return lhs < rhs.asByteArrayView();
254 }
255#endif
256
257 explicit operator QByteArrayView() const { return asByteArrayView(); }
258 explicit operator QByteArray() const { return QByteArray{ asByteArrayView() }; }
259};
260
261class QGValue
262{
263public:
264 explicit QGValue(const GValue *v);
265 const GValue *value;
266
267 bool isNull() const;
268
269 std::optional<bool> toBool() const;
270 std::optional<int> toInt() const;
271 std::optional<int> toInt64() const;
272 template<typename T>
273 T *getPointer() const
274 {
275 return value ? static_cast<T *>(g_value_get_pointer(value)) : nullptr;
276 }
277
278 const char *toString() const;
279 std::optional<float> getFraction() const;
280 std::optional<QGRange<float>> getFractionRange() const;
281 std::optional<QGRange<int>> toIntRange() const;
282
283 QGstStructureView toStructure() const;
284 QGstCaps toCaps() const;
285
286 bool isList() const;
287 int listSize() const;
288 QGValue at(int index) const;
289
290 QList<QAudioFormat::SampleFormat> getSampleFormats() const;
291};
292
293class QGstreamerMessage;
294
295class QGstStructureView
296{
297public:
298 const GstStructure *structure = nullptr;
299 explicit QGstStructureView(const GstStructure *);
300 explicit QGstStructureView(const QUniqueGstStructureHandle &);
301
302 QUniqueGstStructureHandle clone() const;
303
304 bool isNull() const;
305 QByteArrayView name() const;
306 QGValue operator[](const char *fieldname) const;
307
308 QGstCaps caps() const;
309 QGstTagListHandle tags() const;
310
311 QSize resolution() const;
312 QVideoFrameFormat::PixelFormat pixelFormat() const;
313 QGRange<float> frameRateRange() const;
314 std::optional<QGRange<QSize>> resolutionRange() const;
315 QGstreamerMessage getMessage();
316 std::optional<Fraction> pixelAspectRatio() const;
317 QSize nativeSize() const;
318};
319
320struct QSharedGstCapsTraits
321{
322 using Type = GstCaps *;
323 static constexpr Type invalidValue() noexcept { return nullptr; }
324 static GstCaps *ref(GstCaps *arg) noexcept { return gst_caps_ref(caps: arg); }
325 static bool unref(GstCaps *arg) noexcept
326 {
327 gst_caps_unref(caps: arg);
328 return true;
329 }
330};
331
332class QGstCaps : public QtPrivate::QSharedHandle<QSharedGstCapsTraits>
333{
334 using BaseClass = QtPrivate::QSharedHandle<QSharedGstCapsTraits>;
335
336public:
337 using BaseClass::BaseClass;
338 QGstCaps(const QGstCaps &) = default;
339 QGstCaps(QGstCaps &&) noexcept = default;
340 QGstCaps &operator=(const QGstCaps &) = default;
341 QGstCaps &operator=(QGstCaps &&) noexcept = default;
342
343 enum MemoryFormat { CpuMemory, GLTexture, DMABuf };
344
345 int size() const;
346 QGstStructureView at(int index) const;
347 GstCaps *caps() const;
348
349 MemoryFormat memoryFormat() const;
350 std::optional<std::pair<QVideoFrameFormat, GstVideoInfo>> formatAndVideoInfo() const;
351
352 void addPixelFormats(const QList<QVideoFrameFormat::PixelFormat> &formats, const char *modifier = nullptr);
353 void setResolution(QSize);
354
355 static QGstCaps create();
356
357 static QGstCaps fromCameraFormat(const QCameraFormat &format);
358
359 QGstCaps copy() const;
360};
361
362struct QSharedGstObjectTraits
363{
364 using Type = GstObject *;
365 static constexpr Type invalidValue() noexcept { return nullptr; }
366 static GstObject *ref(GstObject *arg) noexcept
367 {
368 gst_object_ref_sink(object: arg);
369 return arg;
370 }
371 static bool unref(GstObject *arg) noexcept
372 {
373 gst_object_unref(object: arg);
374 return true;
375 }
376};
377
378class QGObjectHandlerConnection;
379
380class QGstObject : public QtPrivate::QSharedHandle<QSharedGstObjectTraits>
381{
382 using BaseClass = QtPrivate::QSharedHandle<QSharedGstObjectTraits>;
383
384public:
385 using BaseClass::BaseClass;
386 QGstObject(const QGstObject &) = default;
387 QGstObject(QGstObject &&) noexcept = default;
388
389 QGstObject &operator=(const QGstObject &) = default;
390 QGstObject &operator=(QGstObject &&) noexcept = default;
391
392 void set(const char *property, const char *str);
393 void set(const char *property, bool b);
394 void set(const char *property, int32_t i);
395 void set(const char *property, uint32_t i);
396 void set(const char *property, int64_t i);
397 void set(const char *property, uint64_t i);
398 void set(const char *property, double d);
399 void set(const char *property, const QGstObject &o);
400 void set(const char *property, const QGstCaps &c);
401 void set(const char *property, void *object, GDestroyNotify destroyFunction);
402
403 template <typename Object>
404 void set(const char *property, Object *object, GDestroyNotify destroyFunction)
405 {
406 set(property, object: static_cast<void *>(object), destroyFunction);
407 }
408
409 template <typename Object>
410 void set(const char *property, std::unique_ptr<Object> object)
411 {
412 set(property, static_cast<void *>(object.release()), qDeleteFromVoidPointer<Object>);
413 }
414
415 template <typename T>
416 static void qDeleteFromVoidPointer(void *ptr)
417 {
418 delete reinterpret_cast<T *>(ptr);
419 }
420
421 QGString getString(const char *property) const;
422 QGstStructureView getStructure(const char *property) const;
423 bool getBool(const char *property) const;
424 uint getUInt(const char *property) const;
425 int getInt(const char *property) const;
426 quint64 getUInt64(const char *property) const;
427 qint64 getInt64(const char *property) const;
428 float getFloat(const char *property) const;
429 double getDouble(const char *property) const;
430 QGstObject getGstObject(const char *property) const;
431 void *getObject(const char *property) const;
432
433 template <typename T>
434 T *getObject(const char *property) const
435 {
436 void *rawObject = getObject(property);
437 return reinterpret_cast<T *>(rawObject);
438 }
439
440 QGObjectHandlerConnection connect(const char *name, GCallback callback, gpointer userData);
441 void disconnect(gulong handlerId);
442
443 GType type() const;
444 QLatin1StringView typeName() const;
445 GstObject *object() const;
446 QLatin1StringView name() const;
447};
448
449class QGObjectHandlerConnection
450{
451public:
452 QGObjectHandlerConnection(QGstObject object, gulong handler);
453
454 QGObjectHandlerConnection() = default;
455 QGObjectHandlerConnection(const QGObjectHandlerConnection &) = default;
456 QGObjectHandlerConnection(QGObjectHandlerConnection &&) = default;
457 QGObjectHandlerConnection &operator=(const QGObjectHandlerConnection &) = default;
458 QGObjectHandlerConnection &operator=(QGObjectHandlerConnection &&) = default;
459
460 void disconnect();
461
462private:
463 static constexpr gulong invalidHandlerId = std::numeric_limits<gulong>::max();
464
465 QGstObject object;
466 gulong handlerId = invalidHandlerId;
467};
468
469// disconnects in dtor
470class QGObjectHandlerScopedConnection
471{
472public:
473 QGObjectHandlerScopedConnection(QGObjectHandlerConnection connection);
474
475 QGObjectHandlerScopedConnection() = default;
476 QGObjectHandlerScopedConnection(const QGObjectHandlerScopedConnection &) = delete;
477 QGObjectHandlerScopedConnection &operator=(const QGObjectHandlerScopedConnection &) = delete;
478 QGObjectHandlerScopedConnection(QGObjectHandlerScopedConnection &&) = default;
479 QGObjectHandlerScopedConnection &operator=(QGObjectHandlerScopedConnection &&) = default;
480
481 ~QGObjectHandlerScopedConnection();
482
483 void disconnect();
484
485private:
486 QGObjectHandlerConnection connection;
487};
488
489class QGstElement;
490
491class QGstPad : public QGstObject
492{
493public:
494 using QGstObject::QGstObject;
495 QGstPad(const QGstPad &) = default;
496 QGstPad(QGstPad &&) noexcept = default;
497
498 explicit QGstPad(const QGstObject &o);
499 explicit QGstPad(GstPad *pad, RefMode mode);
500
501 QGstPad &operator=(const QGstPad &) = default;
502 QGstPad &operator=(QGstPad &&) noexcept = default;
503
504 QGstCaps currentCaps() const;
505 QGstCaps queryCaps() const;
506
507 QGstTagListHandle tags() const;
508 QGString streamId() const;
509
510 std::optional<QPlatformMediaPlayer::TrackType>
511 inferTrackTypeFromName() const; // for decodebin3 etc
512
513 bool isLinked() const;
514 bool link(const QGstPad &sink) const;
515 bool unlink(const QGstPad &sink) const;
516 bool unlinkPeer() const;
517 QGstPad peer() const;
518 QGstElement parent() const;
519
520 GstPad *pad() const;
521
522 GstEvent *stickyEvent(GstEventType type);
523 bool sendEvent(GstEvent *event);
524 void sendFlushStartStop(bool resetTime);
525
526 template <auto Member, typename T>
527 void addProbe(T *instance, GstPadProbeType type);
528
529 template <typename Functor>
530 void doInIdleProbe(Functor &&work);
531
532 template <auto Member, typename T>
533 void addEosProbe(T *instance);
534
535 template <typename Functor>
536 void modifyPipelineInIdleProbe(Functor &&f);
537
538 void sendFlushIfPaused();
539};
540
541class QGstClock : public QGstObject
542{
543public:
544 QGstClock() = default;
545 explicit QGstClock(const QGstObject &o);
546 explicit QGstClock(GstClock *clock, RefMode mode);
547
548 GstClock *clock() const;
549 GstClockTime time() const;
550};
551
552class QGstBin;
553class QGstPipeline;
554
555class QGstElement : public QGstObject
556{
557public:
558 using QGstObject::QGstObject;
559
560 QGstElement(const QGstElement &) = default;
561 QGstElement(QGstElement &&) noexcept = default;
562 QGstElement &operator=(const QGstElement &) = default;
563 QGstElement &operator=(QGstElement &&) noexcept = default;
564
565 explicit QGstElement(GstElement *element, RefMode mode);
566 static QGstElement createFromFactory(const char *factory, const char *name = nullptr);
567 static QGstElement createFromFactory(GstElementFactory *, const char *name = nullptr);
568 static QGstElement createFromFactory(const QGstElementFactoryHandle &,
569 const char *name = nullptr);
570 static QGstElement createFromDevice(const QGstDeviceHandle &, const char *name = nullptr);
571 static QGstElement createFromDevice(GstDevice *, const char *name = nullptr);
572 static QGstElement createFromPipelineDescription(const char *);
573 static QGstElement createFromPipelineDescription(const QByteArray &);
574
575 static QGstElementFactoryHandle findFactory(const char *);
576 static QGstElementFactoryHandle findFactory(const QByteArray &name);
577
578 QGstPad staticPad(const char *name) const;
579 QGstPad src() const;
580 QGstPad sink() const;
581 QGstPad getRequestPad(const char *name) const;
582 void releaseRequestPad(const QGstPad &pad) const;
583
584 GstState state(std::chrono::nanoseconds timeout = std::chrono::seconds(0)) const;
585 GstStateChangeReturn setState(GstState state);
586 bool setStateSync(GstState state, std::chrono::nanoseconds timeout = std::chrono::seconds(1));
587 bool syncStateWithParent();
588 bool finishStateChange(std::chrono::nanoseconds timeout = std::chrono::seconds(5));
589 bool hasAsyncStateChange(std::chrono::nanoseconds timeout = std::chrono::seconds(0)) const;
590 bool waitForAsyncStateChangeComplete(
591 std::chrono::nanoseconds timeout = std::chrono::seconds(5)) const;
592
593 void lockState(bool locked);
594 bool isStateLocked() const;
595
596 void sendEvent(GstEvent *event) const;
597 void sendEos() const;
598
599 std::optional<std::chrono::nanoseconds> duration() const;
600 std::optional<std::chrono::milliseconds> durationInMs() const;
601 std::optional<std::chrono::nanoseconds> position() const;
602 std::optional<std::chrono::milliseconds> positionInMs() const;
603 std::optional<bool> canSeek() const;
604
605 template <auto Member, typename T>
606 QGObjectHandlerConnection onPadAdded(T *instance)
607 {
608 struct Impl
609 {
610 static void callback(GstElement *e, GstPad *pad, gpointer userData)
611 {
612 (static_cast<T *>(userData)->*Member)(QGstElement(e, NeedsRef),
613 QGstPad(pad, NeedsRef));
614 };
615 };
616
617 return connect(name: "pad-added", G_CALLBACK(Impl::callback), userData: instance);
618 }
619 template <auto Member, typename T>
620 QGObjectHandlerConnection onPadRemoved(T *instance)
621 {
622 struct Impl
623 {
624 static void callback(GstElement *e, GstPad *pad, gpointer userData)
625 {
626 (static_cast<T *>(userData)->*Member)(QGstElement(e, NeedsRef),
627 QGstPad(pad, NeedsRef));
628 };
629 };
630
631 return connect(name: "pad-removed", G_CALLBACK(Impl::callback), userData: instance);
632 }
633 template <auto Member, typename T>
634 QGObjectHandlerConnection onNoMorePads(T *instance)
635 {
636 struct Impl
637 {
638 static void callback(GstElement *e, gpointer userData)
639 {
640 (static_cast<T *>(userData)->*Member)(QGstElement(e, NeedsRef));
641 };
642 };
643
644 return connect(name: "no-more-pads", G_CALLBACK(Impl::callback), userData: instance);
645 }
646
647 GstClockTime baseTime() const;
648 void setBaseTime(GstClockTime time) const;
649
650 GstElement *element() const;
651
652 QGstElement getParent() const;
653 QGstBin getParentBin() const;
654 QGstPipeline getPipeline() const;
655
656 void removeFromParent();
657 void dumpPipelineGraph(const char *filename) const;
658
659private:
660 QGstQueryHandle &positionQuery() const;
661 mutable QGstQueryHandle m_positionQuery;
662};
663
664// QGstPad implementations
665
666template <auto Member, typename T>
667void QGstPad::addProbe(T *instance, GstPadProbeType type)
668{
669 auto callback = [](GstPad *pad, GstPadProbeInfo *info, gpointer userData) {
670 return (static_cast<T *>(userData)->*Member)(QGstPad(pad, NeedsRef), info);
671 };
672
673 gst_pad_add_probe(pad(), type, callback, instance, nullptr);
674}
675
676template <typename Functor>
677void QGstPad::doInIdleProbe(Functor &&work)
678{
679 using namespace std::chrono_literals;
680
681 struct CallbackData
682 {
683 QSemaphore waitDone;
684 std::once_flag onceFlag;
685 Functor work;
686
687 void run()
688 {
689 std::call_once(onceFlag, [&] {
690 work();
691 });
692 }
693 };
694
695 CallbackData cd{ QSemaphore{}, {}, std::forward<Functor>(work) };
696
697 auto callback = [](GstPad *, GstPadProbeInfo *, gpointer p) {
698 auto cd = reinterpret_cast<CallbackData *>(p);
699 cd->run();
700 cd->waitDone.release();
701 return GST_PAD_PROBE_REMOVE;
702 };
703
704 gulong probe = gst_pad_add_probe(pad(), GST_PAD_PROBE_TYPE_IDLE, callback, &cd, nullptr);
705 if (probe == 0)
706 return; // probe was executed
707
708 bool success = cd.waitDone.try_acquire_for(250ms);
709 if (success)
710 return;
711
712 // the probe has not been executed for 250ms, probably because the element has transitioned
713 // from playing to paused. Flushing the pad would unblock paused pads
714 sendFlushIfPaused();
715
716 success = cd.waitDone.try_acquire_for(1s);
717 if (success)
718 return;
719
720 // if the idle probe still has not been called, we remove it and call it
721 // explicitly to avoid deadlocking the application. Probably not exactly safe,
722 // but better than deadlocking
723 qWarning() << "QGstPad::doInIdleProbe blocked for 1s. Executing the pad probe manually";
724 parent().dumpPipelineGraph(filename: "doInIdleProbeHang");
725 gst_pad_remove_probe(pad: pad(), id: probe);
726 cd.run();
727}
728
729template <auto Member, typename T>
730void QGstPad::addEosProbe(T *instance)
731{
732 auto callback = [](GstPad *, GstPadProbeInfo *info, gpointer userData) {
733 if (GST_EVENT_TYPE(GST_PAD_PROBE_INFO_DATA(info)) != GST_EVENT_EOS)
734 return GST_PAD_PROBE_PASS;
735 (static_cast<T *>(userData)->*Member)();
736 return GST_PAD_PROBE_REMOVE;
737 };
738
739 gst_pad_add_probe(pad(), GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, callback, instance, nullptr);
740}
741template <typename Functor>
742void QGstPad::modifyPipelineInIdleProbe(Functor &&f)
743{
744 using namespace std::chrono_literals;
745
746 GstPadDirection direction = gst_pad_get_direction(pad: pad());
747
748 switch (direction) {
749 case GstPadDirection::GST_PAD_SINK: {
750 // modifying a source: we need to flush the sink pad before we can modify downstream
751 // elements
752 sendFlushIfPaused();
753 doInIdleProbe(f);
754 return;
755 }
756 case GstPadDirection::GST_PAD_SRC: {
757 // modifying a sink: we need to use the idle probes iff the pipeline is playing
758 if (parent().state(timeout: 1s) == GstState::GST_STATE_PLAYING)
759 doInIdleProbe(f);
760 else
761 f();
762 return;
763 }
764
765 default:
766 Q_UNREACHABLE();
767 }
768}
769
770template <typename... Ts>
771std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void>
772qLinkGstElements(const Ts &...ts)
773{
774 bool link_success = [&] {
775 if constexpr (sizeof...(Ts) == 2)
776 return gst_element_link(ts.element()...);
777 else
778 return gst_element_link_many(ts.element()..., nullptr);
779 }();
780
781 if (Q_UNLIKELY(!link_success)) {
782 qWarning() << "qLinkGstElements: could not link elements: "
783 << std::initializer_list<const char *>{
784 (GST_ELEMENT_NAME(ts.element()))...,
785 };
786 }
787}
788
789template <typename... Ts>
790std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void>
791qUnlinkGstElements(const Ts &...ts)
792{
793 if constexpr (sizeof...(Ts) == 2)
794 gst_element_unlink(ts.element()...);
795 else
796 gst_element_unlink_many(ts.element()..., nullptr);
797}
798
799class QGstBin : public QGstElement
800{
801public:
802 using QGstElement::QGstElement;
803 QGstBin(const QGstBin &) = default;
804 QGstBin(QGstBin &&) noexcept = default;
805 QGstBin &operator=(const QGstBin &) = default;
806 QGstBin &operator=(QGstBin &&) noexcept = default;
807
808 explicit QGstBin(GstBin *bin, RefMode mode = NeedsRef);
809 static QGstBin create(const char *name);
810 static QGstBin createFromFactory(const char *factory, const char *name);
811 static QGstBin createFromPipelineDescription(const QByteArray &pipelineDescription,
812 const char *name = nullptr,
813 bool ghostUnlinkedPads = false);
814 static QGstBin createFromPipelineDescription(const char *pipelineDescription,
815 const char *name = nullptr,
816 bool ghostUnlinkedPads = false);
817
818 template <typename... Ts>
819 std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void> add(const Ts &...ts)
820 {
821 if constexpr (sizeof...(Ts) == 1)
822 gst_bin_add(bin(), ts.element()...);
823 else
824 gst_bin_add_many(bin(), ts.element()..., nullptr);
825 }
826
827 template <typename... Ts>
828 std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void> remove(const Ts &...ts)
829 {
830 if constexpr (sizeof...(Ts) == 1)
831 gst_bin_remove(bin(), ts.element()...);
832 else
833 gst_bin_remove_many(bin(), ts.element()..., nullptr);
834 }
835
836 template <typename... Ts>
837 std::enable_if_t<(std::is_base_of_v<QGstElement, std::remove_reference_t<Ts>> && ...), void>
838 stopAndRemoveElements(Ts &&...ts)
839 {
840 bool stateChangeSuccessful = (ts.setStateSync(GST_STATE_NULL) && ...);
841 Q_ASSERT(stateChangeSuccessful);
842 remove(ts...);
843 }
844
845 GstBin *bin() const;
846
847 void addGhostPad(const QGstElement &child, const char *name);
848 void addGhostPad(const char *name, const QGstPad &pad);
849 void addUnlinkedGhostPads(GstPadDirection);
850
851 bool syncChildrenState();
852
853 void dumpGraph(const char *fileNamePrefix) const;
854
855 QGstElement findByName(const char *);
856
857 void recalculateLatency();
858};
859
860class QGstBaseSink : public QGstElement
861{
862public:
863 using QGstElement::QGstElement;
864
865 explicit QGstBaseSink(GstBaseSink *, RefMode);
866
867 QGstBaseSink(const QGstBaseSink &) = default;
868 QGstBaseSink(QGstBaseSink &&) noexcept = default;
869 QGstBaseSink &operator=(const QGstBaseSink &) = default;
870 QGstBaseSink &operator=(QGstBaseSink &&) noexcept = default;
871
872 void setSync(bool);
873
874 GstBaseSink *baseSink() const;
875};
876
877class QGstBaseSrc : public QGstElement
878{
879public:
880 using QGstElement::QGstElement;
881
882 explicit QGstBaseSrc(GstBaseSrc *, RefMode);
883
884 QGstBaseSrc(const QGstBaseSrc &) = default;
885 QGstBaseSrc(QGstBaseSrc &&) noexcept = default;
886 QGstBaseSrc &operator=(const QGstBaseSrc &) = default;
887 QGstBaseSrc &operator=(QGstBaseSrc &&) noexcept = default;
888
889 GstBaseSrc *baseSrc() const;
890};
891
892class QGstAppSink : public QGstBaseSink
893{
894public:
895 using QGstBaseSink::QGstBaseSink;
896
897 explicit QGstAppSink(GstAppSink *, RefMode);
898
899 QGstAppSink(const QGstAppSink &) = default;
900 QGstAppSink(QGstAppSink &&) noexcept = default;
901 QGstAppSink &operator=(const QGstAppSink &) = default;
902 QGstAppSink &operator=(QGstAppSink &&) noexcept = default;
903
904 static QGstAppSink create(const char *name);
905
906 GstAppSink *appSink() const;
907
908 void setMaxBuffers(int);
909# if GST_CHECK_VERSION(1, 24, 0)
910 void setMaxBufferTime(std::chrono::nanoseconds);
911# endif
912
913 void setCaps(const QGstCaps &caps);
914 void setCallbacks(GstAppSinkCallbacks &callbacks, gpointer user_data, GDestroyNotify notify);
915
916 QGstSampleHandle pullSample();
917};
918
919class QGstAppSrc : public QGstBaseSrc
920{
921public:
922 using QGstBaseSrc::QGstBaseSrc;
923
924 explicit QGstAppSrc(GstAppSrc *, RefMode);
925
926 QGstAppSrc(const QGstAppSrc &) = default;
927 QGstAppSrc(QGstAppSrc &&) noexcept = default;
928 QGstAppSrc &operator=(const QGstAppSrc &) = default;
929 QGstAppSrc &operator=(QGstAppSrc &&) noexcept = default;
930
931 static QGstAppSrc create(const char *name);
932
933 GstAppSrc *appSrc() const;
934
935 void setCallbacks(GstAppSrcCallbacks &callbacks, gpointer user_data, GDestroyNotify notify);
936
937 GstFlowReturn pushBuffer(GstBuffer *); // take ownership
938};
939
940inline GstClockTime qGstClockTimeFromChrono(std::chrono::nanoseconds ns)
941{
942 return ns.count();
943}
944
945QString qGstErrorMessageCannotFindElement(std::string_view element);
946
947template <typename Arg, typename... Args>
948std::optional<QString> qGstErrorMessageIfElementsNotAvailable(const Arg &arg, Args... args)
949{
950 QGstElementFactoryHandle factory = QGstElement::findFactory(arg);
951 if (!factory)
952 return qGstErrorMessageCannotFindElement(arg);
953
954 if constexpr (sizeof...(args) != 0)
955 return qGstErrorMessageIfElementsNotAvailable(args...);
956 else
957 return std::nullopt;
958}
959
960template <typename Functor>
961void qForeachStreamInCollection(GstStreamCollection *collection, Functor &&f)
962{
963 guint size = gst_stream_collection_get_size(collection);
964 for (guint index = 0; index != size; ++index)
965 f(gst_stream_collection_get_stream(collection, index));
966}
967
968template <typename Functor>
969void qForeachStreamInCollection(const QGstStreamCollectionHandle &collection, Functor &&f)
970{
971 qForeachStreamInCollection(collection.get(), std::forward<Functor>(f));
972}
973
974QT_END_NAMESPACE
975
976namespace std {
977
978template <>
979struct hash<QT_PREPEND_NAMESPACE(QGstElement)>
980{
981 using argument_type = QT_PREPEND_NAMESPACE(QGstElement);
982 using result_type = size_t;
983 result_type operator()(const argument_type &e) const noexcept
984 {
985 return std::hash<void *>{}(e.element());
986 }
987};
988} // namespace std
989
990#endif
991

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