diff options
Diffstat (limited to 'src/plugins/multimedia/gstreamer/common/qgst_p.h')
-rw-r--r-- | src/plugins/multimedia/gstreamer/common/qgst_p.h | 1096 |
1 files changed, 659 insertions, 437 deletions
diff --git a/src/plugins/multimedia/gstreamer/common/qgst_p.h b/src/plugins/multimedia/gstreamer/common/qgst_p.h index 66b1b156f..68412258e 100644 --- a/src/plugins/multimedia/gstreamer/common/qgst_p.h +++ b/src/plugins/multimedia/gstreamer/common/qgst_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QGST_P_H #define QGST_P_H @@ -51,32 +15,163 @@ // We mean it. // -#include <private/qtmultimediaglobal_p.h> - -#include <QSemaphore> +#include <QtCore/qdebug.h> #include <QtCore/qlist.h> +#include <QtCore/qsemaphore.h> #include <QtMultimedia/qaudioformat.h> #include <QtMultimedia/qvideoframe.h> +#include <QtMultimedia/private/qtmultimediaglobal_p.h> +#include <QtMultimedia/private/qmultimediautils_p.h> +#include <QtMultimedia/private/qplatformmediaplayer_p.h> #include <gst/gst.h> #include <gst/video/video-info.h> -#include <functional> +#include "qgst_handle_types_p.h" + +#include <type_traits> #if QT_CONFIG(gstreamer_photography) -#define GST_USE_UNSTABLE_API -#include <gst/interfaces/photography.h> -#undef GST_USE_UNSTABLE_API +# define GST_USE_UNSTABLE_API +# include <gst/interfaces/photography.h> +# undef GST_USE_UNSTABLE_API #endif -#ifndef QT_NO_DEBUG -#include <qdebug.h> + +#if QT_CONFIG(gstreamer_app) +# include <gst/app/gstappsink.h> +# include <gst/app/gstappsrc.h> #endif QT_BEGIN_NAMESPACE +namespace QGstImpl { + +template <typename T> +struct GstObjectTraits +{ + // using Type = T; + // template <typename U> + // static bool isObjectOfType(U *); + // template <typename U> + // static T *cast(U *); +}; + +#define QGST_DEFINE_CAST_TRAITS(ClassName, MACRO_LABEL) \ + template <> \ + struct GstObjectTraits<ClassName> \ + { \ + using Type = ClassName; \ + template <typename U> \ + static bool isObjectOfType(U *arg) \ + { \ + return GST_IS_##MACRO_LABEL(arg); \ + } \ + template <typename U> \ + static Type *cast(U *arg) \ + { \ + return GST_##MACRO_LABEL##_CAST(arg); \ + } \ + template <typename U> \ + static Type *checked_cast(U *arg) \ + { \ + return GST_##MACRO_LABEL(arg); \ + } \ + }; \ + static_assert(true, "ensure semicolon") + +#define QGST_DEFINE_CAST_TRAITS_FOR_INTERFACE(ClassName, MACRO_LABEL) \ + template <> \ + struct GstObjectTraits<ClassName> \ + { \ + using Type = ClassName; \ + template <typename U> \ + static bool isObjectOfType(U *arg) \ + { \ + return GST_IS_##MACRO_LABEL(arg); \ + } \ + template <typename U> \ + static Type *cast(U *arg) \ + { \ + return checked_cast(arg); \ + } \ + template <typename U> \ + static Type *checked_cast(U *arg) \ + { \ + return GST_##MACRO_LABEL(arg); \ + } \ + }; \ + static_assert(true, "ensure semicolon") + +QGST_DEFINE_CAST_TRAITS(GstBin, BIN); +QGST_DEFINE_CAST_TRAITS(GstClock, CLOCK); +QGST_DEFINE_CAST_TRAITS(GstElement, ELEMENT); +QGST_DEFINE_CAST_TRAITS(GstObject, OBJECT); +QGST_DEFINE_CAST_TRAITS(GstPad, PAD); +QGST_DEFINE_CAST_TRAITS(GstPipeline, PIPELINE); +QGST_DEFINE_CAST_TRAITS(GstBaseSink, BASE_SINK); +QGST_DEFINE_CAST_TRAITS(GstBaseSrc, BASE_SRC); + +QGST_DEFINE_CAST_TRAITS_FOR_INTERFACE(GstTagSetter, TAG_SETTER); + +#if QT_CONFIG(gstreamer_app) +QGST_DEFINE_CAST_TRAITS(GstAppSink, APP_SINK); +QGST_DEFINE_CAST_TRAITS(GstAppSrc, APP_SRC); +#endif + +template <> +struct GstObjectTraits<GObject> +{ + using Type = GObject; + template <typename U> + static bool isObjectOfType(U *arg) + { + return G_IS_OBJECT(arg); + } + template <typename U> + static Type *cast(U *arg) + { + return G_OBJECT(arg); + } + template <typename U> + static Type *checked_cast(U *arg) + { + return G_OBJECT(arg); + } +}; + +#undef QGST_DEFINE_CAST_TRAITS +#undef QGST_DEFINE_CAST_TRAITS_FOR_INTERFACE + +} // namespace QGstImpl + +template <typename DestinationType, typename SourceType> +bool qIsGstObjectOfType(SourceType *arg) +{ + using Traits = QGstImpl::GstObjectTraits<DestinationType>; + return arg && Traits::isObjectOfType(arg); +} + +template <typename DestinationType, typename SourceType> +DestinationType *qGstSafeCast(SourceType *arg) +{ + using Traits = QGstImpl::GstObjectTraits<DestinationType>; + if (arg && Traits::isObjectOfType(arg)) + return Traits::cast(arg); + return nullptr; +} + +template <typename DestinationType, typename SourceType> +DestinationType *qGstCheckedCast(SourceType *arg) +{ + using Traits = QGstImpl::GstObjectTraits<DestinationType>; + if (arg) + Q_ASSERT(Traits::isObjectOfType(arg)); + return Traits::cast(arg); +} + class QSize; -class QGstStructure; +class QGstStructureView; class QGstCaps; class QGstPipelinePrivate; class QCameraFormat; @@ -87,265 +182,284 @@ template <typename T> struct QGRange T max; }; -class QGString +struct QGString : QUniqueGStringHandle { - char *str; -public: - QGString(char *string) : str(string) {} - ~QGString() { g_free(str); } - operator QByteArray() { return QByteArray(str); } - operator const char *() { return str; } + using QUniqueGStringHandle::QUniqueGStringHandle; + + QLatin1StringView asStringView() const { return QLatin1StringView{ get() }; } + QString toQString() const { return QString::fromUtf8(get()); } }; class QGValue { public: - QGValue(const GValue *v) : value(v) {} + explicit QGValue(const GValue *v); const GValue *value; - bool isNull() const { return !value; } + bool isNull() const; - std::optional<bool> toBool() const + std::optional<bool> toBool() const; + std::optional<int> toInt() const; + std::optional<int> toInt64() const; + template<typename T> + T *getPointer() const { - if (!G_VALUE_HOLDS_BOOLEAN(value)) - return std::nullopt; - return g_value_get_boolean(value); + return value ? static_cast<T *>(g_value_get_pointer(value)) : nullptr; } - std::optional<int> toInt() const + + const char *toString() const; + std::optional<float> getFraction() const; + std::optional<QGRange<float>> getFractionRange() const; + std::optional<QGRange<int>> toIntRange() const; + + QGstStructureView toStructure() const; + QGstCaps toCaps() const; + + bool isList() const; + int listSize() const; + QGValue at(int index) const; + + QList<QAudioFormat::SampleFormat> getSampleFormats() const; +}; + +namespace QGstPointerImpl { + +template <typename RefcountedObject> +struct QGstRefcountingAdaptor; + +template <typename GstType> +class QGstObjectWrapper +{ + using Adaptor = QGstRefcountingAdaptor<GstType>; + + GstType *m_object = nullptr; + +public: + enum RefMode { HasRef, NeedsRef }; + + constexpr QGstObjectWrapper() = default; + + explicit QGstObjectWrapper(GstType *object, RefMode mode) : m_object(object) { - if (!G_VALUE_HOLDS_INT(value)) - return std::nullopt; - return g_value_get_int(value); + if (m_object && mode == NeedsRef) + Adaptor::ref(m_object); } - std::optional<int> toInt64() const + + QGstObjectWrapper(const QGstObjectWrapper &other) : m_object(other.m_object) { - if (!G_VALUE_HOLDS_INT64(value)) - return std::nullopt; - return g_value_get_int64(value); + if (m_object) + Adaptor::ref(m_object); } - template<typename T> - T *getPointer() const + + ~QGstObjectWrapper() { - return value ? static_cast<T *>(g_value_get_pointer(value)) : nullptr; + if (m_object) + Adaptor::unref(m_object); } - const char *toString() const + QGstObjectWrapper(QGstObjectWrapper &&other) noexcept + : m_object(std::exchange(other.m_object, nullptr)) { - return value ? g_value_get_string(value) : nullptr; } - std::optional<float> getFraction() const + + QGstObjectWrapper & + operator=(const QGstObjectWrapper &other) // NOLINT: bugprone-unhandled-self-assign { - if (!GST_VALUE_HOLDS_FRACTION(value)) - return std::nullopt; - return (float)gst_value_get_fraction_numerator(value)/(float)gst_value_get_fraction_denominator(value); + if (m_object != other.m_object) { + GstType *originalObject = m_object; + + m_object = other.m_object; + if (m_object) + Adaptor::ref(m_object); + if (originalObject) + Adaptor::unref(originalObject); + } + return *this; } - std::optional<QGRange<float>> getFractionRange() const + QGstObjectWrapper &operator=(QGstObjectWrapper &&other) noexcept { - if (!GST_VALUE_HOLDS_FRACTION_RANGE(value)) - return std::nullopt; - QGValue min = gst_value_get_fraction_range_min(value); - QGValue max = gst_value_get_fraction_range_max(value); - return QGRange<float>{ *min.getFraction(), *max.getFraction() }; + if (this != &other) { + GstType *originalObject = m_object; + m_object = std::exchange(other.m_object, nullptr); + + if (originalObject) + Adaptor::unref(originalObject); + } + return *this; } - std::optional<QGRange<int>> toIntRange() const + friend bool operator==(const QGstObjectWrapper &a, const QGstObjectWrapper &b) { - if (!GST_VALUE_HOLDS_INT_RANGE(value)) - return std::nullopt; - return QGRange<int>{ gst_value_get_int_range_min(value), gst_value_get_int_range_max(value) }; + return a.m_object == b.m_object; + } + friend bool operator!=(const QGstObjectWrapper &a, const QGstObjectWrapper &b) + { + return a.m_object != b.m_object; } - inline QGstStructure toStructure() const; - inline QGstCaps toCaps() const; - - inline bool isList() const { return value && GST_VALUE_HOLDS_LIST(value); } - inline int listSize() const { return gst_value_list_get_size(value); } - inline QGValue at(int index) const { return gst_value_list_get_value(value, index); } + explicit operator bool() const { return bool(m_object); } + bool isNull() const { return !m_object; } + GstType *release() { return std::exchange(m_object, nullptr); } - Q_MULTIMEDIA_EXPORT QList<QAudioFormat::SampleFormat> getSampleFormats() const; +protected: + GstType *get() const { return m_object; } }; -class QGstStructure { +} // namespace QGstPointerImpl + +class QGstreamerMessage; + +class QGstStructureView +{ public: const GstStructure *structure = nullptr; - QGstStructure() = default; - QGstStructure(const GstStructure *s) : structure(s) {} - void free() { if (structure) gst_structure_free(const_cast<GstStructure *>(structure)); structure = nullptr; } + explicit QGstStructureView(const GstStructure *); + explicit QGstStructureView(const QUniqueGstStructureHandle &); - bool isNull() const { return !structure; } + QUniqueGstStructureHandle clone() const; - QByteArrayView name() const { return gst_structure_get_name(structure); } + bool isNull() const; + QByteArrayView name() const; + QGValue operator[](const char *fieldname) const; - QGValue operator[](const char *name) const { return gst_structure_get_value(structure, name); } + QGstCaps caps() const; + QGstTagListHandle tags() const; - Q_MULTIMEDIA_EXPORT QSize resolution() const; - Q_MULTIMEDIA_EXPORT QVideoFrameFormat::PixelFormat pixelFormat() const; - Q_MULTIMEDIA_EXPORT QGRange<float> frameRateRange() const; + QSize resolution() const; + QVideoFrameFormat::PixelFormat pixelFormat() const; + QGRange<float> frameRateRange() const; + QGstreamerMessage getMessage(); + std::optional<Fraction> pixelAspectRatio() const; + QSize nativeSize() const; +}; - QByteArray toString() const - { - char *s = gst_structure_to_string(structure); - QByteArray str(s); - g_free(s); - return str; - } - QGstStructure copy() const { return gst_structure_copy(structure); } +template <> +struct QGstPointerImpl::QGstRefcountingAdaptor<GstCaps> +{ + static void ref(GstCaps *arg) noexcept { gst_caps_ref(arg); } + static void unref(GstCaps *arg) noexcept { gst_caps_unref(arg); } }; -class QGstCaps { - const GstCaps *caps = nullptr; +class QGstCaps : public QGstPointerImpl::QGstObjectWrapper<GstCaps> +{ + using BaseClass = QGstPointerImpl::QGstObjectWrapper<GstCaps>; + public: - enum MemoryFormat { - CpuMemory, - GLTexture, - DMABuf - }; + using BaseClass::BaseClass; + QGstCaps(const QGstCaps &) = default; + QGstCaps(QGstCaps &&) noexcept = default; + QGstCaps &operator=(const QGstCaps &) = default; + QGstCaps &operator=(QGstCaps &&) noexcept = default; - QGstCaps() = default; - QGstCaps(const GstCaps *c) : caps(c) {} + enum MemoryFormat { CpuMemory, GLTexture, DMABuf }; - bool isNull() const { return !caps; } + int size() const; + QGstStructureView at(int index) const; + GstCaps *caps() const; - int size() const { return gst_caps_get_size(caps); } - QGstStructure at(int index) { return gst_caps_get_structure(caps, index); } - const GstCaps *get() const { return caps; } - QByteArray toString() const - { - gchar *c = gst_caps_to_string(caps); - QByteArray b(c); - g_free(c); - return b; - } - MemoryFormat memoryFormat() const { - auto *features = gst_caps_get_features(caps, 0); - if (gst_caps_features_contains(features, "memory:GLMemory")) - return GLTexture; - else if (gst_caps_features_contains(features, "memory:DMABuf")) - return DMABuf; - return CpuMemory; - } - QVideoFrameFormat formatForCaps(GstVideoInfo *info) const; + MemoryFormat memoryFormat() const; + std::optional<std::pair<QVideoFrameFormat, GstVideoInfo>> formatAndVideoInfo() const; + + void addPixelFormats(const QList<QVideoFrameFormat::PixelFormat> &formats, const char *modifier = nullptr); + void setResolution(QSize); + + static QGstCaps create(); + + static QGstCaps fromCameraFormat(const QCameraFormat &format); + + QGstCaps copy() const; +}; + +template <> +struct QGstPointerImpl::QGstRefcountingAdaptor<GstObject> +{ + static void ref(GstObject *arg) noexcept { gst_object_ref_sink(arg); } + static void unref(GstObject *arg) noexcept { gst_object_unref(arg); } }; -class QGstMutableCaps : public QGstCaps { - GstCaps *caps = nullptr; +class QGObjectHandlerConnection; + +class QGstObject : public QGstPointerImpl::QGstObjectWrapper<GstObject> +{ + using BaseClass = QGstPointerImpl::QGstObjectWrapper<GstObject>; + public: - enum RefMode { HasRef, NeedsRef }; - QGstMutableCaps() = default; - QGstMutableCaps(GstCaps *c, RefMode mode = HasRef) - : QGstCaps(c), caps(c) - { - Q_ASSERT(QGstCaps::get() == caps); - if (mode == NeedsRef) - gst_caps_ref(caps); - } - QGstMutableCaps(const QGstMutableCaps &other) - : QGstCaps(other), caps(other.caps) - { - Q_ASSERT(QGstCaps::get() == caps); - if (caps) - gst_caps_ref(caps); - } - QGstMutableCaps &operator=(const QGstMutableCaps &other) - { - QGstCaps::operator=(other); - if (other.caps) - gst_caps_ref(other.caps); - if (caps) - gst_caps_unref(caps); - caps = other.caps; - Q_ASSERT(QGstCaps::get() == caps); - return *this; - } - ~QGstMutableCaps() { - Q_ASSERT(QGstCaps::get() == caps); - if (caps) - gst_caps_unref(caps); - } + using BaseClass::BaseClass; + QGstObject(const QGstObject &) = default; + QGstObject(QGstObject &&) noexcept = default; + + QGstObject &operator=(const QGstObject &) = default; + QGstObject &operator=(QGstObject &&) noexcept = default; + + void set(const char *property, const char *str); + void set(const char *property, bool b); + void set(const char *property, uint i); + void set(const char *property, int i); + void set(const char *property, qint64 i); + void set(const char *property, quint64 i); + void set(const char *property, double d); + void set(const char *property, const QGstObject &o); + void set(const char *property, const QGstCaps &c); + + QGString getString(const char *property) const; + QGstStructureView getStructure(const char *property) const; + bool getBool(const char *property) const; + uint getUInt(const char *property) const; + int getInt(const char *property) const; + quint64 getUInt64(const char *property) const; + qint64 getInt64(const char *property) const; + float getFloat(const char *property) const; + double getDouble(const char *property) const; + QGstObject getObject(const char *property) const; + + QGObjectHandlerConnection connect(const char *name, GCallback callback, gpointer userData); + void disconnect(gulong handlerId); + + GType type() const; + QLatin1StringView typeName() const; + GstObject *object() const; + QLatin1StringView name() const; +}; - void create() { - caps = gst_caps_new_empty(); - QGstCaps::operator=(QGstCaps(caps)); - } +class QGObjectHandlerConnection +{ +public: + QGObjectHandlerConnection(QGstObject object, gulong handler); - void addPixelFormats(const QList<QVideoFrameFormat::PixelFormat> &formats, const char *modifier = nullptr); - static QGstMutableCaps fromCameraFormat(const QCameraFormat &format); + QGObjectHandlerConnection() = default; + QGObjectHandlerConnection(const QGObjectHandlerConnection &) = default; + QGObjectHandlerConnection(QGObjectHandlerConnection &&) = default; + QGObjectHandlerConnection &operator=(const QGObjectHandlerConnection &) = default; + QGObjectHandlerConnection &operator=(QGObjectHandlerConnection &&) = default; + + void disconnect(); + +private: + static constexpr gulong invalidHandlerId = std::numeric_limits<gulong>::max(); - GstCaps *get() const { return caps; } + QGstObject object; + gulong handlerId = invalidHandlerId; }; -class QGstObject +// disconnects in dtor +class QGObjectHandlerScopedConnection { -protected: - GstObject *m_object = nullptr; public: - enum RefMode { HasRef, NeedsRef }; + QGObjectHandlerScopedConnection(QGObjectHandlerConnection connection); - QGstObject() = default; - explicit QGstObject(GstObject *o, RefMode mode = HasRef) - : m_object(o) - { - if (o && mode == NeedsRef) - // Use ref_sink to remove any floating references - gst_object_ref_sink(m_object); - } - QGstObject(const QGstObject &other) - : m_object(other.m_object) - { - if (m_object) - gst_object_ref(m_object); - } - QGstObject &operator=(const QGstObject &other) - { - if (this == &other) - return *this; - if (other.m_object) - gst_object_ref(other.m_object); - if (m_object) - gst_object_unref(m_object); - m_object = other.m_object; - return *this; - } - virtual ~QGstObject() { - if (m_object) - gst_object_unref(m_object); - } + QGObjectHandlerScopedConnection() = default; + QGObjectHandlerScopedConnection(const QGObjectHandlerScopedConnection &) = delete; + QGObjectHandlerScopedConnection &operator=(const QGObjectHandlerScopedConnection &) = delete; + QGObjectHandlerScopedConnection(QGObjectHandlerScopedConnection &&) = default; + QGObjectHandlerScopedConnection &operator=(QGObjectHandlerScopedConnection &&) = default; - friend bool operator==(const QGstObject &a, const QGstObject &b) - { return a.m_object == b.m_object; } - friend bool operator!=(const QGstObject &a, const QGstObject &b) - { return a.m_object != b.m_object; } + ~QGObjectHandlerScopedConnection(); - bool isNull() const { return !m_object; } + void disconnect(); - void set(const char *property, const char *str) { g_object_set(m_object, property, str, nullptr); } - void set(const char *property, bool b) { g_object_set(m_object, property, gboolean(b), nullptr); } - void set(const char *property, uint i) { g_object_set(m_object, property, guint(i), nullptr); } - void set(const char *property, int i) { g_object_set(m_object, property, gint(i), nullptr); } - void set(const char *property, qint64 i) { g_object_set(m_object, property, gint64(i), nullptr); } - void set(const char *property, quint64 i) { g_object_set(m_object, property, guint64(i), nullptr); } - void set(const char *property, double d) { g_object_set(m_object, property, gdouble(d), nullptr); } - void set(const char *property, const QGstObject &o) { g_object_set(m_object, property, o.object(), nullptr); } - void set(const char *property, const QGstMutableCaps &c) { g_object_set(m_object, property, c.get(), nullptr); } - - QGString getString(const char *property) const - { char *s = nullptr; g_object_get(m_object, property, &s, nullptr); return s; } - QGstStructure getStructure(const char *property) const - { GstStructure *s = nullptr; g_object_get(m_object, property, &s, nullptr); return QGstStructure(s); } - bool getBool(const char *property) const { gboolean b = false; g_object_get(m_object, property, &b, nullptr); return b; } - uint getUInt(const char *property) const { guint i = 0; g_object_get(m_object, property, &i, nullptr); return i; } - int getInt(const char *property) const { gint i = 0; g_object_get(m_object, property, &i, nullptr); return i; } - quint64 getUInt64(const char *property) const { guint64 i = 0; g_object_get(m_object, property, &i, nullptr); return i; } - qint64 getInt64(const char *property) const { gint64 i = 0; g_object_get(m_object, property, &i, nullptr); return i; } - float getFloat(const char *property) const { gfloat d = 0; g_object_get(m_object, property, &d, nullptr); return d; } - double getDouble(const char *property) const { gdouble d = 0; g_object_get(m_object, property, &d, nullptr); return d; } - QGstObject getObject(const char *property) const { GstObject *o = nullptr; g_object_get(m_object, property, &o, nullptr); return QGstObject(o, HasRef); } - - void connect(const char *name, GCallback callback, gpointer userData) { g_signal_connect(m_object, name, callback, userData); } - - GstObject *object() const { return m_object; } - const char *name() const { return m_object ? GST_OBJECT_NAME(m_object) : "(null)"; } +private: + QGObjectHandlerConnection connection; }; class QGstElement; @@ -353,48 +467,57 @@ class QGstElement; class QGstPad : public QGstObject { public: - QGstPad() = default; - QGstPad(const QGstObject &o) - : QGstPad(GST_PAD(o.object()), NeedsRef) - {} - QGstPad(GstPad *pad, RefMode mode = NeedsRef) - : QGstObject(&pad->object, mode) - {} - - QGstMutableCaps currentCaps() const - { return QGstMutableCaps(gst_pad_get_current_caps(pad())); } - QGstCaps queryCaps() const - { return QGstCaps(gst_pad_query_caps(pad(), nullptr)); } - - bool isLinked() const { return gst_pad_is_linked(pad()); } - bool link(const QGstPad &sink) const { return gst_pad_link(pad(), sink.pad()) == GST_PAD_LINK_OK; } - bool unlink(const QGstPad &sink) const { return gst_pad_unlink(pad(), sink.pad()); } - bool unlinkPeer() const { return unlink(peer()); } - QGstPad peer() const { return QGstPad(gst_pad_get_peer(pad()), HasRef); } - inline QGstElement parent() const; - - GstPad *pad() const { return GST_PAD_CAST(object()); } - - GstEvent *stickyEvent(GstEventType type) { return gst_pad_get_sticky_event(pad(), type, 0); } - bool sendEvent(GstEvent *event) { return gst_pad_send_event (pad(), event); } + using QGstObject::QGstObject; + QGstPad(const QGstPad &) = default; + QGstPad(QGstPad &&) noexcept = default; + + explicit QGstPad(const QGstObject &o); + explicit QGstPad(GstPad *pad, RefMode mode); + + QGstPad &operator=(const QGstPad &) = default; + QGstPad &operator=(QGstPad &&) noexcept = default; + + QGstCaps currentCaps() const; + QGstCaps queryCaps() const; + + QGstTagListHandle tags() const; + + std::optional<QPlatformMediaPlayer::TrackType> + inferTrackTypeFromName() const; // for decodebin3 etc + + bool isLinked() const; + bool link(const QGstPad &sink) const; + bool unlink(const QGstPad &sink) const; + bool unlinkPeer() const; + QGstPad peer() const; + QGstElement parent() const; + + GstPad *pad() const; + + GstEvent *stickyEvent(GstEventType type); + bool sendEvent(GstEvent *event); template<auto Member, typename T> void addProbe(T *instance, GstPadProbeType type) { - struct Impl { - static GstPadProbeReturn callback(GstPad *pad, GstPadProbeInfo *info, gpointer userData) { - return (static_cast<T *>(userData)->*Member)(QGstPad(pad, NeedsRef), info); - }; + auto callback = [](GstPad *pad, GstPadProbeInfo *info, gpointer userData) { + return (static_cast<T *>(userData)->*Member)(QGstPad(pad, NeedsRef), info); }; - gst_pad_add_probe (pad(), type, Impl::callback, instance, nullptr); + gst_pad_add_probe(pad(), type, callback, instance, nullptr); } - void doInIdleProbe(std::function<void()> work) { + template <typename Functor> + void doInIdleProbe(Functor &&work) + { struct CallbackData { QSemaphore waitDone; - std::function<void()> work; - } cd; - cd.work = work; + Functor work; + }; + + CallbackData cd{ + .waitDone = QSemaphore{}, + .work = std::forward<Functor>(work), + }; auto callback= [](GstPad *, GstPadProbeInfo *, gpointer p) { auto cd = reinterpret_cast<CallbackData*>(p); @@ -409,16 +532,14 @@ public: template<auto Member, typename T> void addEosProbe(T *instance) { - struct Impl { - static GstPadProbeReturn callback(GstPad */*pad*/, GstPadProbeInfo *info, gpointer userData) { - if (GST_EVENT_TYPE(GST_PAD_PROBE_INFO_DATA(info)) != GST_EVENT_EOS) - return GST_PAD_PROBE_PASS; - (static_cast<T *>(userData)->*Member)(); - return GST_PAD_PROBE_REMOVE; - }; + auto callback = [](GstPad *, GstPadProbeInfo *info, gpointer userData) { + if (GST_EVENT_TYPE(GST_PAD_PROBE_INFO_DATA(info)) != GST_EVENT_EOS) + return GST_PAD_PROBE_PASS; + (static_cast<T *>(userData)->*Member)(); + return GST_PAD_PROBE_REMOVE; }; - gst_pad_add_probe (pad(), GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, Impl::callback, instance, nullptr); + gst_pad_add_probe(pad(), GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, callback, instance, nullptr); } }; @@ -426,195 +547,296 @@ class QGstClock : public QGstObject { public: QGstClock() = default; - QGstClock(const QGstObject &o) - : QGstClock(GST_CLOCK(o.object())) - {} - QGstClock(GstClock *clock, RefMode mode = NeedsRef) - : QGstObject(&clock->object, mode) - {} + explicit QGstClock(const QGstObject &o); + explicit QGstClock(GstClock *clock, RefMode mode); - GstClock *clock() const { return GST_CLOCK_CAST(object()); } - - GstClockTime time() const { return gst_clock_get_time(clock()); } + GstClock *clock() const; + GstClockTime time() const; }; +class QGstPipeline; + class QGstElement : public QGstObject { public: - QGstElement() = default; - QGstElement(const QGstObject &o) - : QGstElement(GST_ELEMENT(o.object()), NeedsRef) - {} - QGstElement(GstElement *element, RefMode mode = NeedsRef) - : QGstObject(&element->object, mode) - {} - - QGstElement(const char *factory, const char *name = nullptr) - : QGstElement(gst_element_factory_make(factory, name), NeedsRef) - { - } - - bool linkFiltered(const QGstElement &next, const QGstMutableCaps &caps) - { return gst_element_link_filtered(element(), next.element(), caps.get()); } - bool link(const QGstElement &next) - { return gst_element_link(element(), next.element()); } - bool link(const QGstElement &n1, const QGstElement &n2) - { return gst_element_link_many(element(), n1.element(), n2.element(), nullptr); } - bool link(const QGstElement &n1, const QGstElement &n2, const QGstElement &n3) - { return gst_element_link_many(element(), n1.element(), n2.element(), n3.element(), nullptr); } - bool link(const QGstElement &n1, const QGstElement &n2, const QGstElement &n3, const QGstElement &n4) - { return gst_element_link_many(element(), n1.element(), n2.element(), n3.element(), n4.element(), nullptr); } - bool link(const QGstElement &n1, const QGstElement &n2, const QGstElement &n3, const QGstElement &n4, const QGstElement &n5) - { return gst_element_link_many(element(), n1.element(), n2.element(), n3.element(), n4.element(), n5.element(), nullptr); } - - void unlink(const QGstElement &next) - { gst_element_unlink(element(), next.element()); } - - QGstPad staticPad(const char *name) const { return QGstPad(gst_element_get_static_pad(element(), name), HasRef); } - QGstPad src() const { return staticPad("src"); } - QGstPad sink() const { return staticPad("sink"); } - QGstPad getRequestPad(const char *name) const - { -#if GST_CHECK_VERSION(1,19,1) - return QGstPad(gst_element_request_pad_simple(element(), name), HasRef); -#else - return QGstPad(gst_element_get_request_pad(element(), name), HasRef); -#endif - } - void releaseRequestPad(const QGstPad &pad) const { return gst_element_release_request_pad(element(), pad.pad()); } - - GstState state() const - { - GstState state; - gst_element_get_state(element(), &state, nullptr, 0); - return state; - } - GstStateChangeReturn setState(GstState state) { return gst_element_set_state(element(), state); } - bool setStateSync(GstState state) + using QGstObject::QGstObject; + + QGstElement(const QGstElement &) = default; + QGstElement(QGstElement &&) noexcept = default; + QGstElement &operator=(const QGstElement &) = default; + QGstElement &operator=(QGstElement &&) noexcept = default; + + explicit QGstElement(GstElement *element, RefMode mode); + static QGstElement createFromFactory(const char *factory, const char *name = nullptr); + static QGstElement createFromFactory(GstElementFactory *, const char *name = nullptr); + static QGstElement createFromFactory(const QGstElementFactoryHandle &, + const char *name = nullptr); + static QGstElement createFromDevice(const QGstDeviceHandle &, const char *name = nullptr); + static QGstElement createFromDevice(GstDevice *, const char *name = nullptr); + static QGstElement createFromPipelineDescription(const char *); + static QGstElement createFromPipelineDescription(const QByteArray &); + + static QGstElementFactoryHandle findFactory(const char *); + static QGstElementFactoryHandle findFactory(const QByteArray &name); + + QGstPad staticPad(const char *name) const; + QGstPad src() const; + QGstPad sink() const; + QGstPad getRequestPad(const char *name) const; + void releaseRequestPad(const QGstPad &pad) const; + + GstState state(std::chrono::nanoseconds timeout = std::chrono::seconds(0)) const; + GstStateChangeReturn setState(GstState state); + bool setStateSync(GstState state, std::chrono::nanoseconds timeout = std::chrono::seconds(1)); + bool syncStateWithParent(); + bool finishStateChange(std::chrono::nanoseconds timeout = std::chrono::seconds(5)); + + void lockState(bool locked); + bool isStateLocked() const; + + void sendEvent(GstEvent *event) const; + void sendEos() const; + + template <auto Member, typename T> + QGObjectHandlerConnection onPadAdded(T *instance) { - auto change = gst_element_set_state(element(), state); - if (change == GST_STATE_CHANGE_ASYNC) { - change = gst_element_get_state(element(), nullptr, &state, 1000*1e6 /*nano seconds*/); - } -#ifndef QT_NO_DEBUG - if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) - qWarning() << "Could not change state of" << name() << "to" << state << change; -#endif - return change == GST_STATE_CHANGE_SUCCESS; - } - bool syncStateWithParent() { return gst_element_sync_state_with_parent(element()) == TRUE; } - bool finishStateChange() - { - auto change = gst_element_get_state(element(), nullptr, nullptr, 1000*1e6 /*nano seconds*/); -#ifndef QT_NO_DEBUG - if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) - qWarning() << "Could finish change state of" << name(); -#endif - return change == GST_STATE_CHANGE_SUCCESS; - } - - void lockState(bool locked) { gst_element_set_locked_state(element(), locked); } - bool isStateLocked() const { return gst_element_is_locked_state(element()); } - - void sendEvent(GstEvent *event) const { gst_element_send_event(element(), event); } - void sendEos() const { sendEvent(gst_event_new_eos()); } - - template<auto Member, typename T> - void onPadAdded(T *instance) { - struct Impl { - static void callback(GstElement *e, GstPad *pad, gpointer userData) { - (static_cast<T *>(userData)->*Member)(QGstElement(e), QGstPad(pad, NeedsRef)); + struct Impl + { + static void callback(GstElement *e, GstPad *pad, gpointer userData) + { + (static_cast<T *>(userData)->*Member)(QGstElement(e, NeedsRef), + QGstPad(pad, NeedsRef)); }; }; - connect("pad-added", G_CALLBACK(Impl::callback), instance); + return connect("pad-added", G_CALLBACK(Impl::callback), instance); } - template<auto Member, typename T> - void onPadRemoved(T *instance) { - struct Impl { - static void callback(GstElement *e, GstPad *pad, gpointer userData) { - (static_cast<T *>(userData)->*Member)(QGstElement(e), QGstPad(pad, NeedsRef)); + template <auto Member, typename T> + QGObjectHandlerConnection onPadRemoved(T *instance) + { + struct Impl + { + static void callback(GstElement *e, GstPad *pad, gpointer userData) + { + (static_cast<T *>(userData)->*Member)(QGstElement(e, NeedsRef), + QGstPad(pad, NeedsRef)); }; }; - connect("pad-removed", G_CALLBACK(Impl::callback), instance); + return connect("pad-removed", G_CALLBACK(Impl::callback), instance); } - template<auto Member, typename T> - void onNoMorePads(T *instance) { - struct Impl { - static void callback(GstElement *e, gpointer userData) { - (static_cast<T *>(userData)->*Member)(QGstElement(e)); + template <auto Member, typename T> + QGObjectHandlerConnection onNoMorePads(T *instance) + { + struct Impl + { + static void callback(GstElement *e, gpointer userData) + { + (static_cast<T *>(userData)->*Member)(QGstElement(e, NeedsRef)); }; }; - connect("no-more-pads", G_CALLBACK(Impl::callback), instance); + return connect("no-more-pads", G_CALLBACK(Impl::callback), instance); } - GstClockTime baseTime() const { return gst_element_get_base_time(element()); } - void setBaseTime(GstClockTime time) const { gst_element_set_base_time(element(), time); } + GstClockTime baseTime() const; + void setBaseTime(GstClockTime time) const; + + GstElement *element() const; - GstElement *element() const { return GST_ELEMENT_CAST(m_object); } + QGstElement getParent() const; + QGstPipeline getPipeline() const; + void dumpPipelineGraph(const char *filename) const; }; -inline QGstElement QGstPad::parent() const +template <typename... Ts> +std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void> +qLinkGstElements(const Ts &...ts) { - return QGstElement(gst_pad_get_parent_element(pad()), HasRef); + bool link_success = [&] { + if constexpr (sizeof...(Ts) == 2) + return gst_element_link(ts.element()...); + else + return gst_element_link_many(ts.element()..., nullptr); + }(); + + if (Q_UNLIKELY(!link_success)) { + qWarning() << "qLinkGstElements: could not link elements: " + << std::initializer_list<const char *>{ + (GST_ELEMENT_NAME(ts.element()))..., + }; + } +} + +template <typename... Ts> +std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void> +qUnlinkGstElements(const Ts &...ts) +{ + if constexpr (sizeof...(Ts) == 2) + gst_element_unlink(ts.element()...); + else + gst_element_unlink_many(ts.element()..., nullptr); } class QGstBin : public QGstElement { public: - QGstBin() = default; - QGstBin(const QGstObject &o) - : QGstBin(GST_BIN(o.object()), NeedsRef) - {} - QGstBin(const char *name) - : QGstElement(gst_bin_new(name), NeedsRef) + using QGstElement::QGstElement; + QGstBin(const QGstBin &) = default; + QGstBin(QGstBin &&) noexcept = default; + QGstBin &operator=(const QGstBin &) = default; + QGstBin &operator=(QGstBin &&) noexcept = default; + + explicit QGstBin(GstBin *bin, RefMode mode = NeedsRef); + static QGstBin create(const char *name); + static QGstBin createFromFactory(const char *factory, const char *name); + static QGstBin createFromPipelineDescription(const QByteArray &pipelineDescription, + const char *name = nullptr, + bool ghostUnlinkedPads = false); + static QGstBin createFromPipelineDescription(const char *pipelineDescription, + const char *name = nullptr, + bool ghostUnlinkedPads = false); + + template <typename... Ts> + std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void> add(const Ts &...ts) { + if constexpr (sizeof...(Ts) == 1) + gst_bin_add(bin(), ts.element()...); + else + gst_bin_add_many(bin(), ts.element()..., nullptr); } - QGstBin(GstBin *bin, RefMode mode = NeedsRef) - : QGstElement(&bin->element, mode) - {} - - void add(const QGstElement &element) - { gst_bin_add(bin(), element.element()); } - void add(const QGstElement &e1, const QGstElement &e2) - { gst_bin_add_many(bin(), e1.element(), e2.element(), nullptr); } - void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3) - { gst_bin_add_many(bin(), e1.element(), e2.element(), e3.element(), nullptr); } - void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3, const QGstElement &e4) - { gst_bin_add_many(bin(), e1.element(), e2.element(), e3.element(), e4.element(), nullptr); } - void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3, const QGstElement &e4, const QGstElement &e5) - { gst_bin_add_many(bin(), e1.element(), e2.element(), e3.element(), e4.element(), e5.element(), nullptr); } - void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3, const QGstElement &e4, const QGstElement &e5, const QGstElement &e6) - { gst_bin_add_many(bin(), e1.element(), e2.element(), e3.element(), e4.element(), e5.element(), e6.element(), nullptr); } - void remove(const QGstElement &element) - { gst_bin_remove(bin(), element.element()); } - - GstBin *bin() const { return GST_BIN_CAST(m_object); } - - void addGhostPad(const QGstElement &child, const char *name) + + template <typename... Ts> + std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void> remove(const Ts &...ts) { - addGhostPad(name, child.staticPad(name)); + if constexpr (sizeof...(Ts) == 1) + gst_bin_remove(bin(), ts.element()...); + else + gst_bin_remove_many(bin(), ts.element()..., nullptr); } - void addGhostPad(const char *name, const QGstPad &pad) + + template <typename... Ts> + std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void> + stopAndRemoveElements(Ts... ts) { - gst_element_add_pad(element(), gst_ghost_pad_new(name, pad.pad())); + bool stateChangeSuccessful = (ts.setStateSync(GST_STATE_NULL) && ...); + Q_ASSERT(stateChangeSuccessful); + remove(ts...); } + + GstBin *bin() const; + + void addGhostPad(const QGstElement &child, const char *name); + void addGhostPad(const char *name, const QGstPad &pad); + + bool syncChildrenState(); + + void dumpGraph(const char *fileNamePrefix); + + QGstElement findByName(const char *); +}; + +class QGstBaseSink : public QGstElement +{ +public: + using QGstElement::QGstElement; + + explicit QGstBaseSink(GstBaseSink *, RefMode); + + QGstBaseSink(const QGstBaseSink &) = default; + QGstBaseSink(QGstBaseSink &&) noexcept = default; + QGstBaseSink &operator=(const QGstBaseSink &) = default; + QGstBaseSink &operator=(QGstBaseSink &&) noexcept = default; + + void setSync(bool); + + GstBaseSink *baseSink() const; +}; + +class QGstBaseSrc : public QGstElement +{ +public: + using QGstElement::QGstElement; + + explicit QGstBaseSrc(GstBaseSrc *, RefMode); + + QGstBaseSrc(const QGstBaseSrc &) = default; + QGstBaseSrc(QGstBaseSrc &&) noexcept = default; + QGstBaseSrc &operator=(const QGstBaseSrc &) = default; + QGstBaseSrc &operator=(QGstBaseSrc &&) noexcept = default; + + GstBaseSrc *baseSrc() const; +}; + +#if QT_CONFIG(gstreamer_app) +class QGstAppSink : public QGstBaseSink +{ +public: + using QGstBaseSink::QGstBaseSink; + + explicit QGstAppSink(GstAppSink *, RefMode); + + QGstAppSink(const QGstAppSink &) = default; + QGstAppSink(QGstAppSink &&) noexcept = default; + QGstAppSink &operator=(const QGstAppSink &) = default; + QGstAppSink &operator=(QGstAppSink &&) noexcept = default; + + static QGstAppSink create(const char *name); + + GstAppSink *appSink() const; + + void setMaxBuffers(int); +# if GST_CHECK_VERSION(1, 24, 0) + void setMaxBufferTime(std::chrono::nanoseconds); +# endif + + void setCaps(const QGstCaps &caps); + void setCallbacks(GstAppSinkCallbacks &callbacks, gpointer user_data, GDestroyNotify notify); + + QGstSampleHandle pullSample(); }; -inline QGstStructure QGValue::toStructure() const +class QGstAppSrc : public QGstBaseSrc { - if (!value || !GST_VALUE_HOLDS_STRUCTURE(value)) - return QGstStructure(); - return QGstStructure(gst_value_get_structure(value)); +public: + using QGstBaseSrc::QGstBaseSrc; + + explicit QGstAppSrc(GstAppSrc *, RefMode); + + QGstAppSrc(const QGstAppSrc &) = default; + QGstAppSrc(QGstAppSrc &&) noexcept = default; + QGstAppSrc &operator=(const QGstAppSrc &) = default; + QGstAppSrc &operator=(QGstAppSrc &&) noexcept = default; + + static QGstAppSrc create(const char *name); + + GstAppSrc *appSrc() const; + + void setCallbacks(GstAppSrcCallbacks &callbacks, gpointer user_data, GDestroyNotify notify); + + GstFlowReturn pushBuffer(GstBuffer *); // take ownership +}; + +#endif + +inline GstClockTime qGstClockTimeFromChrono(std::chrono::nanoseconds ns) +{ + return ns.count(); } -inline QGstCaps QGValue::toCaps() const +QString qGstErrorMessageCannotFindElement(std::string_view element); + +template <typename Arg, typename... Args> +std::optional<QString> qGstErrorMessageIfElementsNotAvailable(const Arg &arg, Args... args) { - if (!value || !GST_VALUE_HOLDS_CAPS(value)) - return QGstCaps(); - return QGstCaps(gst_value_get_caps(value)); + QGstElementFactoryHandle factory = QGstElement::findFactory(arg); + if (!factory) + return qGstErrorMessageCannotFindElement(arg); + + if constexpr (sizeof...(args) != 0) + return qGstErrorMessageIfElementsNotAvailable(args...); + else + return std::nullopt; } QT_END_NAMESPACE |