diff options
author | Mathias Hasselmann <mathias.hasselmann@kdab.com> | 2013-10-19 21:48:48 +0200 |
---|---|---|
committer | Mathias Hasselmann <mathias.hasselmann@kdab.com> | 2013-10-19 23:18:12 +0200 |
commit | 05c12c6d9af430c3637d43866059e88af28c034b (patch) | |
tree | b67b6e8eacb2fb73824b7c9a530e7ff64f18a8e3 | |
download | qtquickstreamer-05c12c6d9af430c3637d43866059e88af28c034b.tar.gz qtquickstreamer-05c12c6d9af430c3637d43866059e88af28c034b.tar.xz |
Initial version
-rw-r--r-- | .gitignore | 8 | ||||
-rw-r--r-- | quickstreamer.pro | 3 | ||||
-rw-r--r-- | src/object.cpp | 495 | ||||
-rw-r--r-- | src/object.h | 58 | ||||
-rw-r--r-- | src/plugin.cpp | 27 | ||||
-rw-r--r-- | src/plugin.h | 21 | ||||
-rw-r--r-- | src/qmldir | 2 | ||||
-rw-r--r-- | src/qquickstreamerplugin.json | 1 | ||||
-rw-r--r-- | src/qtquickstreamerplugin.pro | 27 | ||||
-rw-r--r-- | src/src.pro | 4 | ||||
-rw-r--r-- | tests/tests.pro | 4 | ||||
-rw-r--r-- | tests/tst_wrapping/tst_wrapping.pro | 14 | ||||
-rw-r--r-- | tests/tst_wrapping/tst_wrappingtest.cpp | 85 |
13 files changed, 749 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1817e5b --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.o +*.user* +Makefile* +*.moc +moc_*.cpp +*.so +.*.sw? +/tests/tst_wrapping/tst_wrapping diff --git a/quickstreamer.pro b/quickstreamer.pro new file mode 100644 index 0000000..3ca2c54 --- /dev/null +++ b/quickstreamer.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS = src tests diff --git a/src/object.cpp b/src/object.cpp new file mode 100644 index 0000000..31c6504 --- /dev/null +++ b/src/object.cpp @@ -0,0 +1,495 @@ +#include "object.h" + +#include <5.0.1/QtCore/private/qmetaobjectbuilder_p.h> + +#include <QVector> + +#include <gst/gstobject.h> + +#include <functional> + +namespace QQuickStreamer { +namespace Private { + +struct PropertyInfo +{ + typedef std::function<void(GstObject *target, QVariant *value)> ReadDelegate; + typedef std::function<void(GstObject *target, const QVariant &value)> WriteDelegate; + + QByteArray name; + ReadDelegate read; + WriteDelegate write; +}; + +struct TypeInfo +{ + TypeInfo() + : parent(Q_NULLPTR) + , metaObject(Q_NULLPTR) + { + } + + int readProperty(GstObject *object, int id, QVariant *value) const + { + if (parent) + id = parent->readProperty(object, id, value); + if (id < 0) + return id; + + if (id < properties.size()) + properties.at(id).read(object, value); + + return id - properties.size(); + } + + int writeProperty(GstObject *object, int id, const QVariant &value) const + { + if (parent) + id = parent->writeProperty(object, id, value); + if (id < 0) + return id; + + if (id < properties.size()) + properties.at(id).write(object, value); + + return id - properties.size(); + } + + const TypeInfo *parent; + const QMetaObject *metaObject; + QVector<PropertyInfo> properties; +}; + +static QHash<GType, TypeInfo *> cachedTypeInfo; + +static const TypeInfo *makeTypeInfo(GType gtype); + +static GQuark dataKeyQuark() +{ + static const GQuark quark = g_quark_from_static_string("qt-quick-streamer-object-data-key-quark"); + return quark; +} + +void *findWrapper(void *object) +{ + return g_object_get_qdata(G_OBJECT(object), dataKeyQuark()); +} + +static const TypeInfo *findTypeInfo(GType gtype) +{ + Q_ASSERT(g_type_is_a(gtype, G_TYPE_OBJECT)); + const auto *typeInfo = Private::cachedTypeInfo.value(gtype); + + if (typeInfo == Q_NULLPTR) + typeInfo = makeTypeInfo(gtype); + + return typeInfo; +} + +static bool isDash(char ch) +{ + return ch == '-' || ch == '_'; +} + +static QByteArray camelCaseName(const QByteArray &input) +{ + int nDashes = 0; + + for (char ch: input) { + if (isDash(ch)) + ++nDashes; + } + + QByteArray output; + + output.reserve(input.size() - nDashes); + + bool wordBoundary = false; + + for (char ch: input) { + if (isDash(ch)) { + wordBoundary = true; + continue; + } + + if (wordBoundary) { + output += std::toupper(ch, std::locale::classic()); + wordBoundary = false; + } else { + output += std::tolower(ch, std::locale::classic()); + } + } + + return output; +} + +template<typename T> +static QByteArray metaTypeName() +{ + static const QByteArray name = QMetaType::typeName(qMetaTypeId<T>()); + return name; +} + +static QByteArray metaTypeName(GType type) +{ + switch(type) { + case G_TYPE_CHAR: return metaTypeName<char>(); + case G_TYPE_UCHAR: return metaTypeName<uchar>(); + case G_TYPE_BOOLEAN: return metaTypeName<bool>(); + case G_TYPE_INT: return metaTypeName<int>(); + case G_TYPE_UINT: return metaTypeName<uint>(); + case G_TYPE_LONG: return metaTypeName<long>(); + case G_TYPE_ULONG: return metaTypeName<ulong>(); + case G_TYPE_INT64: return metaTypeName<qint64>(); + case G_TYPE_UINT64: return metaTypeName<quint64>(); + case G_TYPE_FLOAT: return metaTypeName<float>(); + case G_TYPE_DOUBLE: return metaTypeName<double>(); + case G_TYPE_STRING: return metaTypeName<QString>(); + //case G_TYPE_ENUM G_TYPE_MAKE_FUNDAMENTAL (12) + //case G_TYPE_FLAGS G_TYPE_MAKE_FUNDAMENTAL (13) + //case G_TYPE_POINTER G_TYPE_MAKE_FUNDAMENTAL (17) + //case G_TYPE_BOXED G_TYPE_MAKE_FUNDAMENTAL (18) + //case G_TYPE_PARAM G_TYPE_MAKE_FUNDAMENTAL (19) + //case G_TYPE_OBJECT G_TYPE_MAKE_FUNDAMENTAL (20) + //case G_TYPE_VARIANT G_TYPE_MAKE_FUNDAMENTAL (21) + } + + qWarning("Ignoring unsupported GType: %s", g_type_name(type)); + return {}; +} + +static void toVariant(const GValue *value, QVariant *variant) +{ + switch(G_VALUE_TYPE(value)) + { + case G_TYPE_CHAR: + variant->setValue(g_value_get_schar(value)); + break; + + case G_TYPE_UCHAR: + variant->setValue(g_value_get_uchar(value)); + break; + + case G_TYPE_BOOLEAN: + variant->setValue<bool>(g_value_get_boolean(value)); + break; + + case G_TYPE_INT: + variant->setValue(g_value_get_int(value)); + break; + + case G_TYPE_UINT: + variant->setValue(g_value_get_uint(value)); + break; + + case G_TYPE_LONG: + variant->setValue(g_value_get_long(value)); + break; + + case G_TYPE_ULONG: + variant->setValue(g_value_get_ulong(value)); + break; + + case G_TYPE_INT64: + variant->setValue(g_value_get_int64(value)); + break; + + case G_TYPE_UINT64: + variant->setValue(g_value_get_uint64(value)); + break; + + case G_TYPE_FLOAT: + variant->setValue(g_value_get_float(value)); + break; + + case G_TYPE_DOUBLE: + variant->setValue(g_value_get_double(value)); + break; + + case G_TYPE_STRING: + variant->setValue(QString::fromUtf8(g_value_get_string(value))); + break; + + //case G_TYPE_ENUM G_TYPE_MAKE_FUNDAMENTAL (12) + //case G_TYPE_FLAGS G_TYPE_MAKE_FUNDAMENTAL (13) + //case G_TYPE_POINTER G_TYPE_MAKE_FUNDAMENTAL (17) + //case G_TYPE_BOXED G_TYPE_MAKE_FUNDAMENTAL (18) + //case G_TYPE_PARAM G_TYPE_MAKE_FUNDAMENTAL (19) + //case G_TYPE_OBJECT G_TYPE_MAKE_FUNDAMENTAL (20) + //case G_TYPE_VARIANT G_TYPE_MAKE_FUNDAMENTAL (21) + + default: + qWarning("Cannot convert unsupported GValue type: %s", G_VALUE_TYPE_NAME(value)); + break; + } +} + +static void toValue(const QVariant &variant, GValue *value) +{ + switch(G_VALUE_TYPE(value)) + { + case G_TYPE_CHAR: + g_value_set_schar(value, variant.value<char>()); + break; + + case G_TYPE_UCHAR: + g_value_set_uchar(value, variant.value<uchar>()); + break; + + case G_TYPE_BOOLEAN: + g_value_set_boolean(value, variant.value<bool>()); + break; + + case G_TYPE_INT: + g_value_set_int(value, variant.value<int>()); + break; + + case G_TYPE_UINT: + g_value_set_uint(value, variant.value<uint>()); + break; + + case G_TYPE_LONG: + g_value_set_long(value, variant.value<long>()); + break; + + case G_TYPE_ULONG: + g_value_set_ulong(value, variant.value<ulong>()); + break; + + case G_TYPE_INT64: + g_value_set_int64(value, variant.value<qint64>()); + break; + + case G_TYPE_UINT64: + g_value_set_uint64(value, variant.value<quint64>()); + break; + + case G_TYPE_FLOAT: + g_value_set_float(value, variant.value<float>()); + break; + + case G_TYPE_DOUBLE: + g_value_set_double(value, variant.value<double>()); + break; + + case G_TYPE_STRING: + g_value_set_string(value, variant.value<QString>().toUtf8().constData()); + break; + + //case G_TYPE_ENUM G_TYPE_MAKE_FUNDAMENTAL (12) + //case G_TYPE_FLAGS G_TYPE_MAKE_FUNDAMENTAL (13) + //case G_TYPE_POINTER G_TYPE_MAKE_FUNDAMENTAL (17) + //case G_TYPE_BOXED G_TYPE_MAKE_FUNDAMENTAL (18) + //case G_TYPE_PARAM G_TYPE_MAKE_FUNDAMENTAL (19) + //case G_TYPE_OBJECT G_TYPE_MAKE_FUNDAMENTAL (20) + //case G_TYPE_VARIANT G_TYPE_MAKE_FUNDAMENTAL (21) + + default: + qWarning("Cannot convert unsupported GValue type: %s", G_VALUE_TYPE_NAME(value)); + break; + } +} + +static void readGObjectProperty(GstObject *target, const QByteArray &propertyName, GType propertyType, QVariant *value) +{ + GValue gvalue = G_VALUE_INIT; + g_value_init(&gvalue, propertyType); + g_object_get_property(G_OBJECT(target), propertyName.constData(), &gvalue); + toVariant(&gvalue, value); + g_value_unset(&gvalue); +} + +static void writeGObjectProperty(GstObject *target, const QByteArray &propertyName, GType propertyType, const QVariant &value) +{ + GValue gvalue = G_VALUE_INIT; + g_value_init(&gvalue, propertyType); + toValue(value, &gvalue); + g_object_set_property(G_OBJECT(target), propertyName.constData(), &gvalue); + g_value_unset(&gvalue); +} + +static void readNothing(GstObject *target, const QByteArray &name, QVariant *) +{ + qWarning("Cannot read \"%s::%s\" which is not declared readable", + G_OBJECT_TYPE_NAME(target), name.constData()); +} + +static void writeNothing(GstObject *target, const QByteArray &name, const QVariant &) +{ + qWarning("Cannot write \"%s::%s\" which is not declared writable", + G_OBJECT_TYPE_NAME(target), name.constData()); +} + +template<typename... Args> +PropertyInfo::ReadDelegate bindReadDelegate(Args... args) +{ + return std::bind(args...); +} + +template<typename... Args> +PropertyInfo::WriteDelegate bindWriteDelegate(Args... args) +{ + return std::bind(args...); +} + +static const TypeInfo *makeTypeInfo(GType gtype) +{ + QScopedPointer<TypeInfo> typeInfo(new TypeInfo); + + QMetaObjectBuilder objectBuilder; + objectBuilder.setClassName(g_type_name(gtype)); + + if (gtype != G_TYPE_OBJECT) + typeInfo->parent = findTypeInfo(g_type_parent(gtype)); + + objectBuilder.setSuperClass(typeInfo->parent ? typeInfo->parent->metaObject + : &QObject::staticMetaObject); + + const auto gobject_class = static_cast<GObjectClass *>(g_type_class_ref(gtype)); + + uint nPSpecs = 0; + auto *const pSpecs = g_object_class_list_properties(gobject_class, &nPSpecs); + + for (uint i = 0; i < nPSpecs; ++i) { + const auto *const pSpec = pSpecs[i]; + + if (pSpec->owner_type != gtype) + continue; + + const auto &name = camelCaseName(pSpec->name); + const auto &type = metaTypeName(pSpec->value_type); + + if (pSpec->flags & G_PARAM_DEPRECATED) { + qWarning("Ignoring deprecated property %s::%s", g_type_name(gtype), pSpec->name); + continue; + } + + if (type.isEmpty()) { + qWarning("Ignoring property %s::%s of unsupported type %s", + g_type_name(gtype), pSpec->name, g_type_name(pSpec->value_type)); + continue; + } + + qDebug("Adding %s::%s", g_type_name(gtype), pSpec->name); + + auto notifier = objectBuilder.addSignal(name + QByteArrayLiteral("Changed()")); + auto property = objectBuilder.addProperty(name, type, notifier.index()); + + property.setReadable(pSpec->flags & G_PARAM_READABLE); + property.setWritable(pSpec->flags & G_PARAM_WRITABLE); + + using namespace std::placeholders; + + const auto readDelegate = property.isReadable() + ? bindReadDelegate(&readGObjectProperty, _1, pSpec->name, pSpec->value_type, _2) + : bindReadDelegate(&readNothing, _1, pSpec->name, _2); + const auto writeDelegate = property.isWritable() + ? bindWriteDelegate(&writeGObjectProperty, _1, pSpec->name, pSpec->value_type, _2) + : bindWriteDelegate(&writeNothing, _1, pSpec->name, _2); + + typeInfo->properties.append({pSpec->name, readDelegate, writeDelegate}); + } + + g_free(pSpecs); + + uint nSignalIds = 0; + auto *const signalIds = g_signal_list_ids(gtype, &nSignalIds); + + for (uint i = 0; i < nSignalIds; ++i) { + GSignalQuery query; + g_signal_query(signalIds[i], &query); + Q_ASSERT(signalIds[i] == query.signal_id); + qDebug("found signal: %s::%s", g_type_name(gtype), query.signal_name); + } + + g_type_class_unref(gobject_class); + + typeInfo->metaObject = objectBuilder.toMetaObject(); + cachedTypeInfo.insert(gtype, typeInfo.data()); + return typeInfo.take(); +} + +} // namespace Private + +Object::Object(GstObject *target) + : QObject() + , m_target(target) +{ + Q_ASSERT(m_target != Q_NULLPTR); + g_object_ref_sink(m_target); + g_object_set_qdata(G_OBJECT(target), Private::dataKeyQuark(), this); +} + +Object::~Object() +{ + g_object_set_qdata(G_OBJECT(m_target), Private::dataKeyQuark(), Q_NULLPTR); + g_object_unref(m_target); +} + +const QMetaObject *Object::metaObject() const +{ + return Private::findTypeInfo(G_OBJECT_TYPE(m_target))->metaObject; +} + +void *Object::qt_metacast(const char *className) +{ + qDebug("%s: %s", Q_FUNC_INFO, className); + + if (className == Q_NULLPTR) + return Q_NULLPTR; + + if (strcmp(className, G_OBJECT_TYPE_NAME(m_target)) == 0) + return this; + + return QObject::qt_metacast(className); +} + +int Object::qt_metacall(QMetaObject::Call call, int id, void **args) +{ + qDebug("%s: call=%d, id=%d", Q_FUNC_INFO, call, id); + id = QObject::qt_metacall(call, id, args); + + if (id < 0) + return id; + + qDebug("-> id=%d", id); + + switch(call) { + case QMetaObject::ReadProperty: + id = readProperty(id, static_cast<QVariant *>(args[0])); + break; + + case QMetaObject::WriteProperty: + id = writeProperty(id, *static_cast<QVariant *>(args[0])); + break; + + case QMetaObject::InvokeMetaMethod: + case QMetaObject::ResetProperty: + case QMetaObject::QueryPropertyDesignable: + case QMetaObject::QueryPropertyScriptable: + case QMetaObject::QueryPropertyStored: + case QMetaObject::QueryPropertyEditable: + case QMetaObject::QueryPropertyUser: + case QMetaObject::CreateInstance: + case QMetaObject::IndexOfMethod: + case QMetaObject::RegisterPropertyMetaType: + case QMetaObject::RegisterMethodArgumentMetaType: + break; + } + + return id; +} + +int Object::readProperty(int id, QVariant *value) +{ + const auto *const typeInfo = Private::findTypeInfo(G_OBJECT_TYPE(m_target)); + return typeInfo->readProperty(m_target, id, value); +} + +int Object::writeProperty(int id, const QVariant &value) +{ + const auto *const typeInfo = Private::findTypeInfo(G_OBJECT_TYPE(m_target)); + return typeInfo->writeProperty(m_target, id, value); +} + +} // namespace QQuickStreamer diff --git a/src/object.h b/src/object.h new file mode 100644 index 0000000..43fe227 --- /dev/null +++ b/src/object.h @@ -0,0 +1,58 @@ +#ifndef QQUICKSTREAMER_OBJECT_H +#define QQUICKSTREAMER_OBJECT_H + +#include <QObject> + +typedef struct _GstObject GstObject; + +namespace QQuickStreamer { + +class Object : public QObject +{ +public: + Q_OBJECT_CHECK + + template<class Wrapper, class Target> + friend Wrapper *wrap(Target *); + +protected: + explicit Object(GstObject *target); + +public: + ~Object(); + + const QMetaObject *metaObject() const Q_DECL_OVERRIDE; + void *qt_metacast(const char *classname) Q_DECL_OVERRIDE; + int qt_metacall(QMetaObject::Call call, int id, void **args) Q_DECL_OVERRIDE; + +protected: + int readProperty(int id, QVariant *value); + int writeProperty(int id, const QVariant &value); + +private: + GstObject *const m_target; +}; + +namespace Private { + +void *findWrapper(void *object); + +} + +template<class Wrapper, class Target> +inline Wrapper *wrap(Target *target) +{ + if (target == Q_NULLPTR) + return Q_NULLPTR; + + Wrapper *wrapper = static_cast<Wrapper *>(Private::findWrapper(target)); + + if (wrapper == Q_NULLPTR) + wrapper = new Wrapper(target); + + return wrapper; +} + +} // namespace QQuickStreamer + +#endif // QQUICKSTREAMEROBJECT_H diff --git a/src/plugin.cpp b/src/plugin.cpp new file mode 100644 index 0000000..3693010 --- /dev/null +++ b/src/plugin.cpp @@ -0,0 +1,27 @@ +#include "plugin.h" +//#include "qquickstreamerpipeline.h" + +#include <QtQml> + +namespace QQuickStreamer { + +static const auto NAMESPACE_URI = QByteArrayLiteral("QtQuickStreamer"); + +Plugin::Plugin(QObject *parent) + : QQmlExtensionPlugin(parent) +{ +} + +void Plugin::registerTypes(const char *uri) +{ + Q_ASSERT(uri == NAMESPACE_URI); + + /* + // @uri com.kdab.QtQuickStreamer + qmlRegisterUncreatableType<QQuickStreamerElement>(uri, 1, 0, "Element", QStringLiteral("This is an abstract type")); + qmlRegisterType<QQuickStreamerBin>(uri, 1, 0, "Bin"); + qmlRegisterType<QQuickStreamerPipeline>(uri, 1, 0, "Pipeline"); + */ +} + +} // namespace QQuickStreamer diff --git a/src/plugin.h b/src/plugin.h new file mode 100644 index 0000000..bf5b747 --- /dev/null +++ b/src/plugin.h @@ -0,0 +1,21 @@ +#ifndef QQUICKSTREAMER_PLUGIN_H +#define QQUICKSTREAMER_PLUGIN_H + +#include <QQmlExtensionPlugin> + +namespace QQuickStreamer { + +class Plugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface" FILE "qquickstreamerplugin.json") + +public: + explicit Plugin(QObject *parent = Q_NULLPTR); + + void registerTypes(const char *uri) Q_DECL_OVERRIDE; +}; + +} // namespace QQuickStreamer + +#endif // QQUICKSTREAMER_PLUGIN_H diff --git a/src/qmldir b/src/qmldir new file mode 100644 index 0000000..3139775 --- /dev/null +++ b/src/qmldir @@ -0,0 +1,2 @@ +module QtQuickStreamer +plugin qtquickstreamerplugin diff --git a/src/qquickstreamerplugin.json b/src/qquickstreamerplugin.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/src/qquickstreamerplugin.json @@ -0,0 +1 @@ +{} diff --git a/src/qtquickstreamerplugin.pro b/src/qtquickstreamerplugin.pro new file mode 100644 index 0000000..bd404bd --- /dev/null +++ b/src/qtquickstreamerplugin.pro @@ -0,0 +1,27 @@ +TEMPLATE = lib +CONFIG += plugin c++11 +#DESTDIR = org/qt-project/QtQuickStreamer + +QT += qml + +CONFIG += link_pkgconfig +PKGCONFIG += gstreamer-1.0 + +SOURCES = \ + plugin.cpp \ + object.cpp + +# qquickstreamerelement.cpp \ +# qquickstreamerbin.cpp \ +# qquickstreamerpipeline.cpp + +OTHER_FILES = \ + qmldir + +HEADERS += \ + plugin.h \ + object.h + +# qquickstreamerelement.h \ +# qquickstreamerbin.h \ +# qquickstreamerpipeline.h diff --git a/src/src.pro b/src/src.pro new file mode 100644 index 0000000..37dfbd1 --- /dev/null +++ b/src/src.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +SUBDIRS = \ + qtquickstreamerplugin.pro diff --git a/tests/tests.pro b/tests/tests.pro new file mode 100644 index 0000000..d9d63e7 --- /dev/null +++ b/tests/tests.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +SUBDIRS += \ + tst_wrapping diff --git a/tests/tst_wrapping/tst_wrapping.pro b/tests/tst_wrapping/tst_wrapping.pro new file mode 100644 index 0000000..ab907c5 --- /dev/null +++ b/tests/tst_wrapping/tst_wrapping.pro @@ -0,0 +1,14 @@ +TEMPLATE = app + +QT += testlib +QT -= gui + +CONFIG += c++11 link_pkgconfig +PKGCONFIG += gstreamer-1.0 + +LIBS += -Wl,-rpath,$$OUT_PWD/../../src -L../../src -lqtquickstreamerplugin + +INCLUDEPATH += ../../src + +SOURCES += tst_wrappingtest.cpp +DEFINES += SRCDIR=\\\"$$PWD/\\\" diff --git a/tests/tst_wrapping/tst_wrappingtest.cpp b/tests/tst_wrapping/tst_wrappingtest.cpp new file mode 100644 index 0000000..4e0a2d2 --- /dev/null +++ b/tests/tst_wrapping/tst_wrappingtest.cpp @@ -0,0 +1,85 @@ +#include <QString> +#include <QtTest> + +#include "object.h" + +#include <gst/gst.h> + +class WrappingTest : public QObject +{ + Q_OBJECT + +public: + WrappingTest() + { + gst_init(Q_NULLPTR, Q_NULLPTR); + } + +private slots: + void testCase1(); + + void elementNameProperty() + { + GstElement *const target = gst_pipeline_new(Q_NULLPTR); + auto *wrapper = QQuickStreamer::wrap<QQuickStreamer::Object>(GST_OBJECT(target)); + QVERIFY(wrapper != Q_NULLPTR); + + QSignalSpy spy(wrapper, SIGNAL(nameChanged())); + + QCOMPARE(wrapper->property("name").toString(), QStringLiteral("pipeline0")); + QCOMPARE(spy.count(), 0); + + wrapper->setProperty("name", QStringLiteral("dummy0")); + + QCOMPARE(wrapper->property("name").toString(), QStringLiteral("dummy0")); + QCOMPARE(spy.count(), 1); + + wrapper->setProperty("name", QStringLiteral("dummy1")); + + QCOMPARE(wrapper->property("name").toString(), QStringLiteral("dummy1")); + QCOMPARE(spy.count(), 2); + } +}; + +void WrappingTest::testCase1() +{ + GstElement *const target = gst_pipeline_new("wrapping_test"); + + auto *wrapper1 = QQuickStreamer::wrap<QQuickStreamer::Object>(GST_OBJECT(target)); + QVERIFY(wrapper1 != Q_NULLPTR); + + auto *wrapper2 = QQuickStreamer::wrap<QQuickStreamer::Object>(GST_OBJECT(target)); + QCOMPARE(wrapper1, wrapper2); + + auto *metaObject = wrapper1->metaObject(); + QVERIFY(metaObject != Q_NULLPTR); + + qDebug() << G_OBJECT_TYPE_NAME(target); + qDebug() << metaObject->className() + << metaObject->propertyCount() + << metaObject->methodCount() + << metaObject->enumeratorCount(); + + for (int i = 0; i < metaObject->propertyCount(); ++i) { + qDebug() << "P" << i << metaObject->property(i).name() + << metaObject->property(i).typeName() + << (metaObject->property(i).isReadable() ? "read" : "") + << (metaObject->property(i).isWritable() ? "write" : "") + << (metaObject->property(i).isEnumType() ? "emum" : "") + << (metaObject->property(i).isFlagType() ? "flag" : ""); + QVERIFY(metaObject->property(i).hasNotifySignal()); + QCOMPARE(i, metaObject->indexOfProperty(metaObject->property(i).name())); + } + + for (int i = 0; i < metaObject->methodCount(); ++i) { + qDebug() << "M" << i << metaObject->method(i).methodSignature() + << metaObject->method(i).methodType(); + } + + for (int i = 0; i < metaObject->enumeratorCount(); ++i) + qDebug() << "E" << i << metaObject->enumerator(i).name(); +} + +QTEST_MAIN(WrappingTest) + +#include "tst_wrappingtest.moc" |