36068fb0afc70cf3473c08f6856086984fe48b31
[platform/upstream/gstreamer.git] / sys / va / gstvautils.c
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 "gstvadisplay_drm.h"
26 #include "gstvadisplay_wrapped.h"
27 #include "gstvautils.h"
28
29 GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
30
31 static void
32 _init_context_debug (void)
33 {
34 #ifndef GST_DISABLE_GST_DEBUG
35   static volatile 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
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
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
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
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
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", 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
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");
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
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") == 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
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 || g_strcmp0 (context_type, "gst.va.display.handle") != 0)
262     return FALSE;
263
264   gst_query_parse_context (query, &old_ctxt);
265
266   if (old_ctxt)
267     ctxt = gst_context_copy (old_ctxt);
268   else
269     ctxt = gst_context_new ("gst.va.display.handle", TRUE);
270
271   gst_context_set_va_display (ctxt, display);
272   gst_query_set_context (query, ctxt);
273   gst_context_unref (ctxt);
274   GST_CAT_DEBUG_OBJECT (GST_CAT_CONTEXT, element,
275       "successuflly %" GST_PTR_FORMAT " on %" GST_PTR_FORMAT, display, query);
276
277   return TRUE;
278 }
279
280 gboolean
281 gst_context_get_va_display (GstContext * context, const gchar * type_name,
282     const gchar * render_device_path, GstVaDisplay ** display_ptr)
283 {
284   const GstStructure *s;
285   GstVaDisplay *display = NULL;
286   gpointer dpy;
287   gboolean is_devnode;
288
289   g_return_val_if_fail (display_ptr, FALSE);
290   g_return_val_if_fail (context, FALSE);
291
292   is_devnode = (g_strstr_len (type_name, -1, "renderD") != NULL);
293
294   s = gst_context_get_structure (context);
295   if (gst_structure_get (s, "gst-display", GST_TYPE_OBJECT, &display, NULL)) {
296     gchar *device_path = NULL;
297     gboolean ret;
298
299     if (GST_IS_VA_DISPLAY_DRM (display)) {
300       g_object_get (display, "path", &device_path, NULL);
301       ret = (g_strcmp0 (device_path, render_device_path) == 0);
302       g_free (device_path);
303       if (ret)
304         goto accept;
305     } else if (GST_IS_VA_DISPLAY (display) && !is_devnode) {
306       goto accept;
307     }
308
309     /* let's try other fields */
310     gst_clear_object (&display);
311   }
312
313   /* if element is render device node specific, it doesn't accept
314    * VADisplay from users */
315   if (!is_devnode
316       && gst_structure_get (s, "va-display", G_TYPE_POINTER, &dpy, NULL)) {
317     if ((display = gst_va_display_wrapped_new ((guintptr) dpy)))
318       goto accept;
319   }
320
321   GST_CAT_DEBUG (GST_CAT_CONTEXT, "No valid GstVaDisplay from context (%p)",
322       context);
323   return FALSE;
324
325 accept:
326   {
327     *display_ptr = display;
328
329     GST_CAT_LOG (GST_CAT_CONTEXT, "got GstVaDisplay (%p) from context (%p)",
330         *display_ptr, context);
331     return TRUE;
332   }
333 }
334
335 void
336 gst_context_set_va_display (GstContext * context, GstVaDisplay * display)
337 {
338   GstStructure *s;
339
340   g_return_if_fail (context != NULL);
341
342   if (display) {
343     GST_CAT_LOG (GST_CAT_CONTEXT,
344         "setting GstVaDisplay (%" GST_PTR_FORMAT ") on context (%"
345         GST_PTR_FORMAT ")", display, context);
346   }
347
348   s = gst_context_writable_structure (context);
349   gst_structure_set (s, "gst-display", GST_TYPE_OBJECT, display, NULL);
350 }