From b1bbdceeb2cf055f8a57785b058ce3db8f84514b Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Mon, 6 Jul 2015 23:10:51 +1000 Subject: [PATCH 01/78] new qt5 qml GL video sink Very much in the same spirit as the Gtk GL sink Two things are provided 1. A QQuickItem subclass that renders out RGBA filled GstGLMemory buffers that is instantiated from qml. 2. A sink element that will push buffers into (1) To use 1. Declare the GstGLVideoItem in qml with an appropriate objectName property set. 2. Get the aforementioned GstGLVideoItem from qml using something like QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); QObject *rootObject = engine.rootObjects().first(); QQuickItem *videoItem = rootObject->findChild ("videoItem"); 3. Set the videoItem on the sink https://bugzilla.gnome.org/show_bug.cgi?id=752185 --- ext/qt/.gitignore | 1 + ext/qt/Makefile.am | 55 ++++ ext/qt/gstplugin.cc | 46 +++ ext/qt/gstqsgtexture.cc | 142 +++++++++ ext/qt/gstqsgtexture.h | 52 ++++ ext/qt/gstqtsink.cc | 500 +++++++++++++++++++++++++++++++ ext/qt/gstqtsink.h | 81 +++++ ext/qt/qtitem.cc | 491 ++++++++++++++++++++++++++++++ ext/qt/qtitem.h | 72 +++++ tests/examples/qt/qml/.gitignore | 3 + tests/examples/qt/qml/main.cpp | 80 +++++ tests/examples/qt/qml/main.qml | 60 ++++ tests/examples/qt/qml/play.pro | 20 ++ tests/examples/qt/qml/qml.qrc | 5 + 14 files changed, 1608 insertions(+) create mode 100644 ext/qt/.gitignore create mode 100644 ext/qt/Makefile.am create mode 100644 ext/qt/gstplugin.cc create mode 100644 ext/qt/gstqsgtexture.cc create mode 100644 ext/qt/gstqsgtexture.h create mode 100644 ext/qt/gstqtsink.cc create mode 100644 ext/qt/gstqtsink.h create mode 100644 ext/qt/qtitem.cc create mode 100644 ext/qt/qtitem.h create mode 100644 tests/examples/qt/qml/.gitignore create mode 100644 tests/examples/qt/qml/main.cpp create mode 100644 tests/examples/qt/qml/main.qml create mode 100644 tests/examples/qt/qml/play.pro create mode 100644 tests/examples/qt/qml/qml.qrc diff --git a/ext/qt/.gitignore b/ext/qt/.gitignore new file mode 100644 index 0000000000..f830c38fc1 --- /dev/null +++ b/ext/qt/.gitignore @@ -0,0 +1 @@ +moc_*.cc diff --git a/ext/qt/Makefile.am b/ext/qt/Makefile.am new file mode 100644 index 0000000000..5dc38dc085 --- /dev/null +++ b/ext/qt/Makefile.am @@ -0,0 +1,55 @@ +# preamble +NULL = + +moc_headers = \ + qtitem.h \ + gstqsgtexture.h \ + $(NULL) + +moc_generated = \ + moc_qtitem.cc \ + moc_gstqsgtexture.cc \ + $(NULL) + +#anything generated by the Qt tools... +BUILT_SOURCES = $(moc_generated) +CLEANFILES = $(moc_generated) + +sources = \ + gstqsgtexture.cc \ + qtitem.cc \ + gstqtsink.cc \ + gstqtsink.h \ + gstplugin.cc \ + $(moc_headers) \ + $(moc_generated) \ + $(NULL) + +libqtsink_la_CXXFLAGS = \ + -I$(top_srcdir)/gst-libs \ + -I$(top_builddir)/gst-libs \ + $(GST_CXXFLAGS) \ + $(GST_BASE_CFLAGS) \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(QT_CFLAGS) +libqtsink_la_LIBADD = \ + $(GST_BASE_LIBS) \ + $(GST_PLUGINS_BASE_LIBS) \ + $(QT_LIBS) \ + $(top_builddir)/gst-libs/gst/gl/libgstgl-$(GST_API_VERSION).la \ + -lgstvideo-$(GST_API_VERSION) + +libqtsink_la_SOURCES = $(sources) +libqtsink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libqtsink_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +plugin_LTLIBRARIES = libqtsink.la + +$(moc_generated): moc_%.cc: %.h + @@MOC@ -o$@ $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(MOC_CPPFLAGS) $< + +ui-%.h: %.ui + @@UIC@ -o $@ $< + +qrc-%.cc: %.qrc + @@RCC@ -o $@ $< diff --git a/ext/qt/gstplugin.cc b/ext/qt/gstplugin.cc new file mode 100644 index 0000000000..70b6310f01 --- /dev/null +++ b/ext/qt/gstplugin.cc @@ -0,0 +1,46 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstqtsink.h" +#include + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_element_register (plugin, "qmlglsink", + GST_RANK_NONE, GST_TYPE_QT_SINK)) { + return FALSE; + } + /* this means the plugin must be loaded before the qml engine is loaded */ + qmlRegisterType ("org.freedesktop.gstreamer.GLVideoItem", 1, 0, "GstGLVideoItem"); + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + qt, + "Qt sink", + plugin_init, PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN) diff --git a/ext/qt/gstqsgtexture.cc b/ext/qt/gstqsgtexture.cc new file mode 100644 index 0000000000..1e5203832a --- /dev/null +++ b/ext/qt/gstqsgtexture.cc @@ -0,0 +1,142 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include "gstqsgtexture.h" + +#define GST_CAT_DEFAULT gst_qsg_texture_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +GstQSGTexture::GstQSGTexture () +{ + static volatile gsize _debug; + + initializeOpenGLFunctions(); + + if (g_once_init_enter (&_debug)) { + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qtqsgtexture", 0, + "Qt Scenegraph Texture"); + g_once_init_leave (&_debug, 1); + } + + gst_video_info_init (&this->v_info); + this->buffer_ = NULL; +} + +GstQSGTexture::~GstQSGTexture () +{ + gst_buffer_replace (&this->buffer_, NULL); +} + +/* only called from the streaming thread with scene graph thread blocked */ +void +GstQSGTexture::setCaps (GstCaps * caps) +{ + GST_LOG ("%p setCaps %" GST_PTR_FORMAT, this, caps); + + gst_video_info_from_caps (&this->v_info, caps); +} + +/* only called from the streaming thread with scene graph thread blocked */ +void +GstQSGTexture::setBuffer (GstBuffer * buffer) +{ + GST_LOG ("%p setBuffer %" GST_PTR_FORMAT, this, buffer); + /* FIXME: update more state here */ + gst_buffer_replace (&this->buffer_, buffer); +} + +/* only called from qt's scene graph render thread */ +void +GstQSGTexture::bind () +{ + guint tex_id; + + if (!this->buffer_) + return; + if (GST_VIDEO_INFO_FORMAT (&this->v_info) == GST_VIDEO_FORMAT_UNKNOWN) + return; + + /* FIXME: should really lock the memory to prevent write access */ + if (!gst_video_frame_map (&this->v_frame, &this->v_info, this->buffer_, + (GstMapFlags) (GST_MAP_READ | GST_MAP_GL))) { + g_assert_not_reached (); + return; + } + + tex_id = *(guint *) this->v_frame.data[0]; + GST_LOG ("%p binding Qt texture %u", this, tex_id); + + glBindTexture (GL_TEXTURE_2D, tex_id); + + gst_video_frame_unmap (&this->v_frame); +} + +/* can be called from any thread */ +int +GstQSGTexture::textureId () const +{ + int tex_id = 0; + + if (this->buffer_) { + GstMemory *mem = gst_buffer_peek_memory (this->buffer_, 0); + + tex_id = ((GstGLMemory *) mem)->tex_id; + } + + GST_LOG ("%p get texture id %u", this, tex_id); + + return tex_id; +} + +/* can be called from any thread */ +QSize +GstQSGTexture::textureSize () const +{ + if (GST_VIDEO_INFO_FORMAT (&this->v_info) == GST_VIDEO_FORMAT_UNKNOWN) + return QSize (0, 0); + + GST_TRACE ("%p get texture size %ux%u", this, this->v_info.width, + this->v_info.height); + + return QSize (this->v_info.width, this->v_info.height); +} + +/* can be called from any thread */ +bool +GstQSGTexture::hasAlphaChannel () const +{ + /* FIXME: support RGB textures */ + return true; +} + +/* can be called from any thread */ +bool +GstQSGTexture::hasMipmaps () const +{ + return false; +} diff --git a/ext/qt/gstqsgtexture.h b/ext/qt/gstqsgtexture.h new file mode 100644 index 0000000000..93fb60d063 --- /dev/null +++ b/ext/qt/gstqsgtexture.h @@ -0,0 +1,52 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_QSG_TEXTURE_H__ +#define __GST_QSG_TEXTURE_H__ + +#include +#include +#include +#include + +class GstQSGTexture : public QSGTexture, protected QOpenGLFunctions +{ + Q_OBJECT +public: + GstQSGTexture (); + ~GstQSGTexture (); + + void setCaps (GstCaps * caps); + void setBuffer (GstBuffer * buffer); + + /* QSGTexture */ + void bind (); + int textureId () const; + QSize textureSize () const; + bool hasAlphaChannel () const; + bool hasMipmaps () const; + +private: + GstBuffer * buffer_; + GstVideoInfo v_info; + GstVideoFrame v_frame; +}; + +#endif /* __GST_QSG_TEXTURE_H__ */ diff --git a/ext/qt/gstqtsink.cc b/ext/qt/gstqtsink.cc new file mode 100644 index 0000000000..40e66d8d70 --- /dev/null +++ b/ext/qt/gstqtsink.cc @@ -0,0 +1,500 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:gstqtsink + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstqtsink.h" +#include + +#define GST_CAT_DEFAULT gst_debug_qt_gl_sink +GST_DEBUG_CATEGORY (GST_CAT_DEFAULT); + +static void gst_qt_sink_finalize (GObject * object); +static void gst_qt_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * param_spec); +static void gst_qt_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * param_spec); + +static gboolean gst_qt_sink_stop (GstBaseSink * bsink); + +static gboolean gst_qt_sink_query (GstBaseSink * bsink, GstQuery * query); + +static GstStateChangeReturn +gst_qt_sink_change_state (GstElement * element, GstStateChange transition); + +static void gst_qt_sink_get_times (GstBaseSink * bsink, GstBuffer * buf, + GstClockTime * start, GstClockTime * end); +static gboolean gst_qt_sink_set_caps (GstBaseSink * bsink, GstCaps * caps); +static GstFlowReturn gst_qt_sink_show_frame (GstVideoSink * bsink, + GstBuffer * buf); +static gboolean gst_qt_sink_propose_allocation (GstBaseSink * bsink, + GstQuery * query); + +static GstStaticPadTemplate gst_qt_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES + (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA"))); + +#define DEFAULT_FORCE_ASPECT_RATIO TRUE +#define DEFAULT_PAR_N 0 +#define DEFAULT_PAR_D 1 + +enum +{ + ARG_0, + PROP_WIDGET, + PROP_FORCE_ASPECT_RATIO, + PROP_PIXEL_ASPECT_RATIO, +}; + +enum +{ + SIGNAL_0, + LAST_SIGNAL +}; + +#define gst_qt_sink_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstQtSink, gst_qt_sink, + GST_TYPE_VIDEO_SINK, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, + "qtsink", 0, "Qt Video Sink")); + +static void +gst_qt_sink_class_init (GstQtSinkClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + GstVideoSinkClass *gstvideosink_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; + gstvideosink_class = (GstVideoSinkClass *) klass; + + gobject_class->set_property = gst_qt_sink_set_property; + gobject_class->get_property = gst_qt_sink_get_property; + + gst_element_class_set_metadata (gstelement_class, "Qt Video Sink", + "Sink/Video", "A video sink the renders to a QQuickItem", + "Matthew Waters "); + + g_object_class_install_property (gobject_class, PROP_WIDGET, + g_param_spec_pointer ("widget", "QQuickItem", + "The QQuickItem to place in the object heirachy", + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO, + g_param_spec_boolean ("force-aspect-ratio", + "Force aspect ratio", + "When enabled, scaling will respect original aspect ratio", + DEFAULT_FORCE_ASPECT_RATIO, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO, + gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio", + "The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D, + G_MAXINT, 1, 1, 1, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_qt_sink_template)); + + gobject_class->finalize = gst_qt_sink_finalize; + + gstelement_class->change_state = gst_qt_sink_change_state; + gstbasesink_class->query = gst_qt_sink_query; + gstbasesink_class->set_caps = gst_qt_sink_set_caps; + gstbasesink_class->get_times = gst_qt_sink_get_times; + gstbasesink_class->propose_allocation = gst_qt_sink_propose_allocation; + gstbasesink_class->stop = gst_qt_sink_stop; + + gstvideosink_class->show_frame = gst_qt_sink_show_frame; +} + +static void +gst_qt_sink_init (GstQtSink * qt_sink) +{ +} + +static void +gst_qt_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstQtSink *qt_sink = GST_QT_SINK (object); + + switch (prop_id) { + case PROP_WIDGET: + qt_sink->widget = static_cast (g_value_get_pointer (value)); + break; + case PROP_FORCE_ASPECT_RATIO: + g_return_if_fail (qt_sink->widget); + qt_sink->widget->setForceAspectRatio (g_value_get_boolean (value)); + break; + case PROP_PIXEL_ASPECT_RATIO: + g_return_if_fail (qt_sink->widget); + qt_sink->widget->setDAR (gst_value_get_fraction_numerator (value), + gst_value_get_fraction_denominator (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +_reset (GstQtSink * qt_sink) +{ + if (qt_sink->display) { + gst_object_unref (qt_sink->display); + qt_sink->display = NULL; + } + + if (qt_sink->context) { + gst_object_unref (qt_sink->context); + qt_sink->context = NULL; + } + + if (qt_sink->qt_context) { + gst_object_unref (qt_sink->qt_context); + qt_sink->qt_context = NULL; + } +} + +static void +gst_qt_sink_finalize (GObject * object) +{ + GstQtSink *qt_sink = GST_QT_SINK (object); + + _reset (qt_sink); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_qt_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstQtSink *qt_sink = GST_QT_SINK (object); + + switch (prop_id) { + case PROP_WIDGET: + g_value_set_pointer (value, qt_sink->widget); + break; + case PROP_FORCE_ASPECT_RATIO: + if (qt_sink->widget) + g_value_set_boolean (value, qt_sink->widget->getForceAspectRatio ()); + else + g_value_set_boolean (value, DEFAULT_FORCE_ASPECT_RATIO); + break; + case PROP_PIXEL_ASPECT_RATIO: + if (qt_sink->widget) { + gint num, den; + qt_sink->widget->getDAR (&num, &den); + gst_value_set_fraction (value, num, den); + } else { + gst_value_set_fraction (value, DEFAULT_PAR_N, DEFAULT_PAR_D); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_qt_sink_query (GstBaseSink * bsink, GstQuery * query) +{ + GstQtSink *qt_sink = GST_QT_SINK (bsink); + gboolean res = FALSE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONTEXT: + { + const gchar *context_type; + GstContext *context, *old_context; + gboolean ret; + + ret = gst_gl_handle_context_query ((GstElement *) qt_sink, query, + &qt_sink->display, &qt_sink->qt_context); + + if (qt_sink->display) + gst_gl_display_filter_gl_api (qt_sink->display, gst_gl_context_get_gl_api (qt_sink->qt_context)); + + gst_query_parse_context_type (query, &context_type); + + if (g_strcmp0 (context_type, "gst.gl.local_context") == 0) { + GstStructure *s; + + gst_query_parse_context (query, &old_context); + + if (old_context) + context = gst_context_copy (old_context); + else + context = gst_context_new ("gst.gl.local_context", FALSE); + + s = gst_context_writable_structure (context); + gst_structure_set (s, "context", GST_GL_TYPE_CONTEXT, qt_sink->context, + NULL); + gst_query_set_context (query, context); + gst_context_unref (context); + + ret = qt_sink->context != NULL; + } + GST_LOG_OBJECT (qt_sink, "context query of type %s %i", context_type, + ret); + + if (ret) + return ret; + } + default: + res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query); + break; + } + + return res; +} + +static gboolean +gst_qt_sink_stop (GstBaseSink * bsink) +{ + return TRUE; +} + +static GstStateChangeReturn +gst_qt_sink_change_state (GstElement * element, GstStateChange transition) +{ + GstQtSink *qt_sink = GST_QT_SINK (element); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + QGuiApplication *app; + + GST_DEBUG ("changing state: %s => %s", + gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)), + gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition))); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + app = dynamic_cast (QCoreApplication::instance ()); + if (!app) { + GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND, + ("%s", "Failed to connect to Qt"), + ("%s", "Could not retreive QGuiApplication instance")); + return GST_STATE_CHANGE_FAILURE; + } + + if (!qt_sink->widget) { + GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND, + ("%s", "Required property \'widget\' not set"), + (NULL)); + return GST_STATE_CHANGE_FAILURE; + } + + if (!qt_item_init_winsys (qt_sink->widget)) { + GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND, + ("%s", "Could not initialize window system"), + (NULL)); + return GST_STATE_CHANGE_FAILURE; + } + + qt_sink->display = qt_item_get_display (qt_sink->widget); + qt_sink->context = qt_item_get_context (qt_sink->widget); + qt_sink->qt_context = qt_item_get_qt_context (qt_sink->widget); + + if (!qt_sink->display || !qt_sink->context || !qt_sink->qt_context) { + GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND, + ("%s", "Could not retreive window system OpenGL configuration"), + (NULL)); + return GST_STATE_CHANGE_FAILURE; + } + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + qt_item_set_buffer (qt_sink->widget, NULL); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return ret; +} + +static void +gst_qt_sink_get_times (GstBaseSink * bsink, GstBuffer * buf, + GstClockTime * start, GstClockTime * end) +{ + GstQtSink *qt_sink; + + qt_sink = GST_QT_SINK (bsink); + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + *start = GST_BUFFER_TIMESTAMP (buf); + if (GST_BUFFER_DURATION_IS_VALID (buf)) + *end = *start + GST_BUFFER_DURATION (buf); + else { + if (GST_VIDEO_INFO_FPS_N (&qt_sink->v_info) > 0) { + *end = *start + + gst_util_uint64_scale_int (GST_SECOND, + GST_VIDEO_INFO_FPS_D (&qt_sink->v_info), + GST_VIDEO_INFO_FPS_N (&qt_sink->v_info)); + } + } + } +} + +gboolean +gst_qt_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) +{ + GstQtSink *qt_sink = GST_QT_SINK (bsink); + + GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps); + + if (!gst_video_info_from_caps (&qt_sink->v_info, caps)) + return FALSE; + + if (!qt_item_set_caps (qt_sink->widget, caps)) + return FALSE; + + return TRUE; +} + +static GstFlowReturn +gst_qt_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf) +{ + GstQtSink *qt_sink; + + GST_TRACE ("rendering buffer:%p", buf); + + qt_sink = GST_QT_SINK (vsink); + + qt_item_set_buffer (qt_sink->widget, buf); + + return GST_FLOW_OK; +} + +static gboolean +gst_qt_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) +{ + GstQtSink *qt_sink = GST_QT_SINK (bsink); + GstBufferPool *pool; + GstStructure *config; + GstCaps *caps; + guint size; + gboolean need_pool; + + if (!qt_sink->display || !qt_sink->context) + return FALSE; + + gst_query_parse_allocation (query, &caps, &need_pool); + + if (caps == NULL) + goto no_caps; + + if ((pool = qt_sink->pool)) + gst_object_ref (pool); + + if (pool != NULL) { + GstCaps *pcaps; + + /* we had a pool, check caps */ + GST_DEBUG_OBJECT (qt_sink, "check existing pool caps"); + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL); + + if (!gst_caps_is_equal (caps, pcaps)) { + GST_DEBUG_OBJECT (qt_sink, "pool has different caps"); + /* different caps, we can't use this pool */ + gst_object_unref (pool); + pool = NULL; + } + gst_structure_free (config); + } + + if (pool == NULL && need_pool) { + GstVideoInfo info; + + if (!gst_video_info_from_caps (&info, caps)) + goto invalid_caps; + + GST_DEBUG_OBJECT (qt_sink, "create new pool"); + pool = gst_gl_buffer_pool_new (qt_sink->context); + + /* the normal size of a frame */ + size = info.size; + + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_set_params (config, caps, size, 0, 0); + if (!gst_buffer_pool_set_config (pool, config)) + goto config_failed; + } + /* we need at least 2 buffer because we hold on to the last one */ + if (pool) { + gst_query_add_allocation_pool (query, pool, size, 2, 0); + gst_object_unref (pool); + } + + /* we also support various metadata */ + gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0); + + if (qt_sink->context->gl_vtable->FenceSync) + gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0); + + return TRUE; + + /* ERRORS */ +no_caps: + { + GST_DEBUG_OBJECT (bsink, "no caps specified"); + return FALSE; + } +invalid_caps: + { + GST_DEBUG_OBJECT (bsink, "invalid caps specified"); + return FALSE; + } +config_failed: + { + GST_DEBUG_OBJECT (bsink, "failed setting config"); + return FALSE; + } +} diff --git a/ext/qt/gstqtsink.h b/ext/qt/gstqtsink.h new file mode 100644 index 0000000000..9a1cfbece8 --- /dev/null +++ b/ext/qt/gstqtsink.h @@ -0,0 +1,81 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_QT_SINK_H__ +#define __GST_QT_SINK_H__ + +#include +#include +#include +#include +#include "qtitem.h" + +typedef struct _GstQtSink GstQtSink; +typedef struct _GstQtSinkClass GstQtSinkClass; +typedef struct _GstQtSinkPrivate GstQtSinkPrivate; + +G_BEGIN_DECLS + +GType gst_qt_sink_get_type (void); +#define GST_TYPE_QT_SINK (gst_qt_sink_get_type()) +#define GST_QT_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_QT_SINK,GstQtSink)) +#define GST_QT_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_QT_SINK,GstQtSinkClass)) +#define GST_IS_QT_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_QT_SINK)) +#define GST_IS_QT_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_QT_SINK)) +#define GST_QT_SINK_CAST(obj) ((GstQtSink*)(obj)) + +/** + * GstQtSink: + * + * Opaque #GstQtSink object + */ +struct _GstQtSink +{ + /* */ + GstVideoSink parent; + + QtGLVideoItem *widget; + + GstVideoInfo v_info; + GstBufferPool *pool; + + GstGLDisplay *display; + GstGLContext *context; + GstGLContext *qt_context; + + GstQtSinkPrivate *priv; +}; + +/** + * GstQtSinkClass: + * + * The #GstQtSinkClass struct only contains private data + */ +struct _GstQtSinkClass +{ + /* */ + GstVideoSinkClass object_class; +}; + +GstQtSink * gst_qt_sink_new (void); + +G_END_DECLS + +#endif /* __GST_QT_SINK_H__ */ diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc new file mode 100644 index 0000000000..5a7ac2e698 --- /dev/null +++ b/ext/qt/qtitem.cc @@ -0,0 +1,491 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include +#include +#include "qtitem.h" +#include "gstqsgtexture.h" + +#if GST_GL_HAVE_WINDOW_X11 +#include +#include +#include +#endif + +#if GST_GL_HAVE_WINDOW_WAYLAND +#include +#endif + +/** + * SECTION:gtkgstglwidget + * @short_description: a #GtkGLArea that renders GStreamer video #GstBuffers + * @see_also: #GtkGLArea, #GstBuffer + * + * #QtGLVideoItem is an #GtkWidget that renders GStreamer video buffers. + */ + +#define GST_CAT_DEFAULT qt_item_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +#define GTK_GST_GL_WIDGET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + GTK_TYPE_GST_GL_WIDGET, QtGLVideoItemPrivate)) + +#define DEFAULT_FORCE_ASPECT_RATIO TRUE +#define DEFAULT_PAR_N 0 +#define DEFAULT_PAR_D 1 + +enum +{ + PROP_0, + PROP_FORCE_ASPECT_RATIO, + PROP_PIXEL_ASPECT_RATIO, +}; + +struct _QtGLVideoItemPrivate +{ + GMutex lock; + + /* properties */ + gboolean force_aspect_ratio; + gint par_n, par_d; + + gint display_width; + gint display_height; + + gboolean negotiated; + GstBuffer *buffer; + GstCaps *caps; + GstVideoInfo v_info; + + gboolean initted; + GstGLDisplay *display; + QOpenGLContext *qt_context; + GstGLContext *other_context; + GstGLContext *context; +}; + +QtGLVideoItem::QtGLVideoItem() +{ + QGuiApplication *app = dynamic_cast (QCoreApplication::instance ()); + static volatile gsize _debug; + + g_assert (app != NULL); + + if (g_once_init_enter (&_debug)) { + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qtglwidget", 0, "Qt GL Widget"); + g_once_init_leave (&_debug, 1); + } + + this->setFlag (QQuickItem::ItemHasContents, true); + + this->priv = g_new0 (QtGLVideoItemPrivate, 1); + + this->priv->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO; + this->priv->par_n = DEFAULT_PAR_N; + this->priv->par_d = DEFAULT_PAR_D; + + g_mutex_init (&this->priv->lock); + +#if GST_GL_HAVE_WINDOW_X11 + if (QString::fromUtf8 ("xcb") == app->platformName()) + this->priv->display = (GstGLDisplay *) + gst_gl_display_x11_new_with_display (QX11Info::display ()); +#endif + + if (!this->priv->display) + this->priv->display = gst_gl_display_new (); + + connect(this, SIGNAL(windowChanged(QQuickWindow*)), this, + SLOT(handleWindowChanged(QQuickWindow*))); + + GST_DEBUG ("%p init Qt Video Item", this); +} + +QtGLVideoItem::~QtGLVideoItem() +{ + g_mutex_clear (&this->priv->lock); + + g_free (this->priv); + this->priv = NULL; +} + +void +QtGLVideoItem::setDAR(gint num, gint den) +{ + this->priv->par_n = num; + this->priv->par_d = den; +} + +void +QtGLVideoItem::getDAR(gint * num, gint * den) +{ + if (num) + *num = this->priv->par_n; + if (den) + *den = this->priv->par_d; +} + +void +QtGLVideoItem::setForceAspectRatio(bool far) +{ + this->priv->force_aspect_ratio = far; +} + +bool +QtGLVideoItem::getForceAspectRatio() +{ + return this->priv->force_aspect_ratio; +} + +QSGNode * +QtGLVideoItem::updatePaintNode(QSGNode * oldNode, + UpdatePaintNodeData * updatePaintNodeData) +{ + QSGSimpleTextureNode *texNode = static_cast (oldNode); + GstVideoRectangle src, dst, result; + GstQSGTexture *tex; + + g_mutex_lock (&this->priv->lock); + + GST_TRACE ("%p updatePaintNode", this); + + if (!this->priv->caps) { + g_mutex_unlock (&this->priv->lock); + return NULL; + } + + if (!texNode) { + texNode = new QSGSimpleTextureNode (); + tex = new GstQSGTexture (); + texNode->setTexture (tex); + } else { + tex = static_cast (texNode->texture()); + } + + tex->setCaps (this->priv->caps); + tex->setBuffer (this->priv->buffer); + + if (this->priv->force_aspect_ratio) { + src.w = this->priv->display_width; + src.h = this->priv->display_height; + + dst.x = boundingRect().x(); + dst.y = boundingRect().y(); + dst.w = boundingRect().width(); + dst.h = boundingRect().height(); + + gst_video_sink_center_rect (src, dst, &result, TRUE); + } else { + result.x = boundingRect().x(); + result.y = boundingRect().y(); + result.w = boundingRect().width(); + result.h = boundingRect().height(); + } + + texNode->setRect (QRectF (result.x, result.y, result.w, result.h)); + + g_mutex_unlock (&this->priv->lock); + + return texNode; +} + +static void +_reset (QtGLVideoItem * qt_item) +{ + gst_buffer_replace (&qt_item->priv->buffer, NULL); + + gst_caps_replace (&qt_item->priv->caps, NULL); + + qt_item->priv->negotiated = FALSE; + qt_item->priv->initted = FALSE; +} + +void +qt_item_set_buffer (QtGLVideoItem * widget, GstBuffer * buffer) +{ + g_return_if_fail (widget != NULL); + g_return_if_fail (widget->priv->negotiated); + + g_mutex_lock (&widget->priv->lock); + + gst_buffer_replace (&widget->priv->buffer, buffer); + + QMetaObject::invokeMethod(widget, "update", Qt::QueuedConnection); + + g_mutex_unlock (&widget->priv->lock); +} + +void +QtGLVideoItem::onSceneGraphInitialized () +{ + GstGLPlatform platform; + GstGLAPI gl_api; + guintptr gl_handle; + + GST_DEBUG ("scene graph initialization with Qt GL context %p", + this->window()->openglContext ()); + + if (this->priv->qt_context == this->window()->openglContext ()) + return; + + this->priv->qt_context = this->window()->openglContext (); + if (this->priv->qt_context == NULL) { + g_assert_not_reached (); + return; + } + +#if GST_GL_HAVE_WINDOW_X11 + if (GST_IS_GL_DISPLAY_X11 (this->priv->display)) { + platform = GST_GL_PLATFORM_GLX; + gl_api = gst_gl_context_get_current_gl_api (NULL, NULL); + gl_handle = gst_gl_context_get_current_gl_context (platform); + if (gl_handle) + this->priv->other_context = + gst_gl_context_new_wrapped (this->priv->display, gl_handle, + platform, gl_api); + } +#endif +#if GST_GL_HAVE_WINDOW_WAYLAND + if (GST_IS_GL_DISPLAY_WAYLAND (this->priv->display)) { + platform = GST_GL_PLATFORM_EGL; + gl_api = gst_gl_context_get_current_gl_api (NULL, NULL); + gl_handle = gst_gl_context_get_current_gl_context (platform); + if (gl_handle) + this->priv->other_context = + gst_gl_context_new_wrapped (this->priv->display, gl_handle, + platform, gl_api); + } +#endif + + (void) platform; + (void) gl_api; + (void) gl_handle; + + if (this->priv->other_context) { + GError *error = NULL; + + gst_gl_context_activate (this->priv->other_context, TRUE); + if (!gst_gl_context_fill_info (this->priv->other_context, &error)) { + GST_ERROR ("%p failed to retreive qt context info: %s", this, error->message); + g_object_unref (this->priv->other_context); + this->priv->other_context = NULL; + } else { + gst_gl_display_filter_gl_api (this->priv->display, gst_gl_context_get_gl_api (this->priv->other_context)); + gst_gl_context_activate (this->priv->other_context, FALSE); + } + } + + GST_DEBUG ("%p created wrapped GL context %" GST_PTR_FORMAT, this, + this->priv->other_context); +} + +void +QtGLVideoItem::onSceneGraphInvalidated () +{ + GST_FIXME ("%p scene graph invalidated", this); +} + +gboolean +qt_item_init_winsys (QtGLVideoItem * widget) +{ + g_return_val_if_fail (widget != NULL, FALSE); + + g_mutex_lock (&widget->priv->lock); + + if (widget->priv->display && widget->priv->qt_context + && widget->priv->other_context && widget->priv->context) { + /* already have the necessary state */ + g_mutex_unlock (&widget->priv->lock); + return TRUE; + } + + if (!GST_IS_GL_DISPLAY (widget->priv->display)) { + GST_ERROR ("%p failed to retreive display connection %" GST_PTR_FORMAT, + widget, widget->priv->display); + g_mutex_unlock (&widget->priv->lock); + return FALSE; + } + + if (!GST_GL_IS_CONTEXT (widget->priv->other_context)) { + GST_ERROR ("%p failed to retreive wrapped context %" GST_PTR_FORMAT, widget, + widget->priv->other_context); + g_mutex_unlock (&widget->priv->lock); + return FALSE; + } + + widget->priv->context = gst_gl_context_new (widget->priv->display); + + if (!widget->priv->context) { + g_mutex_unlock (&widget->priv->lock); + return FALSE; + } + + gst_gl_context_create (widget->priv->context, widget->priv->other_context, + NULL); + + g_mutex_unlock (&widget->priv->lock); + return TRUE; +} + +void +QtGLVideoItem::handleWindowChanged(QQuickWindow *win) +{ + if (win) { + connect(win, SIGNAL(sceneGraphInitialized()), this, SLOT(onSceneGraphInitialized()), Qt::DirectConnection); + connect(win, SIGNAL(sceneGraphInvalidated()), this, SLOT(onSceneGraphInvalidated()), Qt::DirectConnection); + } else { + this->priv->qt_context = NULL; + } +} + +static gboolean +_calculate_par (QtGLVideoItem * widget, GstVideoInfo * info) +{ + gboolean ok; + gint width, height; + gint par_n, par_d; + gint display_par_n, display_par_d; + guint display_ratio_num, display_ratio_den; + + width = GST_VIDEO_INFO_WIDTH (info); + height = GST_VIDEO_INFO_HEIGHT (info); + + par_n = GST_VIDEO_INFO_PAR_N (info); + par_d = GST_VIDEO_INFO_PAR_D (info); + + if (!par_n) + par_n = 1; + + /* get display's PAR */ + if (widget->priv->par_n != 0 && widget->priv->par_d != 0) { + display_par_n = widget->priv->par_n; + display_par_d = widget->priv->par_d; + } else { + display_par_n = 1; + display_par_d = 1; + } + + ok = gst_video_calculate_display_ratio (&display_ratio_num, + &display_ratio_den, width, height, par_n, par_d, display_par_n, + display_par_d); + + if (!ok) + return FALSE; + + GST_LOG ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n, display_par_d); + + if (height % display_ratio_den == 0) { + GST_DEBUG ("keeping video height"); + 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"); + 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"); + 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, + widget->priv->display_height); + + return TRUE; +} + +gboolean +qt_item_set_caps (QtGLVideoItem * widget, GstCaps * caps) +{ + GstVideoInfo v_info; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); + g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); + + if (widget->priv->caps && gst_caps_is_equal_fixed (widget->priv->caps, caps)) + return TRUE; + + if (!gst_video_info_from_caps (&v_info, caps)) + return FALSE; + + g_mutex_lock (&widget->priv->lock); + + _reset (widget); + + gst_caps_replace (&widget->priv->caps, caps); + + if (!_calculate_par (widget, &v_info)) { + g_mutex_unlock (&widget->priv->lock); + return FALSE; + } + + widget->priv->v_info = v_info; + widget->priv->negotiated = TRUE; + + g_mutex_unlock (&widget->priv->lock); + + return TRUE; +} + +GstGLContext * +qt_item_get_qt_context (QtGLVideoItem * qt_item) +{ + g_return_val_if_fail (qt_item != NULL, NULL); + + if (!qt_item->priv->other_context) + return NULL; + + return (GstGLContext *) gst_object_ref (qt_item->priv->other_context); +} + +GstGLContext * +qt_item_get_context (QtGLVideoItem * qt_item) +{ + g_return_val_if_fail (qt_item != NULL, NULL); + + if (!qt_item->priv->context) + return NULL; + + return (GstGLContext *) gst_object_ref (qt_item->priv->context); +} + +GstGLDisplay * +qt_item_get_display (QtGLVideoItem * qt_item) +{ + g_return_val_if_fail (qt_item != NULL, NULL); + + if (!qt_item->priv->display) + return NULL; + + return (GstGLDisplay *) gst_object_ref (qt_item->priv->display); +} diff --git a/ext/qt/qtitem.h b/ext/qt/qtitem.h new file mode 100644 index 0000000000..afd2869d82 --- /dev/null +++ b/ext/qt/qtitem.h @@ -0,0 +1,72 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __QT_ITEM_H__ +#define __QT_ITEM_H__ + +#include +#include +#include +#include +#include + +typedef struct _QtGLVideoItemPrivate QtGLVideoItemPrivate; + +class QtGLVideoItem : public QQuickItem, protected QOpenGLFunctions +{ + Q_OBJECT +public: + QtGLVideoItem(); + ~QtGLVideoItem(); + + void setDAR(gint, gint); + void getDAR(gint *, gint *); + void setForceAspectRatio(bool); + bool getForceAspectRatio(); + + /* private for C interface ... */ + QtGLVideoItemPrivate *priv; + +private Q_SLOTS: + void handleWindowChanged(QQuickWindow * win); + void onSceneGraphInitialized(); + void onSceneGraphInvalidated(); + +protected: + QSGNode * updatePaintNode (QSGNode * oldNode, UpdatePaintNodeData * updatePaintNodeData); + +private: + void setViewportSize(const QSize &size); + void shareContext(); + + QSize m_viewportSize; +}; + +extern "C" +{ +void qt_item_set_buffer (QtGLVideoItem * widget, GstBuffer * buffer); +gboolean qt_item_set_caps (QtGLVideoItem * widget, GstCaps * caps); +gboolean qt_item_init_winsys (QtGLVideoItem * widget); +GstGLContext * qt_item_get_qt_context (QtGLVideoItem * qt_item); +GstGLContext * qt_item_get_context (QtGLVideoItem * qt_item); +GstGLDisplay * qt_item_get_display (QtGLVideoItem * qt_item); +} + +#endif /* __QT_ITEM_H__ */ diff --git a/tests/examples/qt/qml/.gitignore b/tests/examples/qt/qml/.gitignore new file mode 100644 index 0000000000..1f81518587 --- /dev/null +++ b/tests/examples/qt/qml/.gitignore @@ -0,0 +1,3 @@ +deployment.pri +play +qrc_qml.cpp diff --git a/tests/examples/qt/qml/main.cpp b/tests/examples/qt/qml/main.cpp new file mode 100644 index 0000000000..2317b05415 --- /dev/null +++ b/tests/examples/qt/qml/main.cpp @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include +#include + +class SetPlaying : public QRunnable +{ +public: + SetPlaying(GstElement *); + ~SetPlaying(); + + void run (); + +private: + GstElement * pipeline_; +}; + +SetPlaying::SetPlaying (GstElement * pipeline) +{ + this->pipeline_ = pipeline ? static_cast (gst_object_ref (pipeline)) : NULL; +} + +SetPlaying::~SetPlaying () +{ + if (this->pipeline_) + gst_object_unref (this->pipeline_); +} + +void +SetPlaying::run () +{ + if (this->pipeline_) + gst_element_set_state (this->pipeline_, GST_STATE_PLAYING); +} + +int main(int argc, char *argv[]) +{ + int ret; + + QGuiApplication app(argc, argv); + gst_init (&argc, &argv); + + GstElement *pipeline = gst_pipeline_new (NULL); + GstElement *src = gst_element_factory_make ("videotestsrc", NULL); + GstElement *glupload = gst_element_factory_make ("glupload", NULL); + /* 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 (src && glupload && sink); + + gst_bin_add_many (GST_BIN (pipeline), src, glupload, sink, NULL); + gst_element_link_many (src, glupload, sink, NULL); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + QQuickItem *videoItem; + QQuickWindow *rootObject; + + /* find and set the videoItem on the sink */ + rootObject = static_cast (engine.rootObjects().first()); + videoItem = rootObject->findChild ("videoItem"); + g_assert (videoItem); + g_object_set(sink, "widget", videoItem, NULL); + + rootObject->scheduleRenderJob (new SetPlaying (pipeline), + QQuickWindow::BeforeSynchronizingStage); + + 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/qml/main.qml b/tests/examples/qt/qml/main.qml new file mode 100644 index 0000000000..842e98f2eb --- /dev/null +++ b/tests/examples/qt/qml/main.qml @@ -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 : childrenRect.width + 20 + height: childrenRect.height + 20 + 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/qml/play.pro b/tests/examples/qt/qml/play.pro new file mode 100644 index 0000000000..374e40297a --- /dev/null +++ b/tests/examples/qt/qml/play.pro @@ -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 += qml.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = diff --git a/tests/examples/qt/qml/qml.qrc b/tests/examples/qt/qml/qml.qrc new file mode 100644 index 0000000000..5f6483ac33 --- /dev/null +++ b/tests/examples/qt/qml/qml.qrc @@ -0,0 +1,5 @@ + + + main.qml + + From d1ad64fca7891861c948a0ab66cdb6cdf52d9c2f Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Fri, 10 Jul 2015 14:01:43 +0200 Subject: [PATCH 02/78] configure/qt: Fix build without Qt5X11Extras --- ext/qt/qtitem.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 5a7ac2e698..fab2c5dcce 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -31,7 +31,7 @@ #include "qtitem.h" #include "gstqsgtexture.h" -#if GST_GL_HAVE_WINDOW_X11 +#if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11) #include #include #include @@ -111,7 +111,7 @@ QtGLVideoItem::QtGLVideoItem() g_mutex_init (&this->priv->lock); -#if GST_GL_HAVE_WINDOW_X11 +#if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11) if (QString::fromUtf8 ("xcb") == app->platformName()) this->priv->display = (GstGLDisplay *) gst_gl_display_x11_new_with_display (QX11Info::display ()); @@ -259,7 +259,7 @@ QtGLVideoItem::onSceneGraphInitialized () return; } -#if GST_GL_HAVE_WINDOW_X11 +#if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11) if (GST_IS_GL_DISPLAY_X11 (this->priv->display)) { platform = GST_GL_PLATFORM_GLX; gl_api = gst_gl_context_get_current_gl_api (NULL, NULL); From e56df237ee3ef98685d7a59ffea054330864615d Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Sat, 18 Jul 2015 17:19:18 +1000 Subject: [PATCH 03/78] glcontext: fix get_current_gl_api on x11/nvidia drivers They require to get_proc_address some functions through the platform specific {glX,egl}GetProcAddress rather than the default GL library symbol lookup. --- ext/qt/qtitem.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index fab2c5dcce..7686a7378e 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -262,7 +262,7 @@ QtGLVideoItem::onSceneGraphInitialized () #if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11) if (GST_IS_GL_DISPLAY_X11 (this->priv->display)) { platform = GST_GL_PLATFORM_GLX; - gl_api = gst_gl_context_get_current_gl_api (NULL, NULL); + gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL); gl_handle = gst_gl_context_get_current_gl_context (platform); if (gl_handle) this->priv->other_context = @@ -273,7 +273,7 @@ QtGLVideoItem::onSceneGraphInitialized () #if GST_GL_HAVE_WINDOW_WAYLAND if (GST_IS_GL_DISPLAY_WAYLAND (this->priv->display)) { platform = GST_GL_PLATFORM_EGL; - gl_api = gst_gl_context_get_current_gl_api (NULL, NULL); + gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL); gl_handle = gst_gl_context_get_current_gl_context (platform); if (gl_handle) this->priv->other_context = From 2df392541e2329e7da0e93cf0d61890dd143affd Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Wed, 22 Jul 2015 08:05:04 +0200 Subject: [PATCH 04/78] qt: Tidy up makefile a bit more Separate generated files, from disted files --- ext/qt/Makefile.am | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/ext/qt/Makefile.am b/ext/qt/Makefile.am index 5dc38dc085..f80fd6c6d8 100644 --- a/ext/qt/Makefile.am +++ b/ext/qt/Makefile.am @@ -1,29 +1,24 @@ -# preamble -NULL = +plugin_LTLIBRARIES = libqtsink.la -moc_headers = \ - qtitem.h \ +noinst_HEADERS = \ gstqsgtexture.h \ - $(NULL) + gstqtsink.h \ + qtitem.h moc_generated = \ moc_qtitem.cc \ - moc_gstqsgtexture.cc \ - $(NULL) + moc_gstqsgtexture.cc #anything generated by the Qt tools... BUILT_SOURCES = $(moc_generated) CLEANFILES = $(moc_generated) -sources = \ +libqtsink_la_SOURCES = \ gstqsgtexture.cc \ qtitem.cc \ gstqtsink.cc \ gstqtsink.h \ - gstplugin.cc \ - $(moc_headers) \ - $(moc_generated) \ - $(NULL) + gstplugin.cc libqtsink_la_CXXFLAGS = \ -I$(top_srcdir)/gst-libs \ @@ -32,6 +27,7 @@ libqtsink_la_CXXFLAGS = \ $(GST_BASE_CFLAGS) \ $(GST_PLUGINS_BASE_CFLAGS) \ $(QT_CFLAGS) + libqtsink_la_LIBADD = \ $(GST_BASE_LIBS) \ $(GST_PLUGINS_BASE_LIBS) \ @@ -39,17 +35,15 @@ libqtsink_la_LIBADD = \ $(top_builddir)/gst-libs/gst/gl/libgstgl-$(GST_API_VERSION).la \ -lgstvideo-$(GST_API_VERSION) -libqtsink_la_SOURCES = $(sources) libqtsink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libqtsink_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) -plugin_LTLIBRARIES = libqtsink.la $(moc_generated): moc_%.cc: %.h - @@MOC@ -o$@ $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(MOC_CPPFLAGS) $< + @MOC@ -o $@ $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(MOC_CPPFLAGS) $< ui-%.h: %.ui - @@UIC@ -o $@ $< + @UIC@ -o $@ $< qrc-%.cc: %.qrc - @@RCC@ -o $@ $< + @RCC@ -o $@ $< From 523edaf6bd0ca3ebc9969c06892aa46b86b5acac Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Wed, 22 Jul 2015 15:13:48 +0200 Subject: [PATCH 05/78] qt: Don't dist files that might not exist We only require moc building at build time. --- ext/qt/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/qt/Makefile.am b/ext/qt/Makefile.am index f80fd6c6d8..059ee7b860 100644 --- a/ext/qt/Makefile.am +++ b/ext/qt/Makefile.am @@ -13,6 +13,8 @@ moc_generated = \ BUILT_SOURCES = $(moc_generated) CLEANFILES = $(moc_generated) +nodist_libqtsink_la_SOURCES = $(BUILT_SOURCES) + libqtsink_la_SOURCES = \ gstqsgtexture.cc \ qtitem.cc \ From b8bb4cc50f6d7872bab5d026631656a3d345b5f1 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Sat, 8 Aug 2015 17:28:03 +0200 Subject: [PATCH 06/78] qml: implement the required multiple GL context synchonisation From GStreamer's GL context into the QML context --- ext/qt/gstqsgtexture.cc | 32 ++++++++++++++++++++++++++++++-- ext/qt/gstqsgtexture.h | 6 +++++- ext/qt/qtitem.cc | 7 +++++-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/ext/qt/gstqsgtexture.cc b/ext/qt/gstqsgtexture.cc index 1e5203832a..a9be90c198 100644 --- a/ext/qt/gstqsgtexture.cc +++ b/ext/qt/gstqsgtexture.cc @@ -45,11 +45,13 @@ GstQSGTexture::GstQSGTexture () gst_video_info_init (&this->v_info); this->buffer_ = NULL; + this->sync_buffer_ = gst_buffer_new (); } GstQSGTexture::~GstQSGTexture () { gst_buffer_replace (&this->buffer_, NULL); + gst_buffer_replace (&this->sync_buffer_, NULL); } /* only called from the streaming thread with scene graph thread blocked */ @@ -62,18 +64,26 @@ GstQSGTexture::setCaps (GstCaps * caps) } /* only called from the streaming thread with scene graph thread blocked */ -void +gboolean GstQSGTexture::setBuffer (GstBuffer * buffer) { GST_LOG ("%p setBuffer %" GST_PTR_FORMAT, this, buffer); /* FIXME: update more state here */ - gst_buffer_replace (&this->buffer_, buffer); + if (!gst_buffer_replace (&this->buffer_, buffer)) + return FALSE; + + this->qt_context_ = gst_gl_context_get_current (); + + return TRUE; } /* only called from qt's scene graph render thread */ void GstQSGTexture::bind () { + GstGLContext *context; + GstGLSyncMeta *sync_meta; + GstMemory *mem; guint tex_id; if (!this->buffer_) @@ -81,6 +91,10 @@ GstQSGTexture::bind () if (GST_VIDEO_INFO_FORMAT (&this->v_info) == GST_VIDEO_FORMAT_UNKNOWN) return; + this->mem_ = gst_buffer_peek_memory (this->buffer_, 0); + if (!this->mem_) + return; + /* FIXME: should really lock the memory to prevent write access */ if (!gst_video_frame_map (&this->v_frame, &this->v_info, this->buffer_, (GstMapFlags) (GST_MAP_READ | GST_MAP_GL))) { @@ -88,6 +102,20 @@ GstQSGTexture::bind () return; } + mem = gst_buffer_peek_memory (this->buffer_, 0); + g_assert (gst_is_gl_memory (mem)); + + context = ((GstGLBaseBuffer *)mem)->context; + + sync_meta = gst_buffer_get_gl_sync_meta (this->sync_buffer_); + if (!sync_meta) + sync_meta = gst_buffer_add_gl_sync_meta (context, this->sync_buffer_); + + gst_gl_sync_meta_set_sync_point (sync_meta, context); + + g_assert (this->qt_context_); + gst_gl_sync_meta_wait (sync_meta, this->qt_context_); + tex_id = *(guint *) this->v_frame.data[0]; GST_LOG ("%p binding Qt texture %u", this, tex_id); diff --git a/ext/qt/gstqsgtexture.h b/ext/qt/gstqsgtexture.h index 93fb60d063..bf8f03f4ec 100644 --- a/ext/qt/gstqsgtexture.h +++ b/ext/qt/gstqsgtexture.h @@ -25,6 +25,7 @@ #include #include #include +#include class GstQSGTexture : public QSGTexture, protected QOpenGLFunctions { @@ -34,7 +35,7 @@ public: ~GstQSGTexture (); void setCaps (GstCaps * caps); - void setBuffer (GstBuffer * buffer); + gboolean setBuffer (GstBuffer * buffer); /* QSGTexture */ void bind (); @@ -45,6 +46,9 @@ public: private: GstBuffer * buffer_; + GstBuffer * sync_buffer_; + GstGLContext * qt_context_; + GstMemory * mem_; GstVideoInfo v_info; GstVideoFrame v_frame; }; diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 7686a7378e..8c3e4b9be3 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -171,6 +171,7 @@ QtGLVideoItem::updatePaintNode(QSGNode * oldNode, GstQSGTexture *tex; g_mutex_lock (&this->priv->lock); + gst_gl_context_activate (this->priv->other_context, TRUE); GST_TRACE ("%p updatePaintNode", this); @@ -181,14 +182,15 @@ QtGLVideoItem::updatePaintNode(QSGNode * oldNode, if (!texNode) { texNode = new QSGSimpleTextureNode (); - tex = new GstQSGTexture (); - texNode->setTexture (tex); + texNode->setOwnsTexture (true); } else { tex = static_cast (texNode->texture()); } + tex = new GstQSGTexture (); tex->setCaps (this->priv->caps); tex->setBuffer (this->priv->buffer); + texNode->setTexture (tex); if (this->priv->force_aspect_ratio) { src.w = this->priv->display_width; @@ -209,6 +211,7 @@ QtGLVideoItem::updatePaintNode(QSGNode * oldNode, texNode->setRect (QRectF (result.x, result.y, result.w, result.h)); + gst_gl_context_activate (this->priv->other_context, FALSE); g_mutex_unlock (&this->priv->lock); return texNode; From 9846984c6314e365aeef9a22f5f88aacf28c9f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Mon, 31 Aug 2015 18:06:31 +0100 Subject: [PATCH 07/78] gtk, qt, gl: fix typo in debug and error messages --- ext/qt/gstqtsink.cc | 4 ++-- ext/qt/qtitem.cc | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/qt/gstqtsink.cc b/ext/qt/gstqtsink.cc index 40e66d8d70..9ff13b04c0 100644 --- a/ext/qt/gstqtsink.cc +++ b/ext/qt/gstqtsink.cc @@ -303,7 +303,7 @@ gst_qt_sink_change_state (GstElement * element, GstStateChange transition) if (!app) { GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND, ("%s", "Failed to connect to Qt"), - ("%s", "Could not retreive QGuiApplication instance")); + ("%s", "Could not retrieve QGuiApplication instance")); return GST_STATE_CHANGE_FAILURE; } @@ -327,7 +327,7 @@ gst_qt_sink_change_state (GstElement * element, GstStateChange transition) if (!qt_sink->display || !qt_sink->context || !qt_sink->qt_context) { GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND, - ("%s", "Could not retreive window system OpenGL configuration"), + ("%s", "Could not retrieve window system OpenGL configuration"), (NULL)); return GST_STATE_CHANGE_FAILURE; } diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 8c3e4b9be3..9e9e287518 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -294,7 +294,7 @@ QtGLVideoItem::onSceneGraphInitialized () gst_gl_context_activate (this->priv->other_context, TRUE); if (!gst_gl_context_fill_info (this->priv->other_context, &error)) { - GST_ERROR ("%p failed to retreive qt context info: %s", this, error->message); + GST_ERROR ("%p failed to retrieve qt context info: %s", this, error->message); g_object_unref (this->priv->other_context); this->priv->other_context = NULL; } else { @@ -328,14 +328,14 @@ qt_item_init_winsys (QtGLVideoItem * widget) } if (!GST_IS_GL_DISPLAY (widget->priv->display)) { - GST_ERROR ("%p failed to retreive display connection %" GST_PTR_FORMAT, + GST_ERROR ("%p failed to retrieve display connection %" GST_PTR_FORMAT, widget, widget->priv->display); g_mutex_unlock (&widget->priv->lock); return FALSE; } if (!GST_GL_IS_CONTEXT (widget->priv->other_context)) { - GST_ERROR ("%p failed to retreive wrapped context %" GST_PTR_FORMAT, widget, + GST_ERROR ("%p failed to retrieve wrapped context %" GST_PTR_FORMAT, widget, widget->priv->other_context); g_mutex_unlock (&widget->priv->lock); return FALSE; From e99c591dc9b11c2a2c676ba062daa8935aa08506 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 7 Aug 2015 17:27:48 +0530 Subject: [PATCH 08/78] qmlsink: Ensure that at least one windowing system is available Otherwise, we'll just crash at runtime because the gl context is NULL https://bugzilla.gnome.org/show_bug.cgi?id=754108 --- ext/qt/qtitem.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 9e9e287518..6f2200079f 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -37,7 +37,7 @@ #include #endif -#if GST_GL_HAVE_WINDOW_WAYLAND +#if GST_GL_HAVE_WINDOW_WAYLAND && defined (HAVE_QT_WAYLAND) #include #endif @@ -273,7 +273,7 @@ QtGLVideoItem::onSceneGraphInitialized () platform, gl_api); } #endif -#if GST_GL_HAVE_WINDOW_WAYLAND +#if GST_GL_HAVE_WINDOW_WAYLAND && defined (HAVE_QT_WAYLAND) if (GST_IS_GL_DISPLAY_WAYLAND (this->priv->display)) { platform = GST_GL_PLATFORM_EGL; gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL); From 579b734346461894f2e364c3cc260a578c8a052e Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Thu, 10 Sep 2015 00:00:11 +1000 Subject: [PATCH 09/78] qt: use our function table instead of directly calling gl functions Otherwise when building with --as-needed we would need to link to a GL or GLES library. https://bugzilla.gnome.org/show_bug.cgi?id=754732 --- ext/qt/gstqsgtexture.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ext/qt/gstqsgtexture.cc b/ext/qt/gstqsgtexture.cc index a9be90c198..12810fafbb 100644 --- a/ext/qt/gstqsgtexture.cc +++ b/ext/qt/gstqsgtexture.cc @@ -81,6 +81,7 @@ GstQSGTexture::setBuffer (GstBuffer * buffer) void GstQSGTexture::bind () { + const GstGLFuncs *gl; GstGLContext *context; GstGLSyncMeta *sync_meta; GstMemory *mem; @@ -95,6 +96,9 @@ GstQSGTexture::bind () if (!this->mem_) return; + g_assert (this->qt_context_); + gl = this->qt_context_->gl_vtable; + /* FIXME: should really lock the memory to prevent write access */ if (!gst_video_frame_map (&this->v_frame, &this->v_info, this->buffer_, (GstMapFlags) (GST_MAP_READ | GST_MAP_GL))) { @@ -113,13 +117,12 @@ GstQSGTexture::bind () gst_gl_sync_meta_set_sync_point (sync_meta, context); - g_assert (this->qt_context_); gst_gl_sync_meta_wait (sync_meta, this->qt_context_); tex_id = *(guint *) this->v_frame.data[0]; GST_LOG ("%p binding Qt texture %u", this, tex_id); - glBindTexture (GL_TEXTURE_2D, tex_id); + gl->BindTexture (GL_TEXTURE_2D, tex_id); gst_video_frame_unmap (&this->v_frame); } From f1cb602476bbef5e577f124087b981c9adee5c34 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Thu, 10 Sep 2015 00:07:18 +1000 Subject: [PATCH 10/78] gtk, qt: more specifically define the compile time requirements Otherwise we could include headers/configurations that will never been installed. https://bugzilla.gnome.org/show_bug.cgi?id=754732 --- ext/qt/qtitem.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 6f2200079f..7c53979138 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -31,13 +31,13 @@ #include "qtitem.h" #include "gstqsgtexture.h" -#if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11) +#if GST_GL_HAVE_WINDOW_X11 && GST_GL_HAVE_PLATFORM_GLX && defined (HAVE_QT_X11) #include #include #include #endif -#if GST_GL_HAVE_WINDOW_WAYLAND && defined (HAVE_QT_WAYLAND) +#if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_WAYLAND) #include #endif From e29df60665f2cf8b7f4c86fbb351e43a4dc55703 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Wed, 2 Sep 2015 23:39:54 +1000 Subject: [PATCH 11/78] qt: rename library to include gst prefix libqtsink -> libgstqtsink https://bugzilla.gnome.org/show_bug.cgi?id=754466 --- ext/qt/Makefile.am | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ext/qt/Makefile.am b/ext/qt/Makefile.am index 059ee7b860..eb0b79dcab 100644 --- a/ext/qt/Makefile.am +++ b/ext/qt/Makefile.am @@ -1,4 +1,4 @@ -plugin_LTLIBRARIES = libqtsink.la +plugin_LTLIBRARIES = libgstqtsink.la noinst_HEADERS = \ gstqsgtexture.h \ @@ -13,16 +13,16 @@ moc_generated = \ BUILT_SOURCES = $(moc_generated) CLEANFILES = $(moc_generated) -nodist_libqtsink_la_SOURCES = $(BUILT_SOURCES) +nodist_libgstqtsink_la_SOURCES = $(BUILT_SOURCES) -libqtsink_la_SOURCES = \ +libgstqtsink_la_SOURCES = \ gstqsgtexture.cc \ qtitem.cc \ gstqtsink.cc \ gstqtsink.h \ gstplugin.cc -libqtsink_la_CXXFLAGS = \ +libgstqtsink_la_CXXFLAGS = \ -I$(top_srcdir)/gst-libs \ -I$(top_builddir)/gst-libs \ $(GST_CXXFLAGS) \ @@ -30,15 +30,15 @@ libqtsink_la_CXXFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ $(QT_CFLAGS) -libqtsink_la_LIBADD = \ +libgstqtsink_la_LIBADD = \ $(GST_BASE_LIBS) \ $(GST_PLUGINS_BASE_LIBS) \ $(QT_LIBS) \ $(top_builddir)/gst-libs/gst/gl/libgstgl-$(GST_API_VERSION).la \ -lgstvideo-$(GST_API_VERSION) -libqtsink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libqtsink_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) +libgstqtsink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstqtsink_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) $(moc_generated): moc_%.cc: %.h From 5f8a587ddc8c99d5032c081d26aa1022814327eb Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Wed, 2 Sep 2015 23:40:31 +1000 Subject: [PATCH 12/78] qt: don't use CPPFLAGS for tools that cannot use them For example moc will bail out when given arguments it does not know about. The moc specific MOC_CPPFLAGS can still be used to pass flags to moc. https://bugzilla.gnome.org/show_bug.cgi?id=754466 --- ext/qt/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/qt/Makefile.am b/ext/qt/Makefile.am index eb0b79dcab..053a83fdde 100644 --- a/ext/qt/Makefile.am +++ b/ext/qt/Makefile.am @@ -42,7 +42,7 @@ libgstqtsink_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) $(moc_generated): moc_%.cc: %.h - @MOC@ -o $@ $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(MOC_CPPFLAGS) $< + @MOC@ -o $@ $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(MOC_CPPFLAGS) $< ui-%.h: %.ui @UIC@ -o $@ $< From d47398de3b4243600c7c82f241e23c9d105903b7 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Wed, 2 Sep 2015 23:45:07 +1000 Subject: [PATCH 13/78] qt: add support for building/running on android Including: - Necessary configure checks - Necessary compile time platform checks - Necessary runtime qt android platform detection - Escaping GLsync definition with Qt's GLES2 implementation https://bugzilla.gnome.org/show_bug.cgi?id=754466 --- ext/qt/Makefile.am | 1 + ext/qt/gstqsgtexture.h | 6 ++++-- ext/qt/gstqtgl.h | 34 ++++++++++++++++++++++++++++++++++ ext/qt/qtitem.cc | 25 +++++++++++++++++++++++-- ext/qt/qtitem.h | 2 ++ 5 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 ext/qt/gstqtgl.h diff --git a/ext/qt/Makefile.am b/ext/qt/Makefile.am index 053a83fdde..064e8d13f7 100644 --- a/ext/qt/Makefile.am +++ b/ext/qt/Makefile.am @@ -20,6 +20,7 @@ libgstqtsink_la_SOURCES = \ qtitem.cc \ gstqtsink.cc \ gstqtsink.h \ + gstqtgl.h \ gstplugin.cc libgstqtsink_la_CXXFLAGS = \ diff --git a/ext/qt/gstqsgtexture.h b/ext/qt/gstqsgtexture.h index bf8f03f4ec..7b7d1fef64 100644 --- a/ext/qt/gstqsgtexture.h +++ b/ext/qt/gstqsgtexture.h @@ -22,11 +22,13 @@ #define __GST_QSG_TEXTURE_H__ #include -#include -#include #include #include +#include "gstqtgl.h" +#include +#include + class GstQSGTexture : public QSGTexture, protected QOpenGLFunctions { Q_OBJECT diff --git a/ext/qt/gstqtgl.h b/ext/qt/gstqtgl.h new file mode 100644 index 0000000000..673ef22a0f --- /dev/null +++ b/ext/qt/gstqtgl.h @@ -0,0 +1,34 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* qt uses the same trick as us to typedef GLsync on gles2 but to a different + * type which confuses the preprocessor. As it's never actually used by qt + * public headers, define it to something else to avoid redefinition + * warnings/errors */ + +#include +#include + +#if defined(QT_OPENGL_ES_2) && GST_GL_HAVE_WINDOW_ANDROID +#define GLsync gst_qt_GLsync +#include +#include +#undef GLsync +#endif /* defined(QT_OPENGL_ES_2) */ diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 7c53979138..fe56a57581 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -25,11 +25,12 @@ #include #include +#include "qtitem.h" +#include "gstqsgtexture.h" + #include #include #include -#include "qtitem.h" -#include "gstqsgtexture.h" #if GST_GL_HAVE_WINDOW_X11 && GST_GL_HAVE_PLATFORM_GLX && defined (HAVE_QT_X11) #include @@ -41,6 +42,11 @@ #include #endif +#if GST_GL_HAVE_WINDOW_ANDROID && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_ANDROID) +#include +#include +#endif + /** * SECTION:gtkgstglwidget * @short_description: a #GtkGLArea that renders GStreamer video #GstBuffers @@ -116,6 +122,10 @@ QtGLVideoItem::QtGLVideoItem() this->priv->display = (GstGLDisplay *) gst_gl_display_x11_new_with_display (QX11Info::display ()); #endif +#if GST_GL_HAVE_WINDOW_ANDROID && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_ANDROID) + if (QString::fromUtf8 ("android") == app->platformName()) + this->priv->display = (GstGLDisplay *) gst_gl_display_egl_new (); +#endif if (!this->priv->display) this->priv->display = gst_gl_display_new (); @@ -284,6 +294,17 @@ QtGLVideoItem::onSceneGraphInitialized () platform, gl_api); } #endif +#if GST_GL_HAVE_WINDOW_ANDROID && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_ANDROID) + if (GST_IS_GL_DISPLAY_EGL (this->priv->display)) { + platform = GST_GL_PLATFORM_EGL; + gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL); + gl_handle = gst_gl_context_get_current_gl_context (platform); + if (gl_handle) + this->priv->other_context = + gst_gl_context_new_wrapped (this->priv->display, gl_handle, + platform, gl_api); + } +#endif (void) platform; (void) gl_api; diff --git a/ext/qt/qtitem.h b/ext/qt/qtitem.h index afd2869d82..b6260cd16e 100644 --- a/ext/qt/qtitem.h +++ b/ext/qt/qtitem.h @@ -23,6 +23,8 @@ #include #include + +#include "gstqtgl.h" #include #include #include From 704f97defd298132e32b8eef89fb76e627e7b1e4 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Fri, 28 Aug 2015 16:35:39 +0100 Subject: [PATCH 14/78] qml: remove overwritten value Value in tex is overwritten before being used. Removing it. CID 1320715 https://bugzilla.gnome.org/show_bug.cgi?id=754253 --- ext/qt/qtitem.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index fe56a57581..9e7cdbe634 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -193,8 +193,6 @@ QtGLVideoItem::updatePaintNode(QSGNode * oldNode, if (!texNode) { texNode = new QSGSimpleTextureNode (); texNode->setOwnsTexture (true); - } else { - tex = static_cast (texNode->texture()); } tex = new GstQSGTexture (); From c95bb94fc21dcf755763657e84bc5a0a940900c0 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Fri, 28 Aug 2015 16:24:24 +0100 Subject: [PATCH 15/78] qtsink: explicitely fallthrough switch statement In case ret is False, fallthrough to default case. CID #1320705 --- ext/qt/gstqtsink.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/qt/gstqtsink.cc b/ext/qt/gstqtsink.cc index 9ff13b04c0..0f32034d15 100644 --- a/ext/qt/gstqtsink.cc +++ b/ext/qt/gstqtsink.cc @@ -271,6 +271,8 @@ gst_qt_sink_query (GstBaseSink * bsink, GstQuery * query) if (ret) return ret; + + /* fallthrough */ } default: res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query); From c6d03bc7d451f478754872f77fb4ac411bfffded Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Tue, 15 Sep 2015 03:14:37 +1000 Subject: [PATCH 16/78] qt: add support for building on osx/ios Including: - Necessary configure checks - Necessary compile time platform checks - Necessary runtime qt iOS/OSX platform detection https://bugzilla.gnome.org/show_bug.cgi?id=755100 --- ext/qt/gstplugin.cc | 2 +- ext/qt/gstqsgtexture.h | 4 ++-- ext/qt/gstqtsink.cc | 2 +- ext/qt/qtitem.cc | 40 +++++++++++++++++++++++++++++++++++++--- ext/qt/qtitem.h | 6 +++--- 5 files changed, 44 insertions(+), 10 deletions(-) diff --git a/ext/qt/gstplugin.cc b/ext/qt/gstplugin.cc index 70b6310f01..2fa10f5e78 100644 --- a/ext/qt/gstplugin.cc +++ b/ext/qt/gstplugin.cc @@ -23,7 +23,7 @@ #endif #include "gstqtsink.h" -#include +#include static gboolean plugin_init (GstPlugin * plugin) diff --git a/ext/qt/gstqsgtexture.h b/ext/qt/gstqsgtexture.h index 7b7d1fef64..a5a6f9f8be 100644 --- a/ext/qt/gstqsgtexture.h +++ b/ext/qt/gstqsgtexture.h @@ -26,8 +26,8 @@ #include #include "gstqtgl.h" -#include -#include +#include +#include class GstQSGTexture : public QSGTexture, protected QOpenGLFunctions { diff --git a/ext/qt/gstqtsink.cc b/ext/qt/gstqtsink.cc index 0f32034d15..6a25793af1 100644 --- a/ext/qt/gstqtsink.cc +++ b/ext/qt/gstqtsink.cc @@ -28,7 +28,7 @@ #endif #include "gstqtsink.h" -#include +#include #define GST_CAT_DEFAULT gst_debug_qt_gl_sink GST_DEBUG_CATEGORY (GST_CAT_DEFAULT); diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 9e7cdbe634..7c4487f87d 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -28,9 +28,9 @@ #include "qtitem.h" #include "gstqsgtexture.h" -#include -#include -#include +#include +#include +#include #if GST_GL_HAVE_WINDOW_X11 && GST_GL_HAVE_PLATFORM_GLX && defined (HAVE_QT_X11) #include @@ -47,6 +47,10 @@ #include #endif +#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) +#include +#endif + /** * SECTION:gtkgstglwidget * @short_description: a #GtkGLArea that renders GStreamer video #GstBuffers @@ -126,6 +130,14 @@ QtGLVideoItem::QtGLVideoItem() if (QString::fromUtf8 ("android") == app->platformName()) this->priv->display = (GstGLDisplay *) gst_gl_display_egl_new (); #endif +#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) + if (QString::fromUtf8 ("cocoa") == app->platformName()) + this->priv->display = (GstGLDisplay *) gst_gl_display_cocoa_new (); +#endif +#if GST_GL_HAVE_WINDOW_EAGL && GST_GL_HAVE_PLATFORM_EAGL && defined (HAVE_QT_IOS) + if (QString::fromUtf8 ("ios") == app->platformName()) + this->priv->display = gst_gl_display_new (); +#endif if (!this->priv->display) this->priv->display = gst_gl_display_new (); @@ -303,6 +315,28 @@ QtGLVideoItem::onSceneGraphInitialized () platform, gl_api); } #endif +#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) + if (this->priv->display) { + platform = GST_GL_PLATFORM_CGL; + gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL); + gl_handle = gst_gl_context_get_current_gl_context (platform); + if (gl_handle) + this->priv->other_context = + gst_gl_context_new_wrapped (this->priv->display, gl_handle, + platform, gl_api); + } +#endif +#if GST_GL_HAVE_WINDOW_EAGL && GST_GL_HAVE_PLATFORM_EAGL && defined (HAVE_QT_IOS) + if (this->priv->display) { + platform = GST_GL_PLATFORM_EAGL; + gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL); + gl_handle = gst_gl_context_get_current_gl_context (platform); + if (gl_handle) + this->priv->other_context = + gst_gl_context_new_wrapped (this->priv->display, gl_handle, + platform, gl_api); + } +#endif (void) platform; (void) gl_api; diff --git a/ext/qt/qtitem.h b/ext/qt/qtitem.h index b6260cd16e..5a07634551 100644 --- a/ext/qt/qtitem.h +++ b/ext/qt/qtitem.h @@ -25,9 +25,9 @@ #include #include "gstqtgl.h" -#include -#include -#include +#include +#include +#include typedef struct _QtGLVideoItemPrivate QtGLVideoItemPrivate; From b2718cbd81338e7edf037da0c5f69e15c9b50375 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Mon, 19 Oct 2015 15:15:30 +1100 Subject: [PATCH 17/78] gl: be consistent in gobject boilerpate GST_GL_IS_* vs GST_IS_GL_* git grep -l 'GST_GL_IS_' | xargs sed -i 's/GST_GL_IS_/GST_IS_GL_/g' --- ext/qt/qtitem.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 7c4487f87d..69d0324898 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -387,7 +387,7 @@ qt_item_init_winsys (QtGLVideoItem * widget) return FALSE; } - if (!GST_GL_IS_CONTEXT (widget->priv->other_context)) { + if (!GST_IS_GL_CONTEXT (widget->priv->other_context)) { GST_ERROR ("%p failed to retrieve wrapped context %" GST_PTR_FORMAT, widget, widget->priv->other_context); g_mutex_unlock (&widget->priv->lock); From 21ca4ac9d3b29f5c08fa9db8cfbcf08981cfa9f6 Mon Sep 17 00:00:00 2001 From: Roman Nowicki Date: Thu, 19 Nov 2015 11:55:19 +0100 Subject: [PATCH 18/78] qml: proper initialization if scene is already initialized The scene graph can be initialized when the we receive window handle change notification and so we will not receive a scenegraph initialization notification. Initialize ourself in this case. https://bugzilla.gnome.org/show_bug.cgi?id=758337 --- ext/qt/qtitem.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 69d0324898..ec2e859d24 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -412,7 +412,11 @@ void QtGLVideoItem::handleWindowChanged(QQuickWindow *win) { if (win) { - connect(win, SIGNAL(sceneGraphInitialized()), this, SLOT(onSceneGraphInitialized()), Qt::DirectConnection); + if (win->isSceneGraphInitialized()) + onSceneGraphInitialized(); + else + connect(win, SIGNAL(sceneGraphInitialized()), this, SLOT(onSceneGraphInitialized()), Qt::DirectConnection); + connect(win, SIGNAL(sceneGraphInvalidated()), this, SLOT(onSceneGraphInvalidated()), Qt::DirectConnection); } else { this->priv->qt_context = NULL; From aca25b521fcaf67ca6d05d0b897bb894a048031a Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Fri, 20 Nov 2015 11:08:37 +1100 Subject: [PATCH 19/78] qml: activate the wrapped context when binding Mitigates the following critical gst_gl_context_thread_add: assertion 'context->priv->active_thread == g_thread_self ()' failed --- ext/qt/gstqsgtexture.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ext/qt/gstqsgtexture.cc b/ext/qt/gstqsgtexture.cc index 12810fafbb..0986a82bb6 100644 --- a/ext/qt/gstqsgtexture.cc +++ b/ext/qt/gstqsgtexture.cc @@ -87,14 +87,16 @@ GstQSGTexture::bind () GstMemory *mem; guint tex_id; + gst_gl_context_activate (this->qt_context_, TRUE); + if (!this->buffer_) - return; + goto out; if (GST_VIDEO_INFO_FORMAT (&this->v_info) == GST_VIDEO_FORMAT_UNKNOWN) - return; + goto out; this->mem_ = gst_buffer_peek_memory (this->buffer_, 0); if (!this->mem_) - return; + goto out; g_assert (this->qt_context_); gl = this->qt_context_->gl_vtable; @@ -103,7 +105,7 @@ GstQSGTexture::bind () if (!gst_video_frame_map (&this->v_frame, &this->v_info, this->buffer_, (GstMapFlags) (GST_MAP_READ | GST_MAP_GL))) { g_assert_not_reached (); - return; + goto out; } mem = gst_buffer_peek_memory (this->buffer_, 0); @@ -125,6 +127,9 @@ GstQSGTexture::bind () gl->BindTexture (GL_TEXTURE_2D, tex_id); gst_video_frame_unmap (&this->v_frame); + +out: + gst_gl_context_activate (this->qt_context_, FALSE); } /* can be called from any thread */ From 1dc889c6d0d84825924a7ec925275a07c966812b Mon Sep 17 00:00:00 2001 From: Roman Nowicki Date: Fri, 20 Nov 2015 11:18:43 +1100 Subject: [PATCH 20/78] qml: reuse existing GstQSGTexture Fixes a memory leak leaking the texture objects. https://bugzilla.gnome.org/show_bug.cgi?id=758286 --- ext/qt/qtitem.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index ec2e859d24..ca8e535fe3 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -205,12 +205,12 @@ QtGLVideoItem::updatePaintNode(QSGNode * oldNode, if (!texNode) { texNode = new QSGSimpleTextureNode (); texNode->setOwnsTexture (true); + texNode->setTexture (new GstQSGTexture ()); } - tex = new GstQSGTexture (); + tex = static_cast (texNode->texture()); tex->setCaps (this->priv->caps); tex->setBuffer (this->priv->buffer); - texNode->setTexture (tex); if (this->priv->force_aspect_ratio) { src.w = this->priv->display_width; From 28d088fa2ddc7706408faac35f56a65b5addf824 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Mon, 14 Dec 2015 13:43:59 +1100 Subject: [PATCH 21/78] glmemory: base classify and add the pbo memory on top The base class is useful for having multiple backing memory types other than the default. e.g. IOSurface, EGLImage, dmabuf? The PBO transfer logic is now inside GstGLMemoryPBO which uses GstGLBuffer to manage the PBO memory. This also moves the format utility functions into their own file. --- ext/qt/gstqsgtexture.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/qt/gstqsgtexture.cc b/ext/qt/gstqsgtexture.cc index 0986a82bb6..13208c05f2 100644 --- a/ext/qt/gstqsgtexture.cc +++ b/ext/qt/gstqsgtexture.cc @@ -111,7 +111,7 @@ GstQSGTexture::bind () mem = gst_buffer_peek_memory (this->buffer_, 0); g_assert (gst_is_gl_memory (mem)); - context = ((GstGLBaseBuffer *)mem)->context; + context = ((GstGLBaseMemory *)mem)->context; sync_meta = gst_buffer_get_gl_sync_meta (this->sync_buffer_); if (!sync_meta) From 0e6a8838dc0f0bba4ea4994f36c5ed83cf44de86 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Tue, 15 Dec 2015 19:28:05 -0500 Subject: [PATCH 22/78] qtsink: Add configured GL cflags to the build We don't directly link to GL in the element, though we use GL headers. For this reason we need to include the proper GL headers path. This prevent this element from using a different GL header then libgstgl. --- ext/qt/Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/qt/Makefile.am b/ext/qt/Makefile.am index 064e8d13f7..5fdd90a05b 100644 --- a/ext/qt/Makefile.am +++ b/ext/qt/Makefile.am @@ -29,7 +29,8 @@ libgstqtsink_la_CXXFLAGS = \ $(GST_CXXFLAGS) \ $(GST_BASE_CFLAGS) \ $(GST_PLUGINS_BASE_CFLAGS) \ - $(QT_CFLAGS) + $(QT_CFLAGS) \ + $(GL_CFLAGS) libgstqtsink_la_LIBADD = \ $(GST_BASE_LIBS) \ From 1d41990b26e75b5a88cb1e1c33226be17fa71367 Mon Sep 17 00:00:00 2001 From: Sergey Borovkov Date: Fri, 8 Jan 2016 22:19:06 +0300 Subject: [PATCH 23/78] qml: Mark material dirty when texture buffer is updated Qt might not redraw the scene otherwise. https://bugzilla.gnome.org/show_bug.cgi?id=758286 --- ext/qt/qtitem.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index ca8e535fe3..9dc058a23e 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -211,6 +211,7 @@ QtGLVideoItem::updatePaintNode(QSGNode * oldNode, tex = static_cast (texNode->texture()); tex->setCaps (this->priv->caps); tex->setBuffer (this->priv->buffer); + texNode->markDirty(QSGNode::DirtyMaterial); if (this->priv->force_aspect_ratio) { src.w = this->priv->display_width; From 0d851a66af24a3dce3865ca878b852fc79269698 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Mon, 25 Jan 2016 16:29:46 +1100 Subject: [PATCH 24/78] qt: specify that we currently only take 2D textures Fixes black screen video playback on android without a caps filter. --- ext/qt/gstqtsink.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ext/qt/gstqtsink.cc b/ext/qt/gstqtsink.cc index 6a25793af1..b6a6fa52dd 100644 --- a/ext/qt/gstqtsink.cc +++ b/ext/qt/gstqtsink.cc @@ -58,8 +58,12 @@ static GstStaticPadTemplate gst_qt_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES - (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA"))); + GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), " + "format = (string) RGBA, " + "width = " GST_VIDEO_SIZE_RANGE ", " + "height = " GST_VIDEO_SIZE_RANGE ", " + "framerate = " GST_VIDEO_FPS_RANGE ", " + "texture-target = (string) 2D")); #define DEFAULT_FORCE_ASPECT_RATIO TRUE #define DEFAULT_PAR_N 0 From f3e126df884fda631fd028c21779810dc6c9d78b Mon Sep 17 00:00:00 2001 From: Holger Kaelberer Date: Sun, 24 Jan 2016 15:47:12 +0100 Subject: [PATCH 25/78] tests: fix warning in qml example https://bugzilla.gnome.org/show_bug.cgi?id=756082 --- tests/examples/qt/qml/main.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/examples/qt/qml/main.qml b/tests/examples/qt/qml/main.qml index 842e98f2eb..ffd3cd1650 100644 --- a/tests/examples/qt/qml/main.qml +++ b/tests/examples/qt/qml/main.qml @@ -33,8 +33,8 @@ ApplicationWindow { anchors.bottom: video.bottom anchors.bottomMargin: 15 anchors.horizontalCenter: parent.horizontalCenter - width : childrenRect.width + 20 - height: childrenRect.height + 20 + width : parent.width - 30 + height: parent.height - 30 radius: 8 MouseArea { From 472b396528e64caf33788a555a5628a4ae1c8423 Mon Sep 17 00:00:00 2001 From: Sergey Borovkov Date: Sun, 24 Jan 2016 17:40:37 +0300 Subject: [PATCH 26/78] qmlglsink: Schedule onSceneGrpahInitialized to execute on render thread onSceneGraphInitialized() is called from non render thread currently when scene graph is already initialized. https://bugzilla.gnome.org/show_bug.cgi?id=761003 --- ext/qt/qtitem.cc | 32 +++++++++++++++++++++++++++++--- ext/qt/qtitem.h | 4 ++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 9dc058a23e..648e0e84c5 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -28,6 +28,7 @@ #include "qtitem.h" #include "gstqsgtexture.h" +#include #include #include #include @@ -99,6 +100,26 @@ struct _QtGLVideoItemPrivate GstGLContext *context; }; +class InitializeSceneGraph : public QRunnable +{ +public: + InitializeSceneGraph(QtGLVideoItem *item); + void run(); + +private: + QtGLVideoItem *item_; +}; + +InitializeSceneGraph::InitializeSceneGraph(QtGLVideoItem *item) : + item_(item) +{ +} + +void InitializeSceneGraph::run() +{ + item_->onSceneGraphInitialized(); +} + QtGLVideoItem::QtGLVideoItem() { QGuiApplication *app = dynamic_cast (QCoreApplication::instance ()); @@ -110,7 +131,7 @@ QtGLVideoItem::QtGLVideoItem() GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qtglwidget", 0, "Qt GL Widget"); g_once_init_leave (&_debug, 1); } - + this->m_openGlContextInitialized = false; this->setFlag (QQuickItem::ItemHasContents, true); this->priv = g_new0 (QtGLVideoItemPrivate, 1); @@ -188,6 +209,10 @@ QSGNode * QtGLVideoItem::updatePaintNode(QSGNode * oldNode, UpdatePaintNodeData * updatePaintNodeData) { + if (!m_openGlContextInitialized) { + return oldNode; + } + QSGSimpleTextureNode *texNode = static_cast (oldNode); GstVideoRectangle src, dst, result; GstQSGTexture *tex; @@ -354,6 +379,7 @@ QtGLVideoItem::onSceneGraphInitialized () } else { gst_gl_display_filter_gl_api (this->priv->display, gst_gl_context_get_gl_api (this->priv->other_context)); gst_gl_context_activate (this->priv->other_context, FALSE); + m_openGlContextInitialized = true; } } @@ -414,9 +440,9 @@ QtGLVideoItem::handleWindowChanged(QQuickWindow *win) { if (win) { if (win->isSceneGraphInitialized()) - onSceneGraphInitialized(); + win->scheduleRenderJob(new InitializeSceneGraph(this), QQuickWindow::BeforeSynchronizingStage); else - connect(win, SIGNAL(sceneGraphInitialized()), this, SLOT(onSceneGraphInitialized()), Qt::DirectConnection); + connect(win, SIGNAL(sceneGraphInitialized()), this, SLOT(onSceneGraphInitialized()), Qt::DirectConnection); connect(win, SIGNAL(sceneGraphInvalidated()), this, SLOT(onSceneGraphInvalidated()), Qt::DirectConnection); } else { diff --git a/ext/qt/qtitem.h b/ext/qt/qtitem.h index 5a07634551..ae23eff107 100644 --- a/ext/qt/qtitem.h +++ b/ext/qt/qtitem.h @@ -31,6 +31,8 @@ typedef struct _QtGLVideoItemPrivate QtGLVideoItemPrivate; +class InitializeSceneGraph; + class QtGLVideoItem : public QQuickItem, protected QOpenGLFunctions { Q_OBJECT @@ -55,10 +57,12 @@ protected: QSGNode * updatePaintNode (QSGNode * oldNode, UpdatePaintNodeData * updatePaintNodeData); private: + friend class InitializeSceneGraph; void setViewportSize(const QSize &size); void shareContext(); QSize m_viewportSize; + bool m_openGlContextInitialized; }; extern "C" From 6c6a9cf509471d5f1f2947950a9a92b16b5492d2 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Tue, 23 Feb 2016 23:10:20 +1100 Subject: [PATCH 27/78] qt: use a static_cast instead of dynamic one The dynamic_cast is a little but of overkill as the app will still crash if it fails in the later g_assert. Allows compilation with -fno-rtti https://bugzilla.gnome.org/show_bug.cgi?id=762526 --- ext/qt/gstqtsink.cc | 2 +- ext/qt/qtitem.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/qt/gstqtsink.cc b/ext/qt/gstqtsink.cc index b6a6fa52dd..df44a7d50f 100644 --- a/ext/qt/gstqtsink.cc +++ b/ext/qt/gstqtsink.cc @@ -305,7 +305,7 @@ gst_qt_sink_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: - app = dynamic_cast (QCoreApplication::instance ()); + app = static_cast (QCoreApplication::instance ()); if (!app) { GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND, ("%s", "Failed to connect to Qt"), diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 648e0e84c5..57b7bc9738 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -122,7 +122,7 @@ void InitializeSceneGraph::run() QtGLVideoItem::QtGLVideoItem() { - QGuiApplication *app = dynamic_cast (QCoreApplication::instance ()); + QGuiApplication *app = static_cast (QCoreApplication::instance ()); static volatile gsize _debug; g_assert (app != NULL); From c2f17d48345f7cbcaeea1d898549adc28a7a178d Mon Sep 17 00:00:00 2001 From: Sergey Borovkov Date: Tue, 1 Mar 2016 18:22:37 +0300 Subject: [PATCH 28/78] qml: Fix leak of the OpenGL contexts [Matthew Waters]: add NULL checks before unreffing https://bugzilla.gnome.org/show_bug.cgi?id=762999 --- ext/qt/qtitem.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 57b7bc9738..e1f91e76fc 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -172,7 +172,10 @@ QtGLVideoItem::QtGLVideoItem() QtGLVideoItem::~QtGLVideoItem() { g_mutex_clear (&this->priv->lock); - + if (this->priv->context) + gst_object_unref(this->priv->context); + if (this->priv->other_context) + gst_object_unref(this->priv->other_context); g_free (this->priv); this->priv = NULL; } From acb1b44ee0855561719e874c2e93416f2625b62a Mon Sep 17 00:00:00 2001 From: Vineeth TM Date: Fri, 4 Mar 2016 15:50:26 +0900 Subject: [PATCH 29/78] bad: use new gst_element_class_add_static_pad_template() https://bugzilla.gnome.org/show_bug.cgi?id=763081 --- ext/qt/gstqtsink.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/qt/gstqtsink.cc b/ext/qt/gstqtsink.cc index df44a7d50f..62a5373689 100644 --- a/ext/qt/gstqtsink.cc +++ b/ext/qt/gstqtsink.cc @@ -126,8 +126,7 @@ gst_qt_sink_class_init (GstQtSinkClass * klass) G_MAXINT, 1, 1, 1, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&gst_qt_sink_template)); + gst_element_class_add_static_pad_template (gstelement_class, &gst_qt_sink_template); gobject_class->finalize = gst_qt_sink_finalize; From a07a9b0bc3f351225791330e779928513290684b Mon Sep 17 00:00:00 2001 From: Haihua Hu Date: Sun, 12 Jun 2016 15:35:28 +0800 Subject: [PATCH 30/78] qmlglsink: Add Wayland support Don't use gstgldisplay to get wayland display. Should use QPA on wayland to get wayland display for QT. https://bugzilla.gnome.org/show_bug.cgi?id=767553 --- ext/qt/Makefile.am | 1 + ext/qt/qtitem.cc | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/ext/qt/Makefile.am b/ext/qt/Makefile.am index 5fdd90a05b..eab3e97ec6 100644 --- a/ext/qt/Makefile.am +++ b/ext/qt/Makefile.am @@ -26,6 +26,7 @@ libgstqtsink_la_SOURCES = \ libgstqtsink_la_CXXFLAGS = \ -I$(top_srcdir)/gst-libs \ -I$(top_builddir)/gst-libs \ + -I$(QPA_INCLUDE_PATH) \ $(GST_CXXFLAGS) \ $(GST_BASE_CFLAGS) \ $(GST_PLUGINS_BASE_CFLAGS) \ diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index e1f91e76fc..2df087e7d7 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -32,6 +32,7 @@ #include #include #include +#include #if GST_GL_HAVE_WINDOW_X11 && GST_GL_HAVE_PLATFORM_GLX && defined (HAVE_QT_X11) #include @@ -147,6 +148,17 @@ QtGLVideoItem::QtGLVideoItem() this->priv->display = (GstGLDisplay *) gst_gl_display_x11_new_with_display (QX11Info::display ()); #endif +#if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_WAYLAND) + if (QString::fromUtf8 ("wayland") == app->platformName()){ + struct wl_display * wayland_display; + QPlatformNativeInterface *native = + QGuiApplication::platformNativeInterface(); + wayland_display = (struct wl_display *) + native->nativeResourceForWindow("display", NULL); + this->priv->display = (GstGLDisplay *) + gst_gl_display_wayland_new_with_display (wayland_display); + } +#endif #if GST_GL_HAVE_WINDOW_ANDROID && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_ANDROID) if (QString::fromUtf8 ("android") == app->platformName()) this->priv->display = (GstGLDisplay *) gst_gl_display_egl_new (); From f17e58f888425b69346c666fd080ce8b93399621 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Thu, 16 Jun 2016 00:44:16 +1000 Subject: [PATCH 31/78] qmlglsink: also allow wayland-egl as a platform name --- ext/qt/qtitem.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 2df087e7d7..8a73ed4261 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -143,13 +143,16 @@ QtGLVideoItem::QtGLVideoItem() g_mutex_init (&this->priv->lock); + GST_INFO ("QGuiApplication::instance()->platformName() %s", app->platformName().toUtf8().data()); + #if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11) if (QString::fromUtf8 ("xcb") == app->platformName()) this->priv->display = (GstGLDisplay *) gst_gl_display_x11_new_with_display (QX11Info::display ()); #endif #if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_WAYLAND) - if (QString::fromUtf8 ("wayland") == app->platformName()){ + if (QString::fromUtf8 ("wayland") == app->platformName() + || QString::fromUtf8 ("wayland-egl") == app->platformName()){ struct wl_display * wayland_display; QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); From 486d3c3b5964d5ed6c70d71d5c8d21b1f244e070 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Thu, 16 Jun 2016 00:44:48 +1000 Subject: [PATCH 32/78] qmlglsink: propagate GL context creation failure upwards Otherwise an application cannot know if the qmlglsink will be displaying frames incorrectly/at all. --- ext/qt/qtitem.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 8a73ed4261..9c359f85d7 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -414,6 +414,8 @@ QtGLVideoItem::onSceneGraphInvalidated () gboolean qt_item_init_winsys (QtGLVideoItem * widget) { + GError *error = NULL; + g_return_val_if_fail (widget != NULL, FALSE); g_mutex_lock (&widget->priv->lock); @@ -446,8 +448,12 @@ qt_item_init_winsys (QtGLVideoItem * widget) return FALSE; } - gst_gl_context_create (widget->priv->context, widget->priv->other_context, - NULL); + if (!gst_gl_context_create (widget->priv->context, widget->priv->other_context, + &error)) { + GST_ERROR ("%s", error->message); + g_mutex_unlock (&widget->priv->lock); + return FALSE; + } g_mutex_unlock (&widget->priv->lock); return TRUE; From c9dd45393ea63f5ba0bf0a6e4dd38ef1f5d7fc18 Mon Sep 17 00:00:00 2001 From: Sergey Borovkov Date: Thu, 14 Apr 2016 18:14:32 +0300 Subject: [PATCH 33/78] qml: Enable qmlglsink for eglfs https://bugzilla.gnome.org/show_bug.cgi?id=763044 --- ext/qt/qtitem.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 9c359f85d7..505ed03f20 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -44,7 +44,7 @@ #include #endif -#if GST_GL_HAVE_WINDOW_ANDROID && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_ANDROID) +#if GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) #include #include #endif @@ -162,10 +162,14 @@ QtGLVideoItem::QtGLVideoItem() gst_gl_display_wayland_new_with_display (wayland_display); } #endif -#if GST_GL_HAVE_WINDOW_ANDROID && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_ANDROID) +#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_WINDOW_ANDROID if (QString::fromUtf8 ("android") == app->platformName()) this->priv->display = (GstGLDisplay *) gst_gl_display_egl_new (); +#elif GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) + if (QString::fromUtf8("eglfs") == app->platformName()) + this->priv->display = (GstGLDisplay *) gst_gl_display_egl_new (); #endif + #if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) if (QString::fromUtf8 ("cocoa") == app->platformName()) this->priv->display = (GstGLDisplay *) gst_gl_display_cocoa_new (); @@ -348,7 +352,8 @@ QtGLVideoItem::onSceneGraphInitialized () platform, gl_api); } #endif -#if GST_GL_HAVE_WINDOW_ANDROID && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_ANDROID) + +#if GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) if (GST_IS_GL_DISPLAY_EGL (this->priv->display)) { platform = GST_GL_PLATFORM_EGL; gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL); @@ -359,6 +364,7 @@ QtGLVideoItem::onSceneGraphInitialized () platform, gl_api); } #endif + #if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) if (this->priv->display) { platform = GST_GL_PLATFORM_CGL; From 3be4b85a439c63de71a71ec3ddbe0dd7d08a90fc Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Thu, 16 Jun 2016 15:13:02 +1000 Subject: [PATCH 34/78] qmlglsink: add win32 support The current state of c++ ABI's on Window's and Gst's/Qt's conflicting mingw builds means that we cannot use mingw for building the qt plugin. Instead, a qmake .pro file is provided that is expected to be used with the msvc binaries provided by Qt like so: (with the PATH environment variable containing the path to the qt biniaries and PKG_CONFIG_PATH containing the path to GStreamer modules) cd /path/to/sources/gst-plugins-bad/ext/qt qmake -tp vc Then open the resulting VS project and build the library. Then cp debug/libgstqtsink.dll /path/to/prefix/lib/gstreamer-1.0/libgstqtsink.cll https://bugzilla.gnome.org/show_bug.cgi?id=761260 --- ext/qt/qtitem.cc | 93 +++++++++++++++++++++++++-------------------- ext/qt/qtplugin.pro | 39 +++++++++++++++++++ 2 files changed, 91 insertions(+), 41 deletions(-) create mode 100644 ext/qt/qtplugin.pro diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 505ed03f20..67820b9939 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -178,6 +178,10 @@ QtGLVideoItem::QtGLVideoItem() if (QString::fromUtf8 ("ios") == app->platformName()) this->priv->display = gst_gl_display_new (); #endif +#if GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (HAVE_QT_WIN32) + if (QString::fromUtf8 ("windows") == app->platformName()) + this->priv->display = gst_gl_display_new (); +#endif if (!this->priv->display) this->priv->display = gst_gl_display_new (); @@ -216,9 +220,9 @@ QtGLVideoItem::getDAR(gint * num, gint * den) } void -QtGLVideoItem::setForceAspectRatio(bool far) +QtGLVideoItem::setForceAspectRatio(bool force_aspect_ratio) { - this->priv->force_aspect_ratio = far; + this->priv->force_aspect_ratio = !!force_aspect_ratio; } bool @@ -314,7 +318,7 @@ qt_item_set_buffer (QtGLVideoItem * widget, GstBuffer * buffer) void QtGLVideoItem::onSceneGraphInitialized () { - GstGLPlatform platform; + GstGLPlatform platform = (GstGLPlatform) 0; GstGLAPI gl_api; guintptr gl_handle; @@ -333,60 +337,37 @@ QtGLVideoItem::onSceneGraphInitialized () #if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11) if (GST_IS_GL_DISPLAY_X11 (this->priv->display)) { platform = GST_GL_PLATFORM_GLX; - gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL); - gl_handle = gst_gl_context_get_current_gl_context (platform); - if (gl_handle) - this->priv->other_context = - gst_gl_context_new_wrapped (this->priv->display, gl_handle, - platform, gl_api); } #endif #if GST_GL_HAVE_WINDOW_WAYLAND && defined (HAVE_QT_WAYLAND) if (GST_IS_GL_DISPLAY_WAYLAND (this->priv->display)) { platform = GST_GL_PLATFORM_EGL; - gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL); - gl_handle = gst_gl_context_get_current_gl_context (platform); - if (gl_handle) - this->priv->other_context = - gst_gl_context_new_wrapped (this->priv->display, gl_handle, - platform, gl_api); } #endif - #if GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) if (GST_IS_GL_DISPLAY_EGL (this->priv->display)) { platform = GST_GL_PLATFORM_EGL; - gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL); - gl_handle = gst_gl_context_get_current_gl_context (platform); - if (gl_handle) - this->priv->other_context = - gst_gl_context_new_wrapped (this->priv->display, gl_handle, - platform, gl_api); } #endif - + if (platform == 0 && this->priv->display) { #if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) - if (this->priv->display) { platform = GST_GL_PLATFORM_CGL; - gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL); - gl_handle = gst_gl_context_get_current_gl_context (platform); - if (gl_handle) - this->priv->other_context = - gst_gl_context_new_wrapped (this->priv->display, gl_handle, - platform, gl_api); - } -#endif -#if GST_GL_HAVE_WINDOW_EAGL && GST_GL_HAVE_PLATFORM_EAGL && defined (HAVE_QT_IOS) - if (this->priv->display) { +#elif GST_GL_HAVE_WINDOW_EAGL && GST_GL_HAVE_PLATFORM_EAGL && defined (HAVE_QT_IOS) platform = GST_GL_PLATFORM_EAGL; - gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL); - gl_handle = gst_gl_context_get_current_gl_context (platform); - if (gl_handle) - this->priv->other_context = - gst_gl_context_new_wrapped (this->priv->display, gl_handle, - platform, gl_api); - } +#elif GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (HAVE_QT_WIN32) + platform = GST_GL_PLATFORM_WGL; +#else + GST_ERROR ("Unknown platform"); + return; #endif + } + + gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL); + gl_handle = gst_gl_context_get_current_gl_context (platform); + if (gl_handle) + this->priv->other_context = + gst_gl_context_new_wrapped (this->priv->display, gl_handle, + platform, gl_api); (void) platform; (void) gl_api; @@ -402,6 +383,36 @@ QtGLVideoItem::onSceneGraphInitialized () this->priv->other_context = NULL; } else { gst_gl_display_filter_gl_api (this->priv->display, gst_gl_context_get_gl_api (this->priv->other_context)); +#if GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (HAVE_QT_WIN32) + if (!wglGetProcAddress ("wglCreateContextAttribsARB")) { + GstGLWindow *window; + HDC device; + + /* If there's no wglCreateContextAttribsARB() support, then we would fallback to + * wglShareLists() which will fail with ERROR_BUSY (0xaa) if either of the GL + * contexts are current in any other thread. + * + * The workaround here is to temporarily disable Qt's GL context while we + * set up our own. + */ + this->priv->context = gst_gl_context_new (this->priv->display); + window = gst_gl_context_get_window (this->priv->context); + device = (HDC) gst_gl_window_get_display (window); + + wglMakeCurrent (device, 0); + gst_object_unref (window); + if (!gst_gl_context_create (this->priv->context, this->priv->other_context, &error)) { + GST_ERROR ("%p failed to create shared GL context: %s", this, error->message); + g_object_unref (this->priv->context); + this->priv->context = NULL; + g_object_unref (this->priv->other_context); + this->priv->other_context = NULL; + wglMakeCurrent (device, (HGLRC) gl_handle); + return; + } + wglMakeCurrent (device, (HGLRC) gl_handle); + } +#endif gst_gl_context_activate (this->priv->other_context, FALSE); m_openGlContextInitialized = true; } diff --git a/ext/qt/qtplugin.pro b/ext/qt/qtplugin.pro new file mode 100644 index 0000000000..af1d8ddc12 --- /dev/null +++ b/ext/qt/qtplugin.pro @@ -0,0 +1,39 @@ +TEMPLATE = lib + +TARGET = libgstqtsink + +QT += qml quick widgets + +QT_CONFIG -= no-pkg-config +CONFIG += link_pkgconfig debug plugin +PKGCONFIG = \ + gstreamer-1.0 \ + gstreamer-video-1.0 \ + gstreamer-gl-1.0 + +DEFINES += \ + GST_USE_UNSTABLE_API \ + HAVE_QT_WIN32 \ + 'GST_PACKAGE_NAME=\"GStreamer Bad Plug-ins (qmake)\"' \ + 'GST_PACKAGE_ORIGIN=\"Unknown package origin\"' \ + 'GST_LICENSE=\"LGPL\"' \ + 'PACKAGE=\"gst-plugins-bad (qmake)\"' \ + 'PACKAGE_VERSION=\"1.9.0.1\"' + +SOURCES += \ + gstplugin.cc \ + gstqsgtexture.cc \ + gstqtsink.cc \ + qtitem.cc + +HEADERS += \ + gstqsgtexture.h \ + gstqtgl.h \ + gstqtsink.h \ + qtitem.h + +INCLUDEPATH += \ + $$(GSTREAMER_ROOT)/include \ + $$[QT_INSTALL_PREFIX]/include/QtGui/$$[QT_VERSION]/QtGui/ + + \ No newline at end of file From 04fd6dc6c44e7b9227b5ed71ac93a06e0eaf8846 Mon Sep 17 00:00:00 2001 From: Haihua Hu Date: Mon, 27 Jun 2016 18:15:08 +0800 Subject: [PATCH 35/78] qmlglsink: Fix build error when don't have QPA installed. Check header file existance and wrap the header file include in the necessary #ifdef to avoid build error. https://bugzilla.gnome.org/show_bug.cgi?id=767553 --- ext/qt/qtitem.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 67820b9939..c306c6dbd4 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -32,7 +32,6 @@ #include #include #include -#include #if GST_GL_HAVE_WINDOW_X11 && GST_GL_HAVE_PLATFORM_GLX && defined (HAVE_QT_X11) #include @@ -41,6 +40,7 @@ #endif #if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_WAYLAND) +#include #include #endif From 474ece76839578a1f0c4281e6c75c9e702991bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Fri, 1 Jul 2016 19:22:32 +0100 Subject: [PATCH 36/78] qt: fix build some more when QPA is not available Compiler would complain about include directory that didn't exist because QPA_INCLUDE_PATH gets subst-ed regardless (and if it didn't we'd have just an empty -I argument). https://bugzilla.gnome.org/show_bug.cgi?id=767553 --- ext/qt/Makefile.am | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/qt/Makefile.am b/ext/qt/Makefile.am index eab3e97ec6..5fdd90a05b 100644 --- a/ext/qt/Makefile.am +++ b/ext/qt/Makefile.am @@ -26,7 +26,6 @@ libgstqtsink_la_SOURCES = \ libgstqtsink_la_CXXFLAGS = \ -I$(top_srcdir)/gst-libs \ -I$(top_builddir)/gst-libs \ - -I$(QPA_INCLUDE_PATH) \ $(GST_CXXFLAGS) \ $(GST_BASE_CFLAGS) \ $(GST_PLUGINS_BASE_CFLAGS) \ From f7ee11f68b0317db07f34e7177a01e692a802b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 22 Jul 2016 16:57:45 +0300 Subject: [PATCH 37/78] qml: Use glsinkbin instead of glupload directly --- tests/examples/qt/qml/main.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/examples/qt/qml/main.cpp b/tests/examples/qt/qml/main.cpp index 2317b05415..801468b030 100644 --- a/tests/examples/qt/qml/main.cpp +++ b/tests/examples/qt/qml/main.cpp @@ -44,15 +44,17 @@ int main(int argc, char *argv[]) GstElement *pipeline = gst_pipeline_new (NULL); GstElement *src = gst_element_factory_make ("videotestsrc", NULL); - GstElement *glupload = gst_element_factory_make ("glupload", NULL); /* the plugin must be loaded before loading the qml file to register the * GstGLVideoItem qml item */ GstElement *sink = gst_element_factory_make ("qmlglsink", NULL); + GstElement *sinkbin = gst_element_factory_make ("glsinkbin", NULL); - g_assert (src && glupload && sink); + g_assert (src && sink && sinkbin); - gst_bin_add_many (GST_BIN (pipeline), src, glupload, sink, NULL); - gst_element_link_many (src, glupload, sink, NULL); + g_object_set (sinkbin, "sink", sink, NULL); + + gst_bin_add_many (GST_BIN (pipeline), src, sinkbin, NULL); + gst_element_link_many (src, sinkbin, NULL); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); From ac35df33e010ed76580e75644ce7d62fad684507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 22 Jul 2016 17:00:14 +0300 Subject: [PATCH 38/78] qml: Don't forget to unref the actual sink element after setting it on glsinkbin --- tests/examples/qt/qml/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/examples/qt/qml/main.cpp b/tests/examples/qt/qml/main.cpp index 801468b030..bc5d288246 100644 --- a/tests/examples/qt/qml/main.cpp +++ b/tests/examples/qt/qml/main.cpp @@ -52,6 +52,7 @@ int main(int argc, char *argv[]) g_assert (src && sink && sinkbin); g_object_set (sinkbin, "sink", sink, NULL); + g_object_unref (sink); gst_bin_add_many (GST_BIN (pipeline), src, sinkbin, NULL); gst_element_link_many (src, sinkbin, NULL); From 669600617c1ad199394bcf8356c0302abde11d96 Mon Sep 17 00:00:00 2001 From: Haihua Hu Date: Wed, 27 Jul 2016 08:16:47 +0800 Subject: [PATCH 39/78] qt: implement qmlglsrc for qml view grab [Matthew Waters]: gst-indent sources https://bugzilla.gnome.org/show_bug.cgi?id=768160 --- ext/qt/Makefile.am | 24 +- ext/qt/gstplugin.cc | 8 +- ext/qt/gstqtglutility.cc | 211 ++++++++++++++ ext/qt/gstqtglutility.h | 34 +++ ext/qt/gstqtsrc.cc | 606 +++++++++++++++++++++++++++++++++++++++ ext/qt/gstqtsrc.h | 82 ++++++ ext/qt/qtitem.cc | 156 +--------- ext/qt/qtwindow.cc | 371 ++++++++++++++++++++++++ ext/qt/qtwindow.h | 70 +++++ 9 files changed, 1401 insertions(+), 161 deletions(-) create mode 100644 ext/qt/gstqtglutility.cc create mode 100644 ext/qt/gstqtglutility.h create mode 100644 ext/qt/gstqtsrc.cc create mode 100644 ext/qt/gstqtsrc.h create mode 100644 ext/qt/qtwindow.cc create mode 100644 ext/qt/qtwindow.h diff --git a/ext/qt/Makefile.am b/ext/qt/Makefile.am index 5fdd90a05b..b8bd5d4bff 100644 --- a/ext/qt/Makefile.am +++ b/ext/qt/Makefile.am @@ -1,29 +1,37 @@ -plugin_LTLIBRARIES = libgstqtsink.la +plugin_LTLIBRARIES = libgstqmlgl.la noinst_HEADERS = \ gstqsgtexture.h \ + gstqtglutility.h \ gstqtsink.h \ - qtitem.h + gstqtsrc.h \ + qtitem.h \ + qtwindow.h moc_generated = \ moc_qtitem.cc \ + moc_qtwindow.cc \ moc_gstqsgtexture.cc #anything generated by the Qt tools... BUILT_SOURCES = $(moc_generated) CLEANFILES = $(moc_generated) -nodist_libgstqtsink_la_SOURCES = $(BUILT_SOURCES) +nodist_libgstqmlgl_la_SOURCES = $(BUILT_SOURCES) -libgstqtsink_la_SOURCES = \ +libgstqmlgl_la_SOURCES = \ gstqsgtexture.cc \ + gstqtglutility.cc \ qtitem.cc \ + qtwindow.cc \ gstqtsink.cc \ gstqtsink.h \ + gstqtsrc.cc \ + gstqtsrc.h \ gstqtgl.h \ gstplugin.cc -libgstqtsink_la_CXXFLAGS = \ +libgstqmlgl_la_CXXFLAGS = \ -I$(top_srcdir)/gst-libs \ -I$(top_builddir)/gst-libs \ $(GST_CXXFLAGS) \ @@ -32,15 +40,15 @@ libgstqtsink_la_CXXFLAGS = \ $(QT_CFLAGS) \ $(GL_CFLAGS) -libgstqtsink_la_LIBADD = \ +libgstqmlgl_la_LIBADD = \ $(GST_BASE_LIBS) \ $(GST_PLUGINS_BASE_LIBS) \ $(QT_LIBS) \ $(top_builddir)/gst-libs/gst/gl/libgstgl-$(GST_API_VERSION).la \ -lgstvideo-$(GST_API_VERSION) -libgstqtsink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstqtsink_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) +libgstqmlgl_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstqmlgl_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) $(moc_generated): moc_%.cc: %.h diff --git a/ext/qt/gstplugin.cc b/ext/qt/gstplugin.cc index 2fa10f5e78..01ca2752c0 100644 --- a/ext/qt/gstplugin.cc +++ b/ext/qt/gstplugin.cc @@ -23,6 +23,7 @@ #endif #include "gstqtsink.h" +#include "gstqtsrc.h" #include static gboolean @@ -32,6 +33,11 @@ plugin_init (GstPlugin * plugin) GST_RANK_NONE, GST_TYPE_QT_SINK)) { return FALSE; } + + if (!gst_element_register (plugin, "qmlglsrc", + GST_RANK_NONE, GST_TYPE_QT_SRC)) { + return FALSE; + } /* this means the plugin must be loaded before the qml engine is loaded */ qmlRegisterType ("org.freedesktop.gstreamer.GLVideoItem", 1, 0, "GstGLVideoItem"); @@ -41,6 +47,6 @@ plugin_init (GstPlugin * plugin) GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, qt, - "Qt sink", + "Qt gl plugin", plugin_init, PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/qt/gstqtglutility.cc b/ext/qt/gstqtglutility.cc new file mode 100644 index 0000000000..3753037c2c --- /dev/null +++ b/ext/qt/gstqtglutility.cc @@ -0,0 +1,211 @@ +/* + * GStreamer + * Copyright (C) 2016 Freescale Semiconductor, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstqtglutility.h" +#include + +#if GST_GL_HAVE_WINDOW_X11 && GST_GL_HAVE_PLATFORM_GLX && defined (HAVE_QT_X11) +#include +#include +#include +#endif + +#if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_WAYLAND) +#include +#include +#endif + +#if GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) +#include +#include +#endif + +#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) +#include +#endif + +#define GST_CAT_DEFAULT qt_gl_utils_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +GstGLDisplay * +gst_qt_get_gl_display () +{ + GstGLDisplay *display = NULL; + QGuiApplication *app = static_cast (QCoreApplication::instance ()); + static volatile gsize _debug; + + g_assert (app != NULL); + + GST_INFO ("QGuiApplication::instance()->platformName() %s", app->platformName().toUtf8().data()); + + if (g_once_init_enter (&_debug)) { + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qtglutility", 0, + "Qt gl utility functions"); + g_once_init_leave (&_debug, 1); + } + +#if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11) + if (QString::fromUtf8 ("xcb") == app->platformName()) + display = (GstGLDisplay *) + gst_gl_display_x11_new_with_display (QX11Info::display ()); +#endif +#if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_WAYLAND) + if (QString::fromUtf8 ("wayland") == app->platformName() + || QString::fromUtf8 ("wayland-egl") == app->platformName()){ + struct wl_display * wayland_display; + QPlatformNativeInterface *native = + QGuiApplication::platformNativeInterface(); + wayland_display = (struct wl_display *) + native->nativeResourceForWindow("display", NULL); + display = (GstGLDisplay *) + gst_gl_display_wayland_new_with_display (wayland_display); + } +#endif +#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_WINDOW_ANDROID + if (QString::fromUtf8 ("android") == app->platformName()) + display = (GstGLDisplay *) gst_gl_display_egl_new (); +#elif GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) + if (QString::fromUtf8("eglfs") == app->platformName()) + display = (GstGLDisplay *) gst_gl_display_egl_new (); +#endif + +#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) + if (QString::fromUtf8 ("cocoa") == app->platformName()) + display = (GstGLDisplay *) gst_gl_display_cocoa_new (); +#endif +#if GST_GL_HAVE_WINDOW_EAGL && GST_GL_HAVE_PLATFORM_EAGL && defined (HAVE_QT_IOS) + if (QString::fromUtf8 ("ios") == app->platformName()) + display = gst_gl_display_new (); +#endif +#if GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (HAVE_QT_WIN32) + if (QString::fromUtf8 ("windows") == app->platformName()) + display = gst_gl_display_new (); +#endif + + if (!display) + display = gst_gl_display_new (); + + return display; +} + +gboolean +gst_qt_get_gl_wrapcontext (GstGLDisplay * display, + GstGLContext **wrap_glcontext, GstGLContext **context) +{ + GstGLPlatform platform = (GstGLPlatform) 0; + GstGLAPI gl_api; + guintptr gl_handle; + GError *error = NULL; + + g_return_val_if_fail (display != NULL && wrap_glcontext != NULL, FALSE); + +#if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11) + if (GST_IS_GL_DISPLAY_X11 (display)) { + platform = GST_GL_PLATFORM_GLX; + } +#endif +#if GST_GL_HAVE_WINDOW_WAYLAND && defined (HAVE_QT_WAYLAND) + if (GST_IS_GL_DISPLAY_WAYLAND (display)) { + platform = GST_GL_PLATFORM_EGL; + } +#endif +#if GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) + if (GST_IS_GL_DISPLAY_EGL (display)) { + platform = GST_GL_PLATFORM_EGL; + } +#endif + if (platform == 0) { +#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) + platform = GST_GL_PLATFORM_CGL; +#elif GST_GL_HAVE_WINDOW_EAGL && GST_GL_HAVE_PLATFORM_EAGL && defined (HAVE_QT_IOS) + platform = GST_GL_PLATFORM_EAGL; +#elif GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (HAVE_QT_WIN32) + platform = GST_GL_PLATFORM_WGL; +#else + GST_ERROR ("Unknown platform"); + return FALSE; +#endif + } + + gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL); + gl_handle = gst_gl_context_get_current_gl_context (platform); + if (gl_handle) + *wrap_glcontext = + gst_gl_context_new_wrapped (display, gl_handle, + platform, gl_api); + + if (!*wrap_glcontext) { + GST_ERROR ("cannot wrap qt OpenGL context"); + return FALSE; + } + + (void) platform; + (void) gl_api; + (void) gl_handle; + + 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); + *wrap_glcontext = NULL; + return FALSE; + } else { + gst_gl_display_filter_gl_api (display, gst_gl_context_get_gl_api (*wrap_glcontext)); +#if GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (HAVE_QT_WIN32) + g_return_val_if_fail (context != NULL, FALSE); + + if (!wglGetProcAddress ("wglCreateContextAttribsARB")) { + GstGLWindow *window; + HDC device; + + /* If there's no wglCreateContextAttribsARB() support, then we would fallback to + * wglShareLists() which will fail with ERROR_BUSY (0xaa) if either of the GL + * contexts are current in any other thread. + * + * The workaround here is to temporarily disable Qt's GL context while we + * set up our own. + */ + *context = gst_gl_context_new (display); + window = gst_gl_context_get_window (*context); + device = (HDC) gst_gl_window_get_display (window); + + wglMakeCurrent (device, 0); + gst_object_unref (window); + if (!gst_gl_context_create (*context, *wrap_glcontext, &error)) { + GST_ERROR ("%p failed to create shared GL context: %s", this, error->message); + g_object_unref (*context); + *context = NULL; + g_object_unref (*wrap_glcontext); + *wrap_glcontext = NULL; + wglMakeCurrent (device, (HGLRC) gl_handle); + return FALSE; + } + wglMakeCurrent (device, (HGLRC) gl_handle); + } +#endif + gst_gl_context_activate (*wrap_glcontext, FALSE); + } + + return TRUE; +} diff --git a/ext/qt/gstqtglutility.h b/ext/qt/gstqtglutility.h new file mode 100644 index 0000000000..ca9023773a --- /dev/null +++ b/ext/qt/gstqtglutility.h @@ -0,0 +1,34 @@ +/* + * GStreamer + * Copyright (C) 2016 Freescale Semiconductor, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __QT_GL_UTILS_H__ +#define __QT_GL_UTILS_H__ + +#include +#include + +G_BEGIN_DECLS + +GstGLDisplay * gst_qt_get_gl_display (); +gboolean gst_qt_get_gl_wrapcontext (GstGLDisplay * display, + GstGLContext **wrap_glcontext, GstGLContext **context); + +G_END_DECLS +#endif /* __QT_GL_UTILS_H__ */ diff --git a/ext/qt/gstqtsrc.cc b/ext/qt/gstqtsrc.cc new file mode 100644 index 0000000000..3cfc709f18 --- /dev/null +++ b/ext/qt/gstqtsrc.cc @@ -0,0 +1,606 @@ +/* + * GStreamer + * Copyright (C) 2016 Freescale Semiconductor, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:qmlglsrc + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstqtsrc.h" +#include + +#define GST_CAT_DEFAULT gst_debug_qt_gl_src +GST_DEBUG_CATEGORY (GST_CAT_DEFAULT); + +#define DEFAULT_IS_LIVE TRUE + +static void gst_qt_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_qt_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void gst_qt_src_finalize (GObject * object); + +static gboolean gst_qt_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps); +static GstCaps *gst_qt_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter); +static gboolean gst_qt_src_query (GstBaseSrc * bsrc, GstQuery * query); + +static gboolean gst_qt_src_decide_allocation (GstBaseSrc * bsrc, + GstQuery * query); +static GstFlowReturn gst_qt_src_fill (GstPushSrc * psrc, GstBuffer * buffer); +static GstStateChangeReturn gst_qt_src_change_state (GstElement * element, + GstStateChange transition); +static gboolean gst_qt_src_start (GstBaseSrc * basesrc); +static gboolean gst_qt_src_stop (GstBaseSrc * basesrc); + +static GstStaticPadTemplate gst_qt_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), " + "format = (string) RGBA, " + "width = " GST_VIDEO_SIZE_RANGE ", " + "height = " GST_VIDEO_SIZE_RANGE ", " + "framerate = " GST_VIDEO_FPS_RANGE ", " + "texture-target = (string) 2D")); + +enum +{ + ARG_0, + PROP_WINDOW, + PROP_DEFAULT_FBO +}; + +#define gst_qt_src_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstQtSrc, gst_qt_src, + GST_TYPE_PUSH_SRC, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, + "qtsrc", 0, "Qt Video Src")); + +static const gfloat vertical_flip_matrix[] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; + +static void +gst_qt_src_class_init (GstQtSrcClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + GstBaseSrcClass *gstbasesrc_class = (GstBaseSrcClass *) klass; + GstPushSrcClass *gstpushsrc_class = (GstPushSrcClass *) klass; + + gobject_class->set_property = gst_qt_src_set_property; + gobject_class->get_property = gst_qt_src_get_property; + gobject_class->finalize = gst_qt_src_finalize; + + gst_element_class_set_metadata (gstelement_class, "Qt Video Source", + "Source/Video", "A video src the grab window from a qml view", + "Multimedia Team "); + + g_object_class_install_property (gobject_class, PROP_WINDOW, + g_param_spec_pointer ("window", "QQuickWindow", + "The QQuickWindow to place in the object heirachy", + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_DEFAULT_FBO, + g_param_spec_boolean ("use-default-fbo", + "If use default fbo", + "When set TRUE, it will not create new fbo for qml render thread", + FALSE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_qt_src_template)); + + gstelement_class->change_state = gst_qt_src_change_state; + gstbasesrc_class->set_caps = gst_qt_src_setcaps; + gstbasesrc_class->get_caps = gst_qt_src_get_caps; + gstbasesrc_class->query = gst_qt_src_query; + gstbasesrc_class->start = gst_qt_src_start; + gstbasesrc_class->stop = gst_qt_src_stop; + gstbasesrc_class->decide_allocation = gst_qt_src_decide_allocation; + + gstpushsrc_class->fill = gst_qt_src_fill; +} + +static void +gst_qt_src_init (GstQtSrc * src) +{ + gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME); + gst_base_src_set_live (GST_BASE_SRC (src), DEFAULT_IS_LIVE); + src->default_fbo = FALSE; + src->pending_image_orientation = TRUE; +} + +static void +gst_qt_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstQtSrc *qt_src = GST_QT_SRC (object); + + switch (prop_id) { + case PROP_WINDOW:{ + qt_src->qwindow = + static_cast < QQuickWindow * >(g_value_get_pointer (value)); + + if (qt_src->window) + delete qt_src->window; + if (qt_src->qwindow) + qt_src->window = new QtGLWindow (NULL, qt_src->qwindow); + + break; + } + case PROP_DEFAULT_FBO: + qt_src->default_fbo = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_qt_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstQtSrc *qt_src = GST_QT_SRC (object); + + switch (prop_id) { + case PROP_WINDOW: + g_value_set_pointer (value, qt_src->qwindow); + break; + case PROP_DEFAULT_FBO: + g_value_set_boolean (value, qt_src->default_fbo); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_qt_src_finalize (GObject * object) +{ + GstQtSrc *qt_src = GST_QT_SRC (object); + + GST_DEBUG ("qmlglsrc finalize"); + if (qt_src->context) + gst_object_unref (qt_src->context); + qt_src->context = NULL; + + if (qt_src->qt_context) + gst_object_unref (qt_src->qt_context); + qt_src->qt_context = NULL; + + if (qt_src->display) + gst_object_unref (qt_src->display); + qt_src->display = NULL; + + if (qt_src->window) + delete qt_src->window; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_qt_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps) +{ + GstQtSrc *qt_src = GST_QT_SRC (bsrc); + + GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps); + + if (!gst_video_info_from_caps (&qt_src->v_info, caps)) + return FALSE; + + if (!qt_window_set_caps (qt_src->window, caps)) + return FALSE; + + return TRUE; +} + +static GstCaps * +gst_qt_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter) +{ + GstCaps *caps = NULL, *temp = NULL; + GstPadTemplate *pad_template; + GstBaseSrcClass *bclass = GST_BASE_SRC_GET_CLASS (bsrc); + GstQtSrc *qt_src = GST_QT_SRC (bsrc); + guint i; + gint width, height; + + if (qt_src->window) { + qt_src->window->getGeometry (&width, &height); + } + + pad_template = + gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "src"); + if (pad_template != NULL) + caps = gst_pad_template_get_caps (pad_template); + + if (qt_src->window) { + temp = gst_caps_copy (caps); + guint n_caps = gst_caps_get_size (caps); + + for (i = 0; i < n_caps; i++) { + GstStructure *s = gst_caps_get_structure (temp, i); + gst_structure_set (s, "width", G_TYPE_INT, width, NULL); + gst_structure_set (s, "height", G_TYPE_INT, height, NULL); + /* because the framerate is unknown */ + gst_structure_set (s, "framerate", GST_TYPE_FRACTION, 0, 1, NULL); + gst_structure_set (s, "pixel-aspect-ratio", + GST_TYPE_FRACTION, 1, 1, NULL); + } + + gst_caps_unref (caps); + caps = temp; + } + + if (filter) { + GstCaps *intersection; + + intersection = + gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (caps); + caps = intersection; + } + + return caps; +} + +static gboolean +gst_qt_src_query (GstBaseSrc * bsrc, GstQuery * query) +{ + GstQtSrc *qt_src = GST_QT_SRC (bsrc); + gboolean res = FALSE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONTEXT: + { + const gchar *context_type; + GstContext *context, *old_context; + gboolean ret; + + if (!qt_window_is_scenegraph_initialized (qt_src->window)) + return FALSE; + + if (!qt_src->display && !qt_src->qt_context) { + qt_src->display = qt_window_get_display (qt_src->window); + qt_src->qt_context = qt_window_get_qt_context (qt_src->window); + } + + ret = gst_gl_handle_context_query ((GstElement *) qt_src, query, + &qt_src->display, &qt_src->qt_context); + + if (qt_src->display) + gst_gl_display_filter_gl_api (qt_src->display, + gst_gl_context_get_gl_api (qt_src->qt_context)); + + gst_query_parse_context_type (query, &context_type); + + if (g_strcmp0 (context_type, "gst.gl.app_context") == 0) { + GstStructure *s; + + gst_query_parse_context (query, &old_context); + + if (old_context) + context = gst_context_copy (old_context); + else + context = gst_context_new ("gst.gl.app_context", FALSE); + + s = gst_context_writable_structure (context); + gst_structure_set (s, "context", GST_GL_TYPE_CONTEXT, + qt_src->qt_context, NULL); + gst_query_set_context (query, context); + gst_context_unref (context); + + ret = qt_src->qt_context != NULL; + } + GST_LOG_OBJECT (qt_src, "context query of type %s %i", context_type, ret); + + if (ret) + return ret; + + /* fallthrough */ + } + default: + res = GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query); + break; + } + + return res; +} + +static gboolean +_find_local_gl_context (GstQtSrc * qt_src) +{ + GstQuery *query; + GstContext *context; + const GstStructure *s; + + if (qt_src->context) + return TRUE; + + query = gst_query_new_context ("gst.gl.local_context"); + if (!qt_src->context + && gst_gl_run_query (GST_ELEMENT (qt_src), query, GST_PAD_SRC)) { + gst_query_parse_context (query, &context); + if (context) { + s = gst_context_get_structure (context); + gst_structure_get (s, "context", GST_GL_TYPE_CONTEXT, &qt_src->context, + NULL); + } + } + + GST_DEBUG_OBJECT (qt_src, "found local context %p", qt_src->context); + + gst_query_unref (query); + + if (qt_src->context) + return TRUE; + + return FALSE; +} + +static gboolean +gst_qt_src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query) +{ + GstBufferPool *pool = NULL; + GstStructure *config; + GstCaps *caps; + guint min, max, size, n, i; + gboolean update_pool, update_allocator; + GstAllocator *allocator; + GstAllocationParams params; + GstGLVideoAllocationParams *glparams; + GstVideoInfo vinfo; + GstQtSrc *qt_src = GST_QT_SRC (bsrc); + + if (gst_query_find_allocation_meta (query, + GST_VIDEO_AFFINE_TRANSFORMATION_META_API_TYPE, NULL)) { + qt_src->downstream_supports_affine_meta = TRUE; + } else { + qt_src->downstream_supports_affine_meta = FALSE; + } + + gst_query_parse_allocation (query, &caps, NULL); + if (!caps) + return FALSE; + + gst_video_info_from_caps (&vinfo, caps); + + n = gst_query_get_n_allocation_pools (query); + if (n > 0) { + update_pool = TRUE; + for (i = 0; i < n; i++) { + gst_query_parse_nth_allocation_pool (query, i, &pool, &size, &min, &max); + + if (!pool || !GST_IS_GL_BUFFER_POOL (pool)) { + if (pool) + gst_object_unref (pool); + pool = NULL; + } + } + } + + if (!pool) { + size = vinfo.size; + min = max = 0; + update_pool = FALSE; + } + + if (!qt_src->context && !_find_local_gl_context (qt_src)) + return FALSE; + + if (!pool) { + if (!qt_src->context || !GST_IS_GL_CONTEXT (qt_src->context)) + return FALSE; + + pool = gst_gl_buffer_pool_new (qt_src->context); + GST_INFO_OBJECT (qt_src, "No pool, create one ourself %p", pool); + } + + config = gst_buffer_pool_get_config (pool); + + gst_buffer_pool_config_set_params (config, caps, size, min, max); + gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); + if (gst_query_find_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, NULL)) + gst_buffer_pool_config_add_option (config, + GST_BUFFER_POOL_OPTION_GL_SYNC_META); + + if (gst_query_get_n_allocation_params (query) > 0) { + gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms); + gst_buffer_pool_config_set_allocator (config, allocator, ¶ms); + GST_INFO_OBJECT (qt_src, "got allocator %p", allocator); + update_allocator = TRUE; + } else { + allocator = NULL; + gst_allocation_params_init (¶ms); + update_allocator = FALSE; + } + + glparams = + gst_gl_video_allocation_params_new (qt_src->context, ¶ms, &vinfo, 0, + NULL, GST_GL_TEXTURE_TARGET_2D, GST_VIDEO_GL_TEXTURE_TYPE_RGBA); + gst_buffer_pool_config_set_gl_allocation_params (config, + (GstGLAllocationParams *) glparams); + gst_gl_allocation_params_free ((GstGLAllocationParams *) glparams); + + if (!gst_buffer_pool_set_config (pool, config)) + GST_WARNING_OBJECT (qt_src, "Failed to set buffer pool config"); + + if (update_allocator) + gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms); + else + gst_query_add_allocation_param (query, allocator, ¶ms); + if (allocator) + gst_object_unref (allocator); + + if (update_pool) + gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max); + else + gst_query_add_allocation_pool (query, pool, size, min, max); + gst_object_unref (pool); + + GST_INFO_OBJECT (qt_src, "successfully decide_allocation"); + return TRUE; +} + +static GstFlowReturn +gst_qt_src_fill (GstPushSrc * psrc, GstBuffer * buffer) +{ + GstQtSrc *qt_src = GST_QT_SRC (psrc); + + GST_DEBUG_OBJECT (qt_src, "setting buffer %p", buffer); + + if (!qt_window_set_buffer (qt_src->window, buffer)) { + GST_ERROR_OBJECT (qt_src, "failed to fill buffer %p", buffer); + return GST_FLOW_ERROR; + } + + if (!qt_src->downstream_supports_affine_meta) { + if (qt_src->pending_image_orientation) { + /* let downstream know the image orientation is vertical filp */ + GstTagList *image_orientation_tag = + gst_tag_list_new (GST_TAG_IMAGE_ORIENTATION, "flip-rotate-180", NULL); + + gst_pad_push_event (GST_BASE_SRC_PAD (psrc), + gst_event_new_tag (image_orientation_tag)); + + qt_src->pending_image_orientation = FALSE; + } + } else { + GstVideoAffineTransformationMeta *trans_meta; + trans_meta = gst_buffer_add_video_affine_transformation_meta (buffer); + gst_video_affine_transformation_meta_apply_matrix (trans_meta, + vertical_flip_matrix); + } + + GST_DEBUG_OBJECT (qt_src, "buffer fill done %p", buffer); + + return GST_FLOW_OK; +} + +static GstStateChangeReturn +gst_qt_src_change_state (GstElement * element, GstStateChange transition) +{ + GstQtSrc *qt_src = GST_QT_SRC (element); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + QGuiApplication *app; + guint64 frames_rendered = 0; + + GST_DEBUG ("changing state: %s => %s", + gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)), + gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition))); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + app = static_cast < QGuiApplication * >(QCoreApplication::instance ()); + if (!app) { + GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND, + ("%s", "Failed to connect to Qt"), + ("%s", "Could not retrieve QGuiApplication instance")); + return GST_STATE_CHANGE_FAILURE; + } + + if (!qt_src->window) { + GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND, + ("%s", "Required property \'window\' not set"), (NULL)); + return GST_STATE_CHANGE_FAILURE; + } + + if (!qt_window_is_scenegraph_initialized (qt_src->window)) { + GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND, + ("%s", "Could not initialize window system"), (NULL)); + return GST_STATE_CHANGE_FAILURE; + } + + qt_window_use_default_fbo (qt_src->window, qt_src->default_fbo); + + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + qt_src->run_time = gst_element_get_start_time (GST_ELEMENT (qt_src)); + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_NULL: + qt_window_get_total_frames (qt_src->window, &frames_rendered); + if (qt_src->run_time > 0) { + GST_DEBUG ("qmlglsrc Total refresh frames (%ld), playing for (%" + GST_TIME_FORMAT "), fps (%.3f).\n", frames_rendered, + GST_TIME_ARGS (qt_src->run_time), + (gfloat) GST_SECOND * frames_rendered / qt_src->run_time); + } + break; + default: + break; + } + + return ret; +} + +static gboolean +gst_qt_src_start (GstBaseSrc * basesrc) +{ + GstQtSrc *qt_src = GST_QT_SRC (basesrc); + + /* already has get OpenGL configuration from qt */ + if (qt_src->display && qt_src->qt_context) + return TRUE; + + if (!qt_window_is_scenegraph_initialized (qt_src->window)) + return FALSE; + + qt_src->display = qt_window_get_display (qt_src->window); + qt_src->qt_context = qt_window_get_qt_context (qt_src->window); + + if (!qt_src->display || !qt_src->qt_context) { + GST_ERROR_OBJECT (qt_src, + "Could not retrieve window system OpenGL configuration"); + return FALSE; + } + + GST_DEBUG_OBJECT (qt_src, "Got qt display %p and qt gl context %p", + qt_src->display, qt_src->qt_context); + return TRUE; +} + +static gboolean +gst_qt_src_stop (GstBaseSrc * basesrc) +{ + return TRUE; +} diff --git a/ext/qt/gstqtsrc.h b/ext/qt/gstqtsrc.h new file mode 100644 index 0000000000..8bbf4e2ebb --- /dev/null +++ b/ext/qt/gstqtsrc.h @@ -0,0 +1,82 @@ +/* + * GStreamer + * Copyright (C) 2016 Freescale Semiconductor, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_QT_SRC_H__ +#define __GST_QT_SRC_H__ + +#include +#include +#include +#include +#include "qtwindow.h" + +typedef struct _GstQtSrc GstQtSrc; +typedef struct _GstQtSrcClass GstQtSrcClass; + +G_BEGIN_DECLS + +GType gst_qt_src_get_type (void); +#define GST_TYPE_QT_SRC (gst_qt_src_get_type()) +#define GST_QT_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_QT_SRC,GstQtSrc)) +#define GST_QT_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_QT_SRC,GstQtSrcClass)) +#define GST_IS_QT_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_QT_SRC)) +#define GST_IS_QT_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_QT_SRC)) +#define GST_QT_SRC_CAST(obj) ((GstQtSrc*)(obj)) + +/** + * GstQtSrc: + * + * Opaque #GstQtSrc object + */ +struct _GstQtSrc +{ + /* */ + GstPushSrc parent; + + QQuickWindow *qwindow; + QtGLWindow *window; + + GstVideoInfo v_info; + + GstGLDisplay *display; + GstGLContext *context; + GstGLContext *qt_context; + + gboolean default_fbo; + gboolean downstream_supports_affine_meta; + gboolean pending_image_orientation; + /* fps print support */ + GstClockTime run_time; +}; + +/** + * GstQtSrcClass: + * + * The #GstQtSrcClass struct only contains private data + */ +struct _GstQtSrcClass +{ + /* */ + GstPushSrcClass object_class; +}; + +G_END_DECLS + +#endif /* __GST_QT_SRC_H__ */ diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index c306c6dbd4..f088c76fb1 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -27,32 +27,13 @@ #include #include "qtitem.h" #include "gstqsgtexture.h" +#include "gstqtglutility.h" #include #include #include #include -#if GST_GL_HAVE_WINDOW_X11 && GST_GL_HAVE_PLATFORM_GLX && defined (HAVE_QT_X11) -#include -#include -#include -#endif - -#if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_WAYLAND) -#include -#include -#endif - -#if GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) -#include -#include -#endif - -#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) -#include -#endif - /** * SECTION:gtkgstglwidget * @short_description: a #GtkGLArea that renders GStreamer video #GstBuffers @@ -123,11 +104,8 @@ void InitializeSceneGraph::run() QtGLVideoItem::QtGLVideoItem() { - QGuiApplication *app = static_cast (QCoreApplication::instance ()); static volatile gsize _debug; - g_assert (app != NULL); - if (g_once_init_enter (&_debug)) { GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qtglwidget", 0, "Qt GL Widget"); g_once_init_leave (&_debug, 1); @@ -143,48 +121,7 @@ QtGLVideoItem::QtGLVideoItem() g_mutex_init (&this->priv->lock); - GST_INFO ("QGuiApplication::instance()->platformName() %s", app->platformName().toUtf8().data()); - -#if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11) - if (QString::fromUtf8 ("xcb") == app->platformName()) - this->priv->display = (GstGLDisplay *) - gst_gl_display_x11_new_with_display (QX11Info::display ()); -#endif -#if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_WAYLAND) - if (QString::fromUtf8 ("wayland") == app->platformName() - || QString::fromUtf8 ("wayland-egl") == app->platformName()){ - struct wl_display * wayland_display; - QPlatformNativeInterface *native = - QGuiApplication::platformNativeInterface(); - wayland_display = (struct wl_display *) - native->nativeResourceForWindow("display", NULL); - this->priv->display = (GstGLDisplay *) - gst_gl_display_wayland_new_with_display (wayland_display); - } -#endif -#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_WINDOW_ANDROID - if (QString::fromUtf8 ("android") == app->platformName()) - this->priv->display = (GstGLDisplay *) gst_gl_display_egl_new (); -#elif GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) - if (QString::fromUtf8("eglfs") == app->platformName()) - this->priv->display = (GstGLDisplay *) gst_gl_display_egl_new (); -#endif - -#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) - if (QString::fromUtf8 ("cocoa") == app->platformName()) - this->priv->display = (GstGLDisplay *) gst_gl_display_cocoa_new (); -#endif -#if GST_GL_HAVE_WINDOW_EAGL && GST_GL_HAVE_PLATFORM_EAGL && defined (HAVE_QT_IOS) - if (QString::fromUtf8 ("ios") == app->platformName()) - this->priv->display = gst_gl_display_new (); -#endif -#if GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (HAVE_QT_WIN32) - if (QString::fromUtf8 ("windows") == app->platformName()) - this->priv->display = gst_gl_display_new (); -#endif - - if (!this->priv->display) - this->priv->display = gst_gl_display_new (); + this->priv->display = gst_qt_get_gl_display(); connect(this, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(handleWindowChanged(QQuickWindow*))); @@ -318,10 +255,6 @@ qt_item_set_buffer (QtGLVideoItem * widget, GstBuffer * buffer) void QtGLVideoItem::onSceneGraphInitialized () { - GstGLPlatform platform = (GstGLPlatform) 0; - GstGLAPI gl_api; - guintptr gl_handle; - GST_DEBUG ("scene graph initialization with Qt GL context %p", this->window()->openglContext ()); @@ -334,89 +267,8 @@ QtGLVideoItem::onSceneGraphInitialized () return; } -#if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11) - if (GST_IS_GL_DISPLAY_X11 (this->priv->display)) { - platform = GST_GL_PLATFORM_GLX; - } -#endif -#if GST_GL_HAVE_WINDOW_WAYLAND && defined (HAVE_QT_WAYLAND) - if (GST_IS_GL_DISPLAY_WAYLAND (this->priv->display)) { - platform = GST_GL_PLATFORM_EGL; - } -#endif -#if GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) - if (GST_IS_GL_DISPLAY_EGL (this->priv->display)) { - platform = GST_GL_PLATFORM_EGL; - } -#endif - if (platform == 0 && this->priv->display) { -#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) - platform = GST_GL_PLATFORM_CGL; -#elif GST_GL_HAVE_WINDOW_EAGL && GST_GL_HAVE_PLATFORM_EAGL && defined (HAVE_QT_IOS) - platform = GST_GL_PLATFORM_EAGL; -#elif GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (HAVE_QT_WIN32) - platform = GST_GL_PLATFORM_WGL; -#else - GST_ERROR ("Unknown platform"); - return; -#endif - } - - gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL); - gl_handle = gst_gl_context_get_current_gl_context (platform); - if (gl_handle) - this->priv->other_context = - gst_gl_context_new_wrapped (this->priv->display, gl_handle, - platform, gl_api); - - (void) platform; - (void) gl_api; - (void) gl_handle; - - if (this->priv->other_context) { - GError *error = NULL; - - gst_gl_context_activate (this->priv->other_context, TRUE); - if (!gst_gl_context_fill_info (this->priv->other_context, &error)) { - GST_ERROR ("%p failed to retrieve qt context info: %s", this, error->message); - g_object_unref (this->priv->other_context); - this->priv->other_context = NULL; - } else { - gst_gl_display_filter_gl_api (this->priv->display, gst_gl_context_get_gl_api (this->priv->other_context)); -#if GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (HAVE_QT_WIN32) - if (!wglGetProcAddress ("wglCreateContextAttribsARB")) { - GstGLWindow *window; - HDC device; - - /* If there's no wglCreateContextAttribsARB() support, then we would fallback to - * wglShareLists() which will fail with ERROR_BUSY (0xaa) if either of the GL - * contexts are current in any other thread. - * - * The workaround here is to temporarily disable Qt's GL context while we - * set up our own. - */ - this->priv->context = gst_gl_context_new (this->priv->display); - window = gst_gl_context_get_window (this->priv->context); - device = (HDC) gst_gl_window_get_display (window); - - wglMakeCurrent (device, 0); - gst_object_unref (window); - if (!gst_gl_context_create (this->priv->context, this->priv->other_context, &error)) { - GST_ERROR ("%p failed to create shared GL context: %s", this, error->message); - g_object_unref (this->priv->context); - this->priv->context = NULL; - g_object_unref (this->priv->other_context); - this->priv->other_context = NULL; - wglMakeCurrent (device, (HGLRC) gl_handle); - return; - } - wglMakeCurrent (device, (HGLRC) gl_handle); - } -#endif - gst_gl_context_activate (this->priv->other_context, FALSE); - m_openGlContextInitialized = true; - } - } + m_openGlContextInitialized = gst_qt_get_gl_wrapcontext (this->priv->display, + &this->priv->other_context, &this->priv->context); GST_DEBUG ("%p created wrapped GL context %" GST_PTR_FORMAT, this, this->priv->other_context); diff --git a/ext/qt/qtwindow.cc b/ext/qt/qtwindow.cc new file mode 100644 index 0000000000..4702891f1f --- /dev/null +++ b/ext/qt/qtwindow.cc @@ -0,0 +1,371 @@ +/* + * GStreamer + * Copyright (C) 2016 Freescale Semiconductor, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include "qtwindow.h" +#include "gstqsgtexture.h" +#include "gstqtglutility.h" + +#include +#include +#include +#include + +/** + * SECTION: + * + * #QtGLWindow is an #QQuickWindow that grab QtQuick view to GStreamer OpenGL video buffers. + */ + +GST_DEBUG_CATEGORY_STATIC (qt_window_debug); +#define GST_CAT_DEFAULT qt_window_debug + +struct _QtGLWindowPrivate +{ + GMutex lock; + GCond update_cond; + + GstBuffer *buffer; + GstCaps *caps; + GstVideoInfo v_info; + + gboolean initted; + gboolean updated; + gboolean quit; + gboolean result; + gboolean useDefaultFbo; + + GstGLDisplay *display; + GstGLContext *other_context; + + /* frames that qmlview rendered in its gl thread */ + guint64 frames_rendered; +}; + +class InitQtGLContext : public QRunnable +{ +public: + InitQtGLContext(QtGLWindow *window); + void run(); + +private: + QtGLWindow *window_; +}; + +InitQtGLContext::InitQtGLContext(QtGLWindow *window) : + window_(window) +{ +} + +void InitQtGLContext::run() +{ + window_->onSceneGraphInitialized(); +} + +QtGLWindow::QtGLWindow ( QWindow * parent, QQuickWindow *src ) : + QQuickWindow( parent ), source (src) +{ + QGuiApplication *app = static_cast (QCoreApplication::instance ()); + static volatile gsize _debug; + + g_assert (app != NULL); + + if (g_once_init_enter (&_debug)) { + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qtglwindow", 0, "Qt GL QuickWindow"); + g_once_init_leave (&_debug, 1); + } + + this->priv = g_new0 (QtGLWindowPrivate, 1); + + g_mutex_init (&this->priv->lock); + g_cond_init (&this->priv->update_cond); + + this->priv->display = gst_qt_get_gl_display(); + + connect (source, SIGNAL(beforeRendering()), this, SLOT(beforeRendering()), Qt::DirectConnection); + connect (source, SIGNAL(afterRendering()), this, SLOT(afterRendering()), Qt::DirectConnection); + connect (app, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()), Qt::DirectConnection); + if (source->isSceneGraphInitialized()) + source->scheduleRenderJob(new InitQtGLContext(this), QQuickWindow::BeforeSynchronizingStage); + else + connect (source, SIGNAL(sceneGraphInitialized()), this, SLOT(onSceneGraphInitialized()), Qt::DirectConnection); + + GST_DEBUG ("%p init Qt Window", this->priv->display); +} + +QtGLWindow::~QtGLWindow() +{ + GST_DEBUG ("deinit Qt Window"); + g_mutex_clear (&this->priv->lock); + g_cond_clear (&this->priv->update_cond); + if (this->priv->other_context) + gst_object_unref(this->priv->other_context); + if (this->priv->display) + gst_object_unref(this->priv->display); + g_free (this->priv); + this->priv = NULL; +} + +void +QtGLWindow::beforeRendering() +{ + unsigned int width, height; + + g_mutex_lock (&this->priv->lock); + + if (!fbo && !this->priv->useDefaultFbo) { + + width = source->width(); + height = source->height(); + + GST_DEBUG ("create new framebuffer object %dX%d", width, height); + + fbo.reset(new QOpenGLFramebufferObject (width, height, + QOpenGLFramebufferObject::NoAttachment, GL_TEXTURE_2D, GL_RGBA)); + + source->setRenderTarget(fbo.data()); + } + + g_mutex_unlock (&this->priv->lock); +} + + +void +QtGLWindow::afterRendering() +{ + GstVideoFrame gl_frame; + GstVideoInfo *info; + GstGLContext *context; + gboolean ret; + guint width, height; + const GstGLFuncs *gl; + GLuint dst_tex; + + g_mutex_lock (&this->priv->lock); + + this->priv->frames_rendered++; + + if(!this->priv->buffer || this->priv->updated == TRUE) { + GST_DEBUG ("skip this frame"); + g_mutex_unlock (&this->priv->lock); + return; + } + + GST_DEBUG ("copy buffer %p",this->priv->buffer); + + width = GST_VIDEO_INFO_WIDTH (&this->priv->v_info); + height = GST_VIDEO_INFO_HEIGHT (&this->priv->v_info); + info = &this->priv->v_info; + context = this->priv->other_context; + + gst_gl_context_activate (context, TRUE); + gl = context->gl_vtable; + + ret = gst_video_frame_map (&gl_frame, info, this->priv->buffer, + (GstMapFlags) (GST_MAP_WRITE | GST_MAP_GL)); + + if (!ret) { + this->priv->buffer = NULL; + GST_ERROR ("Failed to map video frame"); + goto errors; + } + + gl->BindFramebuffer (GL_READ_FRAMEBUFFER, this->source->renderTargetId()); + + ret = gst_gl_context_check_framebuffer_status (context); + if (!ret) { + GST_ERROR ("FBO errors"); + goto errors; + } + + dst_tex = *(guint *) gl_frame.data[0]; + GST_DEBUG ("qml render target id %d, render to tex %d %dX%d", + this->source->renderTargetId(), dst_tex, width,height); + + gl->BindTexture (GL_TEXTURE_2D, dst_tex); + gl->CopyTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, width, height, 0); + + GST_DEBUG ("rendering finished"); + +errors: + gl->BindFramebuffer (GL_FRAMEBUFFER, 0); + gst_video_frame_unmap (&gl_frame); + + gst_gl_context_activate (context, FALSE); + + this->priv->result = ret; + this->priv->updated = TRUE; + g_cond_signal (&this->priv->update_cond); + g_mutex_unlock (&this->priv->lock); +} + +void +QtGLWindow::aboutToQuit() +{ + g_mutex_lock (&this->priv->lock); + + this->priv->updated = TRUE; + this->priv->quit = TRUE; + g_cond_signal (&this->priv->update_cond); + + g_mutex_unlock (&this->priv->lock); + + GST_DEBUG("about to quit"); +} + +void +QtGLWindow::onSceneGraphInitialized() +{ + GST_DEBUG ("scene graph initialization with Qt GL context %p", + this->source->openglContext ()); + + this->priv->initted = gst_qt_get_gl_wrapcontext (this->priv->display, + &this->priv->other_context, NULL); + + GST_DEBUG ("%p created wrapped GL context %" GST_PTR_FORMAT, this, + this->priv->other_context); +} + +bool +QtGLWindow::getGeometry(int * width, int * height) +{ + if (width == NULL || height == NULL) + return FALSE; + + *width = this->source->width(); + *height = this->source->height(); + + return TRUE; +} + +GstGLContext * +qt_window_get_qt_context (QtGLWindow * qt_window) +{ + g_return_val_if_fail (qt_window != NULL, NULL); + + if (!qt_window->priv->other_context) + return NULL; + + return (GstGLContext *) gst_object_ref (qt_window->priv->other_context); +} + +GstGLDisplay * +qt_window_get_display (QtGLWindow * qt_window) +{ + g_return_val_if_fail (qt_window != NULL, NULL); + + if (!qt_window->priv->display) + return NULL; + + return (GstGLDisplay *) gst_object_ref (qt_window->priv->display); +} + +gboolean +qt_window_is_scenegraph_initialized (QtGLWindow * qt_window) +{ + g_return_val_if_fail (qt_window != NULL, FALSE); + + return qt_window->priv->initted; +} + +gboolean +qt_window_set_caps (QtGLWindow * qt_window, GstCaps * caps) +{ + GstVideoInfo v_info; + + g_return_val_if_fail (qt_window != NULL, FALSE); + g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); + g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); + + if (qt_window->priv->caps && gst_caps_is_equal_fixed (qt_window->priv->caps, caps)) + return TRUE; + + if (!gst_video_info_from_caps (&v_info, caps)) + return FALSE; + + g_mutex_lock (&qt_window->priv->lock); + + gst_caps_replace (&qt_window->priv->caps, caps); + + qt_window->priv->v_info = v_info; + + g_mutex_unlock (&qt_window->priv->lock); + + return TRUE; +} + +gboolean +qt_window_set_buffer (QtGLWindow * qt_window, GstBuffer * buffer) +{ + g_return_val_if_fail (qt_window != NULL, FALSE); + g_return_val_if_fail (qt_window->priv->initted, FALSE); + gboolean ret; + + g_mutex_lock (&qt_window->priv->lock); + + if (qt_window->priv->quit){ + GST_DEBUG("about to quit, drop this buffer"); + g_mutex_unlock (&qt_window->priv->lock); + return TRUE; + } + + qt_window->priv->updated = FALSE; + qt_window->priv->buffer = buffer; + + while (!qt_window->priv->updated) + g_cond_wait (&qt_window->priv->update_cond, &qt_window->priv->lock); + + ret = qt_window->priv->result; + + g_mutex_unlock (&qt_window->priv->lock); + + return ret; +} + +void +qt_window_use_default_fbo (QtGLWindow * qt_window, gboolean useDefaultFbo) +{ + g_return_if_fail (qt_window != NULL); + + g_mutex_lock (&qt_window->priv->lock); + + GST_DEBUG ("set to use default fbo %d", useDefaultFbo); + qt_window->priv->useDefaultFbo = useDefaultFbo; + + g_mutex_unlock (&qt_window->priv->lock); +} + +void +qt_window_get_total_frames (QtGLWindow * qt_window, guint64 *frames) +{ + g_return_if_fail (qt_window != NULL); + + g_mutex_lock (&qt_window->priv->lock); + + *frames = qt_window->priv->frames_rendered; + + g_mutex_unlock (&qt_window->priv->lock); +} diff --git a/ext/qt/qtwindow.h b/ext/qt/qtwindow.h new file mode 100644 index 0000000000..5820e77caf --- /dev/null +++ b/ext/qt/qtwindow.h @@ -0,0 +1,70 @@ +/* + * GStreamer + * Copyright (C) 2016 Freescale Semiconductor, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __QT_WINDOW_H__ +#define __QT_WINDOW_H__ + +#include +#include + +#include "gstqtgl.h" +#include +#include +#include + +typedef struct _QtGLWindowPrivate QtGLWindowPrivate; + +class InitQtGLContext; + +class QtGLWindow : public QQuickWindow, protected QOpenGLFunctions +{ + Q_OBJECT +public: + QtGLWindow (QWindow * parent = NULL, QQuickWindow *source = NULL); + ~QtGLWindow (); + bool getGeometry (int * width, int * height); + + /* private for C interface ... */ + QtGLWindowPrivate *priv; + +private Q_SLOTS: + void beforeRendering (); + void afterRendering (); + void onSceneGraphInitialized (); + void aboutToQuit(); + +private: + friend class InitQtGLContext; + QQuickWindow * source; + QScopedPointer fbo; +}; + +extern "C" +{ +gboolean qt_window_set_buffer (QtGLWindow * qt_window, GstBuffer * buffer); +gboolean qt_window_set_caps (QtGLWindow * qt_window, GstCaps * caps); +GstGLContext * qt_window_get_qt_context (QtGLWindow * qt_window); +GstGLDisplay * qt_window_get_display (QtGLWindow * qt_window); +gboolean qt_window_is_scenegraph_initialized (QtGLWindow * qt_window); +void qt_window_use_default_fbo (QtGLWindow * qt_window, gboolean useDefaultFbo); +void qt_window_get_total_frames (QtGLWindow * qt_window, guint64 *frames); +} + +#endif /* __QT_WINDOW_H__ */ From 2ae2b7fa8010839776f7043f69d68075e66061df Mon Sep 17 00:00:00 2001 From: Haihua Hu Date: Wed, 27 Jul 2016 09:28:23 +0800 Subject: [PATCH 40/78] qmlglsrc: Add qmlglsrc unit test example https://bugzilla.gnome.org/show_bug.cgi?id=768160 --- tests/examples/qt/{qml => qmlsink}/.gitignore | 0 tests/examples/qt/{qml => qmlsink}/main.cpp | 11 +-- tests/examples/qt/{qml => qmlsink}/main.qml | 0 tests/examples/qt/{qml => qmlsink}/play.pro | 0 tests/examples/qt/{qml => qmlsink}/qml.qrc | 0 tests/examples/qt/qmlsrc/.gitignore | 2 + tests/examples/qt/qmlsrc/grabqml.pro | 20 +++++ tests/examples/qt/qmlsrc/main.cpp | 80 +++++++++++++++++++ tests/examples/qt/qmlsrc/main.qml | 65 +++++++++++++++ tests/examples/qt/qmlsrc/qml.qrc | 5 ++ 10 files changed, 176 insertions(+), 7 deletions(-) rename tests/examples/qt/{qml => qmlsink}/.gitignore (100%) rename tests/examples/qt/{qml => qmlsink}/main.cpp (85%) rename tests/examples/qt/{qml => qmlsink}/main.qml (100%) rename tests/examples/qt/{qml => qmlsink}/play.pro (100%) rename tests/examples/qt/{qml => qmlsink}/qml.qrc (100%) create mode 100644 tests/examples/qt/qmlsrc/.gitignore create mode 100644 tests/examples/qt/qmlsrc/grabqml.pro create mode 100644 tests/examples/qt/qmlsrc/main.cpp create mode 100644 tests/examples/qt/qmlsrc/main.qml create mode 100644 tests/examples/qt/qmlsrc/qml.qrc diff --git a/tests/examples/qt/qml/.gitignore b/tests/examples/qt/qmlsink/.gitignore similarity index 100% rename from tests/examples/qt/qml/.gitignore rename to tests/examples/qt/qmlsink/.gitignore diff --git a/tests/examples/qt/qml/main.cpp b/tests/examples/qt/qmlsink/main.cpp similarity index 85% rename from tests/examples/qt/qml/main.cpp rename to tests/examples/qt/qmlsink/main.cpp index bc5d288246..2317b05415 100644 --- a/tests/examples/qt/qml/main.cpp +++ b/tests/examples/qt/qmlsink/main.cpp @@ -44,18 +44,15 @@ int main(int argc, char *argv[]) GstElement *pipeline = gst_pipeline_new (NULL); GstElement *src = gst_element_factory_make ("videotestsrc", NULL); + GstElement *glupload = gst_element_factory_make ("glupload", NULL); /* the plugin must be loaded before loading the qml file to register the * GstGLVideoItem qml item */ GstElement *sink = gst_element_factory_make ("qmlglsink", NULL); - GstElement *sinkbin = gst_element_factory_make ("glsinkbin", NULL); - g_assert (src && sink && sinkbin); + g_assert (src && glupload && sink); - g_object_set (sinkbin, "sink", sink, NULL); - g_object_unref (sink); - - gst_bin_add_many (GST_BIN (pipeline), src, sinkbin, NULL); - gst_element_link_many (src, sinkbin, NULL); + gst_bin_add_many (GST_BIN (pipeline), src, glupload, sink, NULL); + gst_element_link_many (src, glupload, sink, NULL); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); diff --git a/tests/examples/qt/qml/main.qml b/tests/examples/qt/qmlsink/main.qml similarity index 100% rename from tests/examples/qt/qml/main.qml rename to tests/examples/qt/qmlsink/main.qml diff --git a/tests/examples/qt/qml/play.pro b/tests/examples/qt/qmlsink/play.pro similarity index 100% rename from tests/examples/qt/qml/play.pro rename to tests/examples/qt/qmlsink/play.pro diff --git a/tests/examples/qt/qml/qml.qrc b/tests/examples/qt/qmlsink/qml.qrc similarity index 100% rename from tests/examples/qt/qml/qml.qrc rename to tests/examples/qt/qmlsink/qml.qrc diff --git a/tests/examples/qt/qmlsrc/.gitignore b/tests/examples/qt/qmlsrc/.gitignore new file mode 100644 index 0000000000..d2246aeb26 --- /dev/null +++ b/tests/examples/qt/qmlsrc/.gitignore @@ -0,0 +1,2 @@ +grabqml +qrc_qml.cpp diff --git a/tests/examples/qt/qmlsrc/grabqml.pro b/tests/examples/qt/qmlsrc/grabqml.pro new file mode 100644 index 0000000000..374e40297a --- /dev/null +++ b/tests/examples/qt/qmlsrc/grabqml.pro @@ -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 += qml.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = diff --git a/tests/examples/qt/qmlsrc/main.cpp b/tests/examples/qt/qmlsrc/main.cpp new file mode 100644 index 0000000000..35cedcb092 --- /dev/null +++ b/tests/examples/qt/qmlsrc/main.cpp @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +class SetPlaying : public QRunnable +{ +public: + SetPlaying(GstElement *); + ~SetPlaying(); + + void run (); + +private: + GstElement * pipeline_; +}; + +SetPlaying::SetPlaying (GstElement * pipeline) +{ + this->pipeline_ = pipeline ? static_cast (gst_object_ref (pipeline)) : NULL; +} + +SetPlaying::~SetPlaying () +{ + if (this->pipeline_) + gst_object_unref (this->pipeline_); + +} + +void +SetPlaying::run () +{ + if (this->pipeline_) + gst_element_set_state (this->pipeline_, GST_STATE_PLAYING); +} + +int main(int argc, char *argv[]) +{ + int ret; + + QGuiApplication app(argc, argv); + gst_init (&argc, &argv); + + GstElement *pipeline = gst_pipeline_new (NULL); + GstElement *src = gst_element_factory_make ("qmlglsrc", NULL); + GstElement *sink = gst_element_factory_make ("glimagesink", NULL); + + g_assert (src && sink); + + gst_bin_add_many (GST_BIN (pipeline), src, sink, NULL); + gst_element_link_many (src, sink, NULL); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + QQuickWindow *rootObject; + + /* find and set the QQuickWindow on the src */ + rootObject = static_cast (engine.rootObjects().first()); + g_object_set(src, "window", rootObject, NULL); + g_object_set(src, "use-default-fbo", TRUE, NULL); + /* output buffer of qmlglsrc is vertical flip, get the image orientation tag */ + g_object_set(sink, "rotate-method", 8, NULL); + + rootObject->scheduleRenderJob (new SetPlaying (pipeline), + QQuickWindow::BeforeSynchronizingStage); + + 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/qmlsrc/main.qml b/tests/examples/qt/qmlsrc/main.qml new file mode 100644 index 0000000000..18b36e6506 --- /dev/null +++ b/tests/examples/qt/qmlsrc/main.qml @@ -0,0 +1,65 @@ +import QtQuick 2.4 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Window 2.1 + +ApplicationWindow { + id: window + visible: true + width: 640 + height: 480 + x: 30 + y: 30 + color: "dodgerblue" + + Item { + anchors.fill: parent + + Rectangle { + color: Qt.rgba(1, 1, 1, 0.7) + border.width: 1 + border.color: "white" + anchors.bottomMargin: 15 + anchors.horizontalCenter: parent.horizontalCenter + width : parent.width - 30 + height: parent.height - 30 + radius: 8 + + Text { + id: text1 + anchors.centerIn: parent + text: "Hello World!" + font.pointSize: 24 + visible: timer.tex1_visible + } + + Text { + id: text2 + anchors.centerIn: parent + text: "This is qmlglsrc demo!" + font.pointSize: 24 + visible: timer.tex2_visible + } + + Timer { + id: timer + property int count: 0 + property int tex1_visible: 1 + property int tex2_visible: 0 + interval: 30; running: true; repeat: true + onTriggered: { + count++; + if (count%2 == 0) { + tex1_visible = 1; + tex2_visible = 0; + } + else { + tex1_visible = 0; + tex2_visible = 1; + } + } + } + } + } +} diff --git a/tests/examples/qt/qmlsrc/qml.qrc b/tests/examples/qt/qmlsrc/qml.qrc new file mode 100644 index 0000000000..5f6483ac33 --- /dev/null +++ b/tests/examples/qt/qmlsrc/qml.qrc @@ -0,0 +1,5 @@ + + + main.qml + + From f52491d978cec81ce8ed246dee97d5ebb31c5407 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 8 Aug 2016 13:41:14 +1000 Subject: [PATCH 41/78] qt: Move debug statement to after the category init Don't output debug to an uninitialised debug category. --- ext/qt/gstqtglutility.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/qt/gstqtglutility.cc b/ext/qt/gstqtglutility.cc index 3753037c2c..84061202c4 100644 --- a/ext/qt/gstqtglutility.cc +++ b/ext/qt/gstqtglutility.cc @@ -57,13 +57,12 @@ gst_qt_get_gl_display () g_assert (app != NULL); - GST_INFO ("QGuiApplication::instance()->platformName() %s", app->platformName().toUtf8().data()); - if (g_once_init_enter (&_debug)) { GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qtglutility", 0, "Qt gl utility functions"); g_once_init_leave (&_debug, 1); } + GST_INFO ("QGuiApplication::instance()->platformName() %s", app->platformName().toUtf8().data()); #if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11) if (QString::fromUtf8 ("xcb") == app->platformName()) From a50b7c24dac2c692490ea55de19cbf958dc411ec Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Tue, 16 Aug 2016 00:40:53 +1000 Subject: [PATCH 42/78] qt: Use wglShareLists() workaround unconditionally. Sometimes wglCreateContextAttribsARB() exists, but isn't functional (some Intel drivers), so it's easiest to do the workaround unconditionally. --- ext/qt/gstqtglutility.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ext/qt/gstqtglutility.cc b/ext/qt/gstqtglutility.cc index 84061202c4..4d6d318646 100644 --- a/ext/qt/gstqtglutility.cc +++ b/ext/qt/gstqtglutility.cc @@ -174,7 +174,7 @@ gst_qt_get_gl_wrapcontext (GstGLDisplay * display, #if GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (HAVE_QT_WIN32) g_return_val_if_fail (context != NULL, FALSE); - if (!wglGetProcAddress ("wglCreateContextAttribsARB")) { + G_STMT_START { GstGLWindow *window; HDC device; @@ -184,6 +184,10 @@ gst_qt_get_gl_wrapcontext (GstGLDisplay * display, * * The workaround here is to temporarily disable Qt's GL context while we * set up our own. + * + * Sometimes wglCreateContextAttribsARB() + * exists, but isn't functional (some Intel drivers), so it's easiest to do this + * unconditionally. */ *context = gst_gl_context_new (display); window = gst_gl_context_get_window (*context); @@ -204,7 +208,7 @@ gst_qt_get_gl_wrapcontext (GstGLDisplay * display, } #endif gst_gl_context_activate (*wrap_glcontext, FALSE); - } + } G_STMT_END; return TRUE; } From ac06d0d8295a48881275862fbcf19909395371c3 Mon Sep 17 00:00:00 2001 From: Haihua Hu Date: Tue, 6 Sep 2016 13:13:39 +0800 Subject: [PATCH 43/78] qmlglsink: check qt_context_ first in GstQSGTexture::bind() When start qmlglsink app, it will set NULL buffer to GstQSGTexture in which case that qt_context_ will be a random value and cause gst_gl_context_activate() fail. https://bugzilla.gnome.org/show_bug.cgi?id=770925 --- ext/qt/gstqsgtexture.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/qt/gstqsgtexture.cc b/ext/qt/gstqsgtexture.cc index 13208c05f2..85747f260d 100644 --- a/ext/qt/gstqsgtexture.cc +++ b/ext/qt/gstqsgtexture.cc @@ -45,6 +45,7 @@ GstQSGTexture::GstQSGTexture () gst_video_info_init (&this->v_info); this->buffer_ = NULL; + this->qt_context_ = NULL; this->sync_buffer_ = gst_buffer_new (); } @@ -87,6 +88,9 @@ GstQSGTexture::bind () GstMemory *mem; guint tex_id; + if (!this->qt_context_) + return; + gst_gl_context_activate (this->qt_context_, TRUE); if (!this->buffer_) From 642e12ed9f96b540bab2fe5a36231771e16c42c8 Mon Sep 17 00:00:00 2001 From: Sergey Borovkov Date: Sun, 16 Oct 2016 12:40:22 +0200 Subject: [PATCH 44/78] qt: Fix failing build on RPI https://bugzilla.gnome.org/show_bug.cgi?id=773026 --- ext/qt/qtwindow.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ext/qt/qtwindow.cc b/ext/qt/qtwindow.cc index 4702891f1f..c3fd564dfb 100644 --- a/ext/qt/qtwindow.cc +++ b/ext/qt/qtwindow.cc @@ -34,6 +34,11 @@ #include #include +/* compatability definitions... */ +#ifndef GL_READ_FRAMEBUFFER +#define GL_READ_FRAMEBUFFER 0x8CA8 +#endif + /** * SECTION: * From 389e386d5000d3c4447c1ee8de977185941dbe82 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Wed, 5 Oct 2016 12:19:12 +1100 Subject: [PATCH 45/78] gl: GST_GL_TYPE -> GST_TYPE_GL Some deprecated symbols are kept for backwards compatibility --- ext/qt/gstqtsink.cc | 2 +- ext/qt/gstqtsrc.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/qt/gstqtsink.cc b/ext/qt/gstqtsink.cc index 62a5373689..85d6ae3f66 100644 --- a/ext/qt/gstqtsink.cc +++ b/ext/qt/gstqtsink.cc @@ -262,7 +262,7 @@ gst_qt_sink_query (GstBaseSink * bsink, GstQuery * query) context = gst_context_new ("gst.gl.local_context", FALSE); s = gst_context_writable_structure (context); - gst_structure_set (s, "context", GST_GL_TYPE_CONTEXT, qt_sink->context, + gst_structure_set (s, "context", GST_TYPE_GL_CONTEXT, qt_sink->context, NULL); gst_query_set_context (query, context); gst_context_unref (context); diff --git a/ext/qt/gstqtsrc.cc b/ext/qt/gstqtsrc.cc index 3cfc709f18..83aed985fd 100644 --- a/ext/qt/gstqtsrc.cc +++ b/ext/qt/gstqtsrc.cc @@ -310,7 +310,7 @@ gst_qt_src_query (GstBaseSrc * bsrc, GstQuery * query) context = gst_context_new ("gst.gl.app_context", FALSE); s = gst_context_writable_structure (context); - gst_structure_set (s, "context", GST_GL_TYPE_CONTEXT, + gst_structure_set (s, "context", GST_TYPE_GL_CONTEXT, qt_src->qt_context, NULL); gst_query_set_context (query, context); gst_context_unref (context); @@ -348,7 +348,7 @@ _find_local_gl_context (GstQtSrc * qt_src) gst_query_parse_context (query, &context); if (context) { s = gst_context_get_structure (context); - gst_structure_get (s, "context", GST_GL_TYPE_CONTEXT, &qt_src->context, + gst_structure_get (s, "context", GST_TYPE_GL_CONTEXT, &qt_src->context, NULL); } } From 4450152a9e2a6508d99d26816fe05809ecff1669 Mon Sep 17 00:00:00 2001 From: Haihua Hu Date: Mon, 7 Nov 2016 14:47:22 +0800 Subject: [PATCH 46/78] qmlglsrc: some enhancements for qmlglsrc 1. Need set use-default-fbo to qquickwindow during set property to support change render target on the fly. 2. Calculate qmlglsrc refresh frame rate in qtglwindow https://bugzilla.gnome.org/show_bug.cgi?id=774035 --- ext/qt/gstqtsrc.cc | 16 ++++++---------- ext/qt/gstqtsrc.h | 2 -- ext/qt/qtwindow.cc | 36 +++++++++++++++++++++--------------- ext/qt/qtwindow.h | 1 - 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/ext/qt/gstqtsrc.cc b/ext/qt/gstqtsrc.cc index 83aed985fd..1334dc3a93 100644 --- a/ext/qt/gstqtsrc.cc +++ b/ext/qt/gstqtsrc.cc @@ -145,8 +145,11 @@ gst_qt_src_set_property (GObject * object, guint prop_id, qt_src->qwindow = static_cast < QQuickWindow * >(g_value_get_pointer (value)); - if (qt_src->window) + if (qt_src->window) { delete qt_src->window; + qt_src->window = NULL; + } + if (qt_src->qwindow) qt_src->window = new QtGLWindow (NULL, qt_src->qwindow); @@ -154,6 +157,8 @@ gst_qt_src_set_property (GObject * object, guint prop_id, } case PROP_DEFAULT_FBO: qt_src->default_fbo = g_value_get_boolean (value); + if (qt_src->window) + qt_window_use_default_fbo (qt_src->window, qt_src->default_fbo); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -508,7 +513,6 @@ gst_qt_src_change_state (GstElement * element, GstStateChange transition) GstQtSrc *qt_src = GST_QT_SRC (element); GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; QGuiApplication *app; - guint64 frames_rendered = 0; GST_DEBUG ("changing state: %s => %s", gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)), @@ -553,18 +557,10 @@ gst_qt_src_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - qt_src->run_time = gst_element_get_start_time (GST_ELEMENT (qt_src)); break; case GST_STATE_CHANGE_PAUSED_TO_READY: break; case GST_STATE_CHANGE_READY_TO_NULL: - qt_window_get_total_frames (qt_src->window, &frames_rendered); - if (qt_src->run_time > 0) { - GST_DEBUG ("qmlglsrc Total refresh frames (%ld), playing for (%" - GST_TIME_FORMAT "), fps (%.3f).\n", frames_rendered, - GST_TIME_ARGS (qt_src->run_time), - (gfloat) GST_SECOND * frames_rendered / qt_src->run_time); - } break; default: break; diff --git a/ext/qt/gstqtsrc.h b/ext/qt/gstqtsrc.h index 8bbf4e2ebb..d84544224a 100644 --- a/ext/qt/gstqtsrc.h +++ b/ext/qt/gstqtsrc.h @@ -62,8 +62,6 @@ struct _GstQtSrc gboolean default_fbo; gboolean downstream_supports_affine_meta; gboolean pending_image_orientation; - /* fps print support */ - GstClockTime run_time; }; /** diff --git a/ext/qt/qtwindow.cc b/ext/qt/qtwindow.cc index c3fd564dfb..362a173b2c 100644 --- a/ext/qt/qtwindow.cc +++ b/ext/qt/qtwindow.cc @@ -29,6 +29,7 @@ #include "gstqsgtexture.h" #include "gstqtglutility.h" +#include #include #include #include @@ -67,7 +68,9 @@ struct _QtGLWindowPrivate GstGLContext *other_context; /* frames that qmlview rendered in its gl thread */ - guint64 frames_rendered; + quint64 frames_rendered; + quint64 start; + quint64 stop; }; class InitQtGLContext : public QRunnable @@ -141,6 +144,12 @@ QtGLWindow::beforeRendering() g_mutex_lock (&this->priv->lock); + static volatile gsize once = 0; + if (g_once_init_enter(&once)) { + this->priv->start = QDateTime::currentDateTime().toMSecsSinceEpoch(); + g_once_init_leave(&once,1); + } + if (!fbo && !this->priv->useDefaultFbo) { width = source->width(); @@ -152,6 +161,10 @@ QtGLWindow::beforeRendering() QOpenGLFramebufferObject::NoAttachment, GL_TEXTURE_2D, GL_RGBA)); source->setRenderTarget(fbo.data()); + } else if (this->priv->useDefaultFbo) { + GST_DEBUG ("use default fbo for render target"); + fbo.reset(NULL); + source->setRenderTarget(NULL); } g_mutex_unlock (&this->priv->lock); @@ -236,9 +249,14 @@ QtGLWindow::aboutToQuit() this->priv->quit = TRUE; g_cond_signal (&this->priv->update_cond); - g_mutex_unlock (&this->priv->lock); + this->priv->stop = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qint64 duration = this->priv->stop - this->priv->start; + float fps = ((float)this->priv->frames_rendered / duration * 1000); - GST_DEBUG("about to quit"); + GST_DEBUG("about to quit, total refresh frames (%lld) in (%0.3f) seconds, fps: %0.3f", + this->priv->frames_rendered, (float)duration / 1000, fps); + + g_mutex_unlock (&this->priv->lock); } void @@ -362,15 +380,3 @@ qt_window_use_default_fbo (QtGLWindow * qt_window, gboolean useDefaultFbo) g_mutex_unlock (&qt_window->priv->lock); } - -void -qt_window_get_total_frames (QtGLWindow * qt_window, guint64 *frames) -{ - g_return_if_fail (qt_window != NULL); - - g_mutex_lock (&qt_window->priv->lock); - - *frames = qt_window->priv->frames_rendered; - - g_mutex_unlock (&qt_window->priv->lock); -} diff --git a/ext/qt/qtwindow.h b/ext/qt/qtwindow.h index 5820e77caf..a617c076a9 100644 --- a/ext/qt/qtwindow.h +++ b/ext/qt/qtwindow.h @@ -64,7 +64,6 @@ GstGLContext * qt_window_get_qt_context (QtGLWindow * qt_window); GstGLDisplay * qt_window_get_display (QtGLWindow * qt_window); gboolean qt_window_is_scenegraph_initialized (QtGLWindow * qt_window); void qt_window_use_default_fbo (QtGLWindow * qt_window, gboolean useDefaultFbo); -void qt_window_get_total_frames (QtGLWindow * qt_window, guint64 *frames); } #endif /* __QT_WINDOW_H__ */ From 6ce990d24e2de375f6121c2da013d224aef11aa1 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Wed, 7 Dec 2016 22:58:29 +1100 Subject: [PATCH 47/78] tests/examples/qmlsink: scope QApplication/Engine So they are destroyed before gst_deinit() is run and the leaks tracer doesn't show false-positives. https://bugzilla.gnome.org/show_bug.cgi?id=775746 --- tests/examples/qt/qmlsink/main.cpp | 51 ++++++++++++++++-------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/tests/examples/qt/qmlsink/main.cpp b/tests/examples/qt/qmlsink/main.cpp index 2317b05415..a5cd5606b5 100644 --- a/tests/examples/qt/qmlsink/main.cpp +++ b/tests/examples/qt/qmlsink/main.cpp @@ -39,40 +39,43 @@ int main(int argc, char *argv[]) { int ret; - QGuiApplication app(argc, argv); gst_init (&argc, &argv); - GstElement *pipeline = gst_pipeline_new (NULL); - GstElement *src = gst_element_factory_make ("videotestsrc", NULL); - GstElement *glupload = gst_element_factory_make ("glupload", NULL); - /* the plugin must be loaded before loading the qml file to register the - * GstGLVideoItem qml item */ - GstElement *sink = gst_element_factory_make ("qmlglsink", NULL); + { + QGuiApplication app(argc, argv); - g_assert (src && glupload && sink); + GstElement *pipeline = gst_pipeline_new (NULL); + GstElement *src = gst_element_factory_make ("videotestsrc", NULL); + GstElement *glupload = gst_element_factory_make ("glupload", NULL); + /* the plugin must be loaded before loading the qml file to register the + * GstGLVideoItem qml item */ + GstElement *sink = gst_element_factory_make ("qmlglsink", NULL); - gst_bin_add_many (GST_BIN (pipeline), src, glupload, sink, NULL); - gst_element_link_many (src, glupload, sink, NULL); + g_assert (src && glupload && sink); - QQmlApplicationEngine engine; - engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + gst_bin_add_many (GST_BIN (pipeline), src, glupload, sink, NULL); + gst_element_link_many (src, glupload, sink, NULL); - QQuickItem *videoItem; - QQuickWindow *rootObject; + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); - /* find and set the videoItem on the sink */ - rootObject = static_cast (engine.rootObjects().first()); - videoItem = rootObject->findChild ("videoItem"); - g_assert (videoItem); - g_object_set(sink, "widget", videoItem, NULL); + QQuickItem *videoItem; + QQuickWindow *rootObject; - rootObject->scheduleRenderJob (new SetPlaying (pipeline), - QQuickWindow::BeforeSynchronizingStage); + /* find and set the videoItem on the sink */ + rootObject = static_cast (engine.rootObjects().first()); + videoItem = rootObject->findChild ("videoItem"); + g_assert (videoItem); + g_object_set(sink, "widget", videoItem, NULL); - ret = app.exec(); + rootObject->scheduleRenderJob (new SetPlaying (pipeline), + QQuickWindow::BeforeSynchronizingStage); - gst_element_set_state (pipeline, GST_STATE_NULL); - gst_object_unref (pipeline); + ret = app.exec(); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + } gst_deinit (); From 01b6c38ec962a04b208601aa48cc6482fc5eb36b Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Wed, 7 Dec 2016 22:55:46 +1100 Subject: [PATCH 48/78] qml/item: also unref the display on destruction Leaking objects (and a thread!) is never a good idea. https://bugzilla.gnome.org/show_bug.cgi?id=775746 --- ext/qt/qtitem.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index f088c76fb1..8027c741ae 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -136,6 +136,8 @@ QtGLVideoItem::~QtGLVideoItem() gst_object_unref(this->priv->context); if (this->priv->other_context) gst_object_unref(this->priv->other_context); + if (this->priv->display) + gst_object_unref(this->priv->display); g_free (this->priv); this->priv = NULL; } From d1f4c207a8e6d8346017e071e5014ec50e1d36fe Mon Sep 17 00:00:00 2001 From: Sergey Borovkov Date: Thu, 8 Dec 2016 12:37:25 +0300 Subject: [PATCH 49/78] qml: Fix egl being deinitialized on display cleanup Use the with_egl_display() variant in order to not destroy the EGLDisplay on destruction. https://bugzilla.gnome.org/show_bug.cgi?id=775793 --- ext/qt/gstqtglutility.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/qt/gstqtglutility.cc b/ext/qt/gstqtglutility.cc index 4d6d318646..196d010101 100644 --- a/ext/qt/gstqtglutility.cc +++ b/ext/qt/gstqtglutility.cc @@ -83,10 +83,10 @@ gst_qt_get_gl_display () #endif #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_WINDOW_ANDROID if (QString::fromUtf8 ("android") == app->platformName()) - display = (GstGLDisplay *) gst_gl_display_egl_new (); + display = (GstGLDisplay *) gst_gl_display_egl_new_with_egl_display (eglGetDisplay(EGL_DEFAULT_DISPLAY)); #elif GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) if (QString::fromUtf8("eglfs") == app->platformName()) - display = (GstGLDisplay *) gst_gl_display_egl_new (); + display = (GstGLDisplay *) gst_gl_display_egl_new_with_egl_display (eglGetDisplay(EGL_DEFAULT_DISPLAY)); #endif #if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) From 01a462a4c9003f2ed2bf6a6df1f512d00a0a3b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sat, 17 Dec 2016 13:42:34 +0000 Subject: [PATCH 50/78] qt: improve element and property descriptions a bit --- ext/qt/gstqtsink.cc | 4 ++-- ext/qt/gstqtsrc.cc | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/qt/gstqtsink.cc b/ext/qt/gstqtsink.cc index 85d6ae3f66..70e6858b4e 100644 --- a/ext/qt/gstqtsink.cc +++ b/ext/qt/gstqtsink.cc @@ -105,12 +105,12 @@ gst_qt_sink_class_init (GstQtSinkClass * klass) gobject_class->get_property = gst_qt_sink_get_property; gst_element_class_set_metadata (gstelement_class, "Qt Video Sink", - "Sink/Video", "A video sink the renders to a QQuickItem", + "Sink/Video", "A video sink that renders to a QQuickItem", "Matthew Waters "); g_object_class_install_property (gobject_class, PROP_WIDGET, g_param_spec_pointer ("widget", "QQuickItem", - "The QQuickItem to place in the object heirachy", + "The QQuickItem to place in the object hierarchy", (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO, diff --git a/ext/qt/gstqtsrc.cc b/ext/qt/gstqtsrc.cc index 1334dc3a93..849018e8f1 100644 --- a/ext/qt/gstqtsrc.cc +++ b/ext/qt/gstqtsrc.cc @@ -97,18 +97,18 @@ gst_qt_src_class_init (GstQtSrcClass * klass) gobject_class->finalize = gst_qt_src_finalize; gst_element_class_set_metadata (gstelement_class, "Qt Video Source", - "Source/Video", "A video src the grab window from a qml view", + "Source/Video", "A video src that captures a window from a QML view", "Multimedia Team "); g_object_class_install_property (gobject_class, PROP_WINDOW, g_param_spec_pointer ("window", "QQuickWindow", - "The QQuickWindow to place in the object heirachy", + "The QQuickWindow to place in the object hierarchy", (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_DEFAULT_FBO, g_param_spec_boolean ("use-default-fbo", - "If use default fbo", - "When set TRUE, it will not create new fbo for qml render thread", + "Whether to use default FBO", + "When set it will not create a new FBO for the QML render thread", FALSE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); gst_element_class_add_pad_template (gstelement_class, From f54fcb0349e4732641302c6ca058294cd62e135b Mon Sep 17 00:00:00 2001 From: Haihua Hu Date: Tue, 10 Jan 2017 16:54:48 +0800 Subject: [PATCH 51/78] qmlglsrc: use glBlitFramebuffer to copy texture for GLES3.0 If support glBlitFrameBuffer, use it for texture copy instead of glCopyTexImage2D https://bugzilla.gnome.org/show_bug.cgi?id=777078 --- ext/qt/qtwindow.cc | 50 +++++++++++++++++++++++++++++++++++++++++++++- ext/qt/qtwindow.h | 1 + 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/ext/qt/qtwindow.cc b/ext/qt/qtwindow.cc index 362a173b2c..92c5834c1d 100644 --- a/ext/qt/qtwindow.cc +++ b/ext/qt/qtwindow.cc @@ -67,6 +67,8 @@ struct _QtGLWindowPrivate GstGLDisplay *display; GstGLContext *other_context; + GLuint fbo; + /* frames that qmlview rendered in its gl thread */ quint64 frames_rendered; quint64 start; @@ -121,6 +123,8 @@ QtGLWindow::QtGLWindow ( QWindow * parent, QQuickWindow *src ) : else connect (source, SIGNAL(sceneGraphInitialized()), this, SLOT(onSceneGraphInitialized()), Qt::DirectConnection); + connect (source, SIGNAL(sceneGraphInvalidated()), this, SLOT(onSceneGraphInvalidated()), Qt::DirectConnection); + GST_DEBUG ("%p init Qt Window", this->priv->display); } @@ -224,7 +228,23 @@ QtGLWindow::afterRendering() this->source->renderTargetId(), dst_tex, width,height); gl->BindTexture (GL_TEXTURE_2D, dst_tex); - gl->CopyTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, width, height, 0); + if (gl->BlitFramebuffer) { + gl->BindFramebuffer (GL_DRAW_FRAMEBUFFER, this->priv->fbo); + gl->FramebufferTexture2D (GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, dst_tex, 0); + + ret = gst_gl_context_check_framebuffer_status (context); + if (!ret) { + GST_ERROR ("FBO errors"); + goto errors; + } + gl->ReadBuffer (GL_COLOR_ATTACHMENT0); + gl->BlitFramebuffer (0, 0, width, height, + 0, 0, width, height, + GL_COLOR_BUFFER_BIT, GL_LINEAR); + } else { + gl->CopyTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, width, height, 0); + } GST_DEBUG ("rendering finished"); @@ -268,10 +288,38 @@ QtGLWindow::onSceneGraphInitialized() this->priv->initted = gst_qt_get_gl_wrapcontext (this->priv->display, &this->priv->other_context, NULL); + if (this->priv->initted && this->priv->other_context) { + const GstGLFuncs *gl; + + gst_gl_context_activate (this->priv->other_context, TRUE); + gl = this->priv->other_context->gl_vtable; + + gl->GenFramebuffers (1, &this->priv->fbo); + + gst_gl_context_activate (this->priv->other_context, FALSE); + } + GST_DEBUG ("%p created wrapped GL context %" GST_PTR_FORMAT, this, this->priv->other_context); } +void +QtGLWindow::onSceneGraphInvalidated() +{ + GST_DEBUG ("scene graph invalidated"); + + if (this->priv->fbo && this->priv->other_context) { + const GstGLFuncs *gl; + + gst_gl_context_activate (this->priv->other_context, TRUE); + gl = this->priv->other_context->gl_vtable; + + gl->DeleteFramebuffers (1, &this->priv->fbo); + + gst_gl_context_activate (this->priv->other_context, FALSE); + } +} + bool QtGLWindow::getGeometry(int * width, int * height) { diff --git a/ext/qt/qtwindow.h b/ext/qt/qtwindow.h index a617c076a9..697f3897a5 100644 --- a/ext/qt/qtwindow.h +++ b/ext/qt/qtwindow.h @@ -48,6 +48,7 @@ private Q_SLOTS: void beforeRendering (); void afterRendering (); void onSceneGraphInitialized (); + void onSceneGraphInvalidated (); void aboutToQuit(); private: From cd9db288b26bf3f902cdc75242c644cbd3f3a539 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Thu, 12 Jan 2017 21:35:25 +1100 Subject: [PATCH 52/78] gl/utils: also take care of the local GL context in query functions Simplifies a deduplicates a lot of code in elements retrieving/setting the local OpenGL context. --- ext/qt/gstqtsink.cc | 38 +++------------------------ ext/qt/gstqtsrc.cc | 64 ++++----------------------------------------- 2 files changed, 8 insertions(+), 94 deletions(-) diff --git a/ext/qt/gstqtsink.cc b/ext/qt/gstqtsink.cc index 70e6858b4e..2253156e2e 100644 --- a/ext/qt/gstqtsink.cc +++ b/ext/qt/gstqtsink.cc @@ -239,41 +239,9 @@ gst_qt_sink_query (GstBaseSink * bsink, GstQuery * query) switch (GST_QUERY_TYPE (query)) { case GST_QUERY_CONTEXT: { - const gchar *context_type; - GstContext *context, *old_context; - gboolean ret; - - ret = gst_gl_handle_context_query ((GstElement *) qt_sink, query, - &qt_sink->display, &qt_sink->qt_context); - - if (qt_sink->display) - gst_gl_display_filter_gl_api (qt_sink->display, gst_gl_context_get_gl_api (qt_sink->qt_context)); - - gst_query_parse_context_type (query, &context_type); - - if (g_strcmp0 (context_type, "gst.gl.local_context") == 0) { - GstStructure *s; - - gst_query_parse_context (query, &old_context); - - if (old_context) - context = gst_context_copy (old_context); - else - context = gst_context_new ("gst.gl.local_context", FALSE); - - s = gst_context_writable_structure (context); - gst_structure_set (s, "context", GST_TYPE_GL_CONTEXT, qt_sink->context, - NULL); - gst_query_set_context (query, context); - gst_context_unref (context); - - ret = qt_sink->context != NULL; - } - GST_LOG_OBJECT (qt_sink, "context query of type %s %i", context_type, - ret); - - if (ret) - return ret; + if (gst_gl_handle_context_query ((GstElement *) qt_sink, query, + qt_sink->display, qt_sink->context, qt_sink->qt_context)) + return TRUE; /* fallthrough */ } diff --git a/ext/qt/gstqtsrc.cc b/ext/qt/gstqtsrc.cc index 849018e8f1..518a5f3faf 100644 --- a/ext/qt/gstqtsrc.cc +++ b/ext/qt/gstqtsrc.cc @@ -283,10 +283,6 @@ gst_qt_src_query (GstBaseSrc * bsrc, GstQuery * query) switch (GST_QUERY_TYPE (query)) { case GST_QUERY_CONTEXT: { - const gchar *context_type; - GstContext *context, *old_context; - gboolean ret; - if (!qt_window_is_scenegraph_initialized (qt_src->window)) return FALSE; @@ -295,37 +291,9 @@ gst_qt_src_query (GstBaseSrc * bsrc, GstQuery * query) qt_src->qt_context = qt_window_get_qt_context (qt_src->window); } - ret = gst_gl_handle_context_query ((GstElement *) qt_src, query, - &qt_src->display, &qt_src->qt_context); - - if (qt_src->display) - gst_gl_display_filter_gl_api (qt_src->display, - gst_gl_context_get_gl_api (qt_src->qt_context)); - - gst_query_parse_context_type (query, &context_type); - - if (g_strcmp0 (context_type, "gst.gl.app_context") == 0) { - GstStructure *s; - - gst_query_parse_context (query, &old_context); - - if (old_context) - context = gst_context_copy (old_context); - else - context = gst_context_new ("gst.gl.app_context", FALSE); - - s = gst_context_writable_structure (context); - gst_structure_set (s, "context", GST_TYPE_GL_CONTEXT, - qt_src->qt_context, NULL); - gst_query_set_context (query, context); - gst_context_unref (context); - - ret = qt_src->qt_context != NULL; - } - GST_LOG_OBJECT (qt_src, "context query of type %s %i", context_type, ret); - - if (ret) - return ret; + if (gst_gl_handle_context_query ((GstElement *) qt_src, query, + qt_src->display, qt_src->context, qt_src->qt_context)) + return TRUE; /* fallthrough */ } @@ -340,31 +308,9 @@ gst_qt_src_query (GstBaseSrc * bsrc, GstQuery * query) static gboolean _find_local_gl_context (GstQtSrc * qt_src) { - GstQuery *query; - GstContext *context; - const GstStructure *s; - - if (qt_src->context) + if (gst_gl_query_local_gl_context (GST_ELEMENT (qt_src), GST_PAD_SRC, + &qt_src->context)) return TRUE; - - query = gst_query_new_context ("gst.gl.local_context"); - if (!qt_src->context - && gst_gl_run_query (GST_ELEMENT (qt_src), query, GST_PAD_SRC)) { - gst_query_parse_context (query, &context); - if (context) { - s = gst_context_get_structure (context); - gst_structure_get (s, "context", GST_TYPE_GL_CONTEXT, &qt_src->context, - NULL); - } - } - - GST_DEBUG_OBJECT (qt_src, "found local context %p", qt_src->context); - - gst_query_unref (query); - - if (qt_src->context) - return TRUE; - return FALSE; } From d3f6497f50273f10163da7cb13ec6aa557d6cdaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 25 Jan 2017 19:21:03 +0200 Subject: [PATCH 53/78] qt: The code requires at least C++11 ... and clang requires this to be specified on the commandline while gcc nowadays defaults to C++11 or even newer. --- ext/qt/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/qt/Makefile.am b/ext/qt/Makefile.am index b8bd5d4bff..626e3a80aa 100644 --- a/ext/qt/Makefile.am +++ b/ext/qt/Makefile.am @@ -38,7 +38,7 @@ libgstqmlgl_la_CXXFLAGS = \ $(GST_BASE_CFLAGS) \ $(GST_PLUGINS_BASE_CFLAGS) \ $(QT_CFLAGS) \ - $(GL_CFLAGS) + $(GL_CFLAGS) -std=c++11 libgstqmlgl_la_LIBADD = \ $(GST_BASE_LIBS) \ From 219f485c2c52e5852b63befb6de266f9b4ef4f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 17 Feb 2017 14:37:08 +0200 Subject: [PATCH 54/78] qml: Add support for Vivante EGL FS windowing system https://bugzilla.gnome.org/show_bug.cgi?id=778825 --- ext/qt/gstqtglutility.cc | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/ext/qt/gstqtglutility.cc b/ext/qt/gstqtglutility.cc index 196d010101..22aae60da0 100644 --- a/ext/qt/gstqtglutility.cc +++ b/ext/qt/gstqtglutility.cc @@ -37,7 +37,12 @@ #endif #if GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) +#if GST_GL_HAVE_WINDOW_VIV_FB +#include +#include +#else #include +#endif #include #endif @@ -85,9 +90,34 @@ gst_qt_get_gl_display () if (QString::fromUtf8 ("android") == app->platformName()) display = (GstGLDisplay *) gst_gl_display_egl_new_with_egl_display (eglGetDisplay(EGL_DEFAULT_DISPLAY)); #elif GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) - if (QString::fromUtf8("eglfs") == app->platformName()) + if (QString::fromUtf8("eglfs") == app->platformName()) { +#if GST_GL_HAVE_WINDOW_VIV_FB + /* FIXME: Could get the display directly from Qt like this + QPlatformNativeInterface *native = + QGuiApplication::platformNativeInterface(); + EGLDisplay egl_display = (EGLDisplay) + native->nativeResourceForWindow("egldisplay", NULL); + + However we seem to have no way for getting the EGLNativeDisplayType, aka + native_display, via public API. As such we have to assume that display 0 + is always used. Only way around that is parsing the index the same way as + Qt does in QEGLDeviceIntegration::fbDeviceName(), so let's do that. + */ + const gchar *fb_dev; + gint disp_idx = 0; + + fb_dev = g_getenv ("QT_QPA_EGLFS_FB"); + if (fb_dev) { + if (sscanf (fb_dev, "/dev/fb%d", &disp_idx) != 1) + disp_idx = 0; + } + + display = (GstGLDisplay *) gst_gl_display_viv_fb_new (disp_idx); +#else display = (GstGLDisplay *) gst_gl_display_egl_new_with_egl_display (eglGetDisplay(EGL_DEFAULT_DISPLAY)); #endif + } +#endif #if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) if (QString::fromUtf8 ("cocoa") == app->platformName()) @@ -130,7 +160,11 @@ gst_qt_get_gl_wrapcontext (GstGLDisplay * display, } #endif #if GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) +#if GST_GL_HAVE_WINDOW_VIV_FB + if (GST_IS_GL_DISPLAY_VIV_FB (display)) { +#else if (GST_IS_GL_DISPLAY_EGL (display)) { +#endif platform = GST_GL_PLATFORM_EGL; } #endif From f647a79aa8a341a0e21738d7639ff817d5d23887 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Mon, 13 Mar 2017 14:28:47 +1100 Subject: [PATCH 55/78] gl/format: use our own GL format enum's instead of gstvideo's They can describe in more detail (such as component sizes) the requested format. --- ext/qt/gstqtsrc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/qt/gstqtsrc.cc b/ext/qt/gstqtsrc.cc index 518a5f3faf..1cf9592b56 100644 --- a/ext/qt/gstqtsrc.cc +++ b/ext/qt/gstqtsrc.cc @@ -393,7 +393,7 @@ gst_qt_src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query) glparams = gst_gl_video_allocation_params_new (qt_src->context, ¶ms, &vinfo, 0, - NULL, GST_GL_TEXTURE_TARGET_2D, GST_VIDEO_GL_TEXTURE_TYPE_RGBA); + NULL, GST_GL_TEXTURE_TARGET_2D, GST_GL_RGBA); gst_buffer_pool_config_set_gl_allocation_params (config, (GstGLAllocationParams *) glparams); gst_gl_allocation_params_free ((GstGLAllocationParams *) glparams); From 6ebafb7dafa5142b901eb1438f7f9a2450b1ac91 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Tue, 9 May 2017 09:47:10 -0400 Subject: [PATCH 56/78] qmlgl: Make the plugin name match the pugin file name --- ext/qt/gstplugin.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/qt/gstplugin.cc b/ext/qt/gstplugin.cc index 01ca2752c0..1fb1b3e1cd 100644 --- a/ext/qt/gstplugin.cc +++ b/ext/qt/gstplugin.cc @@ -46,7 +46,7 @@ plugin_init (GstPlugin * plugin) GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, - qt, + qmlgl, "Qt gl plugin", plugin_init, PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) From e44d936487705d795383150e3001f03274af0049 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Tue, 16 May 2017 14:05:52 -0400 Subject: [PATCH 57/78] Remove plugin specific static build option Static and dynamic plugins now have the same interface. The standard --enable-static/--enable-shared toggle are sufficient. --- ext/qt/Makefile.am | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/qt/Makefile.am b/ext/qt/Makefile.am index 626e3a80aa..257644a08e 100644 --- a/ext/qt/Makefile.am +++ b/ext/qt/Makefile.am @@ -48,7 +48,6 @@ libgstqmlgl_la_LIBADD = \ -lgstvideo-$(GST_API_VERSION) libgstqmlgl_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstqmlgl_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) $(moc_generated): moc_%.cc: %.h From 5178fcdfda3f982c3dbe92c8a6b0cf92166f5892 Mon Sep 17 00:00:00 2001 From: Haihua Hu Date: Wed, 17 May 2017 10:58:05 +0800 Subject: [PATCH 58/78] qml: Add EGL platform support for x11 backend Add support for EGL platform when x11 is available. This can work e.g. on imx6 platform. https://bugzilla.gnome.org/show_bug.cgi?id=782718 --- ext/qt/gstqtglutility.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ext/qt/gstqtglutility.cc b/ext/qt/gstqtglutility.cc index 22aae60da0..0867f3562d 100644 --- a/ext/qt/gstqtglutility.cc +++ b/ext/qt/gstqtglutility.cc @@ -25,10 +25,14 @@ #include "gstqtglutility.h" #include -#if GST_GL_HAVE_WINDOW_X11 && GST_GL_HAVE_PLATFORM_GLX && defined (HAVE_QT_X11) +#if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11) #include #include +#if GST_GL_HAVE_PLATFORM_GLX #include +#elif GST_GL_HAVE_PLATFORM_EGL +#include +#endif #endif #if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_WAYLAND) @@ -151,7 +155,11 @@ gst_qt_get_gl_wrapcontext (GstGLDisplay * display, #if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11) if (GST_IS_GL_DISPLAY_X11 (display)) { +#if GST_GL_HAVE_PLATFORM_GLX platform = GST_GL_PLATFORM_GLX; +#elif GST_GL_HAVE_PLATFORM_EGL + platform = GST_GL_PLATFORM_EGL; +#endif } #endif #if GST_GL_HAVE_WINDOW_WAYLAND && defined (HAVE_QT_WAYLAND) From b33563294f6a2868b13cd3be66ed21b83ad19144 Mon Sep 17 00:00:00 2001 From: Simon Himmelbauer Date: Thu, 18 May 2017 15:23:14 +0300 Subject: [PATCH 59/78] qt: Use GST_GL_HAVE_PLATFORM_CGL instead of GST_GL_HAVE_PLATFORM_COCOA The latter is not used/available anymore since years. Also fix a typo in the include path for the Cocoa GL display header. --- ext/qt/gstqtglutility.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/qt/gstqtglutility.cc b/ext/qt/gstqtglutility.cc index 0867f3562d..d0f7668742 100644 --- a/ext/qt/gstqtglutility.cc +++ b/ext/qt/gstqtglutility.cc @@ -50,8 +50,8 @@ #include #endif -#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) -#include +#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_CGL && defined (HAVE_QT_MAC) +#include #endif #define GST_CAT_DEFAULT qt_gl_utils_debug @@ -123,7 +123,7 @@ gst_qt_get_gl_display () } #endif -#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) +#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_CGL && defined (HAVE_QT_MAC) if (QString::fromUtf8 ("cocoa") == app->platformName()) display = (GstGLDisplay *) gst_gl_display_cocoa_new (); #endif @@ -177,7 +177,7 @@ gst_qt_get_gl_wrapcontext (GstGLDisplay * display, } #endif if (platform == 0) { -#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_COCOA && defined (HAVE_QT_MAC) +#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_CGL && defined (HAVE_QT_MAC) platform = GST_GL_PLATFORM_CGL; #elif GST_GL_HAVE_WINDOW_EAGL && GST_GL_HAVE_PLATFORM_EAGL && defined (HAVE_QT_IOS) platform = GST_GL_PLATFORM_EAGL; From fdb2784491768400665716213034abc718cabeae Mon Sep 17 00:00:00 2001 From: Carlos Rafael Giani Date: Sun, 21 May 2017 15:26:12 +0200 Subject: [PATCH 60/78] qmlglsink: Add dummy texture that is shown as placeholder for NULL buffers https://bugzilla.gnome.org/show_bug.cgi?id=782917 --- ext/qt/gstqsgtexture.cc | 41 +++++++++++++++++++++++++++++++++++++++++ ext/qt/gstqsgtexture.h | 1 + 2 files changed, 42 insertions(+) diff --git a/ext/qt/gstqsgtexture.cc b/ext/qt/gstqsgtexture.cc index 85747f260d..7391806ba3 100644 --- a/ext/qt/gstqsgtexture.cc +++ b/ext/qt/gstqsgtexture.cc @@ -22,6 +22,7 @@ #include "config.h" #endif +#include #include #include @@ -47,12 +48,17 @@ GstQSGTexture::GstQSGTexture () this->buffer_ = NULL; this->qt_context_ = NULL; this->sync_buffer_ = gst_buffer_new (); + this->dummy_tex_id_ = 0; } GstQSGTexture::~GstQSGTexture () { gst_buffer_replace (&this->buffer_, NULL); gst_buffer_replace (&this->sync_buffer_, NULL); + if (this->dummy_tex_id_ && QOpenGLContext::currentContext ()) { + QOpenGLContext::currentContext ()->functions ()->glDeleteTextures (1, + &this->dummy_tex_id_); + } } /* only called from the streaming thread with scene graph thread blocked */ @@ -87,6 +93,7 @@ GstQSGTexture::bind () GstGLSyncMeta *sync_meta; GstMemory *mem; guint tex_id; + gboolean use_dummy_tex = TRUE; if (!this->qt_context_) return; @@ -132,7 +139,41 @@ GstQSGTexture::bind () gst_video_frame_unmap (&this->v_frame); + /* Texture was successfully bound, so we do not need + * to use the dummy texture */ + use_dummy_tex = FALSE; + out: + if (G_UNLIKELY (use_dummy_tex)) { + QOpenGLContext *qglcontext = QOpenGLContext::currentContext (); + QOpenGLFunctions *funcs = qglcontext->functions (); + + /* Create dummy texture if not already present. + * Use the Qt OpenGL functions instead of the GstGL ones, + * since we are using the Qt OpenGL context here, and we must + * be able to delete the texture in the destructor. */ + if (this->dummy_tex_id_ == 0) { + /* Make this a black 64x64 pixel RGBA texture. + * This size and format is supported pretty much everywhere, so these + * are a safe pick. (64 pixel sidelength must be supported according + * to the GLES2 spec, table 6.18.) + * Set min/mag filters to GL_LINEAR to make sure no mipmapping is used. */ + const int tex_sidelength = 64; + std::vector < guint8 > dummy_data (tex_sidelength * tex_sidelength * 4, 0); + + funcs->glGenTextures (1, &this->dummy_tex_id_); + funcs->glBindTexture (GL_TEXTURE_2D, this->dummy_tex_id_); + funcs->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + funcs->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + funcs->glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tex_sidelength, + tex_sidelength, 0, GL_RGBA, GL_UNSIGNED_BYTE, &dummy_data[0]); + } + + g_assert (this->dummy_tex_id_ != 0); + + funcs->glBindTexture (GL_TEXTURE_2D, this->dummy_tex_id_); + } + gst_gl_context_activate (this->qt_context_, FALSE); } diff --git a/ext/qt/gstqsgtexture.h b/ext/qt/gstqsgtexture.h index a5a6f9f8be..fdabe93a95 100644 --- a/ext/qt/gstqsgtexture.h +++ b/ext/qt/gstqsgtexture.h @@ -51,6 +51,7 @@ private: GstBuffer * sync_buffer_; GstGLContext * qt_context_; GstMemory * mem_; + GLuint dummy_tex_id_; GstVideoInfo v_info; GstVideoFrame v_frame; }; From cbcd0a12b685870f1935b25ec3af39b478541c34 Mon Sep 17 00:00:00 2001 From: Haihua Hu Date: Thu, 25 May 2017 10:09:04 +0800 Subject: [PATCH 61/78] glframebuffer: check frame buffer status need use specific fbo target https://bugzilla.gnome.org/show_bug.cgi?id=783065 --- ext/qt/qtwindow.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/qt/qtwindow.cc b/ext/qt/qtwindow.cc index 92c5834c1d..6f1df7edbd 100644 --- a/ext/qt/qtwindow.cc +++ b/ext/qt/qtwindow.cc @@ -217,7 +217,7 @@ QtGLWindow::afterRendering() gl->BindFramebuffer (GL_READ_FRAMEBUFFER, this->source->renderTargetId()); - ret = gst_gl_context_check_framebuffer_status (context); + ret = gst_gl_context_check_framebuffer_status (context, GL_READ_FRAMEBUFFER); if (!ret) { GST_ERROR ("FBO errors"); goto errors; @@ -233,7 +233,7 @@ QtGLWindow::afterRendering() gl->FramebufferTexture2D (GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0); - ret = gst_gl_context_check_framebuffer_status (context); + ret = gst_gl_context_check_framebuffer_status (context, GL_DRAW_FRAMEBUFFER); if (!ret) { GST_ERROR ("FBO errors"); goto errors; From c4eabb1a87c40104237618aeb78719afbfcb88d8 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 22 Jun 2017 01:01:40 +1000 Subject: [PATCH 62/78] qt: Remove misleading reference to GTK in qtitem.cc --- ext/qt/qtitem.cc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 8027c741ae..be604683bb 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -39,15 +39,12 @@ * @short_description: a #GtkGLArea that renders GStreamer video #GstBuffers * @see_also: #GtkGLArea, #GstBuffer * - * #QtGLVideoItem is an #GtkWidget that renders GStreamer video buffers. + * #QtGLVideoItem is an #QQuickItem that renders GStreamer video buffers. */ #define GST_CAT_DEFAULT qt_item_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); -#define GTK_GST_GL_WIDGET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ - GTK_TYPE_GST_GL_WIDGET, QtGLVideoItemPrivate)) - #define DEFAULT_FORCE_ASPECT_RATIO TRUE #define DEFAULT_PAR_N 0 #define DEFAULT_PAR_D 1 From de26b9e5f32e8d0603716004b68cec36929c1c02 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 22 Jun 2017 01:01:40 +1000 Subject: [PATCH 63/78] qmlsink example: Add CMakeLists.txt Make it possible to build using cmake instead of qmake --- tests/examples/qt/qmlsink/CMakeLists.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/examples/qt/qmlsink/CMakeLists.txt diff --git a/tests/examples/qt/qmlsink/CMakeLists.txt b/tests/examples/qt/qmlsink/CMakeLists.txt new file mode 100644 index 0000000000..02b0e11ea6 --- /dev/null +++ b/tests/examples/qt/qmlsink/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.3) + +include(FindPkgConfig) +if(!${PKG_CONFIG_FOUND}) + MESSAGE(FATAL_ERROR "pkg-config required. Please install it") + return ("Error - pkg-config not found") +endif() + +pkg_check_modules(GSTREAMER gstreamer-1.0>=1.6 gstreamer-video-1.0>=1.6 REQUIRED) + +find_package(Qt5Core REQUIRED) +find_package(Qt5Widgets REQUIRED) +find_package(Qt5Qml REQUIRED) +find_package(Qt5Quick REQUIRED) + +set (SRC_LIST main.cpp) +qt5_add_resources(RESOURCES qml.qrc) +link_directories(${GSTREAMER_LIBRARY_DIRS}) +include_directories (${GSTREAMER_INCLUDE_DIRS}) +add_executable(qml-example ${SRC_LIST} ${RESOURCES}) +target_link_libraries (qml-example ${GSTREAMER_LIBRARIES}) +qt5_use_modules(qml-example Core Widgets Qml Quick) + From ac068bd2b9274aa0d812a7611a601c08fc1958ff Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Wed, 12 Jul 2017 15:29:32 +1000 Subject: [PATCH 64/78] qt: Use a proxy object for access to the QML widget QML can destroy the video widget at any time, leaving us with a dangling pointer. Use a lock and a proxy object to cope with that, and block in the widget destructor if there are ongoing calls into the widget. --- ext/qt/gstqtsink.cc | 38 ++++++++--- ext/qt/gstqtsink.h | 4 +- ext/qt/qtitem.cc | 160 +++++++++++++++++++++++++++++++------------- ext/qt/qtitem.h | 42 +++++++++--- 4 files changed, 174 insertions(+), 70 deletions(-) diff --git a/ext/qt/gstqtsink.cc b/ext/qt/gstqtsink.cc index 2253156e2e..c7ac11ec6e 100644 --- a/ext/qt/gstqtsink.cc +++ b/ext/qt/gstqtsink.cc @@ -143,6 +143,7 @@ gst_qt_sink_class_init (GstQtSinkClass * klass) static void gst_qt_sink_init (GstQtSink * qt_sink) { + qt_sink->widget = QSharedPointer(); } static void @@ -152,9 +153,14 @@ gst_qt_sink_set_property (GObject * object, guint prop_id, GstQtSink *qt_sink = GST_QT_SINK (object); switch (prop_id) { - case PROP_WIDGET: - qt_sink->widget = static_cast (g_value_get_pointer (value)); + case PROP_WIDGET: { + QtGLVideoItem *qt_item = static_cast (g_value_get_pointer (value)); + if (qt_item) + qt_sink->widget = qt_item->getInterface(); + else + qt_sink->widget.clear(); break; + } case PROP_FORCE_ASPECT_RATIO: g_return_if_fail (qt_sink->widget); qt_sink->widget->setForceAspectRatio (g_value_get_boolean (value)); @@ -196,6 +202,8 @@ gst_qt_sink_finalize (GObject * object) _reset (qt_sink); + qt_sink->widget.clear(); + G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -207,7 +215,13 @@ gst_qt_sink_get_property (GObject * object, guint prop_id, switch (prop_id) { case PROP_WIDGET: - g_value_set_pointer (value, qt_sink->widget); + /* This is not really safe - the app needs to be + * sure the widget is going to be kept alive or + * this can crash */ + if (qt_sink->widget) + g_value_set_pointer (value, qt_sink->widget->videoItem()); + else + g_value_set_pointer (value, NULL); break; case PROP_FORCE_ASPECT_RATIO: if (qt_sink->widget) @@ -287,16 +301,16 @@ gst_qt_sink_change_state (GstElement * element, GstStateChange transition) return GST_STATE_CHANGE_FAILURE; } - if (!qt_item_init_winsys (qt_sink->widget)) { + if (!qt_sink->widget->initWinSys()) { GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND, ("%s", "Could not initialize window system"), (NULL)); return GST_STATE_CHANGE_FAILURE; } - qt_sink->display = qt_item_get_display (qt_sink->widget); - qt_sink->context = qt_item_get_context (qt_sink->widget); - qt_sink->qt_context = qt_item_get_qt_context (qt_sink->widget); + qt_sink->display = qt_sink->widget->getDisplay(); + qt_sink->context = qt_sink->widget->getContext(); + qt_sink->qt_context = qt_sink->widget->getQtContext(); if (!qt_sink->display || !qt_sink->context || !qt_sink->qt_context) { GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND, @@ -321,7 +335,8 @@ gst_qt_sink_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_PLAYING_TO_PAUSED: break; case GST_STATE_CHANGE_PAUSED_TO_READY: - qt_item_set_buffer (qt_sink->widget, NULL); + if (qt_sink->widget) + qt_sink->widget->setBuffer(NULL); break; case GST_STATE_CHANGE_READY_TO_NULL: break; @@ -365,10 +380,10 @@ gst_qt_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) if (!gst_video_info_from_caps (&qt_sink->v_info, caps)) return FALSE; - if (!qt_item_set_caps (qt_sink->widget, caps)) + if (!qt_sink->widget) return FALSE; - return TRUE; + return qt_sink->widget->setCaps(caps); } static GstFlowReturn @@ -380,7 +395,8 @@ gst_qt_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf) qt_sink = GST_QT_SINK (vsink); - qt_item_set_buffer (qt_sink->widget, buf); + if (qt_sink->widget) + qt_sink->widget->setBuffer(buf); return GST_FLOW_OK; } diff --git a/ext/qt/gstqtsink.h b/ext/qt/gstqtsink.h index 9a1cfbece8..3ee20b7d5d 100644 --- a/ext/qt/gstqtsink.h +++ b/ext/qt/gstqtsink.h @@ -51,8 +51,6 @@ struct _GstQtSink /* */ GstVideoSink parent; - QtGLVideoItem *widget; - GstVideoInfo v_info; GstBufferPool *pool; @@ -60,7 +58,7 @@ struct _GstQtSink GstGLContext *context; GstGLContext *qt_context; - GstQtSinkPrivate *priv; + QSharedPointer widget; }; /** diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index be604683bb..5806bbcff3 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -30,6 +30,7 @@ #include "gstqtglutility.h" #include +#include #include #include #include @@ -123,11 +124,21 @@ QtGLVideoItem::QtGLVideoItem() connect(this, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(handleWindowChanged(QQuickWindow*))); + this->proxy = QSharedPointer(new QtGLVideoItemInterface(this)); + GST_DEBUG ("%p init Qt Video Item", this); } QtGLVideoItem::~QtGLVideoItem() { + /* Before destroying the priv info, make sure + * 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"); + proxy->invalidateRef(); + proxy.clear(); + g_mutex_clear (&this->priv->lock); if (this->priv->context) gst_object_unref(this->priv->context); @@ -237,18 +248,25 @@ _reset (QtGLVideoItem * qt_item) } void -qt_item_set_buffer (QtGLVideoItem * widget, GstBuffer * buffer) +QtGLVideoItemInterface::setBuffer (GstBuffer * buffer) { - g_return_if_fail (widget != NULL); - g_return_if_fail (widget->priv->negotiated); + QMutexLocker locker(&lock); - g_mutex_lock (&widget->priv->lock); + if (qt_item == NULL) + return; - gst_buffer_replace (&widget->priv->buffer, buffer); + if (!qt_item->priv->negotiated) { + GST_WARNING ("Got buffer on unnegotiated QtGLVideoItem. Dropping"); + return; + } - QMetaObject::invokeMethod(widget, "update", Qt::QueuedConnection); + g_mutex_lock (&qt_item->priv->lock); - g_mutex_unlock (&widget->priv->lock); + gst_buffer_replace (&qt_item->priv->buffer, buffer); + + QMetaObject::invokeMethod(qt_item, "update", Qt::QueuedConnection); + + g_mutex_unlock (&qt_item->priv->lock); } void @@ -280,50 +298,53 @@ QtGLVideoItem::onSceneGraphInvalidated () } gboolean -qt_item_init_winsys (QtGLVideoItem * widget) +QtGLVideoItemInterface::initWinSys () { + QMutexLocker locker(&lock); + GError *error = NULL; - g_return_val_if_fail (widget != NULL, FALSE); + if (qt_item == NULL) + return FALSE; - g_mutex_lock (&widget->priv->lock); + g_mutex_lock (&qt_item->priv->lock); - if (widget->priv->display && widget->priv->qt_context - && widget->priv->other_context && widget->priv->context) { + if (qt_item->priv->display && qt_item->priv->qt_context + && qt_item->priv->other_context && qt_item->priv->context) { /* already have the necessary state */ - g_mutex_unlock (&widget->priv->lock); + g_mutex_unlock (&qt_item->priv->lock); return TRUE; } - if (!GST_IS_GL_DISPLAY (widget->priv->display)) { + if (!GST_IS_GL_DISPLAY (qt_item->priv->display)) { GST_ERROR ("%p failed to retrieve display connection %" GST_PTR_FORMAT, - widget, widget->priv->display); - g_mutex_unlock (&widget->priv->lock); + qt_item, qt_item->priv->display); + g_mutex_unlock (&qt_item->priv->lock); return FALSE; } - if (!GST_IS_GL_CONTEXT (widget->priv->other_context)) { - GST_ERROR ("%p failed to retrieve wrapped context %" GST_PTR_FORMAT, widget, - widget->priv->other_context); - g_mutex_unlock (&widget->priv->lock); + if (!GST_IS_GL_CONTEXT (qt_item->priv->other_context)) { + GST_ERROR ("%p failed to retrieve wrapped context %" GST_PTR_FORMAT, qt_item, + qt_item->priv->other_context); + g_mutex_unlock (&qt_item->priv->lock); return FALSE; } - widget->priv->context = gst_gl_context_new (widget->priv->display); + qt_item->priv->context = gst_gl_context_new (qt_item->priv->display); - if (!widget->priv->context) { - g_mutex_unlock (&widget->priv->lock); + if (!qt_item->priv->context) { + g_mutex_unlock (&qt_item->priv->lock); return FALSE; } - if (!gst_gl_context_create (widget->priv->context, widget->priv->other_context, + if (!gst_gl_context_create (qt_item->priv->context, qt_item->priv->other_context, &error)) { GST_ERROR ("%s", error->message); - g_mutex_unlock (&widget->priv->lock); + g_mutex_unlock (&qt_item->priv->lock); return FALSE; } - g_mutex_unlock (&widget->priv->lock); + g_mutex_unlock (&qt_item->priv->lock); return TRUE; } @@ -403,68 +424,115 @@ _calculate_par (QtGLVideoItem * widget, GstVideoInfo * info) } gboolean -qt_item_set_caps (QtGLVideoItem * widget, GstCaps * caps) +QtGLVideoItemInterface::setCaps (GstCaps * caps) { + QMutexLocker locker(&lock); GstVideoInfo v_info; - g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); - if (widget->priv->caps && gst_caps_is_equal_fixed (widget->priv->caps, caps)) + if (qt_item == NULL) + return FALSE; + + if (qt_item->priv->caps && gst_caps_is_equal_fixed (qt_item->priv->caps, caps)) return TRUE; if (!gst_video_info_from_caps (&v_info, caps)) return FALSE; - g_mutex_lock (&widget->priv->lock); + g_mutex_lock (&qt_item->priv->lock); - _reset (widget); + _reset (qt_item); - gst_caps_replace (&widget->priv->caps, caps); + gst_caps_replace (&qt_item->priv->caps, caps); - if (!_calculate_par (widget, &v_info)) { - g_mutex_unlock (&widget->priv->lock); + if (!_calculate_par (qt_item, &v_info)) { + g_mutex_unlock (&qt_item->priv->lock); return FALSE; } - widget->priv->v_info = v_info; - widget->priv->negotiated = TRUE; + qt_item->priv->v_info = v_info; + qt_item->priv->negotiated = TRUE; - g_mutex_unlock (&widget->priv->lock); + g_mutex_unlock (&qt_item->priv->lock); return TRUE; } GstGLContext * -qt_item_get_qt_context (QtGLVideoItem * qt_item) +QtGLVideoItemInterface::getQtContext () { - g_return_val_if_fail (qt_item != NULL, NULL); + QMutexLocker locker(&lock); - if (!qt_item->priv->other_context) + if (!qt_item || !qt_item->priv->other_context) return NULL; return (GstGLContext *) gst_object_ref (qt_item->priv->other_context); } GstGLContext * -qt_item_get_context (QtGLVideoItem * qt_item) +QtGLVideoItemInterface::getContext () { - g_return_val_if_fail (qt_item != NULL, NULL); + QMutexLocker locker(&lock); - if (!qt_item->priv->context) + if (!qt_item || !qt_item->priv->context) return NULL; return (GstGLContext *) gst_object_ref (qt_item->priv->context); } GstGLDisplay * -qt_item_get_display (QtGLVideoItem * qt_item) +QtGLVideoItemInterface::getDisplay() { - g_return_val_if_fail (qt_item != NULL, NULL); + QMutexLocker locker(&lock); - if (!qt_item->priv->display) + if (!qt_item || !qt_item->priv->display) return NULL; return (GstGLDisplay *) gst_object_ref (qt_item->priv->display); } + +void +QtGLVideoItemInterface::setDAR(gint num, gint den) +{ + QMutexLocker locker(&lock); + if (!qt_item) + return; + qt_item->setDAR(num, den); +} + +void +QtGLVideoItemInterface::getDAR(gint * num, gint * den) +{ + QMutexLocker locker(&lock); + if (!qt_item) + return; + qt_item->getDAR (num, den); +} + +void +QtGLVideoItemInterface::setForceAspectRatio(bool force_aspect_ratio) +{ + QMutexLocker locker(&lock); + if (!qt_item) + return; + qt_item->setForceAspectRatio(force_aspect_ratio); +} + +bool +QtGLVideoItemInterface::getForceAspectRatio() +{ + QMutexLocker locker(&lock); + if (!qt_item) + return FALSE; + return qt_item->getForceAspectRatio(); +} + +void +QtGLVideoItemInterface::invalidateRef() +{ + QMutexLocker locker(&lock); + qt_item = NULL; +} + diff --git a/ext/qt/qtitem.h b/ext/qt/qtitem.h index ae23eff107..b322068f3a 100644 --- a/ext/qt/qtitem.h +++ b/ext/qt/qtitem.h @@ -25,12 +25,40 @@ #include #include "gstqtgl.h" +#include #include #include #include typedef struct _QtGLVideoItemPrivate QtGLVideoItemPrivate; +class QtGLVideoItem; + +class QtGLVideoItemInterface : public QObject +{ + Q_OBJECT +public: + QtGLVideoItemInterface (QtGLVideoItem *w) : qt_item (w), lock() {}; + + void invalidateRef(); + + void setBuffer (GstBuffer * buffer); + gboolean setCaps (GstCaps *caps); + gboolean initWinSys (); + GstGLContext *getQtContext(); + GstGLContext *getContext(); + GstGLDisplay *getDisplay(); + QtGLVideoItem *videoItem () { return qt_item; }; + + void setDAR(gint, gint); + void getDAR(gint *, gint *); + void setForceAspectRatio(bool); + bool getForceAspectRatio(); +private: + QtGLVideoItem *qt_item; + QMutex lock; +}; + class InitializeSceneGraph; class QtGLVideoItem : public QQuickItem, protected QOpenGLFunctions @@ -45,6 +73,7 @@ public: void setForceAspectRatio(bool); bool getForceAspectRatio(); + QSharedPointer getInterface() { return proxy; }; /* private for C interface ... */ QtGLVideoItemPrivate *priv; @@ -57,22 +86,15 @@ protected: QSGNode * updatePaintNode (QSGNode * oldNode, UpdatePaintNodeData * updatePaintNodeData); private: + friend class InitializeSceneGraph; void setViewportSize(const QSize &size); void shareContext(); QSize m_viewportSize; bool m_openGlContextInitialized; + + QSharedPointer proxy; }; -extern "C" -{ -void qt_item_set_buffer (QtGLVideoItem * widget, GstBuffer * buffer); -gboolean qt_item_set_caps (QtGLVideoItem * widget, GstCaps * caps); -gboolean qt_item_init_winsys (QtGLVideoItem * widget); -GstGLContext * qt_item_get_qt_context (QtGLVideoItem * qt_item); -GstGLContext * qt_item_get_context (QtGLVideoItem * qt_item); -GstGLDisplay * qt_item_get_display (QtGLVideoItem * qt_item); -} - #endif /* __QT_ITEM_H__ */ From d9a294ac9f44a23d2d2fe2598cc9c0f33b667a1c Mon Sep 17 00:00:00 2001 From: Carlos Rafael Giani Date: Sun, 21 May 2017 16:01:14 +0200 Subject: [PATCH 65/78] qmlglsink: Add itemInitialized signal to QML item This is useful for autoplay for example. With autoplay, it is necessary to wait until the scene graph is fully set up. This signal is emitted once the QML item node is ready. So, inside a connected slot, the pipeline's state can be set to PLAYING to automatically start playback as soon as the QML script is loaded. https://bugzilla.gnome.org/show_bug.cgi?id=786246 --- ext/qt/qtitem.cc | 2 ++ ext/qt/qtitem.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index 5806bbcff3..b20a6fb5da 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -289,6 +289,8 @@ QtGLVideoItem::onSceneGraphInitialized () GST_DEBUG ("%p created wrapped GL context %" GST_PTR_FORMAT, this, this->priv->other_context); + + emit itemInitialized(); } void diff --git a/ext/qt/qtitem.h b/ext/qt/qtitem.h index b322068f3a..8cffc24454 100644 --- a/ext/qt/qtitem.h +++ b/ext/qt/qtitem.h @@ -77,6 +77,9 @@ public: /* private for C interface ... */ QtGLVideoItemPrivate *priv; +Q_SIGNALS: + void itemInitialized(); + private Q_SLOTS: void handleWindowChanged(QQuickWindow * win); void onSceneGraphInitialized(); From 96a2092b4530cc1659ba55c7f5adc7b7001f8a64 Mon Sep 17 00:00:00 2001 From: Julien Isorce Date: Tue, 22 Aug 2017 12:39:43 +0100 Subject: [PATCH 66/78] qt: fix broken build due to commit 2fd84a6c for gstgl https://bugzilla.gnome.org/show_bug.cgi?id=784779 --- ext/qt/gstqsgtexture.cc | 1 + ext/qt/gstqtglutility.cc | 1 + ext/qt/gstqtsink.cc | 2 ++ ext/qt/qtwindow.cc | 1 + 4 files changed, 5 insertions(+) diff --git a/ext/qt/gstqsgtexture.cc b/ext/qt/gstqsgtexture.cc index 7391806ba3..2b314e0eec 100644 --- a/ext/qt/gstqsgtexture.cc +++ b/ext/qt/gstqsgtexture.cc @@ -27,6 +27,7 @@ #include #include +#include #include "gstqsgtexture.h" #define GST_CAT_DEFAULT gst_qsg_texture_debug diff --git a/ext/qt/gstqtglutility.cc b/ext/qt/gstqtglutility.cc index d0f7668742..ff8d875922 100644 --- a/ext/qt/gstqtglutility.cc +++ b/ext/qt/gstqtglutility.cc @@ -45,6 +45,7 @@ #include #include #else +#include #include #endif #include diff --git a/ext/qt/gstqtsink.cc b/ext/qt/gstqtsink.cc index c7ac11ec6e..31495feca5 100644 --- a/ext/qt/gstqtsink.cc +++ b/ext/qt/gstqtsink.cc @@ -30,6 +30,8 @@ #include "gstqtsink.h" #include +#include + #define GST_CAT_DEFAULT gst_debug_qt_gl_sink GST_DEBUG_CATEGORY (GST_CAT_DEFAULT); diff --git a/ext/qt/qtwindow.cc b/ext/qt/qtwindow.cc index 6f1df7edbd..1151ddc080 100644 --- a/ext/qt/qtwindow.cc +++ b/ext/qt/qtwindow.cc @@ -25,6 +25,7 @@ #include #include +#include #include "qtwindow.h" #include "gstqsgtexture.h" #include "gstqtglutility.h" From 0e47176b4082f356efdb66b3f3517c85ac71fc43 Mon Sep 17 00:00:00 2001 From: Jochen Henneberg Date: Wed, 26 Apr 2017 13:50:41 +0200 Subject: [PATCH 67/78] qt: ensure GL_DRAW_FRAMEBUFFER --- ext/qt/qtwindow.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/qt/qtwindow.cc b/ext/qt/qtwindow.cc index 1151ddc080..62d26b647b 100644 --- a/ext/qt/qtwindow.cc +++ b/ext/qt/qtwindow.cc @@ -40,6 +40,9 @@ #ifndef GL_READ_FRAMEBUFFER #define GL_READ_FRAMEBUFFER 0x8CA8 #endif +#ifndef GL_DRAW_FRAMEBUFFER +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#endif /** * SECTION: From 20d51d876f1c5d91abdc2cfb5eb3582a0d427ea3 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Thu, 31 Aug 2017 14:40:44 +1000 Subject: [PATCH 68/78] qt: the defines for QT_OPENGL_ES_2 have moved Update the includes to account for that --- ext/qt/gstqtgl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/qt/gstqtgl.h b/ext/qt/gstqtgl.h index 673ef22a0f..2c8df24666 100644 --- a/ext/qt/gstqtgl.h +++ b/ext/qt/gstqtgl.h @@ -24,7 +24,7 @@ * warnings/errors */ #include -#include +#include #if defined(QT_OPENGL_ES_2) && GST_GL_HAVE_WINDOW_ANDROID #define GLsync gst_qt_GLsync From 115fb15a40be8a8f0221b2d02422c7d3d1cab635 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Fri, 1 Sep 2017 15:56:04 +0200 Subject: [PATCH 69/78] qt: Only include qtgui-config.h on qt >= 5.9.0 The file does not exist in previous versions --- ext/qt/gstqtgl.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/qt/gstqtgl.h b/ext/qt/gstqtgl.h index 2c8df24666..85aad62052 100644 --- a/ext/qt/gstqtgl.h +++ b/ext/qt/gstqtgl.h @@ -24,7 +24,10 @@ * warnings/errors */ #include +#include +#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) #include +#endif #if defined(QT_OPENGL_ES_2) && GST_GL_HAVE_WINDOW_ANDROID #define GLsync gst_qt_GLsync From 346474c25db324bb3c6931cdc881750dab10b22e Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Tue, 5 Sep 2017 16:20:44 -0400 Subject: [PATCH 70/78] Request minimum buffer even if need_pool is FALSE When tee is used, it will not request a pool, but still it wants to know how many buffers are required. https://bugzilla.gnome.org/show_bug.cgi?id=730758 --- ext/qt/gstqtsink.cc | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/ext/qt/gstqtsink.cc b/ext/qt/gstqtsink.cc index 31495feca5..2a4ad7ccc6 100644 --- a/ext/qt/gstqtsink.cc +++ b/ext/qt/gstqtsink.cc @@ -421,6 +421,7 @@ gst_qt_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) if (caps == NULL) goto no_caps; + /* FIXME re-using buffer pool breaks renegotiation */ if ((pool = qt_sink->pool)) gst_object_ref (pool); @@ -439,30 +440,31 @@ gst_qt_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) pool = NULL; } gst_structure_free (config); - } - - if (pool == NULL && need_pool) { + } else { GstVideoInfo info; if (!gst_video_info_from_caps (&info, caps)) goto invalid_caps; - GST_DEBUG_OBJECT (qt_sink, "create new pool"); - pool = gst_gl_buffer_pool_new (qt_sink->context); - /* the normal size of a frame */ size = info.size; + } + + if (pool == NULL && need_pool) { + + GST_DEBUG_OBJECT (qt_sink, "create new pool"); + pool = gst_gl_buffer_pool_new (qt_sink->context); config = gst_buffer_pool_get_config (pool); gst_buffer_pool_config_set_params (config, caps, size, 0, 0); if (!gst_buffer_pool_set_config (pool, config)) goto config_failed; } + /* we need at least 2 buffer because we hold on to the last one */ - if (pool) { - gst_query_add_allocation_pool (query, pool, size, 2, 0); + gst_query_add_allocation_pool (query, pool, size, 2, 0); + if (pool) gst_object_unref (pool); - } /* we also support various metadata */ gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0); From be7ebefd27730e2458b211d0d2e7aad4eed35c23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Thu, 7 Sep 2017 09:39:13 +0100 Subject: [PATCH 71/78] qt: fix build with qmake Move the package defines for GST_PLUGIN_DEFINE from the command line into the source file to avoid quoting issues (-DPACKAGE_NAME="foo" means the quotes won't actually make it to the compiler and then it no longer gets a string constant). --- ext/qt/gstplugin.cc | 8 ++++++++ ext/qt/qtplugin.pro | 9 ++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ext/qt/gstplugin.cc b/ext/qt/gstplugin.cc index 1fb1b3e1cd..79fb18189a 100644 --- a/ext/qt/gstplugin.cc +++ b/ext/qt/gstplugin.cc @@ -44,6 +44,14 @@ plugin_init (GstPlugin * plugin) return TRUE; } +#ifndef GST_PACKAGE_NAME +#define GST_PACKAGE_NAME "GStreamer Bad Plug-ins (qmake)" +#define GST_PACKAGE_ORIGIN "Unknown package origin" +#define GST_LICENSE "LGPL" +#define PACKAGE "gst-plugins-bad (qmake)" +#define PACKAGE_VERSION "1.13.0.1" +#endif + GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, qmlgl, diff --git a/ext/qt/qtplugin.pro b/ext/qt/qtplugin.pro index af1d8ddc12..ad0fa01df6 100644 --- a/ext/qt/qtplugin.pro +++ b/ext/qt/qtplugin.pro @@ -13,12 +13,7 @@ PKGCONFIG = \ DEFINES += \ GST_USE_UNSTABLE_API \ - HAVE_QT_WIN32 \ - 'GST_PACKAGE_NAME=\"GStreamer Bad Plug-ins (qmake)\"' \ - 'GST_PACKAGE_ORIGIN=\"Unknown package origin\"' \ - 'GST_LICENSE=\"LGPL\"' \ - 'PACKAGE=\"gst-plugins-bad (qmake)\"' \ - 'PACKAGE_VERSION=\"1.9.0.1\"' + HAVE_QT_WIN32 SOURCES += \ gstplugin.cc \ @@ -36,4 +31,4 @@ INCLUDEPATH += \ $$(GSTREAMER_ROOT)/include \ $$[QT_INSTALL_PREFIX]/include/QtGui/$$[QT_VERSION]/QtGui/ - \ No newline at end of file + From a0d526a2a85f350576884c70cf40477fd0c2afd7 Mon Sep 17 00:00:00 2001 From: Jochen Henneberg Date: Wed, 6 Sep 2017 07:59:56 +0000 Subject: [PATCH 72/78] qmlglsink: Expose itemInitialized as property Instead of just signalling when ready exposing the state as a property allows us to bind at any time if player is loaded async. --- ext/qt/qtitem.cc | 8 +++++++- ext/qt/qtitem.h | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc index b20a6fb5da..65b883eab8 100644 --- a/ext/qt/qtitem.cc +++ b/ext/qt/qtitem.cc @@ -178,6 +178,12 @@ QtGLVideoItem::getForceAspectRatio() return this->priv->force_aspect_ratio; } +bool +QtGLVideoItem::itemInitialized() +{ + return m_openGlContextInitialized; +} + QSGNode * QtGLVideoItem::updatePaintNode(QSGNode * oldNode, UpdatePaintNodeData * updatePaintNodeData) @@ -290,7 +296,7 @@ QtGLVideoItem::onSceneGraphInitialized () GST_DEBUG ("%p created wrapped GL context %" GST_PTR_FORMAT, this, this->priv->other_context); - emit itemInitialized(); + emit itemInitializedChanged(); } void diff --git a/ext/qt/qtitem.h b/ext/qt/qtitem.h index 8cffc24454..065d043eaf 100644 --- a/ext/qt/qtitem.h +++ b/ext/qt/qtitem.h @@ -64,6 +64,11 @@ class InitializeSceneGraph; class QtGLVideoItem : public QQuickItem, protected QOpenGLFunctions { Q_OBJECT + + Q_PROPERTY(bool itemInitialized + READ itemInitialized + NOTIFY itemInitializedChanged) + public: QtGLVideoItem(); ~QtGLVideoItem(); @@ -72,13 +77,14 @@ public: void getDAR(gint *, gint *); void setForceAspectRatio(bool); bool getForceAspectRatio(); + bool itemInitialized(); QSharedPointer getInterface() { return proxy; }; /* private for C interface ... */ QtGLVideoItemPrivate *priv; Q_SIGNALS: - void itemInitialized(); + void itemInitializedChanged(); private Q_SLOTS: void handleWindowChanged(QQuickWindow * win); From 43827a05f9331068b6c935e5542c5ea91865e62c Mon Sep 17 00:00:00 2001 From: Cassandra Rommel Date: Mon, 2 Oct 2017 12:35:48 -0700 Subject: [PATCH 73/78] gl: Use GstGLDisplayEGL directly instead of creating a GstGLDisplayVIVFb subclass This simplifies the code a lot without any functional changes apart from not closing the display connection. Closing the display connection is not safe to do as it is shared between all other code in the same process and no reference counting or anything happens at the platform layer. --- ext/qt/gstqtglutility.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ext/qt/gstqtglutility.cc b/ext/qt/gstqtglutility.cc index ff8d875922..84794e4ee2 100644 --- a/ext/qt/gstqtglutility.cc +++ b/ext/qt/gstqtglutility.cc @@ -169,11 +169,7 @@ gst_qt_get_gl_wrapcontext (GstGLDisplay * display, } #endif #if GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) -#if GST_GL_HAVE_WINDOW_VIV_FB - if (GST_IS_GL_DISPLAY_VIV_FB (display)) { -#else if (GST_IS_GL_DISPLAY_EGL (display)) { -#endif platform = GST_GL_PLATFORM_EGL; } #endif From e16ad13ed6f3aabc7fbca1bbcd30663cacb0bf94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Fri, 13 Oct 2017 12:25:22 +0100 Subject: [PATCH 74/78] qt: update qmake .pro file Update for renaming of plugin file, and add some missing source files. --- ext/qt/qtplugin.pro | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ext/qt/qtplugin.pro b/ext/qt/qtplugin.pro index ad0fa01df6..2337087a39 100644 --- a/ext/qt/qtplugin.pro +++ b/ext/qt/qtplugin.pro @@ -1,8 +1,8 @@ TEMPLATE = lib -TARGET = libgstqtsink +TARGET = gstqmlgl -QT += qml quick widgets +QT += qml quick widgets gui QT_CONFIG -= no-pkg-config CONFIG += link_pkgconfig debug plugin @@ -17,14 +17,20 @@ DEFINES += \ SOURCES += \ gstplugin.cc \ + gstqtglutility.cc \ gstqsgtexture.cc \ gstqtsink.cc \ + gstqtsrc.cc \ + qtwindow.cc \ qtitem.cc HEADERS += \ gstqsgtexture.h \ gstqtgl.h \ + gstqtglutility.h \ gstqtsink.h \ + gstqtsrc.h \ + qtwindow.h \ qtitem.h INCLUDEPATH += \ From 4359684dd204af2ea98998ec2993851af30eefbe Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 23 Nov 2017 22:58:40 +1100 Subject: [PATCH 75/78] Revert "gl: Use GstGLDisplayEGL directly instead of creating a GstGLDisplayVIVFb subclass" This reverts commit 47fd4d391e775c11f529705bb0f457a9d25ba5e7. This patch is incorrect. It doesn't actually compile, and causes a crash because the viv-fb window implementation needs a native EGL handle to pass to fbCreateWindow, but the GstGLDisplayEGL handleis actually an EGLDisplay now (and gets cast to the wrong type) --- ext/qt/gstqtglutility.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/qt/gstqtglutility.cc b/ext/qt/gstqtglutility.cc index 84794e4ee2..ff8d875922 100644 --- a/ext/qt/gstqtglutility.cc +++ b/ext/qt/gstqtglutility.cc @@ -169,7 +169,11 @@ gst_qt_get_gl_wrapcontext (GstGLDisplay * display, } #endif #if GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS) +#if GST_GL_HAVE_WINDOW_VIV_FB + if (GST_IS_GL_DISPLAY_VIV_FB (display)) { +#else if (GST_IS_GL_DISPLAY_EGL (display)) { +#endif platform = GST_GL_PLATFORM_EGL; } #endif From 92c89e2a31b7cf0b3d1bf2af6a959c638b4ae947 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Mon, 27 Nov 2017 14:44:58 +1100 Subject: [PATCH 76/78] gl/caopengllayer: use public GstGLContext instead of Cocoa-specific one Allows keeping the GstGLCAOpenGLLayer public but not the winsys-specific context/display/window. --- ext/qt/gstqtglutility.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ext/qt/gstqtglutility.cc b/ext/qt/gstqtglutility.cc index ff8d875922..4a88bb6b7c 100644 --- a/ext/qt/gstqtglutility.cc +++ b/ext/qt/gstqtglutility.cc @@ -51,10 +51,6 @@ #include #endif -#if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_CGL && defined (HAVE_QT_MAC) -#include -#endif - #define GST_CAT_DEFAULT qt_gl_utils_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); @@ -126,7 +122,7 @@ gst_qt_get_gl_display () #if GST_GL_HAVE_WINDOW_COCOA && GST_GL_HAVE_PLATFORM_CGL && defined (HAVE_QT_MAC) if (QString::fromUtf8 ("cocoa") == app->platformName()) - display = (GstGLDisplay *) gst_gl_display_cocoa_new (); + display = (GstGLDisplay *) gst_gl_display_new (); #endif #if GST_GL_HAVE_WINDOW_EAGL && GST_GL_HAVE_PLATFORM_EAGL && defined (HAVE_QT_IOS) if (QString::fromUtf8 ("ios") == app->platformName()) From fab34b7d0e08721a4dae2488b2dd15e5e3e58fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sun, 17 Dec 2017 20:54:06 +0000 Subject: [PATCH 77/78] gl: update plugins to use GstGL from -base --- ext/qt/Makefile.am | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ext/qt/Makefile.am b/ext/qt/Makefile.am index 257644a08e..cf2f761c39 100644 --- a/ext/qt/Makefile.am +++ b/ext/qt/Makefile.am @@ -32,20 +32,20 @@ libgstqmlgl_la_SOURCES = \ gstplugin.cc libgstqmlgl_la_CXXFLAGS = \ - -I$(top_srcdir)/gst-libs \ - -I$(top_builddir)/gst-libs \ - $(GST_CXXFLAGS) \ - $(GST_BASE_CFLAGS) \ + $(GST_PLUGINS_BAD_CFLAGS) \ $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_GL_CFLAGS) \ + $(GST_BASE_CFLAGS) \ + $(GST_CXXFLAGS) \ $(QT_CFLAGS) \ $(GL_CFLAGS) -std=c++11 libgstqmlgl_la_LIBADD = \ - $(GST_BASE_LIBS) \ $(GST_PLUGINS_BASE_LIBS) \ - $(QT_LIBS) \ - $(top_builddir)/gst-libs/gst/gl/libgstgl-$(GST_API_VERSION).la \ - -lgstvideo-$(GST_API_VERSION) + $(GST_GL_LIBS) \ + -lgstvideo-$(GST_API_VERSION) \ + $(GST_BASE_LIBS) \ + $(QT_LIBS) libgstqmlgl_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) From 93cf3ef912e4f2868c3875a631e2ccefedc4f4ea Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Wed, 7 Feb 2018 20:15:00 +1100 Subject: [PATCH 78/78] qt: don't #include platform specific gstglcontext_*.h headers They aren't public headers --- ext/qt/gstqtglutility.cc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ext/qt/gstqtglutility.cc b/ext/qt/gstqtglutility.cc index 4a88bb6b7c..8d5b242838 100644 --- a/ext/qt/gstqtglutility.cc +++ b/ext/qt/gstqtglutility.cc @@ -28,11 +28,6 @@ #if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11) #include #include -#if GST_GL_HAVE_PLATFORM_GLX -#include -#elif GST_GL_HAVE_PLATFORM_EGL -#include -#endif #endif #if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_WAYLAND) @@ -48,7 +43,6 @@ #include #include #endif -#include #endif #define GST_CAT_DEFAULT qt_gl_utils_debug