test/qml: add an dynamically adding qmlglsink element
authorMatthew Waters <matthew@centricular.com>
Tue, 4 Feb 2020 08:43:52 +0000 (19:43 +1100)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Wed, 18 Mar 2020 11:22:39 +0000 (11:22 +0000)
The example shows how to add qmlglsink to an already running pipeline
with pre-existing OpenGL elements.

tests/examples/qt/meson.build
tests/examples/qt/qmlsink-dynamically-added/.gitignore [new file with mode: 0644]
tests/examples/qt/qmlsink-dynamically-added/main.cpp [new file with mode: 0644]
tests/examples/qt/qmlsink-dynamically-added/main.qml [new file with mode: 0644]
tests/examples/qt/qmlsink-dynamically-added/meson.build [new file with mode: 0644]
tests/examples/qt/qmlsink-dynamically-added/play.pro [new file with mode: 0644]
tests/examples/qt/qmlsink-dynamically-added/qmlsink.qrc [new file with mode: 0644]

index 3fa08cf..650216c 100644 (file)
@@ -1,2 +1,3 @@
 subdir('qmlsink')
+subdir('qmlsink-dynamically-added')
 subdir('qmlsrc')
diff --git a/tests/examples/qt/qmlsink-dynamically-added/.gitignore b/tests/examples/qt/qmlsink-dynamically-added/.gitignore
new file mode 100644 (file)
index 0000000..9bbbbc9
--- /dev/null
@@ -0,0 +1,4 @@
+deployment.pri
+play
+qrc_qmlsink.cpp
+*.o
diff --git a/tests/examples/qt/qmlsink-dynamically-added/main.cpp b/tests/examples/qt/qmlsink-dynamically-added/main.cpp
new file mode 100644 (file)
index 0000000..dede86d
--- /dev/null
@@ -0,0 +1,127 @@
+#include <QApplication>
+#include <QQmlApplicationEngine>
+#include <QQuickWindow>
+#include <QQuickItem>
+#include <QRunnable>
+#include <QTimer>
+#include <gst/gst.h>
+#include <gst/gl/gl.h>
+
+static GstBusSyncReply
+on_sync_bus_message (GstBus * bus, GstMessage * msg, gpointer data)
+{
+  GstElement *pipeline = (GstElement *) data;
+
+  switch (GST_MESSAGE_TYPE (msg)) {
+    case GST_MESSAGE_HAVE_CONTEXT: {
+      GstContext *context;
+
+      gst_message_parse_have_context (msg, &context);
+
+      /* if you need specific behviour or a context from a specific element,
+       * you need to be selective about which context's you set on the
+       * pipeline */
+      if (gst_context_has_context_type (context, GST_GL_DISPLAY_CONTEXT_TYPE)) {
+        gst_println ("got have-context %p", context);
+        gst_element_set_context (pipeline, context);
+      }
+
+      if (context)
+        gst_context_unref (context);
+      gst_message_unref (msg);
+      return GST_BUS_DROP;
+    }
+    default:
+      break;
+  }
+
+  return GST_BUS_PASS;
+}
+
+static void
+connect_tee (GstElement * tee, GstElement * queue)
+{
+  gst_println ("attaching tee/queue %p %p", tee, queue);
+  gst_element_link (tee, queue);
+}
+
+static void
+connect_qmlglsink (GstElement * pipeline, GstElement * tee, QQuickWindow * rootObject)
+{
+  GstElement *queue = gst_element_factory_make ("queue", NULL);
+  GstElement *qmlglsink = gst_element_factory_make ("qmlglsink", NULL);
+  QQuickItem *videoItem;
+
+  gst_println ("attaching qmlglsink %s at %p", GST_OBJECT_NAME (qmlglsink), qmlglsink);
+
+  gst_bin_add (GST_BIN (pipeline), queue);
+  gst_bin_add (GST_BIN (pipeline), qmlglsink);
+  gst_element_link (queue, qmlglsink);
+  gst_element_set_state (queue, GST_STATE_PLAYING);
+
+  videoItem = rootObject->findChild<QQuickItem *> ("videoItem");
+  g_assert (videoItem);
+  g_object_set (qmlglsink, "widget", videoItem, NULL);
+
+  gst_element_set_state (qmlglsink, GST_STATE_PAUSED);
+  connect_tee (tee, queue);
+  gst_element_set_state (qmlglsink, GST_STATE_PLAYING);
+}
+
+int main(int argc, char *argv[])
+{
+  int ret;
+
+  gst_init (&argc, &argv);
+
+  {
+    QGuiApplication app(argc, argv);
+
+    /* test a whole bunch of elements respect the change in display
+     * and therefore OpenGL context */
+    GstElement *pipeline = gst_parse_launch ("gltestsrc ! "
+        "capsfilter caps=video/x-raw(ANY),framerate=10/1 ! glupload ! "
+        "glcolorconvert ! glalpha noise-level=16 method=green angle=40 ! "
+        "glcolorbalance hue=0.25 ! gltransformation rotation-x=30 ! "
+        "glvideomixerelement ! glviewconvert output-mode-override=side-by-side ! "
+        "glstereosplit name=s "
+        "glstereomix name=m ! tee name=t ! queue ! fakesink sync=true "
+        "s.left ! queue ! m.sink_0 "
+        "s.right ! queue ! m.sink_1", NULL);
+    GstBus *bus = gst_element_get_bus (pipeline);
+    gst_bus_set_sync_handler (bus, on_sync_bus_message, pipeline, NULL);
+    gst_object_unref (bus);
+    /* the plugin must be loaded before loading the qml file to register the
+     * GstGLVideoItem qml item */
+    GstElement *sink = gst_element_factory_make ("qmlglsink", NULL);
+
+    g_assert (pipeline && sink);
+    gst_object_unref (sink);
+
+    QQuickWindow *rootObject;
+
+    /* The Qml scene starts out with the widget not connected to any qmlglsink
+     * element */
+    QQmlApplicationEngine engine;
+    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+
+    /* find and set the videoItem on the sink */
+    rootObject = static_cast<QQuickWindow *> (engine.rootObjects().first());
+
+    gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+    GstElement *t = gst_bin_get_by_name (GST_BIN (pipeline), "t");
+    gst_object_unref (t);  /* ref held by pipeline */
+    /* add the qmlglsink element */
+    QTimer::singleShot(5000, [pipeline, t, rootObject]() { connect_qmlglsink (pipeline, t, rootObject); } );
+
+    ret = app.exec();
+
+    gst_element_set_state (pipeline, GST_STATE_NULL);
+    gst_object_unref (pipeline);
+  }
+
+  gst_deinit ();
+
+  return ret;
+}
diff --git a/tests/examples/qt/qmlsink-dynamically-added/main.qml b/tests/examples/qt/qmlsink-dynamically-added/main.qml
new file mode 100644 (file)
index 0000000..ffd3cd1
--- /dev/null
@@ -0,0 +1,60 @@
+import QtQuick 2.4
+import QtQuick.Controls 1.1
+import QtQuick.Controls.Styles 1.3
+import QtQuick.Dialogs 1.2
+import QtQuick.Window 2.1
+
+import org.freedesktop.gstreamer.GLVideoItem 1.0
+
+ApplicationWindow {
+    id: window
+    visible: true
+    width: 640
+    height: 480
+    x: 30
+    y: 30
+    color: "black"
+
+    Item {
+        anchors.fill: parent
+
+        GstGLVideoItem {
+            id: video
+            objectName: "videoItem"
+            anchors.centerIn: parent
+            width: parent.width
+            height: parent.height
+        }
+
+        Rectangle {
+            color: Qt.rgba(1, 1, 1, 0.7)
+            border.width: 1
+            border.color: "white"
+            anchors.bottom: video.bottom
+            anchors.bottomMargin: 15
+            anchors.horizontalCenter: parent.horizontalCenter
+            width : parent.width - 30
+            height: parent.height - 30
+            radius: 8
+
+            MouseArea {
+                id: mousearea
+                anchors.fill: parent
+                hoverEnabled: true
+                onEntered: {
+                    parent.opacity = 1.0
+                    hidetimer.start()
+                }
+            }
+
+            Timer {
+                id: hidetimer
+                interval: 5000
+                onTriggered: {
+                    parent.opacity = 0.0
+                    stop()
+                }
+            }
+        }
+    }
+}
diff --git a/tests/examples/qt/qmlsink-dynamically-added/meson.build b/tests/examples/qt/qmlsink-dynamically-added/meson.build
new file mode 100644 (file)
index 0000000..ebee3b0
--- /dev/null
@@ -0,0 +1,20 @@
+sources = [
+  'main.cpp',
+]
+
+if have_cxx and build_gstgl and gstgl_dep.found()
+  qt5_mod = import('qt5')
+  qt5qml_deps = dependency('qt5', modules : ['Core', 'Gui', 'Widgets', 'Qml', 'Quick'],
+                           required: get_option('examples'))
+
+  # FIXME Add a way to get that information out of the qt5 module
+  moc = find_program('moc-qt5', 'moc', required : get_option('examples'))
+  if qt5qml_deps.found() and moc.found()
+    qt_preprocessed = qt5_mod.preprocess(qresources : 'qmlsink.qrc')
+    executable('qmlsink-dynamically-added', sources, qt_preprocessed,
+        dependencies : [gst_dep, gstgl_dep, qt5qml_deps],
+        c_args : gst_plugins_good_args,
+        include_directories : [configinc],
+        install: false)
+  endif
+endif
diff --git a/tests/examples/qt/qmlsink-dynamically-added/play.pro b/tests/examples/qt/qmlsink-dynamically-added/play.pro
new file mode 100644 (file)
index 0000000..9ecaf87
--- /dev/null
@@ -0,0 +1,20 @@
+TEMPLATE = app
+
+QT += qml quick widgets
+
+QT_CONFIG -= no-pkg-config
+CONFIG += link_pkgconfig debug
+PKGCONFIG = \
+    gstreamer-1.0 \
+    gstreamer-video-1.0
+
+DEFINES += GST_USE_UNSTABLE_API
+
+INCLUDEPATH += ../lib
+
+SOURCES += main.cpp
+
+RESOURCES += qmlsink.qrc
+
+# Additional import path used to resolve QML modules in Qt Creator's code model
+QML_IMPORT_PATH =
diff --git a/tests/examples/qt/qmlsink-dynamically-added/qmlsink.qrc b/tests/examples/qt/qmlsink-dynamically-added/qmlsink.qrc
new file mode 100644 (file)
index 0000000..5f6483a
--- /dev/null
@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/">
+        <file>main.qml</file>
+    </qresource>
+</RCC>