diff options
author | Mathias Hasselmann <mathias.hasselmann@kdab.com> | 2013-11-13 08:55:20 +0100 |
---|---|---|
committer | Mathias Hasselmann <mathias.hasselmann@kdab.com> | 2013-11-13 08:55:20 +0100 |
commit | ea170b8ec4567ecd55cbcf9dee5b4631845a9aaf (patch) | |
tree | d1b9d6394450571c86e7a5fbc5882e9dfef9f2d8 | |
parent | d29e3f0f4647f20e6bd5a7fb6d7d0ed4ca5bd8c9 (diff) |
Support enumeration types
-rw-r--r-- | src/QuickStreamer/QuickStreamer.pro | 3 | ||||
-rw-r--r-- | src/QuickStreamer/item.cpp | 296 | ||||
-rw-r--r-- | src/QuickStreamer/item.h | 1 | ||||
-rw-r--r-- | src/QuickStreamer/objectreference.h | 91 | ||||
-rw-r--r-- | src/QuickStreamer/plugin.cpp | 8 | ||||
-rw-r--r-- | src/examples/piano/qml/Piano.qml | 7 | ||||
-rw-r--r-- | src/examples/piano/qml/PianoKey.qml | 2 | ||||
-rw-r--r-- | tests/tst_metaobject/tst_metaobject.pro | 4 | ||||
-rw-r--r-- | tests/tst_metaobject/tst_metaobjecttest.cpp | 97 | ||||
-rw-r--r-- | tests/tst_qml/tst_quickstreamer.qml | 35 |
10 files changed, 438 insertions, 106 deletions
diff --git a/src/QuickStreamer/QuickStreamer.pro b/src/QuickStreamer/QuickStreamer.pro index 7355ae2..f8fd2ce 100644 --- a/src/QuickStreamer/QuickStreamer.pro +++ b/src/QuickStreamer/QuickStreamer.pro @@ -15,7 +15,8 @@ SOURCES = \ HEADERS = \ plugin.h \ item.h \ - itempads.h + itempads.h \ + objectreference.h OTHER_FILES = \ quickstreamerplugin.json diff --git a/src/QuickStreamer/item.cpp b/src/QuickStreamer/item.cpp index 863243a..34e5d31 100644 --- a/src/QuickStreamer/item.cpp +++ b/src/QuickStreamer/item.cpp @@ -1,5 +1,6 @@ #include "item.h" #include "itempads.h" +#include "objectreference.h" #include <gst/gst.h> @@ -12,7 +13,15 @@ namespace QQuickStreamer { namespace Private { -static const GQuark quickStreamItemQuark = g_quark_from_static_string("quick-streamer-item-quark"); +static const GQuark ITEM_DATA_QUARK = g_quark_from_static_string("quick-streamer-item-quark"); + +static const auto CLASS_NAME_PREFIX = QByteArrayLiteral("Gst"); // FIXME: what about GdkPixbuf, Clutter, GIO... namespaces? +static const auto ENUMERATOR_TYPE_SUFFIX = QByteArrayLiteral("::Values"); + +// FIXME: constants.h or pass by argument +static const auto NAMESPACE_URI = QByteArrayLiteral("QuickStreamer"); +const static int MAJOR_VERSION = 1; +const static int MINOR_VERSION = 0; class ObjectLocker { @@ -51,6 +60,8 @@ static bool isDash(char ch) static QByteArray toCamelCase(const QByteArray &input, char (*convertFirst)(char, const std::locale &)) { + Q_ASSERT(not input.isEmpty()); + int nDashes = 0; for (char ch: input) { @@ -80,7 +91,7 @@ static QByteArray toCamelCase(const QByteArray &input, output += std::toupper(*it, loc); wordBoundary = false; } else { - output += std::tolower(*it, loc); + output += *it; } } @@ -89,13 +100,10 @@ static QByteArray toCamelCase(const QByteArray &input, static QByteArray makeClassName(GType type) { - QByteArray typeName = g_type_name(type); + Q_ASSERT(type != G_TYPE_INVALID); - // Auto-generating element factories have such bad names... - if (std::islower(typeName.at(0))) - typeName = toCamelCase(typeName, std::toupper); - - static const auto CLASS_NAME_PREFIX = QByteArrayLiteral("Gst"); + // Auto-generated element factories and several enum types have such bad names... + QByteArray typeName = toCamelCase(g_type_name(type), std::toupper); if (not typeName.startsWith(CLASS_NAME_PREFIX)) typeName = CLASS_NAME_PREFIX + typeName; @@ -103,6 +111,107 @@ static QByteArray makeClassName(GType type) return typeName; } +template<class T> struct EnumeratorTypeHelper +{ + typedef int ValueType; + static const bool IsFlags = false; +}; + +template<> struct EnumeratorTypeHelper<GFlagsClass> +{ + typedef uint ValueType; + static const bool IsFlags = true; +}; + +template<typename ClassType> +static void registerEnumeratorType(GType type, const QByteArray &typeName) +{ + // FIXME: register with as TypeInfo + const ObjectReference<ClassType> enumClass(type); + + QMetaObjectBuilder objectBuilder; + objectBuilder.setClassName(typeName); + + auto enumBuilder = objectBuilder.addEnumerator(QByteArrayLiteral("Values")); + enumBuilder.setIsFlag(EnumeratorTypeHelper<ClassType>::IsFlags); + + for (uint i = 0; i < enumClass->n_values; ++i) { + enumBuilder.addKey(toCamelCase(enumClass->values[i].value_nick, std::toupper), + enumClass->values[i].value); + } + + typedef QtMetaTypePrivate::QMetaTypeFunctionHelper<QObject *> ScopeMetaTypeHelper; + static const QMetaType::TypeFlags scopeTypeFlags(QtPrivate::QMetaTypeTypeFlags<QObject>::Flags); + + typedef typename EnumeratorTypeHelper<ClassType>::ValueType ValueType; + typedef QtMetaTypePrivate::QMetaTypeFunctionHelper<ValueType> ValueMetaTypeHelper; + static const QMetaType::TypeFlags valueTypeFlags(QtPrivate::QMetaTypeTypeFlags<ValueType>::Flags); + + const auto *const metaObject = objectBuilder.toMetaObject(); + + const int scopeTypeId = QMetaType::registerNormalizedType(typeName, + ScopeMetaTypeHelper::Delete, + ScopeMetaTypeHelper::Create, + ScopeMetaTypeHelper::Destruct, + ScopeMetaTypeHelper::Construct, + sizeof(QObject *), + scopeTypeFlags, + metaObject); + + const int valueTypeId = QMetaType::registerNormalizedType(typeName + ENUMERATOR_TYPE_SUFFIX, + ValueMetaTypeHelper::Delete, + ValueMetaTypeHelper::Create, + ValueMetaTypeHelper::Destruct, + ValueMetaTypeHelper::Construct, + sizeof(ValueType), + valueTypeFlags, + Q_NULLPTR); + + if (scopeTypeId == -1 || valueTypeId == -1) + qFatal("Cannot register metatype for enum/flags type %s", typeName.constData()); + + const char *elementName; + + if (type == GST_TYPE_STATE) { + // Avoid name clash with QtQuick/2.0/Component.State + elementName = "ElementState"; + } else { + elementName = qstrdup(typeName.mid(CLASS_NAME_PREFIX.length()).constData()); // FIXME: store in typeinfo + } + + QQmlPrivate::RegisterType qmlType = { + 1, + + scopeTypeId, + 0, 0, Q_NULLPTR, + QStringLiteral("Cannot create instances of enumeration scopes"), + + NAMESPACE_URI, MAJOR_VERSION, MINOR_VERSION, + elementName, metaObject, + + QQmlPrivate::attachedPropertiesFunc<QObject>(), + QQmlPrivate::attachedPropertiesMetaObject<QObject>(), + + QQmlPrivate::StaticCastSelector<QObject, QQmlParserStatus>::cast(), + QQmlPrivate::StaticCastSelector<QObject, QQmlPropertyValueSource>::cast(), + QQmlPrivate::StaticCastSelector<QObject, QQmlPropertyValueInterceptor>::cast(), + + 0, 0, + + 0, + 0 + }; + + if (not QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &qmlType)) + qFatal("Cannot register QML type for enum/flags type %s", typeName.constData()); +} + +static void registerBoxedType(GType type, const QByteArray &typeName) +{ + // FIXME + qDebug("FIXME: register boxed type %s (aka. %s)", typeName.constData(), g_type_name(type)); +} + template<typename T> static QByteArray metaTypeName() { @@ -125,88 +234,115 @@ static QByteArray metaTypeName(GType type) 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_POINTER: return metaTypeName<void *>(); //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)); + const QByteArray &typeName = makeClassName(type); + + if (QMetaType::type(typeName.constData())) + return typeName; + + if (g_type_is_a(type, G_TYPE_ENUM)) { + registerEnumeratorType<GEnumClass>(type, typeName); + return typeName + ENUMERATOR_TYPE_SUFFIX; + } + + if (g_type_is_a(type, G_TYPE_FLAGS)) { + registerEnumeratorType<GFlagsClass>(type, typeName); + return typeName + ENUMERATOR_TYPE_SUFFIX; + } + + if (g_type_is_a(type, G_TYPE_BOXED)) { + registerBoxedType(type, typeName); + return typeName; + } + + qWarning("FIXME: add support for %s (aka. %s)", typeName.constData(), g_type_name(type)); + return {}; } -static void readValue(const GValue *input, void *value) +static void readValue(const GValue *input, void *output) { - switch(G_VALUE_TYPE(input)) + switch(const auto type = G_VALUE_TYPE(input)) { case G_TYPE_CHAR: - *static_cast<char *>(value) = g_value_get_schar(input); + *static_cast<char *>(output) = g_value_get_schar(input); break; case G_TYPE_UCHAR: - *static_cast<uchar *>(value) = g_value_get_uchar(input); + *static_cast<uchar *>(output) = g_value_get_uchar(input); break; case G_TYPE_BOOLEAN: - *static_cast<bool *>(value) = g_value_get_boolean(input); + *static_cast<bool *>(output) = g_value_get_boolean(input); break; case G_TYPE_INT: - *static_cast<int *>(value) = g_value_get_int(input); + *static_cast<int *>(output) = g_value_get_int(input); break; case G_TYPE_UINT: - *static_cast<uint *>(value) = g_value_get_uint(input); + *static_cast<uint *>(output) = g_value_get_uint(input); break; case G_TYPE_LONG: - *static_cast<long *>(value) = g_value_get_long(input); + *static_cast<long *>(output) = g_value_get_long(input); break; case G_TYPE_ULONG: - *static_cast<ulong *>(value) = g_value_get_ulong(input); + *static_cast<ulong *>(output) = g_value_get_ulong(input); break; case G_TYPE_INT64: - *static_cast<qint64 *>(value) = g_value_get_int64(input); + *static_cast<qint64 *>(output) = g_value_get_int64(input); break; case G_TYPE_UINT64: - *static_cast<quint64 *>(value) = g_value_get_uint64(input); + *static_cast<quint64 *>(output) = g_value_get_uint64(input); break; case G_TYPE_FLOAT: - *static_cast<float *>(value) = g_value_get_float(input); + *static_cast<float *>(output) = g_value_get_float(input); break; case G_TYPE_DOUBLE: - *static_cast<double *>(value) = g_value_get_double(input); + *static_cast<double *>(output) = g_value_get_double(input); break; case G_TYPE_STRING: - *static_cast<QString *>(value) = QString::fromUtf8(g_value_get_string(input)); + *static_cast<QString *>(output) = QString::fromUtf8(g_value_get_string(input)); + break; + + case G_TYPE_POINTER: + *static_cast<void **>(output) = g_value_get_pointer(input); 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: + if (g_type_is_a(type, G_TYPE_ENUM)) { + *static_cast<int *>(output) = g_value_get_enum(input); + break; + } + + if (g_type_is_a(type, G_TYPE_FLAGS)) { + *static_cast<uint *>(output) = g_value_get_flags(input); + break; + } + qWarning("Cannot convert unsupported GValue type: %s", G_VALUE_TYPE_NAME(input)); - break; } } static void writeValue(const void *input, GValue *output) { - switch(G_VALUE_TYPE(output)) + switch(const auto type = G_VALUE_TYPE(output)) { case G_TYPE_CHAR: g_value_set_schar(output, *static_cast<const char *>(input)); @@ -256,15 +392,26 @@ static void writeValue(const void *input, GValue *output) g_value_set_string(output, static_cast<const QString *>(input)->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_POINTER: + g_value_set_pointer(output, *static_cast<void *const *>(input)); + break; + //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: + if (g_type_is_a(type, G_TYPE_ENUM)) { + g_value_set_enum(output, *static_cast<const int *>(input)); + break; + } + + if (g_type_is_a(type, G_TYPE_FLAGS)) { + g_value_set_flags(output, *static_cast<const uint *>(input)); + break; + } + qWarning("Cannot convert unsupported GValue type: %s", G_VALUE_TYPE_NAME(output)); break; } @@ -337,8 +484,7 @@ public: ? g_list_nth_data(GST_BIN_CHILDREN(parent->target()), n - offset - 1) : Q_NULLPTR; - return static_cast<Item *>(child ? g_object_get_qdata(G_OBJECT(child), quickStreamItemQuark) - : Q_NULLPTR); + return static_cast<Item *>(child ? g_object_get_qdata(G_OBJECT(child), ITEM_DATA_QUARK) : Q_NULLPTR); } static void clearChildren(QQmlListProperty<Item> *list) @@ -367,9 +513,7 @@ static void readState(Item *item, void *value) static void writeState(Item *item, const void *value) { - const auto success = gst_element_set_state(GST_ELEMENT(item->target()), - *static_cast<const GstState *>(value)); - qDebug("setState(%d) -> %d", *static_cast<const GstState *>(value), success); + gst_element_set_state(GST_ELEMENT(item->target()), *static_cast<const GstState *>(value)); } template<typename... Args> @@ -392,30 +536,32 @@ struct TypeInfo { } - static QMetaObject *createItemMetaObject() - { - QMetaObjectBuilder objectBuilder; - objectBuilder.setClassName(QByteArrayLiteral("QQuickStreamer::Item")); - objectBuilder.setSuperClass(&QObject::staticMetaObject); - return objectBuilder.toMetaObject(); - } - void addProperty(const PropertyInfo &propertyInfo, const QByteArray &propertyName, - const QByteArray &typeName, + const QByteArray &propertyType, QMetaObjectBuilder *objectBuilder) { auto notifierBuilder = objectBuilder->addSignal(propertyName + QByteArrayLiteral("Changed()")); - auto propertyBuilder = objectBuilder->addProperty(propertyName, typeName, notifierBuilder.index()); + auto propertyBuilder = objectBuilder->addProperty(propertyName, propertyType, notifierBuilder.index()); propertyBuilder.setReadable(propertyInfo.read != Q_NULLPTR); propertyBuilder.setWritable(propertyInfo.write != Q_NULLPTR); + if (propertyType.endsWith(ENUMERATOR_TYPE_SUFFIX)) { // FIXME + propertyBuilder.setEnumOrFlag(true); + + const auto &scopeName = propertyType.left(propertyType.length() - ENUMERATOR_TYPE_SUFFIX.length()); + const int scopeId = QMetaType::type(scopeName.constData()); + objectBuilder->addRelatedMetaObject(QMetaType::metaObjectForType(scopeId)); + } + properties.append(propertyInfo); } static const TypeInfo *create(GType type, const MetaTypePads &metaTypePads) { + Q_ASSERT(type != G_TYPE_INVALID); + QScopedPointer<TypeInfo> typeInfo(new TypeInfo); QMetaObjectBuilder objectBuilder; @@ -433,17 +579,10 @@ struct TypeInfo static const auto elementListTypeId = qRegisterNormalizedMetaType<ElementList>(elementListTypeName); if (type == GST_TYPE_ELEMENT) { - auto stateEnum = objectBuilder.addEnumerator(QByteArrayLiteral("State")); - stateEnum.addKey(QByteArrayLiteral("Pending"), static_cast<int>(GST_STATE_VOID_PENDING)); - stateEnum.addKey(QByteArrayLiteral("Null"), static_cast<int>(GST_STATE_NULL)); - stateEnum.addKey(QByteArrayLiteral("Ready"), static_cast<int>(GST_STATE_READY)); - stateEnum.addKey(QByteArrayLiteral("Paused"), static_cast<int>(GST_STATE_PAUSED)); - stateEnum.addKey(QByteArrayLiteral("Playing"), static_cast<int>(GST_STATE_PLAYING)); - // FIXME: also add pendingState property? typeInfo->addProperty({ Q_NULLPTR, readState, writeState }, QByteArrayLiteral("state"), - QByteArrayLiteral("GstElement::State"), + metaTypeName(GST_TYPE_STATE), &objectBuilder); } @@ -454,10 +593,10 @@ struct TypeInfo objectBuilder.addClassInfo(QByteArrayLiteral("DefaultProperty"), propertyName); } - const auto gobject_class = static_cast<GObjectClass *>(g_type_class_ref(type)); + const ObjectReference<GObjectClass> objectClass(type); uint nPSpecs = 0; - auto *const pSpecs = g_object_class_list_properties(gobject_class, &nPSpecs); + auto *const pSpecs = g_object_class_list_properties(objectClass, &nPSpecs); for (uint i = 0; i < nPSpecs; ++i) { const auto *const pSpec = pSpecs[i]; @@ -501,16 +640,14 @@ struct TypeInfo GSignalQuery query; g_signal_query(signalIds[i], &query); Q_ASSERT(signalIds[i] == query.signal_id); - qDebug("signal found: %s::%s", g_type_name(type), query.signal_name); + qDebug("FIXME: register signal %s::%s", g_type_name(type), query.signal_name); } - g_type_class_unref(gobject_class); - typedef QtMetaTypePrivate::QMetaTypeFunctionHelper<Item> MetaTypeHelper; static const QMetaType::TypeFlags typeFlags(QtPrivate::QMetaTypeTypeFlags<Item>::Flags); typeInfo->metaObject = objectBuilder.toMetaObject(); - typeInfo->elementName = objectBuilder.className().mid(3); + typeInfo->elementName = objectBuilder.className().mid(CLASS_NAME_PREFIX.length()); const int typeId = QMetaType::registerNormalizedType(typeInfo->metaObject->className(), MetaTypeHelper::Delete, @@ -521,11 +658,6 @@ struct TypeInfo typeFlags, typeInfo->metaObject); - // FIXME: constants.h or pass by argument - static const auto NAMESPACE_URI = QByteArrayLiteral("QuickStreamer"); - const static int MAJOR_VERSION = 1; - const static int MINOR_VERSION = 0; - // Check if this is GstElement or below const bool abstractType = g_type_is_a(GST_TYPE_ELEMENT, type); @@ -581,6 +713,13 @@ struct TypeInfo Q_ASSERT(GST_IS_ELEMENT_FACTORY(factory)); const auto type = gst_element_factory_get_element_type(factory); + + if (type == G_TYPE_INVALID) { + auto *const feature = GST_PLUGIN_FEATURE(factory); + auto loadedFactory = makeReference(GST_ELEMENT_FACTORY(gst_plugin_feature_load(feature))); + return find(loadedFactory); + } + const auto *typeInfo = cache.value(type); if (typeInfo == Q_NULLPTR) @@ -643,17 +782,17 @@ QHash<GType, TypeInfo *> TypeInfo::cache; using Private::TypeInfo; using Private::toCamelCase; -using Private::quickStreamItemQuark; +using Private::ITEM_DATA_QUARK; Item::Item(GstObject *target, QObject *parent) : QObject(parent) , m_target(target) { Q_ASSERT(m_target != Q_NULLPTR); - Q_ASSERT(g_object_get_qdata(G_OBJECT(m_target), quickStreamItemQuark) == Q_NULLPTR); + Q_ASSERT(g_object_get_qdata(G_OBJECT(m_target), ITEM_DATA_QUARK) == Q_NULLPTR); g_object_ref_sink(m_target); - g_object_set_qdata(G_OBJECT(m_target), quickStreamItemQuark, this); + g_object_set_qdata(G_OBJECT(m_target), ITEM_DATA_QUARK, this); auto notifyCallback = reinterpret_cast<GCallback>(&Item::emitPropertyChanged); g_signal_connect_swapped(target, "notify", notifyCallback, this); @@ -670,6 +809,11 @@ Item::~Item() g_object_unref(m_target); } +bool Item::registerElementFactory(const QByteArray &name) +{ + return registerElementFactory(makeReference(gst_element_factory_find(name.constData()))); +} + bool Item::registerElementFactory(GstElementFactory *factory) { return TypeInfo::find(factory) != Q_NULLPTR; @@ -702,7 +846,6 @@ void *Item::qt_metacast(const char *className) int Item::qt_metacall(QMetaObject::Call call, int id, void **args) { - qDebug("%s: %d(%d)", Q_FUNC_INFO, call, id); id = QObject::qt_metacall(call, id, args); if (id < 0) @@ -728,6 +871,7 @@ int Item::qt_metacall(QMetaObject::Call call, int id, void **args) case QMetaObject::IndexOfMethod: case QMetaObject::RegisterPropertyMetaType: case QMetaObject::RegisterMethodArgumentMetaType: + qDebug("%s: %d(%d)", Q_FUNC_INFO, call, id); break; } @@ -745,8 +889,8 @@ void Item::componentComplete() qDebug() << Q_FUNC_INFO - << gst_element_get_bus(GST_ELEMENT(m_target)) - << gst_object_get_parent(m_target); + << "bus:" << gst_element_get_bus(GST_ELEMENT(m_target)) + << "parent: " << gst_object_get_parent(m_target); if (GST_IS_BIN(m_target)) { auto l = g_list_last(GST_BIN_CHILDREN(m_target)); diff --git a/src/QuickStreamer/item.h b/src/QuickStreamer/item.h index bc0bf24..5a24439 100644 --- a/src/QuickStreamer/item.h +++ b/src/QuickStreamer/item.h @@ -24,6 +24,7 @@ public: ~Item(); + static bool registerElementFactory(const QByteArray &name); static bool registerElementFactory(GstElementFactory *factory); static bool registerObjectClass(GType type); diff --git a/src/QuickStreamer/objectreference.h b/src/QuickStreamer/objectreference.h new file mode 100644 index 0000000..1075c14 --- /dev/null +++ b/src/QuickStreamer/objectreference.h @@ -0,0 +1,91 @@ +#ifndef QQUICKSTREAMER_OBJECTREFERENCE_H +#define QQUICKSTREAMER_OBJECTREFERENCE_H + +#include <glib-object.h> + +typedef struct _GstElementFactory GstElementFactory; + +namespace QQuickStreamer { +namespace Private { + +template<class T> +struct TypeClassHelper +{ + typedef GType InitializerType; + + static T *addReference(GType type) + { + return static_cast<T *>(g_type_class_ref(type)); + } + + static void removeReference(T *p) + { + g_type_class_unref(p); + } +}; + +template<class T> +struct ObjectHelper +{ + typedef T *InitializerType; + + static T *addReference(T *p) + { + return static_cast<T *>(g_object_ref(p)); + } + + static void removeReference(T *p) + { + g_object_unref(p); + } +}; + +template<class T> struct ReferenceHelper { }; + +template<> struct ReferenceHelper<GObjectClass> : public TypeClassHelper<GObjectClass> { }; +template<> struct ReferenceHelper<GEnumClass> : public TypeClassHelper<GEnumClass> { }; +template<> struct ReferenceHelper<GFlagsClass> : public TypeClassHelper<GFlagsClass> { }; + +template<> struct ReferenceHelper<GstElementFactory> : public ObjectHelper<GstElementFactory> { }; + +} // namespace Private + +template<class T> +class ObjectReference +{ + typedef Private::ReferenceHelper<T> TypeHelper; + +public: + explicit ObjectReference(typename TypeHelper::InitializerType init) + : d(TypeHelper::addReference(init)) + { + } + + ~ObjectReference() + { + TypeHelper::removeReference(d); + } + + operator T *() const + { + return d; + } + + T *operator ->() const + { + return d; + } + +private: + T *const d; +}; + +template<class T> +inline ObjectReference<T> makeReference(T *p) +{ + return ObjectReference<T>(p); +} + +} // namespace QQuickStreamer + +#endif // QQUICKSTREAMER_OBJECTREFERENCE_H diff --git a/src/QuickStreamer/plugin.cpp b/src/QuickStreamer/plugin.cpp index 936eb68..6f63395 100644 --- a/src/QuickStreamer/plugin.cpp +++ b/src/QuickStreamer/plugin.cpp @@ -34,12 +34,8 @@ void Plugin::registerTypes(const char *uri) auto *const elements = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_ANY, GST_RANK_NONE); - for (auto *l = elements; l; l = l->next) { - auto *const feature = GST_PLUGIN_FEATURE(l->data); - auto *const factory = GST_ELEMENT_FACTORY(gst_plugin_feature_load(feature)); - Item::registerElementFactory(factory); - g_object_unref(factory); - } + for (auto *l = elements; l; l = l->next) + Item::registerElementFactory(GST_ELEMENT_FACTORY(l->data)); gst_plugin_feature_list_free(elements); } diff --git a/src/examples/piano/qml/Piano.qml b/src/examples/piano/qml/Piano.qml index ce73862..2aa7c0a 100644 --- a/src/examples/piano/qml/Piano.qml +++ b/src/examples/piano/qml/Piano.qml @@ -35,7 +35,7 @@ Rectangle { onPressedChanged: { audioSource.freq = frequency audioSource.volume = pressed ? 1 : 0 - pipeline.state = Element.Playing + pipeline.state = ElementState.Playing } } } @@ -69,7 +69,7 @@ Rectangle { onPressedChanged: { audioSource.freq = frequency audioSource.volume = pressed ? 1 : 0 - pipeline.state = Element.Playing + pipeline.state = ElementState.Playing } } } @@ -78,7 +78,7 @@ Rectangle { Pipeline { id: pipeline - // state: Element.Playing // FIXME: this should be set after plugging all elements + // state: ElementState.Playing // FIXME: this should be set after plugging all elements AudioTestSrc { id: audioSource @@ -100,6 +100,7 @@ Rectangle { */ volume: 0 + wave: AudioTestSrcWave.SineTable // wave: 12 } diff --git a/src/examples/piano/qml/PianoKey.qml b/src/examples/piano/qml/PianoKey.qml index a695557..94543de 100644 --- a/src/examples/piano/qml/PianoKey.qml +++ b/src/examples/piano/qml/PianoKey.qml @@ -31,6 +31,7 @@ Rectangle { font.bold: parent.pressed } + /* Text { anchors { bottom: parent.top @@ -42,4 +43,5 @@ Rectangle { font.pixelSize: 8 rotation: 90 } + */ } diff --git a/tests/tst_metaobject/tst_metaobject.pro b/tests/tst_metaobject/tst_metaobject.pro index f42ab91..c73e7d2 100644 --- a/tests/tst_metaobject/tst_metaobject.pro +++ b/tests/tst_metaobject/tst_metaobject.pro @@ -3,7 +3,9 @@ include(../tests.pri) CONFIG += link_pkgconfig PKGCONFIG += gstreamer-1.0 -LIBS += -L$$TOP_OUTDIR/src/QuickStreamer -lquickstreamerplugin +LIBS += \ + -Wl,-rpath,$$TOP_OUTDIR/src/QuickStreamer \ + -L$$TOP_OUTDIR/src/QuickStreamer -lquickstreamerplugin SOURCES += \ tst_metaobjecttest.cpp diff --git a/tests/tst_metaobject/tst_metaobjecttest.cpp b/tests/tst_metaobject/tst_metaobjecttest.cpp index d765db0..0eeeed1 100644 --- a/tests/tst_metaobject/tst_metaobjecttest.cpp +++ b/tests/tst_metaobject/tst_metaobjecttest.cpp @@ -12,6 +12,8 @@ class MetaObjectTest : public QObject { Q_OBJECT + typedef QPair<int, QByteArray> NameValuePair; + public: MetaObjectTest() { @@ -22,6 +24,8 @@ private slots: { gst_init(Q_NULLPTR, Q_NULLPTR); QVERIFY(Item::registerObjectClass(GST_TYPE_PIPELINE)); + QVERIFY(Item::registerElementFactory("audiotestsrc")); + QVERIFY(Item::registerElementFactory("encodebin")); } void testClassName() @@ -32,31 +36,49 @@ private slots: void testProperties_data() { + QTest::addColumn<QByteArray>("factory"); QTest::addColumn<QByteArray>("name"); QTest::addColumn<QByteArray>("type"); QTest::addColumn<bool>("readable"); QTest::addColumn<bool>("writable"); + QTest::addColumn<bool>("enumType"); QTest::newRow("name") + << QByteArrayLiteral("pipeline") << QByteArrayLiteral("name") << QByteArrayLiteral("QString") - << true << true; + << true << true << false; QTest::newRow("children") + << QByteArrayLiteral("pipeline") << QByteArrayLiteral("children") - << QByteArrayLiteral("QQmlListProperty<QQuickStreamer::Item>") - << true << false; - + << QByteArrayLiteral("QQmlListProperty<GstElement>") + << true << false << false; + + QTest::newRow("state") + << QByteArrayLiteral("pipeline") + << QByteArrayLiteral("state") + << QByteArrayLiteral("GstState::Values") + << true << true << true; + + QTest::newRow("wave") + << QByteArrayLiteral("audiotestsrc") + << QByteArrayLiteral("wave") + << QByteArrayLiteral("GstAudioTestSrcWave::Values") + << true << true << true; } void testProperties() { + QFETCH(QByteArray, factory); QFETCH(QByteArray, name); QFETCH(QByteArray, type); QFETCH(bool, readable); QFETCH(bool, writable); + QFETCH(bool, enumType); - Item item(GST_OBJECT(gst_pipeline_new(Q_NULLPTR))); + Item item(GST_OBJECT(gst_element_factory_make(factory.constData(), Q_NULLPTR))); + QVERIFY(item.target()); const int propertyIndex = item.metaObject()->indexOfProperty(name.constData()); @@ -64,8 +86,11 @@ private slots: const auto &property = item.metaObject()->property(propertyIndex); + qDebug() << property.isEnumType() << property.isFlagType(); + QCOMPARE(property.isReadable(), readable); QCOMPARE(property.isWritable(), writable); + QCOMPARE(property.isEnumType(), enumType); QVERIFY(property.hasNotifySignal()); QCOMPARE(property.typeName(), type.constData()); QVERIFY(QMetaType::type(type.constData()) != 0); @@ -78,24 +103,64 @@ private slots: QCOMPARE(qobject_cast<QQmlParserStatus *>(&item), &item); } - /* - void testCoerce() + void testEnumerators_data() { + QTest::addColumn<QByteArray>("typeName"); + QTest::addColumn<bool>("isFlag"); + QTest::addColumn<QList<NameValuePair>>("values"); + + QTest::newRow("enum") + << QByteArrayLiteral("GstAudioTestSrcWave") << false + << QList<NameValuePair>({ + qMakePair(0, QByteArrayLiteral("Sine")), + qMakePair(1, QByteArrayLiteral("Square")), + qMakePair(5, QByteArrayLiteral("WhiteNoise")) + }); + + QTest::newRow("state") + << QByteArrayLiteral("GstState") << false + << QList<NameValuePair>({ + qMakePair(0, QByteArrayLiteral("VoidPending")), // FIXME: explicitly rename to just "Pending"? + qMakePair(1, QByteArrayLiteral("Null")), + qMakePair(2, QByteArrayLiteral("Ready")), + qMakePair(3, QByteArrayLiteral("Paused")), + qMakePair(4, QByteArrayLiteral("Playing")) + }); + + QTest::newRow("flags") + << QByteArrayLiteral("GstEncodeBinFlags") << true + << QList<NameValuePair>({ + qMakePair(1, QByteArrayLiteral("NoAudioConversion")), + qMakePair(2, QByteArrayLiteral("NoVideoConversion")) + }); } - bool QQmlCompiler::canCoerce(int to, QQmlScript::Object *from) + void testEnumerators() { - QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to); - QQmlPropertyCache *fromMo = from->metatype; + QFETCH(QByteArray, typeName); + QFETCH(bool, isFlag); + QFETCH(QList<NameValuePair>, values); + + const int typeId = QMetaType::type(typeName.constData()); + QVERIFY(typeId > 0); + + auto *const metaObject = QMetaType::metaObjectForType(typeId); + QVERIFY(metaObject != Q_NULLPTR); + QCOMPARE(metaObject->enumeratorCount(), 1); + + const auto enumerator = metaObject->enumerator(0); + QVERIFY(enumerator.isValid()); + QCOMPARE(enumerator.name(), "Values"); + QCOMPARE(enumerator.isFlag(), isFlag); + + for (const auto &p: values) { + QCOMPARE(enumerator.valueToKey(p.first), p.second.constData()); - while (fromMo) { - if (fromMo == toMo) - return true; - fromMo = fromMo->parent(); + bool ok = false; + QCOMPARE(enumerator.keyToValue(p.second, &ok), p.first); + QVERIFY(ok); } - return false; } - */ }; QTEST_MAIN(MetaObjectTest) diff --git a/tests/tst_qml/tst_quickstreamer.qml b/tests/tst_qml/tst_quickstreamer.qml index 6ce8dc5..acf32c5 100644 --- a/tests/tst_qml/tst_quickstreamer.qml +++ b/tests/tst_qml/tst_quickstreamer.qml @@ -1,4 +1,3 @@ -import QtQuick 2.0 import QtTest 1.0 import QuickStreamer 1.0 @@ -18,6 +17,16 @@ TestCase { } } + AudioTestSrc { + id: audioTestSourceByIdentifier + wave: AudioTestSrcWave.WhiteNoise + } + + AudioTestSrc { + id: audioTestSourceByString + wave: "VioletNoise" + } + function test_nameProperty() { compare(pipeline.name, "pipeline0") @@ -27,8 +36,6 @@ TestCase { function test_children() { - console.log(pipeline.children) - compare(pipeline.children.length, 2) compare(pipeline.children[0], appSource) compare(pipeline.children[1], appSink) @@ -36,4 +43,26 @@ TestCase { compare(appSink.children, undefined) compare(appSource.children, undefined) } + + function test_enums() + { + compare(AudioTestSrcWave.Sine, 0) + compare(AudioTestSrcWave.Square, 1) + compare(AudioTestSrcWave.Saw, 2) + compare(AudioTestSrcWave.Triangle, 3) + compare(AudioTestSrcWave.Silence, 4) + compare(AudioTestSrcWave.WhiteNoise, 5) + compare(AudioTestSrcWave.PinkNoise, 6) + compare(AudioTestSrcWave.SineTable, 7) + compare(AudioTestSrcWave.Ticks, 8) + compare(AudioTestSrcWave.GaussianNoise, 9) + compare(AudioTestSrcWave.RedNoise, 10) + compare(AudioTestSrcWave.BlueNoise, 11) + compare(AudioTestSrcWave.VioletNoise, 12) + + compare(audioTestSourceByIdentifier.wave, 5) + compare(audioTestSourceByString.wave, 12) + + compare(ElementState.Playing, 4) + } } |