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 | |
48 | QT_BEGIN_NAMESPACE |
49 | |
50 | namespace QGstImpl { |
51 | |
52 | template <typename T> |
53 | struct 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 | |
108 | QGST_DEFINE_CAST_TRAITS(GstBin, BIN); |
109 | QGST_DEFINE_CAST_TRAITS(GstClock, CLOCK); |
110 | QGST_DEFINE_CAST_TRAITS(GstElement, ELEMENT); |
111 | QGST_DEFINE_CAST_TRAITS(GstObject, OBJECT); |
112 | QGST_DEFINE_CAST_TRAITS(GstPad, PAD); |
113 | QGST_DEFINE_CAST_TRAITS(GstPipeline, PIPELINE); |
114 | QGST_DEFINE_CAST_TRAITS(GstBaseSink, BASE_SINK); |
115 | QGST_DEFINE_CAST_TRAITS(GstBaseSrc, BASE_SRC); |
116 | QGST_DEFINE_CAST_TRAITS(GstAppSink, APP_SINK); |
117 | QGST_DEFINE_CAST_TRAITS(GstAppSrc, APP_SRC); |
118 | |
119 | QGST_DEFINE_CAST_TRAITS_FOR_INTERFACE(GstTagSetter, TAG_SETTER); |
120 | |
121 | |
122 | template <> |
123 | struct 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 | |
148 | template <typename DestinationType, typename SourceType> |
149 | bool qIsGstObjectOfType(SourceType *arg) |
150 | { |
151 | using Traits = QGstImpl::GstObjectTraits<DestinationType>; |
152 | return arg && Traits::isObjectOfType(arg); |
153 | } |
154 | |
155 | template <typename DestinationType, typename SourceType> |
156 | DestinationType *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 | |
164 | template <typename DestinationType, typename SourceType> |
165 | DestinationType *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 | |
173 | class QSize; |
174 | class QGstStructureView; |
175 | class QGstCaps; |
176 | class QCameraFormat; |
177 | |
178 | template <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 | |
193 | struct 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 | |
260 | class QGValue |
261 | { |
262 | public: |
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 | |
292 | namespace QGstPointerImpl { |
293 | |
294 | template <typename RefcountedObject> |
295 | struct QGstRefcountingAdaptor; |
296 | |
297 | template <typename GstType> |
298 | class QGstObjectWrapper |
299 | { |
300 | using Adaptor = QGstRefcountingAdaptor<GstType>; |
301 | |
302 | GstType *m_object = nullptr; |
303 | |
304 | public: |
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 | |
382 | protected: |
383 | GstType *get() const { return m_object; } |
384 | }; |
385 | |
386 | } // namespace QGstPointerImpl |
387 | |
388 | class QGstreamerMessage; |
389 | |
390 | class QGstStructureView |
391 | { |
392 | public: |
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 | |
415 | template <> |
416 | struct 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 | |
422 | class QGstCaps : public QGstPointerImpl::QGstObjectWrapper<GstCaps> |
423 | { |
424 | using BaseClass = QGstPointerImpl::QGstObjectWrapper<GstCaps>; |
425 | |
426 | public: |
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 | |
452 | template <> |
453 | struct 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 | |
459 | class QGObjectHandlerConnection; |
460 | |
461 | class QGstObject : public QGstPointerImpl::QGstObjectWrapper<GstObject> |
462 | { |
463 | using BaseClass = QGstPointerImpl::QGstObjectWrapper<GstObject>; |
464 | |
465 | public: |
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 | |
530 | class QGObjectHandlerConnection |
531 | { |
532 | public: |
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 | |
543 | private: |
544 | static constexpr gulong invalidHandlerId = std::numeric_limits<gulong>::max(); |
545 | |
546 | QGstObject object; |
547 | gulong handlerId = invalidHandlerId; |
548 | }; |
549 | |
550 | // disconnects in dtor |
551 | class QGObjectHandlerScopedConnection |
552 | { |
553 | public: |
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 | |
566 | private: |
567 | QGObjectHandlerConnection connection; |
568 | }; |
569 | |
570 | class QGstElement; |
571 | |
572 | class QGstPad : public QGstObject |
573 | { |
574 | public: |
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 | |
622 | class QGstClock : public QGstObject |
623 | { |
624 | public: |
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 | |
633 | class QGstPipeline; |
634 | |
635 | class QGstElement : public QGstObject |
636 | { |
637 | public: |
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 | |
736 | private: |
737 | QGstQueryHandle &positionQuery() const; |
738 | mutable QGstQueryHandle m_positionQuery; |
739 | }; |
740 | |
741 | // QGstPad implementations |
742 | |
743 | template <auto Member, typename T> |
744 | void 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 | |
753 | template <typename Functor> |
754 | void 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 | |
810 | template <auto Member, typename T> |
811 | void 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 | } |
822 | template <typename Functor> |
823 | void 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 | |
851 | template <typename... Ts> |
852 | std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void> |
853 | qLinkGstElements(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 | |
870 | template <typename... Ts> |
871 | std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void> |
872 | qUnlinkGstElements(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 | |
880 | class QGstBin : public QGstElement |
881 | { |
882 | public: |
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 | |
940 | class QGstBaseSink : public QGstElement |
941 | { |
942 | public: |
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 | |
957 | class QGstBaseSrc : public QGstElement |
958 | { |
959 | public: |
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 | |
972 | class QGstAppSink : public QGstBaseSink |
973 | { |
974 | public: |
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 | |
999 | class QGstAppSrc : public QGstBaseSrc |
1000 | { |
1001 | public: |
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 | |
1020 | inline GstClockTime qGstClockTimeFromChrono(std::chrono::nanoseconds ns) |
1021 | { |
1022 | return ns.count(); |
1023 | } |
1024 | |
1025 | QString qGstErrorMessageCannotFindElement(std::string_view element); |
1026 | |
1027 | template <typename Arg, typename... Args> |
1028 | std::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 | |
1040 | template <typename Functor> |
1041 | void 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 | |
1048 | template <typename Functor> |
1049 | void qForeachStreamInCollection(const QGstStreamCollectionHandle &collection, Functor &&f) |
1050 | { |
1051 | qForeachStreamInCollection(collection.get(), std::forward<Functor>(f)); |
1052 | } |
1053 | |
1054 | QT_END_NAMESPACE |
1055 | |
1056 | namespace std { |
1057 | |
1058 | template <> |
1059 | struct 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 |
Definitions
- GstObjectTraits
- GstObjectTraits
- isObjectOfType
- isObjectOfType
- isObjectOfType
- isObjectOfType
- isObjectOfType
- isObjectOfType
- isObjectOfType
- isObjectOfType
- isObjectOfType
- isObjectOfType
- cast
- cast
- cast
- cast
- cast
- cast
- cast
- cast
- cast
- cast
- checked_cast
- checked_cast
- checked_cast
- checked_cast
- checked_cast
- checked_cast
- checked_cast
- checked_cast
- checked_cast
- checked_cast
- GstObjectTraits
- isObjectOfType
- cast
- checked_cast
- GstObjectTraits
- isObjectOfType
- cast
- checked_cast
- qIsGstObjectOfType
- qGstSafeCast
- qGstCheckedCast
- QGRange
- operator==
- QGString
- asStringView
- asByteArrayView
- toQString
- operator==
- operator==
- operator==
- operator!=
- operator!=
- operator!=
- operator<
- operator<
- operator<
- operator<
- operator<
- operator QByteArrayView
- operator QByteArray
- QGValue
- getPointer
- QGstObjectWrapper
- RefMode
- QGstObjectWrapper
- QGstObjectWrapper
- QGstObjectWrapper
- ~QGstObjectWrapper
- QGstObjectWrapper
- operator=
- operator=
- operator==
- operator!=
- operator<
- operator bool
- isNull
- release
- get
- QGstStructureView
- QGstRefcountingAdaptor
- ref
- unref
- QGstCaps
- QGstCaps
- QGstCaps
- operator=
- operator=
- MemoryFormat
- QGstRefcountingAdaptor
- ref
- unref
- QGstObject
- QGstObject
- QGstObject
- operator=
- operator=
- set
- set
- qDeleteFromVoidPointer
- getObject
- QGObjectHandlerConnection
- QGObjectHandlerConnection
- QGObjectHandlerConnection
- QGObjectHandlerConnection
- operator=
- operator=
- invalidHandlerId
- QGObjectHandlerScopedConnection
- QGObjectHandlerScopedConnection
- QGObjectHandlerScopedConnection
- operator=
- QGObjectHandlerScopedConnection
- operator=
- QGstPad
- QGstPad
- QGstPad
- operator=
- operator=
- QGstClock
- QGstClock
- QGstElement
- QGstElement
- QGstElement
- operator=
- operator=
- onPadAdded
- onPadRemoved
- onNoMorePads
- addProbe
- doInIdleProbe
- addEosProbe
- modifyPipelineInIdleProbe
- qLinkGstElements
- qUnlinkGstElements
- QGstBin
- QGstBin
- QGstBin
- operator=
- operator=
- add
- remove
- stopAndRemoveElements
- QGstBaseSink
- QGstBaseSink
- QGstBaseSink
- operator=
- operator=
- QGstBaseSrc
- QGstBaseSrc
- QGstBaseSrc
- operator=
- operator=
- QGstAppSink
- QGstAppSink
- QGstAppSink
- operator=
- operator=
- QGstAppSrc
- QGstAppSrc
- QGstAppSrc
- operator=
- operator=
- qGstClockTimeFromChrono
- qGstErrorMessageIfElementsNotAvailable
- qForeachStreamInCollection
- qForeachStreamInCollection
- hash
Learn Advanced QML with KDAB
Find out more