summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathias Hasselmann <mathias.hasselmann@kdab.com>2013-11-13 08:55:20 +0100
committerMathias Hasselmann <mathias.hasselmann@kdab.com>2013-11-13 08:55:20 +0100
commitea170b8ec4567ecd55cbcf9dee5b4631845a9aaf (patch)
treed1b9d6394450571c86e7a5fbc5882e9dfef9f2d8
parentd29e3f0f4647f20e6bd5a7fb6d7d0ed4ca5bd8c9 (diff)
Support enumeration types
-rw-r--r--src/QuickStreamer/QuickStreamer.pro3
-rw-r--r--src/QuickStreamer/item.cpp296
-rw-r--r--src/QuickStreamer/item.h1
-rw-r--r--src/QuickStreamer/objectreference.h91
-rw-r--r--src/QuickStreamer/plugin.cpp8
-rw-r--r--src/examples/piano/qml/Piano.qml7
-rw-r--r--src/examples/piano/qml/PianoKey.qml2
-rw-r--r--tests/tst_metaobject/tst_metaobject.pro4
-rw-r--r--tests/tst_metaobject/tst_metaobjecttest.cpp97
-rw-r--r--tests/tst_qml/tst_quickstreamer.qml35
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)
+ }
}