From 7a25fb5b087f64413afc737317bee1ac8517455d Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Wed, 26 Feb 2020 18:29:06 +1100 Subject: [PATCH] qt: add a qml overlay filter element [part 2] 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 | 30 +-- ext/qt/gstqtoverlay.cc | 7 + ext/qt/qtglrenderer.cc | 285 +++++++++++++++++++++------- ext/qt/qtglrenderer.h | 9 +- ext/qt/qtitem.cc | 27 ++- tests/examples/qt/qmloverlay/main.cpp | 16 +- tests/examples/qt/qmloverlay/overlay2.qml | 57 ++++++ tests/examples/qt/qmloverlay/qmloverlay.qrc | 1 + 8 files changed, 333 insertions(+), 99 deletions(-) create mode 100644 tests/examples/qt/qmloverlay/overlay2.qml diff --git a/ext/qt/gstqtglutility.cc b/ext/qt/gstqtglutility.cc index ba17282..50ff8d4 100644 --- a/ext/qt/gstqtglutility.cc +++ b/ext/qt/gstqtglutility.cc @@ -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) diff --git a/ext/qt/gstqtoverlay.cc b/ext/qt/gstqtoverlay.cc index 0c52634..b9aa5fb 100644 --- a/ext/qt/gstqtoverlay.cc +++ b/ext/qt/gstqtoverlay.cc @@ -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; diff --git a/ext/qt/qtglrenderer.cc b/ext/qt/qtglrenderer.cc index 0e1b3ae..e78bcdf 100644 --- a/ext/qt/qtglrenderer.cc +++ b/ext/qt/qtglrenderer.cc @@ -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(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 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(m_surface); + GstBackingSurface *surface = + static_cast(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(m_surface)->setSize(w, h); + static_cast(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(); diff --git a/ext/qt/qtglrenderer.h b/ext/qt/qtglrenderer.h index 256b1b0..acfcbfd 100644 --- a/ext/qt/qtglrenderer.h +++ b/ext/qt/qtglrenderer.h @@ -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__ */ diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 62c28a5..4bb1e89 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -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; diff --git a/tests/examples/qt/qmloverlay/main.cpp b/tests/examples/qt/qmloverlay/main.cpp index ba8f1e8..26f20b9 100644 --- a/tests/examples/qt/qmloverlay/main.cpp +++ b/tests/examples/qt/qmloverlay/main.cpp @@ -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 index 0000000..fbe832a --- /dev/null +++ b/tests/examples/qt/qmloverlay/overlay2.qml @@ -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" + } + } + } +} diff --git a/tests/examples/qt/qmloverlay/qmloverlay.qrc b/tests/examples/qt/qmloverlay/qmloverlay.qrc index 099a78d..42fb250 100644 --- a/tests/examples/qt/qmloverlay/qmloverlay.qrc +++ b/tests/examples/qt/qmloverlay/qmloverlay.qrc @@ -2,5 +2,6 @@ main.qml overlay.qml + overlay2.qml -- 2.7.4