From f7cfb422f16d1f89d0cf51abc21a66fc307838e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Manuel=20J=C3=A1quez=20Leal?= Date: Sun, 2 Aug 2020 15:51:08 +0200 Subject: [PATCH] va: context: instanciate VA display through GstContext Add all the machinery to instanciate VA display through GstContext, thus all va elements can share the same display and the user can set a custom one. --- sys/va/gstvah264dec.c | 56 ++++++- sys/va/gstvautils.c | 344 ++++++++++++++++++++++++++++++++++++++++++ sys/va/gstvautils.h | 48 ++++++ sys/va/meson.build | 1 + 4 files changed, 442 insertions(+), 7 deletions(-) create mode 100644 sys/va/gstvautils.c create mode 100644 sys/va/gstvautils.h diff --git a/sys/va/gstvah264dec.c b/sys/va/gstvah264dec.c index 320f40c483..471e3da6cd 100644 --- a/sys/va/gstvah264dec.c +++ b/sys/va/gstvah264dec.c @@ -33,6 +33,7 @@ #include "gstvadisplay_drm.h" #include "gstvapool.h" #include "gstvaprofile.h" +#include "gstvautils.h" #include "gstvavideoformat.h" GST_DEBUG_CATEGORY_STATIC (gst_va_h264dec_debug); @@ -56,6 +57,7 @@ struct _GstVaH264Dec { GstH264Decoder parent; + GstVaDisplay *display; GstVaDecoder *decoder; GstBufferPool *other_pool; @@ -747,19 +749,15 @@ gst_va_h264_dec_new_sequence (GstH264Decoder * decoder, const GstH264SPS * sps, static gboolean gst_va_h264_dec_open (GstVideoDecoder * decoder) { - GstVaDisplay *display; GstVaH264Dec *self = GST_VA_H264_DEC (decoder); GstVaH264DecClass *klass = GST_VA_H264_DEC_GET_CLASS (decoder); - /* @XXX(victor): query pipeline's context */ - display = gst_va_display_drm_new_from_path (klass->render_device_path); - if (!display) + if (!gst_va_ensure_element_data (decoder, klass->render_device_path, + &self->display)) return FALSE; if (!self->decoder) - self->decoder = gst_va_decoder_new (display, H264); - - gst_object_unref (display); + self->decoder = gst_va_decoder_new (self->display, H264); return (self->decoder != NULL); } @@ -770,6 +768,7 @@ gst_va_h264_dec_close (GstVideoDecoder * decoder) GstVaH264Dec *self = GST_VA_H264_DEC (decoder); gst_clear_object (&self->decoder); + gst_clear_object (&self->display); return TRUE; } @@ -837,6 +836,10 @@ gst_va_h264_dec_src_query (GstVideoDecoder * decoder, GstQuery * query) gboolean ret = FALSE; switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONTEXT:{ + return gst_va_handle_context_query (GST_ELEMENT_CAST (self), query, + self->display); + } case GST_QUERY_CAPS:{ GstCaps *caps = NULL, *tmp, *filter = NULL; @@ -867,6 +870,19 @@ gst_va_h264_dec_src_query (GstVideoDecoder * decoder, GstQuery * query) return ret; } +static gboolean +gst_va_h264_dec_sink_query (GstVideoDecoder * decoder, GstQuery * query) +{ + GstVaH264Dec *self = GST_VA_H264_DEC (decoder); + + if (GST_QUERY_TYPE (query) == GST_QUERY_CONTEXT) { + return gst_va_handle_context_query (GST_ELEMENT_CAST (self), query, + self->display); + } + + return GST_VIDEO_DECODER_CLASS (parent_class)->sink_query (decoder, query); +} + static gboolean gst_va_h264_dec_stop (GstVideoDecoder * decoder) { @@ -1276,6 +1292,29 @@ wrong_caps: } } +static void +gst_va_h264_dec_set_context (GstElement * element, GstContext * context) +{ + GstVaDisplay *old_display, *new_display; + GstVaH264Dec *self = GST_VA_H264_DEC (element); + GstVaH264DecClass *klass = GST_VA_H264_DEC_GET_CLASS (self); + + old_display = self->display ? gst_object_ref (self->display) : NULL; + gst_va_handle_set_context (element, context, klass->render_device_path, + &self->display); + new_display = self->display ? gst_object_ref (self->display) : NULL; + + if (old_display && new_display && old_display != new_display && self->decoder) { + GST_ELEMENT_WARNING (element, RESOURCE, BUSY, + ("Can't replace VA display while operating"), (NULL)); + } + + gst_clear_object (&old_display); + gst_clear_object (&new_display); + + GST_ELEMENT_CLASS (parent_class)->set_context (element, context); +} + static void gst_va_h264_dec_dispose (GObject * object) { @@ -1328,11 +1367,14 @@ gst_va_h264_dec_class_init (gpointer g_class, gpointer class_data) gobject_class->dispose = gst_va_h264_dec_dispose; + element_class->set_context = gst_va_h264_dec_set_context; + decoder_class->open = GST_DEBUG_FUNCPTR (gst_va_h264_dec_open); decoder_class->close = GST_DEBUG_FUNCPTR (gst_va_h264_dec_close); decoder_class->stop = GST_DEBUG_FUNCPTR (gst_va_h264_dec_stop); decoder_class->getcaps = GST_DEBUG_FUNCPTR (gst_va_h264_dec_sink_getcaps); decoder_class->src_query = GST_DEBUG_FUNCPTR (gst_va_h264_dec_src_query); + decoder_class->sink_query = GST_DEBUG_FUNCPTR (gst_va_h264_dec_sink_query); decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_va_h264_dec_negotiate); decoder_class->decide_allocation = GST_DEBUG_FUNCPTR (gst_va_h264_dec_decide_allocation); diff --git a/sys/va/gstvautils.c b/sys/va/gstvautils.c new file mode 100644 index 0000000000..3a0b0d2627 --- /dev/null +++ b/sys/va/gstvautils.c @@ -0,0 +1,344 @@ +/* GStreamer + * Copyright (C) 2020 Igalia, S.L. + * Author: Víctor Jáquez + * + * 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 "gstvadisplay_drm.h" +#include "gstvadisplay_wrapped.h" +#include "gstvautils.h" + +GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT); + +static void +_init_context_debug (void) +{ +#ifndef GST_DISABLE_GST_DEBUG + static volatile gsize _init = 0; + + if (g_once_init_enter (&_init)) { + GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT"); + g_once_init_leave (&_init, 1); + } +#endif +} + +static gboolean +gst_va_display_found (GstElement * element, GstVaDisplay * display) +{ + _init_context_debug (); + + if (display) { + GST_CAT_LOG_OBJECT (GST_CAT_CONTEXT, element, "already have a display (%p)", + display); + return TRUE; + } + + return FALSE; +} + +static gboolean +pad_query (const GValue * item, GValue * value, gpointer user_data) +{ + GstPad *pad = g_value_get_object (item); + GstQuery *query = user_data; + gboolean res; + + _init_context_debug (); + + 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, "pad peer query failed"); + return TRUE; +} + +static gboolean +_gst_va_run_query (GstElement * element, GstQuery * query, + GstPadDirection direction) +{ + GstIterator *it; + GstIteratorFoldFunction func = pad_query; + GValue res = G_VALUE_INIT; + + g_value_init (&res, G_TYPE_BOOLEAN); + g_value_set_boolean (&res, FALSE); + + 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_context_query (GstElement * element, const gchar * context_type) +{ + GstQuery *query; + GstContext *ctxt = NULL; + + _init_context_debug (); + + /* 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. + */ + query = gst_query_new_context (context_type); + if (_gst_va_run_query (element, query, GST_PAD_SRC)) { + gst_query_parse_context (query, &ctxt); + GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element, + "found context (%p) in downstream query", ctxt); + gst_element_set_context (element, ctxt); + } else if (_gst_va_run_query (element, query, GST_PAD_SINK)) { + gst_query_parse_context (query, &ctxt); + GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element, + "found context (%p) in upstream query", ctxt); + gst_element_set_context (element, 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), + 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_va_handle_set_context(). + */ + + gst_query_unref (query); +} + +/* 4) Create a context by itself and post a GST_MESSAGE_HAVE_CONTEXT + * message. + */ +void +gst_va_element_propagate_display_context (GstElement * element, + GstVaDisplay * display) +{ + GstContext *ctxt; + GstMessage *msg; + + if (!display) { + GST_ERROR_OBJECT (element, "Could not get GL display connection"); + return; + } + + _init_context_debug (); + + ctxt = gst_context_new ("gst.va.display.handle", TRUE); + gst_context_set_va_display (ctxt, display); + + GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element, + "post have context (%p) message with display (%p)", ctxt, display); + msg = gst_message_new_have_context (GST_OBJECT_CAST (element), ctxt); + gst_element_post_message (element, msg); +} + +gboolean +gst_va_ensure_element_data (gpointer element, const gchar * render_device_path, + GstVaDisplay ** display_ptr) +{ + GstVaDisplay *display; + + g_return_val_if_fail (element, FALSE); + g_return_val_if_fail (render_device_path, FALSE); + g_return_val_if_fail (display_ptr, FALSE); + + /* 1) Check if the element already has a context of the specific + * type. + */ + if (gst_va_display_found (element, *display_ptr)) + goto done; + + _gst_context_query (element, "gst.va.display.handle"); + + /* Neighbour found and it updated the display */ + if (gst_va_display_found (element, *display_ptr)) + goto done; + + /* If no neighbor, or application not interested, use drm */ + display = gst_va_display_drm_new_from_path (render_device_path); + + *display_ptr = display; + + gst_va_element_propagate_display_context (element, display); + +done: + return *display_ptr != NULL; +} + +gboolean +gst_va_handle_set_context (GstElement * element, GstContext * context, + const gchar * render_device_path, GstVaDisplay ** display_ptr) +{ + GstVaDisplay *display_replacement = NULL; + const gchar *context_type, *type_name; + + g_return_val_if_fail (display_ptr, FALSE); + + if (!context) + return FALSE; + + context_type = gst_context_get_context_type (context); + + if (g_strcmp0 (context_type, "gst.va.display.handle") == 0) { + type_name = G_OBJECT_TYPE_NAME (element); + if (!gst_context_get_va_display (context, type_name, render_device_path, + &display_replacement)) { + GST_CAT_WARNING_OBJECT (GST_CAT_CONTEXT, element, + "Failed to get display from context"); + return FALSE; + } + } + + if (display_replacement) { + GstVaDisplay *old = *display_ptr; + *display_ptr = display_replacement; + + if (old) + gst_object_unref (old); + } + + return TRUE; +} + +gboolean +gst_va_handle_context_query (GstElement * element, GstQuery * query, + GstVaDisplay * display) +{ + const gchar *context_type; + GstContext *ctxt, *old_ctxt; + + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + g_return_val_if_fail (GST_IS_QUERY (query), FALSE); + g_return_val_if_fail (display && GST_IS_VA_DISPLAY (display), FALSE); + + GST_CAT_LOG_OBJECT (GST_CAT_CONTEXT, element, + "handle context query %" GST_PTR_FORMAT, query); + gst_query_parse_context_type (query, &context_type); + + if (!display || g_strcmp0 (context_type, "gst.va.display.handle") != 0) + return FALSE; + + gst_query_parse_context (query, &old_ctxt); + + if (old_ctxt) + ctxt = gst_context_copy (old_ctxt); + else + ctxt = gst_context_new ("gst.va.display.handle", TRUE); + + gst_context_set_va_display (ctxt, display); + gst_query_set_context (query, ctxt); + gst_context_unref (ctxt); + GST_CAT_DEBUG_OBJECT (GST_CAT_CONTEXT, element, + "successuflly %" GST_PTR_FORMAT " on %" GST_PTR_FORMAT, display, query); + + return TRUE; +} + +gboolean +gst_context_get_va_display (GstContext * context, const gchar * type_name, + const gchar * render_device_path, GstVaDisplay ** display_ptr) +{ + const GstStructure *s; + GstVaDisplay *display = NULL; + gpointer dpy; + gboolean is_devnode; + + g_return_val_if_fail (display_ptr, FALSE); + g_return_val_if_fail (context, FALSE); + + is_devnode = (g_strstr_len (type_name, -1, "renderD") != NULL); + + s = gst_context_get_structure (context); + if (gst_structure_get (s, "gst-display", GST_TYPE_VA_DISPLAY, &display, NULL)) { + gchar *device_path = NULL; + + if (GST_IS_VA_DISPLAY_DRM (display)) { + g_object_get (display, "path", &device_path, NULL); + if (g_strcmp0 (device_path, render_device_path) == 0) + goto accept; + } else if (!is_devnode) { + goto accept; + } + + /* let's try other fields */ + gst_clear_object (&display); + } + + /* if element is render device node specific, it doesn't accept + * VADisplay from users */ + if (!is_devnode + && gst_structure_get (s, "va-display", G_TYPE_POINTER, &dpy, NULL)) { + if ((display = gst_va_display_wrapped_new ((guintptr) dpy))) + goto accept; + } + + GST_CAT_DEBUG (GST_CAT_CONTEXT, "No valid GstVaDisplay from context (%p)", + context); + return FALSE; + +accept: + { + *display_ptr = display; + + GST_CAT_LOG (GST_CAT_CONTEXT, "got GstVaDisplay (%p) from context (%p)", + *display_ptr, context); + return TRUE; + } +} + +void +gst_context_set_va_display (GstContext * context, GstVaDisplay * display) +{ + GstStructure *s; + + g_return_if_fail (context != NULL); + + if (display) + GST_CAT_LOG (GST_CAT_CONTEXT, + "setting GstVaDisplay (%" GST_PTR_FORMAT ") on context (%" + GST_PTR_FORMAT ")", display, context); + + s = gst_context_writable_structure (context); + gst_structure_set (s, "gst-display", GST_TYPE_VA_DISPLAY, display, NULL); +} diff --git a/sys/va/gstvautils.h b/sys/va/gstvautils.h new file mode 100644 index 0000000000..ed0dd4785c --- /dev/null +++ b/sys/va/gstvautils.h @@ -0,0 +1,48 @@ +/* GStreamer + * Copyright (C) 2020 Igalia, S.L. + * Author: Víctor Jáquez + * + * 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. + */ + +#pragma once + +#include "gstvadisplay.h" + +G_BEGIN_DECLS + +gboolean gst_va_ensure_element_data (gpointer element, + const gchar *render_device_path, + GstVaDisplay ** display_ptr); +gboolean gst_va_handle_set_context (GstElement * element, + GstContext * context, + const gchar *render_device_path, + GstVaDisplay ** display_ptr); +gboolean gst_va_handle_context_query (GstElement * element, + GstQuery * query, + GstVaDisplay * display); +void gst_va_element_propagate_display_context (GstElement * element, + GstVaDisplay * display); + +gboolean gst_context_get_va_display (GstContext * context, + const gchar * type_name, + const gchar * render_device_path, + GstVaDisplay ** display_ptr); +void gst_context_set_va_display (GstContext * context, + GstVaDisplay * display); + + +G_END_DECLS diff --git a/sys/va/meson.build b/sys/va/meson.build index ba19076566..c9d92af2c2 100644 --- a/sys/va/meson.build +++ b/sys/va/meson.build @@ -10,6 +10,7 @@ va_sources = [ 'gstvah264dec.c', 'gstvapool.c', 'gstvaprofile.c', + 'gstvautils.c', 'gstvavideoformat.c', ]