summaryrefslogtreecommitdiffstats
path: root/src/QuickStreamer/item.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/QuickStreamer/item.cpp')
-rw-r--r--src/QuickStreamer/item.cpp623
1 files changed, 623 insertions, 0 deletions
diff --git a/src/QuickStreamer/item.cpp b/src/QuickStreamer/item.cpp
new file mode 100644
index 0000000..f710f04
--- /dev/null
+++ b/src/QuickStreamer/item.cpp
@@ -0,0 +1,623 @@
+#include "item.h"
+#include "metapads.h"
+
+#include <gst/gst.h>
+
+#include <QtCore/private/qmetaobjectbuilder_p.h>
+#include <QtCore/QVector>
+#include <QtQml/QtQml>
+
+#include <functional>
+
+namespace QQuickStreamer {
+namespace Private {
+
+struct PropertyInfo
+{
+ typedef std::function<void(GstObject *target, void *value)> ReadDelegate;
+ typedef std::function<void(GstObject *target, const void *value)> WriteDelegate;
+
+ const char *name;
+ ReadDelegate read;
+ WriteDelegate write;
+};
+
+static bool isDash(char ch)
+{
+ return ch == '-' || ch == '_';
+}
+
+static QByteArray toCamelCase(const QByteArray &input,
+ char (*convertFirst)(char, const std::locale &))
+{
+ int nDashes = 0;
+
+ for (char ch: input) {
+ if (isDash(ch))
+ ++nDashes;
+ }
+
+ QByteArray output;
+
+ output.reserve(input.size() - nDashes);
+
+ bool wordBoundary = false;
+
+ auto it = std::begin(input);
+ const auto end = std::end(input);
+ const auto &loc = std::locale::classic();
+
+ output += convertFirst(*it, loc);
+
+ while (++it != end) {
+ if (isDash(*it)) {
+ wordBoundary = true;
+ continue;
+ }
+
+ if (wordBoundary) {
+ output += std::toupper(*it, loc);
+ wordBoundary = false;
+ } else {
+ output += std::tolower(*it, loc);
+ }
+ }
+
+ return output;
+}
+
+static QByteArray makeClassName(GType type)
+{
+ QByteArray typeName = g_type_name(type);
+
+ // 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");
+
+ if (not typeName.startsWith(CLASS_NAME_PREFIX))
+ typeName = CLASS_NAME_PREFIX + typeName;
+
+ return typeName;
+}
+
+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 readValue(const GValue *input, void *value)
+{
+ switch(G_VALUE_TYPE(input))
+ {
+ case G_TYPE_CHAR:
+ *static_cast<char *>(value) = g_value_get_schar(input);
+ break;
+
+ case G_TYPE_UCHAR:
+ *static_cast<uchar *>(value) = g_value_get_uchar(input);
+ break;
+
+ case G_TYPE_BOOLEAN:
+ *static_cast<bool *>(value) = g_value_get_boolean(input);
+ break;
+
+ case G_TYPE_INT:
+ *static_cast<int *>(value) = g_value_get_int(input);
+ break;
+
+ case G_TYPE_UINT:
+ *static_cast<uint *>(value) = g_value_get_uint(input);
+ break;
+
+ case G_TYPE_LONG:
+ *static_cast<long *>(value) = g_value_get_long(input);
+ break;
+
+ case G_TYPE_ULONG:
+ *static_cast<ulong *>(value) = g_value_get_ulong(input);
+ break;
+
+ case G_TYPE_INT64:
+ *static_cast<qint64 *>(value) = g_value_get_int64(input);
+ break;
+
+ case G_TYPE_UINT64:
+ *static_cast<quint64 *>(value) = g_value_get_uint64(input);
+ break;
+
+ case G_TYPE_FLOAT:
+ *static_cast<float *>(value) = g_value_get_float(input);
+ break;
+
+ case G_TYPE_DOUBLE:
+ *static_cast<double *>(value) = g_value_get_double(input);
+ break;
+
+ case G_TYPE_STRING:
+ *static_cast<QString *>(value) = QString::fromUtf8(g_value_get_string(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:
+ 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))
+ {
+ case G_TYPE_CHAR:
+ g_value_set_schar(output, *static_cast<const char *>(input));
+ break;
+
+ case G_TYPE_UCHAR:
+ g_value_set_uchar(output, *static_cast<const uchar *>(input));
+ break;
+
+ case G_TYPE_BOOLEAN:
+ g_value_set_boolean(output, *static_cast<const bool *>(input));
+ break;
+
+ case G_TYPE_INT:
+ g_value_set_int(output, *static_cast<const int *>(input));
+ break;
+
+ case G_TYPE_UINT:
+ g_value_set_uint(output, *static_cast<const uint *>(input));
+ break;
+
+ case G_TYPE_LONG:
+ g_value_set_long(output, *static_cast<const long *>(input));
+ break;
+
+ case G_TYPE_ULONG:
+ g_value_set_ulong(output, *static_cast<const ulong *>(input));
+ break;
+
+ case G_TYPE_INT64:
+ g_value_set_int64(output, *static_cast<const qint64 *>(input));
+ break;
+
+ case G_TYPE_UINT64:
+ g_value_set_uint64(output, *static_cast<const quint64 *>(input));
+ break;
+
+ case G_TYPE_FLOAT:
+ g_value_set_float(output, *static_cast<const float *>(input));
+ break;
+
+ case G_TYPE_DOUBLE:
+ g_value_set_double(output, *static_cast<const double *>(input));
+ break;
+
+ case G_TYPE_STRING:
+ 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_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(output));
+ break;
+ }
+}
+
+static void readGObjectProperty(GstObject *target, const QByteArray &propertyName,
+ GType propertyType, void *value)
+{
+ GValue gvalue = G_VALUE_INIT;
+ g_value_init(&gvalue, propertyType);
+ g_object_get_property(G_OBJECT(target), propertyName.constData(), &gvalue);
+ readValue(&gvalue, value);
+ g_value_unset(&gvalue);
+}
+
+static void writeGObjectProperty(GstObject *target, const QByteArray &propertyName,
+ GType propertyType, const void *value)
+{
+ GValue gvalue = G_VALUE_INIT;
+ g_value_init(&gvalue, propertyType);
+ writeValue(value, &gvalue);
+ g_object_set_property(G_OBJECT(target), propertyName.constData(), &gvalue);
+ g_value_unset(&gvalue);
+}
+
+static void readNothing(GstObject *target, const QByteArray &name, void *)
+{
+ 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 void *)
+{
+ 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...);
+}
+
+struct TypeInfo
+{
+ TypeInfo()
+ : parent(Q_NULLPTR)
+ , metaObject(Q_NULLPTR)
+ {
+ }
+
+ static const TypeInfo *create(GType type, const MetaTypePads &metaTypePads)
+ {
+ QScopedPointer<TypeInfo> typeInfo(new TypeInfo);
+
+ QMetaObjectBuilder objectBuilder;
+ objectBuilder.setClassName(makeClassName(type));
+
+ if (type != G_TYPE_OBJECT)
+ typeInfo->parent = find(g_type_parent(type));
+
+ objectBuilder.setSuperClass(typeInfo->parent ? typeInfo->parent->metaObject
+ : &QObject::staticMetaObject);
+
+ const auto gobject_class = static_cast<GObjectClass *>(g_type_class_ref(type));
+
+ 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 != type)
+ continue;
+
+ const auto &propertyName = toCamelCase(pSpec->name, std::tolower);
+ const auto &propertyType = metaTypeName(pSpec->value_type);
+
+ if (pSpec->flags & G_PARAM_DEPRECATED) {
+ qWarning("Ignoring deprecated property %s::%s", g_type_name(type), pSpec->name);
+ continue;
+ }
+
+ if (propertyType.isEmpty()) {
+ qWarning("Ignoring property %s::%s of unsupported type %s",
+ g_type_name(type), pSpec->name, g_type_name(pSpec->value_type));
+ continue;
+ }
+
+ auto notifier = objectBuilder.addSignal(propertyName + QByteArrayLiteral("Changed()"));
+ auto property = objectBuilder.addProperty(propertyName, propertyType, 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(type, &nSignalIds);
+
+ for (uint i = 0; i < nSignalIds; ++i) {
+ 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);
+ }
+
+ 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);
+
+ const int typeId = QMetaType::registerNormalizedType(typeInfo->metaObject->className(),
+ MetaTypeHelper::Delete,
+ metaTypePads.create,
+ MetaTypeHelper::Destruct,
+ metaTypePads.construct,
+ sizeof(Item),
+ 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;
+
+ auto pointerName = objectBuilder.className() + QByteArrayLiteral(" *");
+
+ QQmlPrivate::RegisterType qmlType = {
+ 1,
+
+ typeId,
+ 0 /*qRegisterNormalizedMetaType<QQmlListProperty<T> >(listName.constData())*/,
+ sizeof(Item), metaTypePads.createInto,
+ QString(),
+
+ NAMESPACE_URI, MAJOR_VERSION, MINOR_VERSION,
+ typeInfo->elementName,
+ typeInfo->metaObject,
+
+ QQmlPrivate::attachedPropertiesFunc<Item>(),
+ QQmlPrivate::attachedPropertiesMetaObject<Item>(),
+
+ QQmlPrivate::StaticCastSelector<Item, QQmlParserStatus>::cast(),
+ QQmlPrivate::StaticCastSelector<Item, QQmlPropertyValueSource>::cast(),
+ QQmlPrivate::StaticCastSelector<Item, QQmlPropertyValueInterceptor>::cast(),
+
+ 0, 0,
+
+ 0,
+ 0
+ };
+
+ QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &qmlType);
+
+ cache.insert(type, typeInfo.data());
+ return typeInfo.take();
+ }
+
+ static const TypeInfo *find(GType type)
+ {
+ Q_ASSERT(g_type_is_a(type, G_TYPE_OBJECT));
+ Q_ASSERT(not g_type_is_a(type, GST_TYPE_ELEMENT_FACTORY));
+
+ const auto *typeInfo = cache.value(type);
+
+ if (typeInfo == Q_NULLPTR)
+ typeInfo = create(type, MetaTypePads::forObjectType(type));
+
+ return typeInfo;
+ }
+
+ static const TypeInfo *find(GstElementFactory *factory)
+ {
+ Q_ASSERT(GST_IS_ELEMENT_FACTORY(factory));
+
+ const auto type = gst_element_factory_get_element_type(factory);
+ const auto *typeInfo = cache.value(type);
+
+ if (typeInfo == Q_NULLPTR)
+ typeInfo = create(type, MetaTypePads::forElementFactory(factory));
+
+ return typeInfo;
+ }
+
+ 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 void *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();
+ }
+
+ void emitPropertyChanged(Item *object, const GParamSpec *pspec) const
+ {
+ for (int i = 0; i < properties.size(); ++i) {
+ // can compare by pointer because GParamSpec::name is interned
+ if (pspec->name == properties.at(i).name) {
+ QMetaObject::activate(object, metaObject, i, Q_NULLPTR);
+ return;
+ }
+ }
+
+ if (parent)
+ parent->emitPropertyChanged(object, pspec);
+ }
+
+ const TypeInfo *parent;
+ const QMetaObject *metaObject;
+
+ QByteArray elementName;
+ QVector<PropertyInfo> properties;
+ static QHash<GType, TypeInfo *> cache;
+};
+
+QHash<GType, TypeInfo *> TypeInfo::cache;
+
+} // namespace Private
+
+using Private::TypeInfo;
+using Private::toCamelCase;
+
+Item::Item(GstObject *target, QObject *parent)
+ : QObject(parent)
+ , m_target(target)
+{
+ Q_ASSERT(m_target != Q_NULLPTR);
+
+ g_object_ref_sink(m_target);
+ g_signal_connect_swapped(target, "notify", reinterpret_cast<GCallback>(&Item::emitPropertyChanged), this);
+}
+
+Item::Item(const Item &other)
+ : QObject(other.parent())
+ , m_target(static_cast<GstObject *>(g_object_ref(other.m_target)))
+{
+}
+
+Item::~Item()
+{
+ g_object_unref(m_target);
+}
+
+bool Item::registerElementFactory(GstElementFactory *factory)
+{
+ return TypeInfo::find(factory) != Q_NULLPTR;
+}
+
+bool Item::registerObjectClass(GType type)
+{
+ return TypeInfo::find(type) != Q_NULLPTR;
+}
+
+const QMetaObject *Item::metaObject() const
+{
+ return TypeInfo::find(G_OBJECT_TYPE(m_target))->metaObject;
+}
+
+void *Item::qt_metacast(const char *className)
+{
+ qDebug("%s: %s %p", Q_FUNC_INFO, className, this);
+
+ if (className == Q_NULLPTR)
+ return Q_NULLPTR;
+
+ if (strcmp(className, G_OBJECT_TYPE_NAME(m_target)) == 0)
+ return this;
+ if (strcmp(className, QQmlParserStatus_iid) == 0)
+ return static_cast<QQmlParserStatus *>(this);
+
+ return QObject::qt_metacast(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)
+ return id;
+
+ switch(call) {
+ case QMetaObject::ReadProperty:
+ id = readProperty(id, static_cast<QVariant *>(args[0]));
+ break;
+
+ case QMetaObject::WriteProperty:
+ id = writeProperty(id, 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;
+}
+
+void Item::classBegin()
+{
+ Q_ASSERT(parent() != Q_NULLPTR);
+ qDebug() << Q_FUNC_INFO << metaObject()->className() << parent()->children();
+}
+
+void Item::componentComplete()
+{
+ Q_ASSERT(parent() != Q_NULLPTR);
+ qDebug() << Q_FUNC_INFO << metaObject()->className() << parent()->children();
+}
+
+int Item::readProperty(int id, QVariant *value)
+{
+ const auto *const typeInfo = TypeInfo::find(G_OBJECT_TYPE(m_target));
+ return typeInfo->readProperty(m_target, id, value);
+}
+
+int Item::writeProperty(int id, const void *value)
+{
+ const auto *const typeInfo = TypeInfo::find(G_OBJECT_TYPE(m_target));
+ return typeInfo->writeProperty(m_target, id, value);
+}
+
+void Item::emitPropertyChanged(Item *self, GParamSpec *pspec)
+{
+ const auto *const typeInfo = TypeInfo::find(G_OBJECT_TYPE(self->m_target));
+ typeInfo->emitPropertyChanged(self, pspec);
+}
+
+} // namespace QQuickStreamer