From: Matthew Waters Date: Wed, 5 Feb 2020 01:26:54 +0000 (+1100) Subject: glbasefilter: add support for changing the display X-Git-Tag: 1.19.3~511^2~757 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7e2073000a82a1e3361ac806ea698d2cf7ff0a30;p=platform%2Fupstream%2Fgstreamer.git glbasefilter: add support for changing the display Each element will remove its usage of the old display and context and try to retrieve a new GL context. --- diff --git a/ext/gl/gstglcolorconvertelement.c b/ext/gl/gstglcolorconvertelement.c index 25e7daa..256e5ef 100644 --- a/ext/gl/gstglcolorconvertelement.c +++ b/ext/gl/gstglcolorconvertelement.c @@ -36,15 +36,14 @@ G_DEFINE_TYPE_WITH_CODE (GstGLColorConvertElement, gst_gl_color_convert_element, "glconvertelement", 0, "convert"); ); -static gboolean gst_gl_color_convert_element_set_caps (GstBaseTransform * bt, - GstCaps * in_caps, GstCaps * out_caps); +static gboolean gst_gl_color_convert_element_gl_set_caps (GstGLBaseFilter * + base_filter, GstCaps * in_caps, GstCaps * out_caps); static GstCaps *gst_gl_color_convert_element_transform_caps (GstBaseTransform * bt, GstPadDirection direction, GstCaps * caps, GstCaps * filter); static gboolean gst_gl_color_convert_element_get_unit_size (GstBaseTransform * trans, GstCaps * caps, gsize * size); -static gboolean -gst_gl_color_convert_element_filter_meta (GstBaseTransform * trans, - GstQuery * query, GType api, const GstStructure * params); +static gboolean gst_gl_color_convert_element_filter_meta (GstBaseTransform * + trans, GstQuery * query, GType api, const GstStructure * params); static gboolean gst_gl_color_convert_element_decide_allocation (GstBaseTransform * trans, GstQuery * query); static GstFlowReturn @@ -52,8 +51,8 @@ gst_gl_color_convert_element_prepare_output_buffer (GstBaseTransform * bt, GstBuffer * inbuf, GstBuffer ** outbuf); static GstFlowReturn gst_gl_color_convert_element_transform (GstBaseTransform * bt, GstBuffer * inbuf, GstBuffer * outbuf); -static GstCaps *gst_gl_color_convert_element_fixate_caps (GstBaseTransform * - bt, GstPadDirection direction, GstCaps * caps, GstCaps * othercaps); +static GstCaps *gst_gl_color_convert_element_fixate_caps (GstBaseTransform * bt, + GstPadDirection direction, GstCaps * caps, GstCaps * othercaps); static GstStateChangeReturn gst_gl_color_convert_element_change_state (GstElement * element, GstStateChange transition); @@ -70,34 +69,33 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_ALWAYS, GST_STATIC_CAPS (GST_GL_COLOR_CONVERT_VIDEO_CAPS)); -static gboolean -gst_gl_color_convert_element_stop (GstBaseTransform * bt) +static void +gst_gl_color_convert_element_gl_stop (GstGLBaseFilter * filter) { - GstGLColorConvertElement *convert = GST_GL_COLOR_CONVERT_ELEMENT (bt); + GstGLColorConvertElement *convert = GST_GL_COLOR_CONVERT_ELEMENT (filter); if (convert->convert) { gst_object_unref (convert->convert); convert->convert = NULL; } - return GST_BASE_TRANSFORM_CLASS (parent_class)->stop (bt); + GST_GL_BASE_FILTER_CLASS (parent_class)->gl_stop (filter); } static void gst_gl_color_convert_element_class_init (GstGLColorConvertElementClass * klass) { + GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_CLASS (klass); GstBaseTransformClass *bt_class = GST_BASE_TRANSFORM_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass); bt_class->transform_caps = gst_gl_color_convert_element_transform_caps; - bt_class->set_caps = gst_gl_color_convert_element_set_caps; bt_class->get_unit_size = gst_gl_color_convert_element_get_unit_size; bt_class->filter_meta = gst_gl_color_convert_element_filter_meta; bt_class->decide_allocation = gst_gl_color_convert_element_decide_allocation; bt_class->prepare_output_buffer = gst_gl_color_convert_element_prepare_output_buffer; bt_class->transform = gst_gl_color_convert_element_transform; - bt_class->stop = gst_gl_color_convert_element_stop; bt_class->fixate_caps = gst_gl_color_convert_element_fixate_caps; bt_class->passthrough_on_same_caps = TRUE; @@ -113,6 +111,9 @@ gst_gl_color_convert_element_class_init (GstGLColorConvertElementClass * klass) "OpenGL color converter", "Filter/Converter/Video", "Converts between color spaces using OpenGL shaders", "Matthew Waters "); + + filter_class->gl_stop = gst_gl_color_convert_element_gl_stop; + filter_class->gl_set_caps = gst_gl_color_convert_element_gl_set_caps; } static void @@ -123,10 +124,14 @@ gst_gl_color_convert_element_init (GstGLColorConvertElement * convert) } static gboolean -gst_gl_color_convert_element_set_caps (GstBaseTransform * bt, +gst_gl_color_convert_element_gl_set_caps (GstGLBaseFilter * base_filter, GstCaps * in_caps, GstCaps * out_caps) { - GstGLColorConvertElement *convert = GST_GL_COLOR_CONVERT_ELEMENT (bt); + GstGLColorConvertElement *convert = + GST_GL_COLOR_CONVERT_ELEMENT (base_filter); + + if (!convert->convert && base_filter->context) + convert->convert = gst_gl_color_convert_new (base_filter->context); if (!gst_gl_color_convert_set_caps (convert->convert, in_caps, out_caps)) return FALSE; @@ -141,16 +146,21 @@ gst_gl_color_convert_element_transform_caps (GstBaseTransform * bt, GstGLColorConvertElement *convert = GST_GL_COLOR_CONVERT_ELEMENT (bt); GstGLBaseFilter *base_filter = GST_GL_BASE_FILTER (bt); GstGLContext *context; + GstCaps *ret; if (base_filter->display && !gst_gl_base_filter_find_gl_context (base_filter)) return NULL; - context = GST_GL_BASE_FILTER (bt)->context; + context = gst_gl_base_filter_get_gl_context (base_filter); if (!convert->convert && context) convert->convert = gst_gl_color_convert_new (context); - return gst_gl_color_convert_transform_caps (context, direction, caps, filter); + ret = gst_gl_color_convert_transform_caps (context, direction, caps, filter); + + gst_clear_object (&context); + + return ret; } static gboolean diff --git a/gst-libs/gst/gl/gstglbasefilter.c b/gst-libs/gst/gl/gstglbasefilter.c index 6b2f88a..1244653 100644 --- a/gst-libs/gst/gl/gstglbasefilter.c +++ b/gst-libs/gst/gl/gstglbasefilter.c @@ -48,6 +48,9 @@ struct _GstGLBaseFilterPrivate gboolean gl_result; gboolean gl_started; + + GRecMutex context_lock; + gboolean new_gl_context; }; /* Properties */ @@ -91,6 +94,8 @@ static void gst_gl_base_filter_gl_stop (GstGLContext * context, gpointer data); static gboolean gst_gl_base_filter_default_gl_start (GstGLBaseFilter * filter); static void gst_gl_base_filter_default_gl_stop (GstGLBaseFilter * filter); +static gboolean gst_gl_base_filter_find_gl_context_unlocked (GstGLBaseFilter * + filter); static void gst_gl_base_filter_class_init (GstGLBaseFilterClass * klass) { @@ -131,6 +136,8 @@ gst_gl_base_filter_init (GstGLBaseFilter * filter) gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (filter), TRUE); filter->priv = gst_gl_base_filter_get_instance_private (filter); + + g_rec_mutex_init (&filter->priv->context_lock); } static void @@ -141,6 +148,8 @@ gst_gl_base_filter_finalize (GObject * object) gst_caps_replace (&filter->in_caps, NULL); gst_caps_replace (&filter->out_caps, NULL); + g_rec_mutex_clear (&filter->priv->context_lock); + G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -171,32 +180,30 @@ gst_gl_base_filter_get_property (GObject * object, guint prop_id, } } -static void -gst_gl_base_filter_set_context (GstElement * element, GstContext * context) -{ - GstGLBaseFilter *filter = GST_GL_BASE_FILTER (element); - GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter); - - GST_OBJECT_LOCK (filter); - gst_gl_handle_set_context (element, context, &filter->display, - &filter->priv->other_context); - if (filter->display) - gst_gl_display_filter_gl_api (filter->display, - filter_class->supported_gl_api); - GST_OBJECT_UNLOCK (filter); - - GST_ELEMENT_CLASS (parent_class)->set_context (element, context); -} - static gboolean _find_local_gl_context (GstGLBaseFilter * filter) { + GstGLContext *context = filter->context; + if (gst_gl_query_local_gl_context (GST_ELEMENT (filter), GST_PAD_SRC, - &filter->context)) - return TRUE; + &context)) { + if (context->display == filter->display) { + filter->context = context; + return TRUE; + } + if (context != filter->context) + gst_clear_object (&context); + } + context = filter->context; if (gst_gl_query_local_gl_context (GST_ELEMENT (filter), GST_PAD_SINK, - &filter->context)) - return TRUE; + &context)) { + if (context->display == filter->display) { + filter->context = context; + return TRUE; + } + if (context != filter->context) + gst_clear_object (&context); + } return FALSE; } @@ -211,7 +218,9 @@ gst_gl_base_filter_query (GstBaseTransform * trans, GstPadDirection direction, { if (direction == GST_PAD_SINK && gst_base_transform_is_passthrough (trans)) { + g_rec_mutex_lock (&filter->priv->context_lock); _find_local_gl_context (filter); + g_rec_mutex_unlock (&filter->priv->context_lock); return gst_pad_peer_query (GST_BASE_TRANSFORM_SRC_PAD (trans), query); } @@ -220,10 +229,10 @@ gst_gl_base_filter_query (GstBaseTransform * trans, GstPadDirection direction, case GST_QUERY_CONTEXT: { gboolean ret; - GST_OBJECT_LOCK (filter); + g_rec_mutex_lock (&filter->priv->context_lock); ret = gst_gl_handle_context_query ((GstElement *) filter, query, filter->display, filter->context, filter->priv->other_context); - GST_OBJECT_UNLOCK (filter); + g_rec_mutex_unlock (&filter->priv->context_lock); if (ret) return TRUE; break; @@ -239,17 +248,14 @@ gst_gl_base_filter_query (GstBaseTransform * trans, GstPadDirection direction, static void gst_gl_base_filter_reset (GstGLBaseFilter * filter) { - GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter); - + g_rec_mutex_lock (&filter->priv->context_lock); if (filter->context) { - if (filter_class->gl_stop != NULL) { - gst_gl_context_thread_add (filter->context, gst_gl_base_filter_gl_stop, - filter); - } - + gst_gl_context_thread_add (filter->context, gst_gl_base_filter_gl_stop, + filter); gst_object_unref (filter->context); filter->context = NULL; } + g_rec_mutex_unlock (&filter->priv->context_lock); } static gboolean @@ -280,6 +286,7 @@ gst_gl_base_filter_gl_start (GstGLContext * context, gpointer data) GstGLBaseFilter *filter = GST_GL_BASE_FILTER (data); GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter); + GST_INFO_OBJECT (filter, "starting"); gst_gl_insert_debug_marker (filter->context, "starting element %s", GST_OBJECT_NAME (filter)); @@ -297,6 +304,7 @@ gst_gl_base_filter_gl_stop (GstGLContext * context, gpointer data) GstGLBaseFilter *filter = GST_GL_BASE_FILTER (data); GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter); + GST_INFO_OBJECT (filter, "stopping"); gst_gl_insert_debug_marker (filter->context, "stopping element %s", GST_OBJECT_NAME (filter)); @@ -311,33 +319,53 @@ _gl_set_caps (GstGLContext * context, GstGLBaseFilter * filter) { GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter); + GST_INFO_OBJECT (filter, "set GL caps input %" GST_PTR_FORMAT, + filter->in_caps); + GST_INFO_OBJECT (filter, "set GL caps output %" GST_PTR_FORMAT, + filter->out_caps); + if (filter_class->gl_set_caps) filter->priv->gl_result = filter_class->gl_set_caps (filter, filter->in_caps, filter->out_caps); } static gboolean -gst_gl_base_filter_decide_allocation (GstBaseTransform * trans, - GstQuery * query) +gl_set_caps_unlocked (GstGLBaseFilter * filter) { - GstGLBaseFilter *filter = GST_GL_BASE_FILTER (trans); GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter); - if (!gst_gl_base_filter_find_gl_context (filter)) - return FALSE; - if (filter_class->gl_set_caps) { gst_gl_context_thread_add (filter->context, (GstGLContextThreadFunc) _gl_set_caps, filter); - if (!filter->priv->gl_result) - goto error; + return filter->priv->gl_result; } + return TRUE; +} + +static gboolean +gst_gl_base_filter_decide_allocation (GstBaseTransform * trans, + GstQuery * query) +{ + GstGLBaseFilter *filter = GST_GL_BASE_FILTER (trans); + + g_rec_mutex_lock (&filter->priv->context_lock); + if (!gst_gl_base_filter_find_gl_context_unlocked (filter)) { + g_rec_mutex_unlock (&filter->priv->context_lock); + return FALSE; + } + + if (!gl_set_caps_unlocked (filter)) + goto error; + + g_rec_mutex_unlock (&filter->priv->context_lock); + return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans, query); error: { + g_rec_mutex_unlock (&filter->priv->context_lock); GST_ELEMENT_ERROR (trans, LIBRARY, INIT, ("Subclass failed to initialize."), (NULL)); return FALSE; @@ -389,6 +417,7 @@ gst_gl_base_filter_change_state (GstElement * element, switch (transition) { case GST_STATE_CHANGE_READY_TO_NULL: + g_rec_mutex_lock (&filter->priv->context_lock); if (filter->priv->other_context) { gst_object_unref (filter->priv->other_context); filter->priv->other_context = NULL; @@ -403,6 +432,7 @@ gst_gl_base_filter_change_state (GstElement * element, gst_object_unref (filter->context); filter->context = NULL; } + g_rec_mutex_unlock (&filter->priv->context_lock); break; default: break; @@ -411,21 +441,49 @@ gst_gl_base_filter_change_state (GstElement * element, return ret; } -/** - * gst_gl_base_filter_find_gl_context: - * @filter: a #GstGLBaseFilter - * - * Returns: Whether an OpenGL context could be retrieved or created successfully - * - * Since: 1.16 - */ -gboolean -gst_gl_base_filter_find_gl_context (GstGLBaseFilter * filter) +static void +gst_gl_base_filter_set_context (GstElement * element, GstContext * context) +{ + GstGLBaseFilter *filter = GST_GL_BASE_FILTER (element); + GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter); + GstGLDisplay *old_display, *new_display; + + g_rec_mutex_lock (&filter->priv->context_lock); + old_display = filter->display ? gst_object_ref (filter->display) : NULL; + gst_gl_handle_set_context (element, context, &filter->display, + &filter->priv->other_context); + if (filter->display) + gst_gl_display_filter_gl_api (filter->display, + filter_class->supported_gl_api); + new_display = filter->display ? gst_object_ref (filter->display) : NULL; + + if (old_display && new_display) { + if (old_display != new_display) { + gst_clear_object (&filter->context); + if (gst_gl_base_filter_find_gl_context_unlocked (filter)) { + if (filter->in_caps && filter->out_caps) { + gl_set_caps_unlocked (filter); + } + } + } + } + g_rec_mutex_unlock (&filter->priv->context_lock); + gst_clear_object (&old_display); + gst_clear_object (&new_display); + + GST_ELEMENT_CLASS (parent_class)->set_context (element, context); +} + +static gboolean +gst_gl_base_filter_find_gl_context_unlocked (GstGLBaseFilter * filter) { GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter); GError *error = NULL; gboolean new_context = FALSE; + GST_DEBUG_OBJECT (filter, "attempting to find an OpenGL context, existing %" + GST_PTR_FORMAT, filter->context); + if (!filter->context) new_context = TRUE; @@ -449,6 +507,8 @@ gst_gl_base_filter_find_gl_context (GstGLBaseFilter * filter) } while (!gst_gl_display_add_context (filter->display, filter->context)); GST_OBJECT_UNLOCK (filter->display); } + GST_INFO_OBJECT (filter, "found OpenGL context %" GST_PTR_FORMAT, + filter->context); if (new_context || !filter->priv->gl_started) { if (filter->priv->gl_started) @@ -499,3 +559,42 @@ error: return FALSE; } } + +/** + * gst_gl_base_filter_find_gl_context: + * @filter: a #GstGLBaseFilter + * + * Returns: Whether an OpenGL context could be retrieved or created successfully + * + * Since: 1.16 + */ +gboolean +gst_gl_base_filter_find_gl_context (GstGLBaseFilter * filter) +{ + gboolean ret; + g_rec_mutex_lock (&filter->priv->context_lock); + ret = gst_gl_base_filter_find_gl_context_unlocked (filter); + g_rec_mutex_unlock (&filter->priv->context_lock); + return ret; +} + +/** + * gst_gl_base_filter_get_gl_context: + * @filter: a #GstGLBaseFilter + * + * Returns: (transfer full) (nullable): the #GstGLContext found by @filter + * + * Since: 1.18 + */ +GstGLContext * +gst_gl_base_filter_get_gl_context (GstGLBaseFilter * filter) +{ + GstGLContext *ret; + + g_return_val_if_fail (GST_IS_GL_BASE_FILTER (filter), NULL); + + g_rec_mutex_lock (&filter->priv->context_lock); + ret = filter->context ? gst_object_ref (filter->context) : NULL; + g_rec_mutex_unlock (&filter->priv->context_lock); + return ret; +} diff --git a/gst-libs/gst/gl/gstglbasefilter.h b/gst-libs/gst/gl/gstglbasefilter.h index 15361ad..57fb3df 100644 --- a/gst-libs/gst/gl/gstglbasefilter.h +++ b/gst-libs/gst/gl/gstglbasefilter.h @@ -70,6 +70,8 @@ struct _GstGLBaseFilter * @gl_start: called in the GL thread to setup the element GL state. * @gl_stop: called in the GL thread to setup the element GL state. * @gl_set_caps: called in the GL thread when caps are set on @filter. + * Note: this will also be called when changing OpenGL contexts + * where #GstBaseTransform::set_caps may not. * * The base class for GStreamer GL Filter. */ @@ -90,6 +92,8 @@ struct _GstGLBaseFilterClass GST_GL_API gboolean gst_gl_base_filter_find_gl_context (GstGLBaseFilter * filter); +GST_GL_API +GstGLContext * gst_gl_base_filter_get_gl_context (GstGLBaseFilter * filter); G_END_DECLS diff --git a/tests/check/elements/glfilter.c b/tests/check/elements/glfilter.c new file mode 100644 index 0000000..0d8d676 --- /dev/null +++ b/tests/check/elements/glfilter.c @@ -0,0 +1,131 @@ +/* GStreamer + * + * Copyright (C) 2019 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 + +static void +replace_display (GstHarness * h) +{ + GstContext *new_context; + GstGLDisplay *new_display; + GstGLContext *expected, *gl_context; + GstBuffer *buf; + + /* replaces the GstGLDisplay used by @h with verification */ + + buf = gst_harness_create_buffer (h, 4); + buf = gst_harness_push_and_pull (h, buf); + fail_unless (buf != NULL); + gst_clear_buffer (&buf); + + g_object_get (G_OBJECT (h->element), "context", &gl_context, NULL); + fail_unless (gl_context != NULL); + gst_clear_object (&gl_context); + + new_display = gst_gl_display_new (); + fail_unless (gst_gl_display_create_context (new_display, NULL, &expected, + NULL)); + fail_unless (expected != NULL); + gst_gl_display_add_context (new_display, expected); + + new_context = gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE); + gst_context_set_gl_display (new_context, new_display); + + gst_element_set_context (h->element, new_context); + gst_context_unref (new_context); + new_context = NULL; + + buf = gst_harness_create_buffer (h, 4); + buf = gst_harness_push_and_pull (h, buf); + fail_unless (buf != NULL); + gst_clear_buffer (&buf); + + g_object_get (G_OBJECT (h->element), "context", &gl_context, NULL); + fail_unless (gl_context != NULL); + + fail_unless (gl_context == expected); + fail_unless (new_display == gl_context->display); + + gst_object_unref (expected); + gst_object_unref (gl_context); + gst_object_unref (new_display); +} + +GST_START_TEST (test_glupload_display_replace) +{ + GstHarness *upload; + + upload = gst_harness_new ("glupload"); + gst_harness_set_caps_str (upload, "video/x-raw,format=RGBA,width=1,height=1", + "video/x-raw(memory:GLMemory),format=RGBA,width=1,height=1"); + + replace_display (upload); + + gst_harness_teardown (upload); +} + +GST_END_TEST; + +GST_START_TEST (test_glcolorconvert_display_replace) +{ + GstHarness *convert; + + convert = gst_harness_new ("glcolorconvert"); + gst_harness_set_caps_str (convert, + "video/x-raw(memory:GLMemory),format=RGBA,width=1,height=1,texture-target=2D", + "video/x-raw(memory:GLMemory),format=RGBA,width=1,height=1,texture-target=2D"); + + replace_display (convert); + + gst_harness_teardown (convert); +} + +GST_END_TEST; + +static Suite * +glfilter_suite (void) +{ + Suite *s = suite_create ("glfilter"); + TCase *tc = tcase_create ("general"); + + tcase_add_test (tc, test_glupload_display_replace); + tcase_add_test (tc, test_glcolorconvert_display_replace); + suite_add_tcase (s, tc); + + return s; +} + +int +main (int argc, char **argv) +{ + Suite *s; + g_setenv ("GST_GL_XINITTHREADS", "1", TRUE); + gst_check_init (&argc, &argv); + s = glfilter_suite (); + return gst_check_run_suite (s, "glfilter", __FILE__); +} diff --git a/tests/check/meson.build b/tests/check/meson.build index c2b1ffe..cc064f3 100644 --- a/tests/check/meson.build +++ b/tests/check/meson.build @@ -100,6 +100,7 @@ if build_gstgl and host_machine.system() != 'windows' [ 'elements/glimagesink.c', not build_gstgl, [gstgl_dep]], [ 'elements/glbin.c', not build_gstgl ], [ 'pipelines/gl-launch-lines.c', not build_gstgl ], + [ 'elements/glfilter.c', not build_gstgl, [gstgl_dep]], ] endif