qt: add a qml overlay filter element [part 2]
authorMatthew Waters <matthew@centricular.com>
Wed, 26 Feb 2020 07:29:06 +0000 (18:29 +1100)
committerMatthew Waters <matthew@centricular.com>
Thu, 19 Mar 2020 06:26:54 +0000 (17:26 +1100)
It takes a qml scene description and renders it using a possible input
stream.

Currently supported on GLX and WGL.

Follow up to (as that MR had an old version of the commit):
- https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/475
4778d7166a02caf793df4f845dc35b6933d87c81: qt: add a qml overlay filter element

ext/qt/gstqtglutility.cc
ext/qt/gstqtoverlay.cc
ext/qt/qtglrenderer.cc
ext/qt/qtglrenderer.h
ext/qt/qtitem.cc
tests/examples/qt/qmloverlay/main.cpp
tests/examples/qt/qmloverlay/overlay2.qml [new file with mode: 0644]
tests/examples/qt/qmloverlay/qmloverlay.qrc

index ba17282..50ff8d4 100644 (file)
@@ -251,7 +251,7 @@ gst_qt_get_gl_wrapcontext (GstGLDisplay * display,
   gst_gl_context_activate(*wrap_glcontext, TRUE);
   if (!gst_gl_context_fill_info (*wrap_glcontext, &error)) {
     GST_ERROR ("failed to retrieve qt context info: %s", error->message);
-    g_object_unref (*wrap_glcontext);
+    gst_object_unref (*wrap_glcontext);
     *wrap_glcontext = NULL;
     return FALSE;
   } else {
@@ -282,9 +282,9 @@ gst_qt_get_gl_wrapcontext (GstGLDisplay * display,
       gst_object_unref (window);
       if (!gst_gl_context_create (*context, *wrap_glcontext, &error)) {
         GST_ERROR ("failed to create shared GL context: %s", error->message);
-        g_object_unref (*context);
+        gst_object_unref (*context);
         *context = NULL;
-        g_object_unref (*wrap_glcontext);
+        gst_object_unref (*wrap_glcontext);
         *wrap_glcontext = NULL;
         wglMakeCurrent (device, (HGLRC) gl_handle);
         return FALSE;
@@ -317,18 +317,26 @@ qt_opengl_native_context_from_gst_gl_context (GstGLContext * context)
 #endif
 #if GST_GL_HAVE_PLATFORM_EGL
     if (platform == GST_GL_PLATFORM_EGL) {
-#if GST_GL_HAVE_WINDOW_WAYLAND && defined (HAVE_QT_WAYLAND)
+        EGLDisplay egl_display = EGL_DEFAULT_DISPLAY;
         GstGLDisplay *display = gst_gl_context_get_display (context);
+        GstGLDisplayEGL *display_egl = gst_gl_display_egl_from_gl_display (display);
+#if GST_GL_HAVE_WINDOW_WAYLAND && defined (HAVE_QT_WAYLAND)
         if (gst_gl_display_get_handle_type (display) == GST_GL_DISPLAY_TYPE_WAYLAND) {
-            GstGLDisplayEGL *display_egl = gst_gl_display_egl_from_gl_display (display);
-            if (display_egl) {
-                EGLDisplay egl_display = (EGLDisplay) gst_gl_display_get_handle ((GstGLDisplay *) display_egl);
-                gst_object_unref (display_egl);
-                gst_object_unref (display);
-                return QVariant::fromValue(QEGLNativeContext((EGLContext) handle, egl_display));
-            }
+#if 1
+            g_warning ("Qt does not support wrapping native OpenGL contexts "
+                "on wayland. See https://bugreports.qt.io/browse/QTBUG-82528");
+            gst_object_unref (display_egl);
+            gst_object_unref (display);
+            return QVariant::fromValue(nullptr);
+#else
+            if (display_egl)
+                egl_display = (EGLDisplay) gst_gl_display_get_handle ((GstGLDisplay *) display_egl);
+#endif
         }
 #endif
+        gst_object_unref (display_egl);
+        gst_object_unref (display);
+        return QVariant::fromValue(QEGLNativeContext((EGLContext) handle, egl_display));
     }
 #endif
 #if GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (HAVE_QT_WIN32)
index 0c52634..b9aa5fb 100644 (file)
@@ -238,6 +238,9 @@ gst_qt_overlay_gl_stop (GstGLBaseFilter * bfilter)
 {
   GstQtOverlay *qt_overlay = GST_QT_OVERLAY (bfilter);
 
+  if (qt_overlay->widget)
+    qt_overlay->widget->setBuffer (NULL);
+
   if (qt_overlay->renderer) {
     qt_overlay->renderer->cleanup();
     delete qt_overlay->renderer;
@@ -272,6 +275,7 @@ gst_qt_overlay_prepare_output_buffer (GstBaseTransform * btrans,
   GstGLFilter *filter = GST_GL_FILTER (btrans);
   GstQtOverlay *qt_overlay = GST_QT_OVERLAY (btrans);
   GstGLMemory *out_mem;
+  GstGLSyncMeta *sync_meta;
 
   if (qt_overlay->widget) {
     qt_overlay->widget->setCaps (bfilter->in_caps); 
@@ -292,6 +296,9 @@ gst_qt_overlay_prepare_output_buffer (GstBaseTransform * btrans,
       GST_VIDEO_INFO_WIDTH (&filter->in_info),
       GST_VIDEO_INFO_HEIGHT (&filter->out_info));
 
+  sync_meta = gst_buffer_add_gl_sync_meta (bfilter->context, *outbuf);
+  gst_gl_sync_meta_set_sync_point (sync_meta, bfilter->context);
+
   bclass->copy_metadata (btrans, buffer, *outbuf);
 
   return GST_FLOW_OK;
index 0e1b3ae..e78bcdf 100644 (file)
@@ -104,6 +104,62 @@ void GstAnimationDriver::setNextTime(qint64 ms)
     m_next = ms;
 }
 
+struct SharedRenderData
+{
+  volatile int refcount;
+  GMutex lock;
+  GstAnimationDriver *m_animationDriver;
+  QOpenGLContext *m_context;
+  GstBackingSurface *m_surface;
+};
+
+static struct SharedRenderData *
+shared_render_data_new (void)
+{
+  struct SharedRenderData *ret = g_new0 (struct SharedRenderData, 1);
+
+  ret->refcount = 1;
+  g_mutex_init (&ret->lock);
+
+  return ret;
+}
+
+static void
+shared_render_data_free (struct SharedRenderData * data)
+{
+  GST_DEBUG ("%p freeing shared render data", data);
+
+  g_mutex_clear (&data->lock);
+
+  if (data->m_animationDriver) {
+    data->m_animationDriver->uninstall();
+    delete data->m_animationDriver;
+  }
+  data->m_animationDriver = nullptr;
+  if (data->m_context)
+    delete data->m_context;
+  data->m_context = nullptr;
+  if (data->m_surface)
+    delete data->m_surface;
+  data->m_surface = nullptr;
+}
+
+static struct SharedRenderData *
+shared_render_data_ref (struct SharedRenderData * data)
+{
+  GST_TRACE ("%p reffing shared render data", data);
+  g_atomic_int_inc (&data->refcount);
+  return data;
+}
+
+static void
+shared_render_data_unref (struct SharedRenderData * data)
+{
+  GST_TRACE ("%p unreffing shared render data", data);
+  if (g_atomic_int_dec_and_test (&data->refcount))
+    shared_render_data_free (data);
+}
+
 void
 GstQuickRenderer::deactivateContext ()
 {
@@ -123,25 +179,34 @@ delete_cxx (QOpenGLFramebufferObject * cxx)
 
 GstQuickRenderer::GstQuickRenderer()
     : gl_context(NULL),
-      m_context(nullptr),
-      m_renderThread(nullptr),
-      m_surface(nullptr),
       m_fbo(nullptr),
       m_quickWindow(nullptr),
       m_renderControl(nullptr),
       m_qmlEngine(nullptr),
       m_qmlComponent(nullptr),
       m_rootItem(nullptr),
-      m_animationDriver(nullptr),
       gl_allocator(NULL),
       gl_params(NULL),
-      gl_mem(NULL)
+      gl_mem(NULL),
+      m_sharedRenderData(NULL)
 {
     init_debug ();
 }
 
+static gpointer
+dup_shared_render_data (gpointer data, gpointer user_data)
+{
+    struct SharedRenderData *render_data = (struct SharedRenderData *) data;
+
+    if (render_data)
+      return shared_render_data_ref (render_data);
+
+    return NULL;
+}
+
 bool GstQuickRenderer::init (GstGLContext * context, GError ** error)
 {
+    g_return_val_if_fail (GST_IS_GL_CONTEXT (context), false);
     g_return_val_if_fail (gst_gl_context_get_current () == context, false);
 
     QVariant qt_native_context = qt_opengl_native_context_from_gst_gl_context (context);
@@ -152,40 +217,63 @@ bool GstQuickRenderer::init (GstGLContext * context, GError ** error)
             "native context");
         return false;
     }
-
-    m_context = new QOpenGLContext;
-    m_context->setNativeHandle(qt_native_context);
-
-    m_surface = new GstBackingSurface;
-    m_surface->create();  /* FIXME: may need to be called on Qt's main thread */
-
-    m_renderThread = QThread::currentThread();
-    gst_gl_context_activate (context, FALSE);
-
-    /* Qt does some things that it may require the OpenGL context current in
-     * ->create() so that it has the necessry information to create the
-     * QOpenGLContext from the native handle. This may fail if the OpenGL
-     * context is already current in another thread so we need to deactivate
-     * the context from GStreamer's thread before asking Qt to create the
-     * QOpenGLContext with ->create().
-     */
-    m_context->create();
-    m_context->doneCurrent();
-
-    m_context->moveToThread (m_renderThread);
-    if (!m_context->makeCurrent(m_surface)) {
-        g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND,
-            "Could not make Qt OpenGL context current");
-        /* try to keep the same OpenGL context state */
-        gst_gl_context_activate (context, TRUE);
-        return false;
-    }
-
-    if (!gst_gl_context_activate (context, TRUE)) {
-        g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND,
-            "Could not make OpenGL context current again");
-        return false;
+    QThread *renderThread = QThread::currentThread();
+
+    struct SharedRenderData *render_data = NULL, *old_render_data;
+    do {
+        if (render_data)
+            shared_render_data_unref (render_data);
+
+        old_render_data = render_data = (struct SharedRenderData *)
+                g_object_dup_data (G_OBJECT (context),
+                "qt.gl.render.shared.data", dup_shared_render_data, NULL);
+        if (!render_data)
+            render_data = shared_render_data_new ();
+    } while (old_render_data != render_data
+            && !g_object_replace_data (G_OBJECT (context),
+                "qt.gl.render.shared.data", old_render_data, render_data,
+                NULL, NULL));
+    m_sharedRenderData = render_data;
+
+    g_mutex_lock (&m_sharedRenderData->lock);
+    if (!m_sharedRenderData->m_context) {
+        m_sharedRenderData->m_context = new QOpenGLContext;
+        GST_TRACE ("%p new QOpenGLContext %p", this, m_sharedRenderData->m_context);
+        m_sharedRenderData->m_context->setNativeHandle(qt_native_context);
+
+        m_sharedRenderData->m_surface = new GstBackingSurface;
+        m_sharedRenderData->m_surface->create();  /* FIXME: may need to be called on Qt's main thread */
+
+        gst_gl_context_activate (context, FALSE);
+
+        /* Qt does some things that it may require the OpenGL context current in
+         * ->create() so that it has the necessry information to create the
+         * QOpenGLContext from the native handle. This may fail if the OpenGL
+         * context is already current in another thread so we need to deactivate
+         * the context from GStreamer's thread before asking Qt to create the
+         * QOpenGLContext with ->create().
+         */
+        m_sharedRenderData->m_context->create();
+        m_sharedRenderData->m_context->doneCurrent();
+
+        m_sharedRenderData->m_context->moveToThread (renderThread);
+        if (!m_sharedRenderData->m_context->makeCurrent(m_sharedRenderData->m_surface)) {
+            g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND,
+                "Could not make Qt OpenGL context current");
+            /* try to keep the same OpenGL context state */
+            gst_gl_context_activate (context, TRUE);
+            g_mutex_unlock (&m_sharedRenderData->lock);
+            return false;
+        }
+
+        if (!gst_gl_context_activate (context, TRUE)) {
+            g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND,
+                "Could not make OpenGL context current again");
+            g_mutex_unlock (&m_sharedRenderData->lock);
+            return false;
+        }
     }
+    g_mutex_unlock (&m_sharedRenderData->lock);
 
     m_renderControl = new QQuickRenderControl();
     /* Create a QQuickWindow that is associated with our render control. Note that this
@@ -194,7 +282,7 @@ bool GstQuickRenderer::init (GstGLContext * context, GError ** error)
      */
     m_quickWindow = new QQuickWindow(m_renderControl);
     /* after QQuickWindow creation as QQuickRenderControl requires it */
-    m_renderControl->prepareThread (m_renderThread);
+    m_renderControl->prepareThread (renderThread);
 
     /* Create a QML engine. */
     m_qmlEngine = new QQmlEngine;
@@ -203,9 +291,47 @@ bool GstQuickRenderer::init (GstGLContext * context, GError ** error)
 
     gl_context = static_cast<GstGLContext*>(gst_object_ref (context));
     gl_allocator = (GstGLBaseMemoryAllocator *) gst_gl_memory_allocator_get_default (gl_context);
-    gl_params = (GstGLAllocationParams *) (gst_gl_video_allocation_params_new_wrapped_texture (gl_context,
-        NULL, &this->v_info, 0, NULL, GST_GL_TEXTURE_TARGET_2D, GST_GL_RGBA8,
-        0, NULL, (GDestroyNotify) delete_cxx));
+    gl_params = (GstGLAllocationParams *)
+        gst_gl_video_allocation_params_new_wrapped_texture (gl_context,
+            NULL, &this->v_info, 0, NULL, GST_GL_TEXTURE_TARGET_2D, GST_GL_RGBA8,
+            0, NULL, (GDestroyNotify) delete_cxx);
+
+    /* This is a gross hack relying on the internals of Qt and GStreamer
+     * however it's the only way to remove this warning on shutdown of all
+     * resources.
+     *
+     * GLib-CRITICAL **: 17:35:24.988: g_main_context_pop_thread_default: assertion 'g_queue_peek_head (stack) == context' failed
+     *
+     * The reason is that libgstgl has a GMainContext that it pushes as the
+     * thread default context.  Then later, Qt pushes a thread default main
+     * context.  The detruction order of the GMainContext's is reversed as
+     * GStreamer will explicitly pop the thread default main context however
+     * Qt pops when the thread is about to be destroyed.  GMainContext is
+     * unhappy with the ordering of the pops.
+     */
+    GMainContext *gst_main_context = g_main_context_ref_thread_default ();
+
+    /* make Qt allocate and push a thread-default GMainContext if it is
+     * going to */
+    QEventLoop loop;
+    if (loop.processEvents())
+        GST_LOG ("pending QEvents processed");
+
+    GMainContext *qt_main_context = g_main_context_ref_thread_default ();
+
+    if (qt_main_context == gst_main_context) {
+        g_main_context_unref (qt_main_context);
+        g_main_context_unref (gst_main_context);
+    } else {
+        /* We flip the order of the GMainContext's so that the destruction
+         * order can be preserved. */
+        g_main_context_pop_thread_default (qt_main_context);
+        g_main_context_pop_thread_default (gst_main_context);
+        g_main_context_push_thread_default (qt_main_context);
+        g_main_context_push_thread_default (gst_main_context);
+        g_main_context_unref (qt_main_context);
+        g_main_context_unref (gst_main_context);
+    }
 
     return true;
 }
@@ -213,11 +339,14 @@ bool GstQuickRenderer::init (GstGLContext * context, GError ** error)
 GstQuickRenderer::~GstQuickRenderer()
 {
     gst_gl_allocation_params_free (gl_params);
+    gst_clear_object (&gl_allocator);
 }
 
-void GstQuickRenderer::stop ()
+void GstQuickRenderer::stopGL ()
 {
-    g_assert (QOpenGLContext::currentContext() == m_context);
+    GST_DEBUG ("%p stop QOpenGLContext curent: %p stored: %p", this,
+        QOpenGLContext::currentContext(), m_sharedRenderData->m_context);
+    g_assert (QOpenGLContext::currentContext() == m_sharedRenderData->m_context);
 
     if (m_renderControl)
         m_renderControl->invalidate();
@@ -226,10 +355,13 @@ void GstQuickRenderer::stop ()
         delete m_fbo;
     m_fbo = nullptr;
 
-    m_context->doneCurrent();
-    if (m_animationDriver)
-        delete m_animationDriver;
-    m_animationDriver = nullptr;
+    QEventLoop loop;
+    if (loop.processEvents())
+        GST_LOG ("%p pending QEvents processed", this);
+
+    if (m_sharedRenderData)
+        shared_render_data_unref (m_sharedRenderData);
+    m_sharedRenderData = NULL;
 }
 
 void GstQuickRenderer::cleanup()
@@ -253,28 +385,27 @@ void GstQuickRenderer::cleanup()
     if (m_qmlEngine)
         delete m_qmlEngine;
     m_qmlEngine = nullptr;
+    if (m_rootItem)
+        delete m_rootItem;
+    m_rootItem = nullptr;
 
     gst_clear_object (&gl_context);
-
-    if (m_context)
-        delete m_context;
-    m_context = nullptr;
 }
 
 void GstQuickRenderer::ensureFbo()
 {
-    if (m_fbo && m_fbo->size() != m_surface->size()) {
-        GST_INFO ("removing old framebuffer created with size %ix%i",
-            m_fbo->size().width(), m_fbo->size().height());
+    if (m_fbo && m_fbo->size() != m_sharedRenderData->m_surface->size()) {
+        GST_INFO ("%p removing old framebuffer created with size %ix%i",
+            this, m_fbo->size().width(), m_fbo->size().height());
         delete m_fbo;
         m_fbo = nullptr;
     }
 
     if (!m_fbo) {
-        m_fbo = new QOpenGLFramebufferObject(m_surface->size(),
-                                             QOpenGLFramebufferObject::CombinedDepthStencil);
+        m_fbo = new QOpenGLFramebufferObject(m_sharedRenderData->m_surface->size(),
+                QOpenGLFramebufferObject::CombinedDepthStencil);
         m_quickWindow->setRenderTarget(m_fbo);
-        GST_DEBUG ("new framebuffer created with size %ix%i",
+        GST_DEBUG ("%p new framebuffer created with size %ix%i", this,
             m_fbo->size().width(), m_fbo->size().height());
     }
 }
@@ -282,15 +413,18 @@ void GstQuickRenderer::ensureFbo()
 void
 GstQuickRenderer::renderGstGL ()
 {
-    GST_DEBUG ("current QOpenGLContext %p", QOpenGLContext::currentContext());
+    GST_TRACE ("%p current QOpenGLContext %p", this,
+        QOpenGLContext::currentContext());
     m_quickWindow->resetOpenGLState();
 
-    m_animationDriver->advance();
+    m_sharedRenderData->m_animationDriver->advance();
 
     QEventLoop loop;
     if (loop.processEvents())
         GST_LOG ("pending QEvents processed");
 
+    loop.exit();
+
     ensureFbo();
 
     /* Synchronization and rendering happens here on the render thread. */
@@ -310,7 +444,7 @@ GstQuickRenderer::renderGstGL ()
 
 GstGLMemory *GstQuickRenderer::generateOutput(GstClockTime input_ns)
 {
-    m_animationDriver->setNextTime(input_ns / GST_MSECOND);
+    m_sharedRenderData->m_animationDriver->setNextTime(input_ns / GST_MSECOND);
 
     /* run an event loop to update any changed values for rendering */
     QEventLoop loop;
@@ -326,7 +460,8 @@ GstGLMemory *GstQuickRenderer::generateOutput(GstClockTime input_ns)
     m_renderControl->polishItems();
 
     /* TODO: an async version could be used where */
-    gst_gl_context_thread_add (gl_context, (GstGLContextThreadFunc) GstQuickRenderer::render_gst_gl_c, this);
+    gst_gl_context_thread_add (gl_context,
+            (GstGLContextThreadFunc) GstQuickRenderer::render_gst_gl_c, this);
 
     GstGLMemory *tmp = gl_mem;
     gl_mem = NULL;
@@ -337,12 +472,12 @@ GstGLMemory *GstQuickRenderer::generateOutput(GstClockTime input_ns)
 void GstQuickRenderer::initializeGstGL ()
 {
     GST_TRACE ("current QOpenGLContext %p", QOpenGLContext::currentContext());
-    if (!m_context->makeCurrent(m_surface)) {
+    if (!m_sharedRenderData->m_context->makeCurrent(m_sharedRenderData->m_surface)) {
         m_errorString = "Failed to make Qt's wrapped OpenGL context current";
         return;
     }
     GST_INFO ("current QOpenGLContext %p", QOpenGLContext::currentContext());
-    m_renderControl->initialize(m_context);
+    m_renderControl->initialize(m_sharedRenderData->m_context);
 
     /* 1. QAnimationDriver's are thread-specific
      * 2. QAnimationDriver controls the 'animation time' that the Qml scene is
@@ -350,13 +485,18 @@ void GstQuickRenderer::initializeGstGL ()
      */
     /* FIXME: what happens with multiple qmlgloverlay elements?  Do we need a 
      * shared animation driver? */
-    m_animationDriver = new GstAnimationDriver;
-    m_animationDriver->install();
+    g_mutex_lock (&m_sharedRenderData->lock);
+    if (m_sharedRenderData->m_animationDriver == nullptr) {
+        m_sharedRenderData->m_animationDriver = new GstAnimationDriver;
+        m_sharedRenderData->m_animationDriver->install();
+    }
+    g_mutex_unlock (&m_sharedRenderData->lock);
 }
 
 void GstQuickRenderer::initializeQml()
 {
-    disconnect(m_qmlComponent, &QQmlComponent::statusChanged, this, &GstQuickRenderer::initializeQml);
+    disconnect(m_qmlComponent, &QQmlComponent::statusChanged, this,
+            &GstQuickRenderer::initializeQml);
 
     if (m_qmlComponent->isError()) {
         const QList<QQmlError> errorList = m_qmlComponent->errors();
@@ -388,12 +528,14 @@ void GstQuickRenderer::initializeQml()
     updateSizes();
 
     /* Initialize the render control and our OpenGL resources. */
-    gst_gl_context_thread_add (gl_context, (GstGLContextThreadFunc) GstQuickRenderer::initialize_gst_gl_c, this);
+    gst_gl_context_thread_add (gl_context,
+            (GstGLContextThreadFunc) GstQuickRenderer::initialize_gst_gl_c, this);
 }
 
 void GstQuickRenderer::updateSizes()
 {
-    GstBackingSurface *surface = static_cast<GstBackingSurface *>(m_surface);
+    GstBackingSurface *surface =
+            static_cast<GstBackingSurface *>(m_sharedRenderData->m_surface);
     /* Behave like SizeRootObjectToView. */
     QSize size = surface->size();
 
@@ -411,7 +553,7 @@ void GstQuickRenderer::updateSizes()
 
 void GstQuickRenderer::setSize(int w, int h)
 {
-    static_cast<GstBackingSurface *>(m_surface)->setSize(w, h);
+    static_cast<GstBackingSurface *>(m_sharedRenderData->m_surface)->setSize(w, h);
     updateSizes();
 }
 
@@ -427,7 +569,8 @@ bool GstQuickRenderer::setQmlScene (const gchar * scene, GError ** error)
     m_qmlComponent->setData(QByteArray (scene), QUrl(""));
     if (m_qmlComponent->isLoading())
         /* TODO: handle async properly */
-        connect(m_qmlComponent, &QQmlComponent::statusChanged, this, &GstQuickRenderer::initializeQml);
+        connect(m_qmlComponent, &QQmlComponent::statusChanged, this,
+                &GstQuickRenderer::initializeQml);
     else
         initializeQml();
 
index 256b1b0..acfcbfd 100644 (file)
@@ -80,8 +80,8 @@ private:
     static void initialize_gst_gl_c (GstGLContext * context, GstQuickRenderer * self) { self->initializeGstGL (); }
     void initializeGstGL ();
 
-    static void stop_c (GstGLContext * context, GstQuickRenderer * self) { self->stop (); }
-    void stop ();
+    static void stop_c (GstGLContext * context, GstQuickRenderer * self) { self->stopGL (); }
+    void stopGL ();
 
     static void activate_context_c (GstGLContext * context, GstQuickRenderer * self) { self->activateContext (); }
     void activateContext ();
@@ -90,16 +90,12 @@ private:
     void deactivateContext ();
 
     GstGLContext *gl_context;
-    QOpenGLContext *m_context;
-    QThread *m_renderThread;
-    GstBackingSurface *m_surface;
     QOpenGLFramebufferObject *m_fbo;
     QQuickWindow *m_quickWindow;
     QQuickRenderControl *m_renderControl;
     QQmlEngine *m_qmlEngine;
     QQmlComponent *m_qmlComponent;
     QQuickItem *m_rootItem;
-    GstAnimationDriver *m_animationDriver;
 
     GstGLBaseMemoryAllocator *gl_allocator;
     GstGLAllocationParams *gl_params;
@@ -107,6 +103,7 @@ private:
     GstGLMemory *gl_mem;
 
     QString m_errorString;
+    struct SharedRenderData *m_sharedRenderData;
 };
 
 #endif /* __QT_QUICK_RENDER_H__ */
index 62c28a5..4bb1e89 100644 (file)
@@ -135,7 +135,7 @@ QtGLVideoItem::~QtGLVideoItem()
    * no qmlglsink's will call in again, and that
    * any ongoing calls are done by invalidating the proxy
    * pointer */
-  GST_INFO ("Destroying QtGLVideoItem and invalidating the proxy");
+  GST_INFO ("%p Destroying QtGLVideoItem and invalidating the proxy %p", this, proxy.get());
   proxy->invalidateRef();
   proxy.clear();
 
@@ -147,6 +147,8 @@ QtGLVideoItem::~QtGLVideoItem()
   if (this->priv->display)
     gst_object_unref(this->priv->display);
 
+  gst_buffer_replace (&this->priv->buffer, NULL);
+
   gst_caps_replace (&this->priv->caps, NULL);
   g_free (this->priv);
   this->priv = NULL;
@@ -261,11 +263,13 @@ QtGLVideoItemInterface::setBuffer (GstBuffer * buffer)
 {
   QMutexLocker locker(&lock);
 
-  if (qt_item == NULL)
+  if (qt_item == NULL) {
+    GST_WARNING ("%p actual item is NULL. setBuffer call ignored", this);
     return;
+  }
 
   if (!qt_item->priv->negotiated) {
-    GST_WARNING ("Got buffer on unnegotiated QtGLVideoItem. Dropping");
+    GST_WARNING ("%p Got buffer on unnegotiated QtGLVideoItem. Dropping", this);
     return;
   }
 
@@ -281,7 +285,7 @@ QtGLVideoItemInterface::setBuffer (GstBuffer * buffer)
 void
 QtGLVideoItem::onSceneGraphInitialized ()
 {
-  GST_DEBUG ("scene graph initialization with Qt GL context %p",
+  GST_DEBUG ("%p scene graph initialization with Qt GL context %p", this,
       this->window()->openglContext ());
 
   if (this->priv->qt_context == this->window()->openglContext ())
@@ -308,6 +312,10 @@ QtGLVideoItem::onSceneGraphInvalidated ()
   GST_FIXME ("%p scene graph invalidated", this);
 }
 
+/**
+ * Retrieve and populate the GL context information from the current
+ * OpenGL context.
+ */
 gboolean
 QtGLVideoItemInterface::initWinSys ()
 {
@@ -408,27 +416,28 @@ _calculate_par (QtGLVideoItem * widget, GstVideoInfo * info)
   if (!ok)
     return FALSE;
 
-  GST_LOG ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n, display_par_d);
+  GST_LOG ("%p PAR: %u/%u DAR:%u/%u", widget, par_n, par_d, display_par_n,
+      display_par_d);
 
   if (height % display_ratio_den == 0) {
-    GST_DEBUG ("keeping video height");
+    GST_DEBUG ("%p keeping video height", widget);
     widget->priv->display_width = (guint)
         gst_util_uint64_scale_int (height, display_ratio_num,
         display_ratio_den);
     widget->priv->display_height = height;
   } else if (width % display_ratio_num == 0) {
-    GST_DEBUG ("keeping video width");
+    GST_DEBUG ("%p keeping video width", widget);
     widget->priv->display_width = width;
     widget->priv->display_height = (guint)
         gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
   } else {
-    GST_DEBUG ("approximating while keeping video height");
+    GST_DEBUG ("%p approximating while keeping video height", widget);
     widget->priv->display_width = (guint)
         gst_util_uint64_scale_int (height, display_ratio_num,
         display_ratio_den);
     widget->priv->display_height = height;
   }
-  GST_DEBUG ("scaling to %dx%d", widget->priv->display_width,
+  GST_DEBUG ("%p scaling to %dx%d", widget, widget->priv->display_width,
       widget->priv->display_height);
 
   return TRUE;
index ba8f1e8..26f20b9 100644 (file)
@@ -60,12 +60,13 @@ int main(int argc, char *argv[])
     /* the plugin must be loaded before loading the qml file to register the
      * GstGLVideoItem qml item */
     GstElement *overlay = gst_element_factory_make ("qmlgloverlay", NULL);
+    GstElement *overlay2 = gst_element_factory_make ("qmlgloverlay", NULL);
     GstElement *sink = gst_element_factory_make ("qmlglsink", NULL);
 
     g_assert (src && glupload && overlay && sink);
 
-    gst_bin_add_many (GST_BIN (pipeline), src, glupload, overlay, sink, NULL);
-    gst_element_link_many (src, glupload, overlay, sink, NULL);
+    gst_bin_add_many (GST_BIN (pipeline), src, glupload, overlay, overlay2, sink, NULL);
+    gst_element_link_many (src, glupload, overlay, overlay2, sink, NULL);
 
     /* load qmlglsink output */
     QQmlApplicationEngine engine;
@@ -93,10 +94,21 @@ int main(int argc, char *argv[])
     QByteArray overlay_scene = f.readAll();
     qDebug() << overlay_scene;
 
+    QFile f2(":/overlay2.qml");
+    if(!f2.open(QIODevice::ReadOnly)) {
+        qWarning() << "error: " << f2.errorString();
+        return 1;
+    }
+    QByteArray overlay_scene2 = f2.readAll();
+    qDebug() << overlay_scene2;
+
     /* load qmlgloverlay contents */
     g_signal_connect (overlay, "qml-scene-initialized", G_CALLBACK (on_overlay_scene_initialized), NULL);
     g_object_set (overlay, "qml-scene", overlay_scene.data(), NULL);
 
+    g_signal_connect (overlay2, "qml-scene-initialized", G_CALLBACK (on_overlay_scene_initialized), NULL);
+    g_object_set (overlay2, "qml-scene", overlay_scene2.data(), NULL);
+
     rootObject->scheduleRenderJob (new SetPlaying (pipeline),
         QQuickWindow::BeforeSynchronizingStage);
 
diff --git a/tests/examples/qt/qmloverlay/overlay2.qml b/tests/examples/qt/qmloverlay/overlay2.qml
new file mode 100644 (file)
index 0000000..fbe832a
--- /dev/null
@@ -0,0 +1,57 @@
+import QtQuick 2.4
+
+import org.freedesktop.gstreamer.GLVideoItem 1.0
+
+Item {
+    /* render upside down for GStreamer */
+    transform: Scale { origin.x : 0; origin.y : height / 2.; yScale : -1 }
+
+    GstGLVideoItem {
+        id: video
+        objectName: "inputVideoItem"
+        anchors.centerIn: parent
+        width: parent.width
+        height: parent.height
+    }
+
+    Text {
+        id: rotatingText
+        anchors.centerIn: parent
+        text: "Qt Quick\nrendered to\na texture"
+        font.pointSize: 20
+        color: "black"
+        style: Text.Outline
+        styleColor: "white"
+
+        RotationAnimator {
+            target: rotatingText;
+            from: 0;
+            to: 360;
+            duration: 10000
+            running: true
+            loops: Animation.Infinite
+        }
+    }
+
+    Text {
+        property int elapsedTime: 0
+
+        id: time
+        anchors.bottom: rotatingText.top
+        anchors.horizontalCenter: rotatingText.horizontalCenter
+        font.pointSize: 12
+        style: Text.Outline
+        styleColor: "red"
+        color: "black"
+
+        Timer {
+            interval: 1000
+            running: true
+            repeat: true
+            onTriggered: {
+                parent.elapsedTime += interval / 1000
+                parent.text = "overlay: " + parent.elapsedTime.toString() + " seconds"
+            }
+        }
+    }
+}
index 099a78d..42fb250 100644 (file)
@@ -2,5 +2,6 @@
     <qresource prefix="/">
         <file>main.qml</file>
         <file>overlay.qml</file>
+        <file>overlay2.qml</file>
     </qresource>
 </RCC>