diff --git a/gst-libs/gst/gl/gstgldisplay.c b/gst-libs/gst/gl/gstgldisplay.c index 0bb00d52ef..198e766f26 100644 --- a/gst-libs/gst/gl/gstgldisplay.c +++ b/gst-libs/gst/gl/gstgldisplay.c @@ -28,11 +28,13 @@ #include "gl.h" #include "gstgldisplay.h" +GST_DEBUG_CATEGORY_STATIC (gst_context); GST_DEBUG_CATEGORY_STATIC (gst_gl_display_debug); #define GST_CAT_DEFAULT gst_gl_display_debug #define DEBUG_INIT \ - GST_DEBUG_CATEGORY_INIT (gst_gl_display_debug, "gldisplay", 0, "opengl display"); + GST_DEBUG_CATEGORY_INIT (gst_gl_display_debug, "gldisplay", 0, "opengl display"); \ + GST_DEBUG_CATEGORY_GET (gst_context, "GST_CONTEXT"); G_DEFINE_TYPE_WITH_CODE (GstGLDisplay, gst_gl_display, G_TYPE_OBJECT, DEBUG_INIT); @@ -66,6 +68,8 @@ gst_gl_display_init (GstGLDisplay * display) display->gl_api = GST_GL_API_NONE; + GST_TRACE ("init %p", display); + gst_gl_memory_init (); } @@ -79,6 +83,8 @@ gst_gl_display_finalize (GObject * object) display->context = NULL; } + GST_TRACE ("finalize %p", object); + G_OBJECT_CLASS (gst_gl_display_parent_class)->finalize (object); } @@ -142,6 +148,11 @@ gst_context_set_gl_display (GstContext * context, GstGLDisplay * display) { GstStructure *s; + g_return_if_fail (context != NULL); + + GST_CAT_LOG (gst_context, "setting GstGLDisplay(%p) on context(%p)", display, + context); + s = gst_context_writable_structure (context); gst_structure_set (s, GST_GL_DISPLAY_CONTEXT_TYPE, GST_TYPE_GL_DISPLAY, display, NULL); @@ -151,8 +162,17 @@ gboolean gst_context_get_gl_display (GstContext * context, GstGLDisplay ** display) { const GstStructure *s; + gboolean ret; + + g_return_val_if_fail (display != NULL, FALSE); + g_return_val_if_fail (context != NULL, FALSE); s = gst_context_get_structure (context); - return gst_structure_get (s, GST_GL_DISPLAY_CONTEXT_TYPE, + ret = gst_structure_get (s, GST_GL_DISPLAY_CONTEXT_TYPE, GST_TYPE_GL_DISPLAY, display, NULL); + + GST_CAT_LOG (gst_context, "got GstGLDisplay(%p) from context(%p)", *display, + context); + + return ret; } diff --git a/gst-libs/gst/gl/gstglfilter.c b/gst-libs/gst/gl/gstglfilter.c index e31eff24d0..e8e962659f 100644 --- a/gst-libs/gst/gl/gstglfilter.c +++ b/gst-libs/gst/gl/gstglfilter.c @@ -62,6 +62,8 @@ static void gst_gl_filter_set_property (GObject * object, guint prop_id, static void gst_gl_filter_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); +static void gst_gl_filter_set_context (GstElement * element, + GstContext * context); static gboolean gst_gl_filter_query (GstBaseTransform * trans, GstPadDirection direction, GstQuery * query); static GstCaps *gst_gl_filter_transform_caps (GstBaseTransform * bt, @@ -112,6 +114,8 @@ gst_gl_filter_class_init (GstGLFilterClass * klass) gst_gl_filter_decide_allocation; GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size = gst_gl_filter_get_unit_size; + element_class->set_context = gst_gl_filter_set_context; + g_object_class_install_property (gobject_class, PROP_OTHER_CONTEXT, g_param_spec_object ("other-context", "External OpenGL context", @@ -176,6 +180,14 @@ gst_gl_filter_get_property (GObject * object, guint prop_id, } } +static void +gst_gl_filter_set_context (GstElement * element, GstContext * context) +{ + GstGLFilter *filter = GST_GL_FILTER (element); + + gst_gl_handle_set_context (element, context, &filter->display); +} + static gboolean gst_gl_filter_query (GstBaseTransform * trans, GstPadDirection direction, GstQuery * query) @@ -186,13 +198,19 @@ gst_gl_filter_query (GstBaseTransform * trans, GstPadDirection direction, filter = GST_GL_FILTER (trans); switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONTEXT: + { + res = gst_gl_handle_context_query ((GstElement *) filter, query, + &filter->display); + break; + } case GST_QUERY_CUSTOM: { GstStructure *structure = gst_query_writable_structure (query); if (direction == GST_PAD_SINK && - gst_structure_has_name (structure, "gstgldisplay")) { - gst_structure_set (structure, "gstgldisplay", G_TYPE_POINTER, - filter->display, NULL); + gst_structure_has_name (structure, "gstglcontext")) { + gst_structure_set (structure, "gstglcontext", G_TYPE_POINTER, + filter->context, NULL); res = TRUE; } else res = @@ -243,6 +261,10 @@ gst_gl_filter_reset (GstGLFilter * filter) if (filter->other_context) gst_object_unref (filter->other_context); filter->other_context = NULL; + + if (filter->context) + gst_object_unref (filter->context); + filter->context = NULL; } static gboolean @@ -251,28 +273,29 @@ gst_gl_filter_start (GstBaseTransform * bt) GstGLFilter *filter = GST_GL_FILTER (bt); GstGLFilterClass *filter_class = GST_GL_FILTER_GET_CLASS (filter); GstStructure *structure; - GstQuery *display_query; + GstQuery *context_query; const GValue *id_value; - structure = gst_structure_new_empty ("gstgldisplay"); - display_query = gst_query_new_custom (GST_QUERY_CUSTOM, structure); + if (!gst_gl_ensure_display (filter, &filter->display)) + return FALSE; - if (!gst_pad_peer_query (bt->srcpad, display_query)) { + structure = gst_structure_new_empty ("gstglcontext"); + context_query = gst_query_new_custom (GST_QUERY_CUSTOM, structure); + + if (!gst_pad_peer_query (bt->srcpad, context_query)) { GST_WARNING - ("Could not query GstGLDisplay from downstream (peer query failed)"); + ("Could not query GstGLContext from downstream (peer query failed)"); } - id_value = gst_structure_get_value (structure, "gstgldisplay"); + id_value = gst_structure_get_value (structure, "gstglcontext"); if (G_VALUE_HOLDS_POINTER (id_value)) { /* at least one gl element is after in our gl chain */ - filter->display = - gst_object_ref (GST_GL_DISPLAY (g_value_get_pointer (id_value))); - filter->context = gst_gl_display_get_context (filter->display); + filter->context = + gst_object_ref (GST_GL_CONTEXT (g_value_get_pointer (id_value))); } else { GError *error = NULL; - GST_INFO ("Creating GstGLDisplay"); - filter->display = gst_gl_display_new (); + GST_INFO ("Creating GstGLContext"); filter->context = gst_gl_context_new (filter->display); if (!gst_gl_context_create (filter->context, filter->other_context, &error)) { @@ -282,6 +305,8 @@ gst_gl_filter_start (GstBaseTransform * bt) } } + gst_query_unref (context_query); + if (filter_class->onStart) filter_class->onStart (filter); @@ -1067,6 +1092,9 @@ gst_gl_filter_transform (GstBaseTransform * bt, GstBuffer * inbuf, filter = GST_GL_FILTER (bt); filter_class = GST_GL_FILTER_GET_CLASS (bt); + if (!gst_gl_ensure_display (filter, &filter->display)) + return GST_FLOW_NOT_NEGOTIATED; + g_assert (filter_class->filter || filter_class->filter_texture); if (filter_class->filter) diff --git a/gst-libs/gst/gl/gstglfilter.h b/gst-libs/gst/gl/gstglfilter.h index c984eaacc8..e5dec42bdd 100644 --- a/gst-libs/gst/gl/gstglfilter.h +++ b/gst-libs/gst/gl/gstglfilter.h @@ -65,7 +65,6 @@ struct _GstGLFilter GstBufferPool *pool; GstGLDisplay *display; - GstGLContext *context; GstVideoInfo in_info; GstVideoInfo out_info; @@ -81,6 +80,7 @@ struct _GstGLFilter GstGLShader *default_shader; + GstGLContext *context; GstGLContext *other_context; }; diff --git a/gst-libs/gst/gl/gstglmixer.c b/gst-libs/gst/gl/gstglmixer.c index e5251e47b1..92678fbdc8 100644 --- a/gst-libs/gst/gl/gstglmixer.c +++ b/gst-libs/gst/gl/gstglmixer.c @@ -60,6 +60,8 @@ static gboolean gst_gl_mixer_sink_event (GstCollectPads * pads, GstCollectData * cdata, GstEvent * event, GstGLMixer * mix); static gboolean gst_gl_mixer_src_setcaps (GstPad * pad, GstGLMixer * mix, GstCaps * caps); +static void gst_gl_mixer_set_context (GstElement * element, + GstContext * context); enum { @@ -346,15 +348,21 @@ gst_gl_mixer_sink_query (GstCollectPads * pads, GstCollectData * data, ret = TRUE; break; } + case GST_QUERY_CONTEXT: + { + ret = gst_gl_handle_context_query ((GstElement *) mix, query, + &mix->display); + break; + } case GST_QUERY_CUSTOM: { /* mix is a sink in terms of gl chain, so we are sharing the gldisplay that * comes from src pad with every display of the sink pads */ GstStructure *structure = gst_query_writable_structure (query); - if (gst_structure_has_name (structure, "gstgldisplay")) { - gst_structure_set (structure, "gstgldisplay", G_TYPE_POINTER, - mix->display, NULL); + if (gst_structure_has_name (structure, "gstglcontext")) { + gst_structure_set (structure, "gstglcontext", G_TYPE_POINTER, + mix->context, NULL); } else { ret = gst_collect_pads_query_default (pads, data, query, FALSE); } @@ -513,6 +521,7 @@ gst_gl_mixer_class_init (GstGLMixerClass * klass) GST_DEBUG_FUNCPTR (gst_gl_mixer_request_new_pad); element_class->release_pad = GST_DEBUG_FUNCPTR (gst_gl_mixer_release_pad); element_class->change_state = GST_DEBUG_FUNCPTR (gst_gl_mixer_change_state); + element_class->set_context = GST_DEBUG_FUNCPTR (gst_gl_mixer_set_context); /* Register the pad class */ g_type_class_ref (GST_TYPE_GL_MIXER_PAD); @@ -845,6 +854,14 @@ gst_gl_mixer_read_qos (GstGLMixer * mix, gdouble * proportion, GST_OBJECT_UNLOCK (mix); } +static void +gst_gl_mixer_set_context (GstElement * element, GstContext * context) +{ + GstGLMixer *mix = GST_GL_MIXER (element); + + gst_gl_handle_set_context (element, context, &mix->display); +} + static gboolean gst_gl_mixer_src_query (GstPad * pad, GstObject * parent, GstQuery * query) { @@ -852,6 +869,12 @@ gst_gl_mixer_src_query (GstPad * pad, GstObject * parent, GstQuery * query) gboolean res = FALSE; switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONTEXT: + { + res = gst_gl_handle_context_query ((GstElement *) mix, query, + &mix->display); + break; + } case GST_QUERY_POSITION: { GstFormat format; @@ -918,27 +941,29 @@ gst_gl_mixer_activate (GstGLMixer * mix, gboolean activate) { if (activate) { GstStructure *structure; - GstQuery *display_query; + GstQuery *context_query; const GValue *id_value; - structure = gst_structure_new_empty ("gstgldisplay"); - display_query = gst_query_new_custom (GST_QUERY_CUSTOM, structure); - - if (!gst_pad_peer_query (mix->srcpad, display_query)) { - GST_WARNING - ("Could not query GstGLDisplay from downstream (peer query failed)"); + if (!gst_gl_ensure_display (mix, &mix->display)) { + return FALSE; } - id_value = gst_structure_get_value (structure, "gstgldisplay"); + structure = gst_structure_new_empty ("gstglcontext"); + context_query = gst_query_new_custom (GST_QUERY_CUSTOM, structure); + + if (!gst_pad_peer_query (mix->srcpad, context_query)) { + GST_WARNING + ("Could not query GstGLContext from downstream (peer query failed)"); + } + + id_value = gst_structure_get_value (structure, "gstglcontext"); if (G_VALUE_HOLDS_POINTER (id_value)) { - mix->display = - gst_object_ref (GST_GL_DISPLAY (g_value_get_pointer (id_value))); - mix->context = gst_gl_display_get_context (mix->display); + mix->context = + gst_object_ref (GST_GL_CONTEXT (g_value_get_pointer (id_value))); } else { GError *error = NULL; GST_INFO ("Creating GstGLDisplay"); - mix->display = gst_gl_display_new (); mix->context = gst_gl_context_new (mix->display); gst_gl_display_set_context (mix->display, mix->context); diff --git a/gst-libs/gst/gl/gstglutils.c b/gst-libs/gst/gl/gstglutils.c index 0935a89927..2634547903 100644 --- a/gst-libs/gst/gl/gstglutils.c +++ b/gst-libs/gst/gl/gstglutils.c @@ -365,3 +365,227 @@ gst_gl_context_del_shader (GstGLContext * context, GstGLShader * shader) { gst_object_unref (shader); } + +static gboolean +gst_gl_display_found (GstElement * element, GstGLDisplay * display) +{ + if (display) { + GST_INFO_OBJECT (element, "already have a display (%p)", display); + return TRUE; + } + + return FALSE; +} + +GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT); + +static gboolean +context_pad_query (const GValue * item, GValue * value, gpointer user_data) +{ + GstPad *pad = g_value_get_object (item); + GstQuery *query = user_data; + gboolean res; + + res = gst_pad_peer_query (pad, query); + + if (res) { + g_value_set_boolean (value, TRUE); + return FALSE; + } + + GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "context pad peer query failed"); + return TRUE; +} + +static gboolean +run_context_query (GstElement * element, GstQuery * query, + GstPadDirection direction) +{ + GstIterator *it; + GstIteratorFoldFunction func = context_pad_query; + GValue res = { 0 }; + + g_value_init (&res, G_TYPE_BOOLEAN); + g_value_set_boolean (&res, FALSE); + + /* Ask neighbor */ + if (direction == GST_PAD_SRC) + it = gst_element_iterate_src_pads (element); + else + it = gst_element_iterate_sink_pads (element); + + while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC) + gst_iterator_resync (it); + + gst_iterator_free (it); + + return g_value_get_boolean (&res); +} + +static void +gst_gl_display_context_prepare (GstElement * element, + GstGLDisplay ** display_ptr) +{ + GstContext *ctxt; + GstQuery *query; + + if (!GST_CAT_CONTEXT) + GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT"); + + /* 2a) Query downstream with GST_QUERY_CONTEXT for the context and + * check if downstream already has a context of the specific type + * 2b) Query upstream as above. + */ + ctxt = NULL; + query = gst_query_new_context (GST_GL_DISPLAY_CONTEXT_TYPE); + if (run_context_query (element, query, GST_PAD_SRC)) { + GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element, + "found context (%p) in downstream query", ctxt); + gst_query_parse_context (query, &ctxt); + } else if (run_context_query (element, query, GST_PAD_SINK)) { + GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element, + "found context (%p) in upstream query", ctxt); + gst_query_parse_context (query, &ctxt); + } else { + /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with + * the required context type and afterwards check if a + * usable context was set now as in 1). The message could + * be handled by the parent bins of the element and the + * application. + */ + GstMessage *msg; + + GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element, + "posting need context message"); + msg = gst_message_new_need_context (GST_OBJECT_CAST (element), + GST_GL_DISPLAY_CONTEXT_TYPE); + gst_element_post_message (element, msg); + } + + /* + * Whomever responds to the need-context message performs a + * GstElement::set_context() with the required context in which the element + * is required to update the display_ptr or call gst_gl_handle_set_context(). + */ + if (ctxt) { + if (gst_context_has_context_type (ctxt, GST_GL_DISPLAY_CONTEXT_TYPE)) { + gst_context_get_gl_display (ctxt, display_ptr); + } + } + + gst_query_unref (query); +} + +/* 4) Create a context by itself and post a GST_MESSAGE_HAVE_CONTEXT + * message. + */ +static void +gst_gl_display_context_propagate (GstElement * element, GstGLDisplay * display) +{ + GstContext *context; + GstMessage *msg; + + if (!display) { + GST_ERROR_OBJECT (element, "Could not get GL display connection"); + return; + } + + context = gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE); + gst_context_set_gl_display (context, display); + + GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element, + "posting have context (%p) message with display (%p)", context, display); + msg = gst_message_new_have_context (GST_OBJECT_CAST (element), context); + gst_element_post_message (GST_ELEMENT_CAST (element), msg); +} + +gboolean +gst_gl_ensure_display (gpointer element, GstGLDisplay ** display_ptr) +{ + GstGLDisplay *display; + + g_return_val_if_fail (element != NULL, FALSE); + g_return_val_if_fail (display_ptr != NULL, FALSE); + + /* 1) Check if the element already has a context of the specific + * type. + */ + display = *display_ptr; + if (gst_gl_display_found (element, display)) + return TRUE; + + gst_gl_display_context_prepare (element, display_ptr); + + /* Neighbour found and it updated the display */ + if (gst_gl_display_found (element, *display_ptr)) + return TRUE; + + /* If no neighboor, or application not interested, use system default */ + display = gst_gl_display_new (); + + *display_ptr = display; + + gst_gl_display_context_propagate (element, display); + + return display != NULL; +} + +gboolean +gst_gl_handle_set_context (GstElement * element, GstContext * context, + GstGLDisplay ** display) +{ + GstGLDisplay *replacement = NULL; + const gchar *context_type; + + g_return_val_if_fail (display, FALSE); + + if (!context) + return FALSE; + + context_type = gst_context_get_context_type (context); + + if (g_strcmp0 (context_type, GST_GL_DISPLAY_CONTEXT_TYPE) == 0) { + if (!gst_context_get_gl_display (context, &replacement)) { + GST_WARNING_OBJECT (element, "Failed to get display from context"); + return FALSE; + } + } + + if (replacement) + gst_object_replace ((GstObject **) display, (GstObject *) replacement); + + return TRUE; +} + +gboolean +gst_gl_handle_context_query (GstElement * element, GstQuery * query, + GstGLDisplay ** display) +{ + gboolean res = FALSE; + const gchar *context_type; + GstContext *context, *old_context; + + g_return_val_if_fail (element != NULL, FALSE); + g_return_val_if_fail (query != NULL, FALSE); + g_return_val_if_fail (display != NULL, FALSE); + + gst_query_parse_context_type (query, &context_type); + + if (g_strcmp0 (context_type, GST_GL_DISPLAY_CONTEXT_TYPE) == 0) { + + gst_query_parse_context (query, &old_context); + + if (old_context) + context = gst_context_copy (old_context); + else + context = gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE); + + gst_context_set_gl_display (context, *display); + gst_query_set_context (query, context); + gst_context_unref (context); + + res = *display != NULL; + } + + return res; +} diff --git a/gst-libs/gst/gl/gstglutils.h b/gst-libs/gst/gl/gstglutils.h index 35717c0b30..7b481fc0f8 100644 --- a/gst-libs/gst/gl/gstglutils.h +++ b/gst-libs/gst/gl/gstglutils.h @@ -102,4 +102,10 @@ gboolean gst_gl_context_check_framebuffer_status (GstGLContext * context); void gst_gl_context_set_error (GstGLContext * context, const char * format, ...); gchar *gst_gl_context_get_error (void); +gboolean gst_gl_ensure_display (gpointer element, GstGLDisplay **display_ptr); +gboolean gst_gl_handle_set_context (GstElement * element, GstContext * context, + GstGLDisplay ** display); +gboolean gst_gl_handle_context_query (GstElement * element, GstQuery * query, + GstGLDisplay ** display); + #endif /* __GST_GL_UTILS_H__ */