• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2020 Igalia, S.L.
3  *     Author: Víctor Jáquez <vjaquez@igalia.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "gstvautils.h"
26 #include <gst/va/gstvadisplay_drm.h>
27 #include <gst/va/gstvadisplay_wrapped.h>
28 
29 GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
30 
31 static void
_init_context_debug(void)32 _init_context_debug (void)
33 {
34 #ifndef GST_DISABLE_GST_DEBUG
35   static gsize _init = 0;
36 
37   if (g_once_init_enter (&_init)) {
38     GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
39     g_once_init_leave (&_init, 1);
40   }
41 #endif
42 }
43 
44 static gboolean
gst_va_display_found(GstElement * element,GstVaDisplay * display)45 gst_va_display_found (GstElement * element, GstVaDisplay * display)
46 {
47   _init_context_debug ();
48 
49   if (display) {
50     GST_CAT_LOG_OBJECT (GST_CAT_CONTEXT, element, "already have a display (%p)",
51         display);
52     return TRUE;
53   }
54 
55   return FALSE;
56 }
57 
58 static gboolean
pad_query(const GValue * item,GValue * value,gpointer user_data)59 pad_query (const GValue * item, GValue * value, gpointer user_data)
60 {
61   GstPad *pad = g_value_get_object (item);
62   GstQuery *query = user_data;
63   gboolean res;
64 
65   _init_context_debug ();
66 
67   res = gst_pad_peer_query (pad, query);
68 
69   if (res) {
70     g_value_set_boolean (value, TRUE);
71     return FALSE;
72   }
73 
74   GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "pad peer query failed");
75   return TRUE;
76 }
77 
78 static gboolean
_gst_va_run_query(GstElement * element,GstQuery * query,GstPadDirection direction)79 _gst_va_run_query (GstElement * element, GstQuery * query,
80     GstPadDirection direction)
81 {
82   GstIterator *it;
83   GstIteratorFoldFunction func = pad_query;
84   GValue res = G_VALUE_INIT;
85 
86   g_value_init (&res, G_TYPE_BOOLEAN);
87   g_value_set_boolean (&res, FALSE);
88 
89   if (direction == GST_PAD_SRC)
90     it = gst_element_iterate_src_pads (element);
91   else
92     it = gst_element_iterate_sink_pads (element);
93 
94   while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
95     gst_iterator_resync (it);
96 
97   gst_iterator_free (it);
98 
99   return g_value_get_boolean (&res);
100 }
101 
102 static void
_gst_context_query(GstElement * element,const gchar * context_type)103 _gst_context_query (GstElement * element, const gchar * context_type)
104 {
105   GstQuery *query;
106   GstContext *ctxt = NULL;
107 
108   _init_context_debug ();
109 
110   /*  2a) Query downstream with GST_QUERY_CONTEXT for the context and
111    *      check if downstream already has a context of the specific type
112    *  2b) Query upstream as above.
113    */
114   query = gst_query_new_context (context_type);
115   if (_gst_va_run_query (element, query, GST_PAD_SRC)) {
116     gst_query_parse_context (query, &ctxt);
117     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
118         "found context (%p) in downstream query", ctxt);
119     gst_element_set_context (element, ctxt);
120   } else if (_gst_va_run_query (element, query, GST_PAD_SINK)) {
121     gst_query_parse_context (query, &ctxt);
122     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
123         "found context (%p) in upstream query", ctxt);
124     gst_element_set_context (element, ctxt);
125   } else {
126     /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
127      *    the required context type and afterwards check if a
128      *    usable context was set now as in 1). The message could
129      *    be handled by the parent bins of the element and the
130      *    application.
131      */
132     GstMessage *msg;
133 
134     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
135         "posting need context message");
136     msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
137         context_type);
138     gst_element_post_message (element, msg);
139   }
140 
141   /*
142    * Whomever responds to the need-context message performs a
143    * GstElement::set_context() with the required context in which the element
144    * is required to update the display_ptr or call gst_va_handle_set_context().
145    */
146 
147   gst_query_unref (query);
148 }
149 
150 /*  4) Create a context by itself and post a GST_MESSAGE_HAVE_CONTEXT
151  *     message.
152  */
153 void
gst_va_element_propagate_display_context(GstElement * element,GstVaDisplay * display)154 gst_va_element_propagate_display_context (GstElement * element,
155     GstVaDisplay * display)
156 {
157   GstContext *ctxt;
158   GstMessage *msg;
159 
160   if (!display) {
161     GST_ERROR_OBJECT (element, "Could not get VA display connection");
162     return;
163   }
164 
165   _init_context_debug ();
166 
167   ctxt = gst_context_new (GST_VA_DISPLAY_HANDLE_CONTEXT_TYPE_STR, TRUE);
168   gst_context_set_va_display (ctxt, display);
169 
170   GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
171       "post have context (%p) message with display (%p)", ctxt, display);
172   msg = gst_message_new_have_context (GST_OBJECT_CAST (element), ctxt);
173   gst_element_post_message (element, msg);
174 }
175 
176 gboolean
gst_va_ensure_element_data(gpointer element,const gchar * render_device_path,GstVaDisplay ** display_ptr)177 gst_va_ensure_element_data (gpointer element, const gchar * render_device_path,
178     GstVaDisplay ** display_ptr)
179 {
180   GstVaDisplay *display;
181 
182   g_return_val_if_fail (element, FALSE);
183   g_return_val_if_fail (render_device_path, FALSE);
184   g_return_val_if_fail (display_ptr, FALSE);
185 
186   /*  1) Check if the element already has a context of the specific
187    *     type.
188    */
189   if (gst_va_display_found (element, g_atomic_pointer_get (display_ptr)))
190     goto done;
191 
192   _gst_context_query (element, GST_VA_DISPLAY_HANDLE_CONTEXT_TYPE_STR);
193 
194   /* Neighbour found and it updated the display */
195   if (gst_va_display_found (element, g_atomic_pointer_get (display_ptr)))
196     goto done;
197 
198   /* If no neighbor, or application not interested, use drm */
199   display = gst_va_display_drm_new_from_path (render_device_path);
200 
201   gst_object_replace ((GstObject **) display_ptr, (GstObject *) display);
202 
203   gst_va_element_propagate_display_context (element, display);
204 
205   gst_clear_object (&display);
206 
207 done:
208   return g_atomic_pointer_get (display_ptr) != NULL;
209 }
210 
211 gboolean
gst_va_handle_set_context(GstElement * element,GstContext * context,const gchar * render_device_path,GstVaDisplay ** display_ptr)212 gst_va_handle_set_context (GstElement * element, GstContext * context,
213     const gchar * render_device_path, GstVaDisplay ** display_ptr)
214 {
215   GstVaDisplay *display_replacement = NULL;
216   const gchar *context_type, *type_name;
217 
218   g_return_val_if_fail (display_ptr, FALSE);
219 
220   if (!context)
221     return FALSE;
222 
223   context_type = gst_context_get_context_type (context);
224 
225   if (g_strcmp0 (context_type, GST_VA_DISPLAY_HANDLE_CONTEXT_TYPE_STR) == 0) {
226     type_name = G_OBJECT_TYPE_NAME (element);
227     if (!gst_context_get_va_display (context, type_name, render_device_path,
228             &display_replacement)) {
229       GST_CAT_WARNING_OBJECT (GST_CAT_CONTEXT, element,
230           "Failed to get display from context");
231       return FALSE;
232     }
233   }
234 
235   if (display_replacement) {
236     gst_object_replace ((GstObject **) display_ptr,
237         (GstObject *) display_replacement);
238     gst_object_unref (display_replacement);
239   }
240 
241   return TRUE;
242 }
243 
244 gboolean
gst_va_handle_context_query(GstElement * element,GstQuery * query,GstVaDisplay * display)245 gst_va_handle_context_query (GstElement * element, GstQuery * query,
246     GstVaDisplay * display)
247 {
248   const gchar *context_type;
249   GstContext *ctxt, *old_ctxt;
250 
251   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
252   g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
253   g_return_val_if_fail (!display || GST_IS_VA_DISPLAY (display), FALSE);
254 
255   _init_context_debug ();
256 
257   GST_CAT_LOG_OBJECT (GST_CAT_CONTEXT, element,
258       "handle context query %" GST_PTR_FORMAT, query);
259   gst_query_parse_context_type (query, &context_type);
260 
261   if (!display
262       || g_strcmp0 (context_type, GST_VA_DISPLAY_HANDLE_CONTEXT_TYPE_STR) != 0)
263     return FALSE;
264 
265   gst_query_parse_context (query, &old_ctxt);
266 
267   if (old_ctxt)
268     ctxt = gst_context_copy (old_ctxt);
269   else
270     ctxt = gst_context_new (GST_VA_DISPLAY_HANDLE_CONTEXT_TYPE_STR, TRUE);
271 
272   gst_context_set_va_display (ctxt, display);
273   gst_query_set_context (query, ctxt);
274   gst_context_unref (ctxt);
275   GST_CAT_DEBUG_OBJECT (GST_CAT_CONTEXT, element,
276       "successuflly %" GST_PTR_FORMAT " on %" GST_PTR_FORMAT, display, query);
277 
278   return TRUE;
279 }
280 
281 gboolean
gst_context_get_va_display(GstContext * context,const gchar * type_name,const gchar * render_device_path,GstVaDisplay ** display_ptr)282 gst_context_get_va_display (GstContext * context, const gchar * type_name,
283     const gchar * render_device_path, GstVaDisplay ** display_ptr)
284 {
285   const GstStructure *s;
286   GstVaDisplay *display = NULL;
287   gpointer dpy;
288   gboolean is_devnode;
289 
290   g_return_val_if_fail (display_ptr, FALSE);
291   g_return_val_if_fail (context, FALSE);
292 
293   is_devnode = (g_strstr_len (type_name, -1, "renderD") != NULL);
294 
295   s = gst_context_get_structure (context);
296   if (gst_structure_get (s, "gst-display", GST_TYPE_OBJECT, &display, NULL)) {
297     gchar *device_path = NULL;
298     gboolean ret;
299 
300     if (GST_IS_VA_DISPLAY_DRM (display)) {
301       g_object_get (display, "path", &device_path, NULL);
302       ret = (g_strcmp0 (device_path, render_device_path) == 0);
303       g_free (device_path);
304       if (ret)
305         goto accept;
306     } else if (GST_IS_VA_DISPLAY (display) && !is_devnode) {
307       goto accept;
308     }
309 
310     /* let's try other fields */
311     gst_clear_object (&display);
312   }
313 
314   /* if element is render device node specific, it doesn't accept
315    * VADisplay from users */
316   if (!is_devnode
317       && gst_structure_get (s, "va-display", G_TYPE_POINTER, &dpy, NULL)) {
318     if ((display = gst_va_display_wrapped_new (dpy)))
319       goto accept;
320   }
321 
322   GST_CAT_DEBUG (GST_CAT_CONTEXT, "No valid GstVaDisplay from context (%p)",
323       context);
324   return FALSE;
325 
326 accept:
327   {
328     *display_ptr = display;
329 
330     GST_CAT_LOG (GST_CAT_CONTEXT, "got GstVaDisplay (%p) from context (%p)",
331         *display_ptr, context);
332     return TRUE;
333   }
334 }
335 
336 void
gst_context_set_va_display(GstContext * context,GstVaDisplay * display)337 gst_context_set_va_display (GstContext * context, GstVaDisplay * display)
338 {
339   GstStructure *s;
340 
341   g_return_if_fail (context != NULL);
342 
343   if (display) {
344     GST_CAT_LOG (GST_CAT_CONTEXT,
345         "setting GstVaDisplay (%" GST_PTR_FORMAT ") on context (%"
346         GST_PTR_FORMAT ")", display, context);
347   }
348 
349   s = gst_context_writable_structure (context);
350   gst_structure_set (s, "gst-display", GST_TYPE_OBJECT, display, NULL);
351 }
352