1 /*
2  * GStreamer
3  * Copyright (C) 2013 Matthew Waters <ystreet00@gmail.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 /**
22  * SECTION:gstglutils
23  * @title: GstGLUtils
24  * @short_description: some miscellaneous utilities for OpenGL
25  * @see_also: #GstGLContext
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include <stdio.h>
33 
34 #include <gst/gst.h>
35 #include <glib/gprintf.h>
36 
37 #include "gl.h"
38 #include "gstglutils.h"
39 #include "gstglutils_private.h"
40 
41 #if GST_GL_HAVE_WINDOW_X11
42 #include <gst/gl/x11/gstgldisplay_x11.h>
43 #endif
44 #if GST_GL_HAVE_WINDOW_WAYLAND
45 #include <gst/gl/wayland/gstgldisplay_wayland.h>
46 #endif
47 
48 #define USING_OPENGL(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL, 1, 0))
49 #define USING_OPENGL3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL3, 3, 1))
50 #define USING_GLES(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES, 1, 0))
51 #define USING_GLES2(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0))
52 #define USING_GLES3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 0))
53 
54 #ifndef GST_DISABLE_GST_DEBUG
55 GST_DEBUG_CATEGORY_STATIC (gst_gl_utils_debug);
56 static GstDebugCategory *
_init_gl_utils_debug_category(void)57 _init_gl_utils_debug_category (void)
58 {
59   static volatile gsize _init = 0;
60 
61   if (g_once_init_enter (&_init)) {
62     GST_DEBUG_CATEGORY_INIT (gst_gl_utils_debug, "glutils", 0,
63         "OpenGL Utilities");
64     g_once_init_leave (&_init, 1);
65   }
66 
67   return gst_gl_utils_debug;
68 }
69 
70 #define GST_CAT_DEFAULT _init_gl_utils_debug_category()
71 #endif
72 
73 static gboolean
gst_gl_display_found(GstElement * element,GstGLDisplay * display)74 gst_gl_display_found (GstElement * element, GstGLDisplay * display)
75 {
76   if (display) {
77     GST_LOG_OBJECT (element, "already have a display (%p)", display);
78     return TRUE;
79   }
80 
81   return FALSE;
82 }
83 
84 GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
85 
86 static void
_init_context_debug(void)87 _init_context_debug (void)
88 {
89 #ifndef GST_DISABLE_GST_DEBUG
90   static volatile gsize _init = 0;
91 
92   if (g_once_init_enter (&_init)) {
93     GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
94     g_once_init_leave (&_init, 1);
95   }
96 #endif
97 }
98 
99 static gboolean
pad_query(const GValue * item,GValue * value,gpointer user_data)100 pad_query (const GValue * item, GValue * value, gpointer user_data)
101 {
102   GstPad *pad = g_value_get_object (item);
103   GstQuery *query = user_data;
104   gboolean res;
105 
106   _init_context_debug ();
107 
108   res = gst_pad_peer_query (pad, query);
109 
110   if (res) {
111     g_value_set_boolean (value, TRUE);
112     return FALSE;
113   }
114 
115   GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "pad peer query failed");
116   return TRUE;
117 }
118 
119 gboolean
gst_gl_run_query(GstElement * element,GstQuery * query,GstPadDirection direction)120 gst_gl_run_query (GstElement * element, GstQuery * query,
121     GstPadDirection direction)
122 {
123   GstIterator *it;
124   GstIteratorFoldFunction func = pad_query;
125   GValue res = { 0 };
126 
127   g_value_init (&res, G_TYPE_BOOLEAN);
128   g_value_set_boolean (&res, FALSE);
129 
130   /* Ask neighbor */
131   if (direction == GST_PAD_SRC)
132     it = gst_element_iterate_src_pads (element);
133   else
134     it = gst_element_iterate_sink_pads (element);
135 
136   while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
137     gst_iterator_resync (it);
138 
139   gst_iterator_free (it);
140 
141   return g_value_get_boolean (&res);
142 }
143 
144 static void
_gst_context_query(GstElement * element,const gchar * display_type)145 _gst_context_query (GstElement * element, const gchar * display_type)
146 {
147   GstQuery *query;
148   GstContext *ctxt;
149 
150   _init_context_debug ();
151 
152   /*  2a) Query downstream with GST_QUERY_CONTEXT for the context and
153    *      check if downstream already has a context of the specific type
154    *  2b) Query upstream as above.
155    */
156   query = gst_query_new_context (display_type);
157   if (gst_gl_run_query (element, query, GST_PAD_SRC)) {
158     gst_query_parse_context (query, &ctxt);
159     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
160         "found context (%p) in downstream query", ctxt);
161     gst_element_set_context (element, ctxt);
162   } else if (gst_gl_run_query (element, query, GST_PAD_SINK)) {
163     gst_query_parse_context (query, &ctxt);
164     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
165         "found context (%p) in upstream query", ctxt);
166     gst_element_set_context (element, ctxt);
167   } else {
168     /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
169      *    the required context type and afterwards check if a
170      *    usable context was set now as in 1). The message could
171      *    be handled by the parent bins of the element and the
172      *    application.
173      */
174     GstMessage *msg;
175 
176     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
177         "posting need context message");
178     msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
179         display_type);
180     gst_element_post_message (element, msg);
181   }
182 
183   /*
184    * Whomever responds to the need-context message performs a
185    * GstElement::set_context() with the required context in which the element
186    * is required to update the display_ptr or call gst_gl_handle_set_context().
187    */
188 
189   gst_query_unref (query);
190 }
191 
192 static void
gst_gl_display_context_query(GstElement * element,GstGLDisplay ** display_ptr)193 gst_gl_display_context_query (GstElement * element, GstGLDisplay ** display_ptr)
194 {
195   _gst_context_query (element, GST_GL_DISPLAY_CONTEXT_TYPE);
196   if (*display_ptr)
197     return;
198 
199 #if GST_GL_HAVE_WINDOW_X11
200   _gst_context_query (element, "gst.x11.display.handle");
201   if (*display_ptr)
202     return;
203 #endif
204 
205 #if GST_GL_HAVE_WINDOW_WAYLAND
206   _gst_context_query (element, "GstWaylandDisplayHandleContextType");
207   if (*display_ptr)
208     return;
209 #endif
210 }
211 
212 static void
gst_gl_context_query(GstElement * element)213 gst_gl_context_query (GstElement * element)
214 {
215   _gst_context_query (element, "gst.gl.app_context");
216 }
217 
218 /*  4) Create a context by itself and post a GST_MESSAGE_HAVE_CONTEXT
219  *     message.
220  */
221 void
gst_gl_element_propagate_display_context(GstElement * element,GstGLDisplay * display)222 gst_gl_element_propagate_display_context (GstElement * element,
223     GstGLDisplay * display)
224 {
225   GstContext *context;
226   GstMessage *msg;
227 
228   if (!display) {
229     GST_ERROR_OBJECT (element, "Could not get GL display connection");
230     return;
231   }
232 
233   _init_context_debug ();
234 
235   context = gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
236   gst_context_set_gl_display (context, display);
237 
238   gst_element_set_context (element, context);
239 
240   GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
241       "posting have context (%p) message with display (%p)", context, display);
242   msg = gst_message_new_have_context (GST_OBJECT_CAST (element), context);
243   gst_element_post_message (GST_ELEMENT_CAST (element), msg);
244 }
245 
246 /**
247  * gst_gl_ensure_element_data:
248  * @element: the #GstElement running the query
249  * @display_ptr: (inout): the resulting #GstGLDisplay
250  * @other_context_ptr: (inout): the resulting #GstGLContext
251  *
252  * Perform the steps necessary for retrieving a #GstGLDisplay and (optionally)
253  * an application provided #GstGLContext from the surrounding elements or from
254  * the application using the #GstContext mechanism.
255  *
256  * If the contents of @display_ptr or @other_context_ptr are not %NULL, then no
257  * #GstContext query is necessary for #GstGLDisplay or #GstGLContext retrieval
258  * or is performed.
259  *
260  * This performs #GstContext queries (if necessary) for a winsys display
261  * connection with %GST_GL_DISPLAY_CONTEXT_TYPE, "gst.x11.display.handle", and
262  * "GstWaylandDisplayHandleContextType" stopping after the first successful
263  * retrieval.
264  *
265  * This also performs a #GstContext query (if necessary) for an optional
266  * application provided #GstGLContext using the name "gst.gl.app_context".
267  * The returned #GstGLContext will be shared with a GStreamer created OpenGL context.
268  *
269  * Returns: whether a #GstGLDisplay exists in @display_ptr
270  */
271 gboolean
gst_gl_ensure_element_data(gpointer element,GstGLDisplay ** display_ptr,GstGLContext ** other_context_ptr)272 gst_gl_ensure_element_data (gpointer element, GstGLDisplay ** display_ptr,
273     GstGLContext ** other_context_ptr)
274 {
275   GstGLDisplay *display;
276 
277   g_return_val_if_fail (element != NULL, FALSE);
278   g_return_val_if_fail (display_ptr != NULL, FALSE);
279   g_return_val_if_fail (other_context_ptr != NULL, FALSE);
280 
281   /*  1) Check if the element already has a context of the specific
282    *     type.
283    */
284   display = *display_ptr;
285   if (gst_gl_display_found (element, display))
286     goto done;
287 
288   gst_gl_display_context_query (element, display_ptr);
289 
290   /* Neighbour found and it updated the display */
291   if (gst_gl_display_found (element, *display_ptr))
292     goto get_gl_context;
293 
294   /* If no neighboor, or application not interested, use system default */
295   display = gst_gl_display_new ();
296 
297   *display_ptr = display;
298 
299   gst_gl_element_propagate_display_context (element, display);
300 
301 get_gl_context:
302   if (*other_context_ptr)
303     goto done;
304 
305   gst_gl_context_query (element);
306 
307 done:
308   return *display_ptr != NULL;
309 }
310 
311 /**
312  * gst_gl_handle_set_context:
313  * @element: a #GstElement
314  * @context: a #GstContext
315  * @display: (inout) (transfer full): location of a #GstGLDisplay
316  * @other_context: (inout) (transfer full): location of a #GstGLContext
317  *
318  * Helper function for implementing #GstElementClass.set_context() in
319  * OpenGL capable elements.
320  *
321  * Retrieve's the #GstGLDisplay or #GstGLContext in @context and places the
322  * result in @display or @other_context respectively.
323  *
324  * Returns: whether the @display or @other_context could be set successfully
325  */
326 gboolean
gst_gl_handle_set_context(GstElement * element,GstContext * context,GstGLDisplay ** display,GstGLContext ** other_context)327 gst_gl_handle_set_context (GstElement * element, GstContext * context,
328     GstGLDisplay ** display, GstGLContext ** other_context)
329 {
330   GstGLDisplay *display_replacement = NULL;
331   GstGLContext *context_replacement = NULL;
332   const gchar *context_type;
333 
334   g_return_val_if_fail (display != NULL, FALSE);
335   g_return_val_if_fail (other_context != NULL, FALSE);
336 
337   if (!context)
338     return FALSE;
339 
340   context_type = gst_context_get_context_type (context);
341 
342   if (g_strcmp0 (context_type, GST_GL_DISPLAY_CONTEXT_TYPE) == 0) {
343     if (!gst_context_get_gl_display (context, &display_replacement)) {
344       GST_WARNING_OBJECT (element, "Failed to get display from context");
345       return FALSE;
346     }
347   }
348 #if GST_GL_HAVE_WINDOW_X11
349   else if (g_strcmp0 (context_type, "gst.x11.display.handle") == 0) {
350     const GstStructure *s;
351     Display *display;
352 
353     s = gst_context_get_structure (context);
354     if (gst_structure_get (s, "display", G_TYPE_POINTER, &display, NULL))
355       display_replacement =
356           (GstGLDisplay *) gst_gl_display_x11_new_with_display (display);
357   }
358 #endif
359 #if GST_GL_HAVE_WINDOW_WAYLAND
360   else if (g_strcmp0 (context_type, "GstWaylandDisplayHandleContextType") == 0) {
361     const GstStructure *s;
362     struct wl_display *display;
363 
364     s = gst_context_get_structure (context);
365     if (gst_structure_get (s, "display", G_TYPE_POINTER, &display, NULL))
366       display_replacement =
367           (GstGLDisplay *) gst_gl_display_wayland_new_with_display (display);
368   }
369 #endif
370   else if (g_strcmp0 (context_type, "gst.gl.app_context") == 0) {
371     const GstStructure *s = gst_context_get_structure (context);
372     GstGLDisplay *context_display;
373     GstGLDisplay *element_display;
374 
375     if (gst_structure_get (s, "context", GST_TYPE_GL_CONTEXT,
376             &context_replacement, NULL)) {
377       context_display = gst_gl_context_get_display (context_replacement);
378       element_display = display_replacement ? display_replacement : *display;
379       if (element_display
380           && (gst_gl_display_get_handle_type (element_display) &
381               gst_gl_display_get_handle_type (context_display)) == 0) {
382         GST_ELEMENT_WARNING (element, LIBRARY, SETTINGS, ("%s",
383                 "Cannot set a GL context with a different display type"), ("%s",
384                 "Cannot set a GL context with a different display type"));
385         gst_object_unref (context_replacement);
386         context_replacement = NULL;
387       }
388       gst_object_unref (context_display);
389     }
390   }
391 
392   if (display_replacement) {
393     GstGLDisplay *old = *display;
394     *display = display_replacement;
395 
396     if (old)
397       gst_object_unref (old);
398   }
399 
400   if (context_replacement) {
401     GstGLContext *old = *other_context;
402     *other_context = context_replacement;
403 
404     if (old)
405       gst_object_unref (old);
406   }
407 
408   return TRUE;
409 }
410 
411 /**
412  * gst_gl_handle_context_query:
413  * @element: a #GstElement
414  * @query: a #GstQuery of type %GST_QUERY_CONTEXT
415  * @display: (transfer none) (nullable): a #GstGLDisplay
416  * @context: (transfer none) (nullable): a #GstGLContext
417  * @other_context: (transfer none) (nullable): application provided #GstGLContext
418  *
419  * Returns: Whether the @query was successfully responded to from the passed
420  *          @display, @context, and @other_context.
421  */
422 gboolean
gst_gl_handle_context_query(GstElement * element,GstQuery * query,GstGLDisplay * display,GstGLContext * gl_context,GstGLContext * other_context)423 gst_gl_handle_context_query (GstElement * element, GstQuery * query,
424     GstGLDisplay * display, GstGLContext * gl_context,
425     GstGLContext * other_context)
426 {
427   const gchar *context_type;
428   GstContext *context, *old_context;
429 
430   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
431   g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
432   g_return_val_if_fail (display == NULL || GST_IS_GL_DISPLAY (display), FALSE);
433   g_return_val_if_fail (gl_context == NULL
434       || GST_IS_GL_CONTEXT (gl_context), FALSE);
435   g_return_val_if_fail (other_context == NULL
436       || GST_IS_GL_CONTEXT (other_context), FALSE);
437 
438   GST_LOG_OBJECT (element, "handle context query %" GST_PTR_FORMAT, query);
439   gst_query_parse_context_type (query, &context_type);
440 
441   if (display && g_strcmp0 (context_type, GST_GL_DISPLAY_CONTEXT_TYPE) == 0) {
442     gst_query_parse_context (query, &old_context);
443 
444     if (old_context)
445       context = gst_context_copy (old_context);
446     else
447       context = gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
448 
449     gst_context_set_gl_display (context, display);
450     gst_query_set_context (query, context);
451     gst_context_unref (context);
452     GST_DEBUG_OBJECT (element, "successfully set %" GST_PTR_FORMAT
453         " on %" GST_PTR_FORMAT, display, query);
454 
455     return TRUE;
456   }
457 #if GST_GL_HAVE_WINDOW_X11
458   else if (display && g_strcmp0 (context_type, "gst.x11.display.handle") == 0) {
459     GstStructure *s;
460 
461     gst_query_parse_context (query, &old_context);
462 
463     if (old_context)
464       context = gst_context_copy (old_context);
465     else
466       context = gst_context_new ("gst.x11.display.handle", TRUE);
467 
468     if (gst_gl_display_get_handle_type (display) & GST_GL_DISPLAY_TYPE_X11) {
469       Display *x11_display = (Display *) gst_gl_display_get_handle (display);
470 
471       if (x11_display) {
472         s = gst_context_writable_structure (context);
473         gst_structure_set (s, "display", G_TYPE_POINTER, x11_display, NULL);
474 
475         gst_query_set_context (query, context);
476         gst_context_unref (context);
477 
478         GST_DEBUG_OBJECT (element, "successfully set x11 display %p (from %"
479             GST_PTR_FORMAT ") on %" GST_PTR_FORMAT, x11_display, display,
480             query);
481 
482         return TRUE;
483       }
484     }
485   }
486 #endif
487 #if GST_GL_HAVE_WINDOW_WAYLAND
488   else if (display
489       && g_strcmp0 (context_type, "GstWaylandDisplayHandleContextType") == 0) {
490     GstStructure *s;
491 
492     gst_query_parse_context (query, &old_context);
493 
494     if (old_context)
495       context = gst_context_copy (old_context);
496     else
497       context = gst_context_new ("GstWaylandDisplayHandleContextType", TRUE);
498 
499     if (gst_gl_display_get_handle_type (display) & GST_GL_DISPLAY_TYPE_WAYLAND) {
500       struct wl_display *wayland_display =
501           (struct wl_display *) gst_gl_display_get_handle (display);
502 
503       if (wayland_display) {
504         s = gst_context_writable_structure (context);
505         gst_structure_set (s, "display", G_TYPE_POINTER, wayland_display, NULL);
506 
507         gst_query_set_context (query, context);
508         gst_context_unref (context);
509 
510         GST_DEBUG_OBJECT (element, "successfully set wayland display %p (from %"
511             GST_PTR_FORMAT ") on %" GST_PTR_FORMAT, wayland_display, display,
512             query);
513 
514         return TRUE;
515       }
516     }
517   }
518 #endif
519   else if (other_context && g_strcmp0 (context_type, "gst.gl.app_context") == 0) {
520     GstStructure *s;
521 
522     gst_query_parse_context (query, &old_context);
523 
524     if (old_context)
525       context = gst_context_copy (old_context);
526     else
527       context = gst_context_new ("gst.gl.app_context", TRUE);
528 
529     s = gst_context_writable_structure (context);
530     gst_structure_set (s, "context", GST_TYPE_GL_CONTEXT, other_context, NULL);
531     gst_query_set_context (query, context);
532     gst_context_unref (context);
533 
534     GST_DEBUG_OBJECT (element, "successfully set application GL context %"
535         GST_PTR_FORMAT " on %" GST_PTR_FORMAT, other_context, query);
536 
537     return TRUE;
538   } else if (gl_context
539       && g_strcmp0 (context_type, "gst.gl.local_context") == 0) {
540     GstStructure *s;
541 
542     gst_query_parse_context (query, &old_context);
543 
544     if (old_context)
545       context = gst_context_copy (old_context);
546     else
547       context = gst_context_new ("gst.gl.local_context", TRUE);
548 
549     s = gst_context_writable_structure (context);
550     gst_structure_set (s, "context", GST_TYPE_GL_CONTEXT, gl_context, NULL);
551     gst_query_set_context (query, context);
552     gst_context_unref (context);
553 
554     GST_DEBUG_OBJECT (element, "successfully set GL context %"
555         GST_PTR_FORMAT " on %" GST_PTR_FORMAT, gl_context, query);
556 
557     return TRUE;
558   }
559 
560   return FALSE;
561 }
562 
563 /**
564  * gst_gl_query_local_gl_context:
565  * @element: a #GstElement to query from
566  * @direction: the #GstPadDirection to query
567  * @context_ptr: (inout): location containing the current and/or resulting
568  *                      #GstGLContext
569  *
570  * Performs a GST_QUERY_CONTEXT query of type "gst.gl.local_context" on all
571  * #GstPads in @element of @direction for the local OpenGL context used by
572  * GStreamer elements.
573  *
574  * Returns: whether @context_ptr contains a #GstGLContext
575  */
576 gboolean
gst_gl_query_local_gl_context(GstElement * element,GstPadDirection direction,GstGLContext ** context_ptr)577 gst_gl_query_local_gl_context (GstElement * element, GstPadDirection direction,
578     GstGLContext ** context_ptr)
579 {
580   GstQuery *query;
581   GstContext *context;
582   const GstStructure *s;
583 
584   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
585   g_return_val_if_fail (context_ptr != NULL, FALSE);
586 
587   if (*context_ptr)
588     return TRUE;
589 
590   query = gst_query_new_context ("gst.gl.local_context");
591   if (gst_gl_run_query (GST_ELEMENT (element), query, direction)) {
592     gst_query_parse_context (query, &context);
593     if (context) {
594       s = gst_context_get_structure (context);
595       gst_structure_get (s, "context", GST_TYPE_GL_CONTEXT, context_ptr, NULL);
596     }
597   }
598 
599   gst_query_unref (query);
600 
601   return *context_ptr != NULL;
602 }
603 
604 /**
605  * gst_gl_get_plane_data_size:
606  * @info: a #GstVideoInfo
607  * @align: a #GstVideoAlignment or %NULL
608  * @plane: plane number in @info to retrieve the data size of
609  *
610  * Retrieve the size in bytes of a video plane of data with a certain alignment
611  */
612 gsize
gst_gl_get_plane_data_size(GstVideoInfo * info,GstVideoAlignment * align,guint plane)613 gst_gl_get_plane_data_size (GstVideoInfo * info, GstVideoAlignment * align,
614     guint plane)
615 {
616   gint padded_height;
617   gsize plane_size;
618 
619   padded_height = info->height;
620 
621   if (align)
622     padded_height += align->padding_top + align->padding_bottom;
623 
624   padded_height =
625       GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, plane, padded_height);
626 
627   plane_size = GST_VIDEO_INFO_PLANE_STRIDE (info, plane) * padded_height;
628 
629   return plane_size;
630 }
631 
632 /**
633  * gst_gl_get_plane_start:
634  * @info: a #GstVideoInfo
635  * @valign: a #GstVideoAlignment or %NULL
636  * @plane: plane number in @info to retrieve the data size of
637  *
638  * Returns: difference between the supposed start of the plane from the @info
639  *          and where the data from the previous plane ends.
640  */
641 gsize
gst_gl_get_plane_start(GstVideoInfo * info,GstVideoAlignment * valign,guint plane)642 gst_gl_get_plane_start (GstVideoInfo * info, GstVideoAlignment * valign,
643     guint plane)
644 {
645   gsize plane_start;
646   gint i;
647 
648   /* find the start of the plane data including padding */
649   plane_start = 0;
650   for (i = 0; i < plane; i++) {
651     plane_start += gst_gl_get_plane_data_size (info, valign, i);
652   }
653 
654   /* offset between the plane data start and where the video frame starts */
655   return (GST_VIDEO_INFO_PLANE_OFFSET (info, plane)) - plane_start;
656 }
657 
658 /**
659  * gst_gl_value_get_texture_target_mask:
660  * @value: an initialized #GValue of type G_TYPE_STRING
661  *
662  * See gst_gl_value_set_texture_target_from_mask() for what entails a mask
663  *
664  * Returns: the mask of #GstGLTextureTarget's in @value
665  */
666 GstGLTextureTarget
gst_gl_value_get_texture_target_mask(const GValue * targets)667 gst_gl_value_get_texture_target_mask (const GValue * targets)
668 {
669   guint new_targets = 0;
670 
671   g_return_val_if_fail (targets != NULL, GST_GL_TEXTURE_TARGET_NONE);
672 
673   if (G_TYPE_CHECK_VALUE_TYPE (targets, G_TYPE_STRING)) {
674     GstGLTextureTarget target;
675     const gchar *str;
676 
677     str = g_value_get_string (targets);
678     target = gst_gl_texture_target_from_string (str);
679 
680     if (target)
681       new_targets |= 1 << target;
682   } else if (G_TYPE_CHECK_VALUE_TYPE (targets, GST_TYPE_LIST)) {
683     gint j, m;
684 
685     m = gst_value_list_get_size (targets);
686     for (j = 0; j < m; j++) {
687       const GValue *val = gst_value_list_get_value (targets, j);
688       GstGLTextureTarget target;
689       const gchar *str;
690 
691       str = g_value_get_string (val);
692       target = gst_gl_texture_target_from_string (str);
693       if (target)
694         new_targets |= 1 << target;
695     }
696   }
697 
698   return new_targets;
699 }
700 
701 /**
702  * gst_gl_value_set_texture_target:
703  * @value: an initialized #GValue of type G_TYPE_STRING
704  * @target: a #GstGLTextureTarget's
705  *
706  * Returns: whether the @target could be set on @value
707  */
708 gboolean
gst_gl_value_set_texture_target(GValue * value,GstGLTextureTarget target)709 gst_gl_value_set_texture_target (GValue * value, GstGLTextureTarget target)
710 {
711   g_return_val_if_fail (value != NULL, FALSE);
712   g_return_val_if_fail (target != GST_GL_TEXTURE_TARGET_NONE, FALSE);
713 
714   if (target == GST_GL_TEXTURE_TARGET_2D) {
715     g_value_set_static_string (value, GST_GL_TEXTURE_TARGET_2D_STR);
716   } else if (target == GST_GL_TEXTURE_TARGET_RECTANGLE) {
717     g_value_set_static_string (value, GST_GL_TEXTURE_TARGET_RECTANGLE_STR);
718   } else if (target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) {
719     g_value_set_static_string (value, GST_GL_TEXTURE_TARGET_EXTERNAL_OES_STR);
720   } else {
721     return FALSE;
722   }
723 
724   return TRUE;
725 }
726 
727 static guint64
_gst_gl_log2_int64(guint64 value)728 _gst_gl_log2_int64 (guint64 value)
729 {
730   guint64 ret = 0;
731 
732   while (value >>= 1)
733     ret++;
734 
735   return ret;
736 }
737 
738 /**
739  * gst_gl_value_set_texture_target_from_mask:
740  * @value: an uninitialized #GValue
741  * @target_mask: a bitwise mask of #GstGLTextureTarget's
742  *
743  * A mask is a bitwise OR of (1 << target) where target is a valid
744  * #GstGLTextureTarget
745  *
746  * Returns: whether the @target_mask could be set on @value
747  */
748 gboolean
gst_gl_value_set_texture_target_from_mask(GValue * value,GstGLTextureTarget target_mask)749 gst_gl_value_set_texture_target_from_mask (GValue * value,
750     GstGLTextureTarget target_mask)
751 {
752   g_return_val_if_fail (value != NULL, FALSE);
753   g_return_val_if_fail (target_mask != GST_GL_TEXTURE_TARGET_NONE, FALSE);
754 
755   if ((target_mask & (target_mask - 1)) == 0) {
756     /* only one texture target set */
757     g_value_init (value, G_TYPE_STRING);
758     return gst_gl_value_set_texture_target (value,
759         _gst_gl_log2_int64 (target_mask));
760   } else {
761     GValue item = G_VALUE_INIT;
762     gboolean ret = FALSE;
763 
764     g_value_init (value, GST_TYPE_LIST);
765     g_value_init (&item, G_TYPE_STRING);
766     if (target_mask & (1 << GST_GL_TEXTURE_TARGET_2D)) {
767       gst_gl_value_set_texture_target (&item, GST_GL_TEXTURE_TARGET_2D);
768       gst_value_list_append_value (value, &item);
769       ret = TRUE;
770     }
771     if (target_mask & (1 << GST_GL_TEXTURE_TARGET_RECTANGLE)) {
772       gst_gl_value_set_texture_target (&item, GST_GL_TEXTURE_TARGET_RECTANGLE);
773       gst_value_list_append_value (value, &item);
774       ret = TRUE;
775     }
776     if (target_mask & (1 << GST_GL_TEXTURE_TARGET_EXTERNAL_OES)) {
777       gst_gl_value_set_texture_target (&item,
778           GST_GL_TEXTURE_TARGET_EXTERNAL_OES);
779       gst_value_list_append_value (value, &item);
780       ret = TRUE;
781     }
782 
783     g_value_unset (&item);
784     return ret;
785   }
786 }
787 
788 static const gfloat identity_matrix[] = {
789   1.0, 0.0, 0.0, 0.0,
790   0.0, 1.0, 0.0, 0.0,
791   0.0, 0.0, 1.0, 0.0,
792   0.0, 0.0, 0.0, 1.0,
793 };
794 
795 static const gfloat from_ndc_matrix[] = {
796   0.5, 0.0, 0.0, 0.0,
797   0.0, 0.5, 0.0, 0.0,
798   0.0, 0.0, 0.5, 0.0,
799   0.5, 0.5, 0.5, 1.0,
800 };
801 
802 static const gfloat to_ndc_matrix[] = {
803   2.0, 0.0, 0.0, 0.0,
804   0.0, 2.0, 0.0, 0.0,
805   0.0, 0.0, 2.0, 0.0,
806   -1.0, -1.0, -1.0, 1.0,
807 };
808 
809 /* multiplies two 4x4 matrices, @a X @b, and stores the result in @result
810  * https://en.wikipedia.org/wiki/Matrix_multiplication
811  */
812 static void
gst_gl_multiply_matrix4(const gfloat * a,const gfloat * b,gfloat * result)813 gst_gl_multiply_matrix4 (const gfloat * a, const gfloat * b, gfloat * result)
814 {
815   int i, j, k;
816   gfloat tmp[16] = { 0.0f };
817 
818   if (!a || !b || !result)
819     return;
820   for (i = 0; i < 4; i++) {     /* column */
821     for (j = 0; j < 4; j++) {   /* row */
822       for (k = 0; k < 4; k++) {
823         tmp[j + (i * 4)] += a[k + (i * 4)] * b[j + (k * 4)];
824       }
825     }
826   }
827 
828   for (i = 0; i < 16; i++)
829     result[i] = tmp[i];
830 }
831 
832 /*
833  * gst_gl_get_affine_transformation_meta_as_ndc:
834  * @meta: (nullable): a #GstVideoAffineTransformationMeta
835  * @matrix: (out): result of the 4x4 matrix
836  *
837  * Retrieves the stored 4x4 affine transformation matrix stored in @meta in
838  * NDC coordinates. if @meta is NULL, an identity matrix is returned.
839  *
840  * NDC is a left-handed coordinate sytem
841  * - x - [-1, 1] - +ve X moves right
842  * - y - [-1, 1] - +ve Y moves up
843  * - z - [-1, 1] - +ve Z moves into
844  */
845 void
gst_gl_get_affine_transformation_meta_as_ndc(GstVideoAffineTransformationMeta * meta,gfloat * matrix)846 gst_gl_get_affine_transformation_meta_as_ndc (GstVideoAffineTransformationMeta *
847     meta, gfloat * matrix)
848 {
849   if (!meta) {
850     int i;
851 
852     for (i = 0; i < 16; i++) {
853       matrix[i] = identity_matrix[i];
854     }
855   } else {
856     float tmp[16];
857 
858     /* change of basis multiplications */
859     gst_gl_multiply_matrix4 (from_ndc_matrix, meta->matrix, tmp);
860     gst_gl_multiply_matrix4 (tmp, to_ndc_matrix, matrix);
861   }
862 }
863 
gst_gl_set_affine_transformation_meta_from_ndc(GstVideoAffineTransformationMeta * meta,const gfloat * matrix)864 void gst_gl_set_affine_transformation_meta_from_ndc
865     (GstVideoAffineTransformationMeta * meta, const gfloat * matrix)
866 {
867   float tmp[16];
868 
869   g_return_if_fail (meta != NULL);
870 
871   /* change of basis multiplications */
872   gst_gl_multiply_matrix4 (to_ndc_matrix, matrix, tmp);
873   gst_gl_multiply_matrix4 (tmp, from_ndc_matrix, meta->matrix);
874 }
875