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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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