glutils: fix matrix operations everywhere
[platform/upstream/gstreamer.git] / gst-libs / gst / gl / gstglutils.c
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 *
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
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
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
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
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
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
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
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 static void
222 gst_gl_display_context_propagate (GstElement * element, GstGLDisplay * display)
223 {
224   GstContext *context;
225   GstMessage *msg;
226
227   if (!display) {
228     GST_ERROR_OBJECT (element, "Could not get GL display connection");
229     return;
230   }
231
232   _init_context_debug ();
233
234   context = gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
235   gst_context_set_gl_display (context, display);
236
237   gst_element_set_context (element, context);
238
239   GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
240       "posting have context (%p) message with display (%p)", context, display);
241   msg = gst_message_new_have_context (GST_OBJECT_CAST (element), context);
242   gst_element_post_message (GST_ELEMENT_CAST (element), msg);
243 }
244
245 /**
246  * gst_gl_ensure_element_data:
247  * @element: the #GstElement running the query
248  * @display_ptr: (inout): the resulting #GstGLDisplay
249  * @other_context_ptr: (inout): the resulting #GstGLContext
250  *
251  * Perform the steps necessary for retrieving a #GstGLDisplay and (optionally)
252  * an application provided #GstGLContext from the surrounding elements or from
253  * the application using the #GstContext mechanism.
254  *
255  * If the contents of @display_ptr or @other_context_ptr are not %NULL, then no
256  * #GstContext query is necessary for #GstGLDisplay or #GstGLContext retrieval
257  * or is performed.
258  *
259  * This performs #GstContext queries (if necessary) for a winsys display
260  * connection with %GST_GL_DISPLAY_CONTEXT_TYPE, "gst.x11.display.handle", and
261  * "GstWaylandDisplayHandleContextType" stopping after the first successful
262  * retrieval.
263  *
264  * This also performs a #GstContext query (if necessary) for an optional
265  * application provided #GstGLContext using the name "gst.gl.app_context".
266  * The returned #GstGLContext will be shared with a GStreamer created OpenGL context.
267  *
268  * Returns: whether a #GstGLDisplay exists in @display_ptr
269  */
270 gboolean
271 gst_gl_ensure_element_data (gpointer element, GstGLDisplay ** display_ptr,
272     GstGLContext ** other_context_ptr)
273 {
274   GstGLDisplay *display;
275
276   g_return_val_if_fail (element != NULL, FALSE);
277   g_return_val_if_fail (display_ptr != NULL, FALSE);
278   g_return_val_if_fail (other_context_ptr != NULL, FALSE);
279
280   /*  1) Check if the element already has a context of the specific
281    *     type.
282    */
283   display = *display_ptr;
284   if (gst_gl_display_found (element, display))
285     goto done;
286
287   gst_gl_display_context_query (element, display_ptr);
288
289   /* Neighbour found and it updated the display */
290   if (gst_gl_display_found (element, *display_ptr))
291     goto get_gl_context;
292
293   /* If no neighboor, or application not interested, use system default */
294   display = gst_gl_display_new ();
295
296   *display_ptr = display;
297
298   gst_gl_display_context_propagate (element, display);
299
300 get_gl_context:
301   if (*other_context_ptr)
302     goto done;
303
304   gst_gl_context_query (element);
305
306 done:
307   return *display_ptr != NULL;
308 }
309
310 /**
311  * gst_gl_handle_set_context:
312  * @element: a #GstElement
313  * @context: a #GstContext
314  * @display: (inout) (transfer full): location of a #GstGLDisplay
315  * @other_context: (inout) (transfer full): location of a #GstGLContext
316  *
317  * Helper function for implementing GstElement::set_context() in OpenGL capable
318  * elements.
319  *
320  * Retrieve's the #GstGLDisplay or #GstGLContext in @context and places the
321  * result in @display or @other_context respectively.
322  *
323  * Returns: whether the @display or @other_context could be set successfully
324  */
325 gboolean
326 gst_gl_handle_set_context (GstElement * element, GstContext * context,
327     GstGLDisplay ** display, GstGLContext ** other_context)
328 {
329   GstGLDisplay *display_replacement = NULL;
330   GstGLContext *context_replacement = NULL;
331   const gchar *context_type;
332
333   g_return_val_if_fail (display != NULL, FALSE);
334   g_return_val_if_fail (other_context != NULL, FALSE);
335
336   if (!context)
337     return FALSE;
338
339   context_type = gst_context_get_context_type (context);
340
341   if (g_strcmp0 (context_type, GST_GL_DISPLAY_CONTEXT_TYPE) == 0) {
342     if (!gst_context_get_gl_display (context, &display_replacement)) {
343       GST_WARNING_OBJECT (element, "Failed to get display from context");
344       return FALSE;
345     }
346   }
347 #if GST_GL_HAVE_WINDOW_X11
348   else if (g_strcmp0 (context_type, "gst.x11.display.handle") == 0) {
349     const GstStructure *s;
350     Display *display;
351
352     s = gst_context_get_structure (context);
353     if (gst_structure_get (s, "display", G_TYPE_POINTER, &display, NULL))
354       display_replacement =
355           (GstGLDisplay *) gst_gl_display_x11_new_with_display (display);
356   }
357 #endif
358 #if GST_GL_HAVE_WINDOW_WAYLAND
359   else if (g_strcmp0 (context_type, "GstWaylandDisplayHandleContextType") == 0) {
360     const GstStructure *s;
361     struct wl_display *display;
362
363     s = gst_context_get_structure (context);
364     if (gst_structure_get (s, "display", G_TYPE_POINTER, &display, NULL))
365       display_replacement =
366           (GstGLDisplay *) gst_gl_display_wayland_new_with_display (display);
367   }
368 #endif
369   else if (g_strcmp0 (context_type, "gst.gl.app_context") == 0) {
370     const GstStructure *s = gst_context_get_structure (context);
371     GstGLDisplay *context_display;
372     GstGLDisplay *element_display;
373
374     if (gst_structure_get (s, "context", GST_TYPE_GL_CONTEXT,
375             &context_replacement, NULL)) {
376       context_display = gst_gl_context_get_display (context_replacement);
377       element_display = display_replacement ? display_replacement : *display;
378       if (element_display
379           && (gst_gl_display_get_handle_type (element_display) &
380               gst_gl_display_get_handle_type (context_display)) == 0) {
381         GST_ELEMENT_WARNING (element, LIBRARY, SETTINGS, ("%s",
382                 "Cannot set a GL context with a different display type"), ("%s",
383                 "Cannot set a GL context with a different display type"));
384         gst_object_unref (context_replacement);
385         context_replacement = NULL;
386       }
387       gst_object_unref (context_display);
388     }
389   }
390
391   if (display_replacement) {
392     GstGLDisplay *old = *display;
393     *display = display_replacement;
394
395     if (old)
396       gst_object_unref (old);
397   }
398
399   if (context_replacement) {
400     GstGLContext *old = *other_context;
401     *other_context = context_replacement;
402
403     if (old)
404       gst_object_unref (old);
405   }
406
407   return TRUE;
408 }
409
410 /**
411  * gst_gl_handle_context_query:
412  * @element: a #GstElement
413  * @query: a #GstQuery of type %GST_QUERY_CONTEXT
414  * @display: (transfer none) (nullable): a #GstGLDisplay
415  * @context: (transfer none) (nullable): a #GstGLContext
416  * @other_context: (transfer none) (nullable): application provided #GstGLContext
417  *
418  * Returns: Whether the @query was successfully responded to from the passed
419  *          @display, @context, and @other_context.
420  */
421 gboolean
422 gst_gl_handle_context_query (GstElement * element, GstQuery * query,
423     GstGLDisplay * display, GstGLContext * gl_context,
424     GstGLContext * other_context)
425 {
426   const gchar *context_type;
427   GstContext *context, *old_context;
428
429   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
430   g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
431   g_return_val_if_fail (display == NULL || GST_IS_GL_DISPLAY (display), FALSE);
432   g_return_val_if_fail (gl_context == NULL
433       || GST_IS_GL_CONTEXT (gl_context), FALSE);
434   g_return_val_if_fail (other_context == NULL
435       || GST_IS_GL_CONTEXT (other_context), FALSE);
436
437   GST_LOG_OBJECT (element, "handle context query %" GST_PTR_FORMAT, query);
438   gst_query_parse_context_type (query, &context_type);
439
440   if (display && g_strcmp0 (context_type, GST_GL_DISPLAY_CONTEXT_TYPE) == 0) {
441     gst_query_parse_context (query, &old_context);
442
443     if (old_context)
444       context = gst_context_copy (old_context);
445     else
446       context = gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
447
448     gst_context_set_gl_display (context, display);
449     gst_query_set_context (query, context);
450     gst_context_unref (context);
451     GST_DEBUG_OBJECT (element, "successfully set %" GST_PTR_FORMAT
452         " on %" GST_PTR_FORMAT, display, query);
453
454     return TRUE;
455   }
456 #if GST_GL_HAVE_WINDOW_X11
457   else if (display && g_strcmp0 (context_type, "gst.x11.display.handle") == 0) {
458     GstStructure *s;
459
460     gst_query_parse_context (query, &old_context);
461
462     if (old_context)
463       context = gst_context_copy (old_context);
464     else
465       context = gst_context_new ("gst.x11.display.handle", TRUE);
466
467     if (gst_gl_display_get_handle_type (display) & GST_GL_DISPLAY_TYPE_X11) {
468       Display *x11_display = (Display *) gst_gl_display_get_handle (display);
469
470       if (x11_display) {
471         s = gst_context_writable_structure (context);
472         gst_structure_set (s, "display", G_TYPE_POINTER, x11_display, NULL);
473
474         gst_query_set_context (query, context);
475         gst_context_unref (context);
476
477         GST_DEBUG_OBJECT (element, "successfully set x11 display %p (from %"
478             GST_PTR_FORMAT ") on %" GST_PTR_FORMAT, x11_display, display,
479             query);
480
481         return TRUE;
482       }
483     }
484   }
485 #endif
486 #if GST_GL_HAVE_WINDOW_WAYLAND
487   else if (display
488       && g_strcmp0 (context_type, "GstWaylandDisplayHandleContextType") == 0) {
489     GstStructure *s;
490
491     gst_query_parse_context (query, &old_context);
492
493     if (old_context)
494       context = gst_context_copy (old_context);
495     else
496       context = gst_context_new ("GstWaylandDisplayHandleContextType", TRUE);
497
498     if (gst_gl_display_get_handle_type (display) & GST_GL_DISPLAY_TYPE_WAYLAND) {
499       struct wl_display *wayland_display =
500           (struct wl_display *) gst_gl_display_get_handle (display);
501
502       if (wayland_display) {
503         s = gst_context_writable_structure (context);
504         gst_structure_set (s, "display", G_TYPE_POINTER, wayland_display, NULL);
505
506         gst_query_set_context (query, context);
507         gst_context_unref (context);
508
509         GST_DEBUG_OBJECT (element, "successfully set wayland display %p (from %"
510             GST_PTR_FORMAT ") on %" GST_PTR_FORMAT, wayland_display, display,
511             query);
512
513         return TRUE;
514       }
515     }
516   }
517 #endif
518   else if (other_context && g_strcmp0 (context_type, "gst.gl.app_context") == 0) {
519     GstStructure *s;
520
521     gst_query_parse_context (query, &old_context);
522
523     if (old_context)
524       context = gst_context_copy (old_context);
525     else
526       context = gst_context_new ("gst.gl.app_context", TRUE);
527
528     s = gst_context_writable_structure (context);
529     gst_structure_set (s, "context", GST_TYPE_GL_CONTEXT, other_context, NULL);
530     gst_query_set_context (query, context);
531     gst_context_unref (context);
532
533     GST_DEBUG_OBJECT (element, "successfully set application GL context %"
534         GST_PTR_FORMAT " on %" GST_PTR_FORMAT, other_context, query);
535
536     return TRUE;
537   } else if (gl_context
538       && g_strcmp0 (context_type, "gst.gl.local_context") == 0) {
539     GstStructure *s;
540
541     gst_query_parse_context (query, &old_context);
542
543     if (old_context)
544       context = gst_context_copy (old_context);
545     else
546       context = gst_context_new ("gst.gl.local_context", TRUE);
547
548     s = gst_context_writable_structure (context);
549     gst_structure_set (s, "context", GST_TYPE_GL_CONTEXT, gl_context, NULL);
550     gst_query_set_context (query, context);
551     gst_context_unref (context);
552
553     GST_DEBUG_OBJECT (element, "successfully set GL context %"
554         GST_PTR_FORMAT " on %" GST_PTR_FORMAT, gl_context, query);
555
556     return TRUE;
557   }
558
559   return FALSE;
560 }
561
562 /**
563  * gst_gl_query_local_gl_context:
564  * @element: a #GstElement to query from
565  * @direction: the #GstPadDirection to query
566  * @context_ptr: (inout): location containing the current and/or resulting
567  *                      #GstGLContext
568  *
569  * Performs a GST_QUERY_CONTEXT query of type "gst.gl.local_context" on all
570  * #GstPads in @element of @direction for the local OpenGL context used by
571  * GStreamer elements.
572  *
573  * Returns: whether @context_ptr contains a #GstGLContext
574  */
575 gboolean
576 gst_gl_query_local_gl_context (GstElement * element, GstPadDirection direction,
577     GstGLContext ** context_ptr)
578 {
579   GstQuery *query;
580   GstContext *context;
581   const GstStructure *s;
582
583   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
584   g_return_val_if_fail (context_ptr != NULL, FALSE);
585
586   if (*context_ptr)
587     return TRUE;
588
589   query = gst_query_new_context ("gst.gl.local_context");
590   if (gst_gl_run_query (GST_ELEMENT (element), query, direction)) {
591     gst_query_parse_context (query, &context);
592     if (context) {
593       s = gst_context_get_structure (context);
594       gst_structure_get (s, "context", GST_TYPE_GL_CONTEXT, context_ptr, NULL);
595     }
596   }
597
598   gst_query_unref (query);
599
600   return *context_ptr != NULL;
601 }
602
603 /**
604  * gst_gl_get_plane_data_size:
605  * @info: a #GstVideoInfo
606  * @align: a #GstVideoAlignment or %NULL
607  * @plane: plane number in @info to retrieve the data size of
608  *
609  * Retrieve the size in bytes of a video plane of data with a certain alignment
610  */
611 gsize
612 gst_gl_get_plane_data_size (GstVideoInfo * info, GstVideoAlignment * align,
613     guint plane)
614 {
615   gint padded_height;
616   gsize plane_size;
617
618   padded_height = info->height;
619
620   if (align)
621     padded_height += align->padding_top + align->padding_bottom;
622
623   padded_height =
624       GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, plane, padded_height);
625
626   plane_size = GST_VIDEO_INFO_PLANE_STRIDE (info, plane) * padded_height;
627
628   return plane_size;
629 }
630
631 /**
632  * gst_gl_get_plane_start:
633  * @info: a #GstVideoInfo
634  * @valign: a #GstVideoAlignment or %NULL
635  * @plane: plane number in @info to retrieve the data size of
636  *
637  * Returns: difference between the supposed start of the plane from the @info
638  *          and where the data from the previous plane ends.
639  */
640 gsize
641 gst_gl_get_plane_start (GstVideoInfo * info, GstVideoAlignment * valign,
642     guint plane)
643 {
644   gsize plane_start;
645   gint i;
646
647   /* find the start of the plane data including padding */
648   plane_start = 0;
649   for (i = 0; i < plane; i++) {
650     plane_start += gst_gl_get_plane_data_size (info, valign, i);
651   }
652
653   /* offset between the plane data start and where the video frame starts */
654   return (GST_VIDEO_INFO_PLANE_OFFSET (info, plane)) - plane_start;
655 }
656
657 /**
658  * gst_gl_value_get_texture_target_mask:
659  * @value: an initialized #GValue of type G_TYPE_STRING
660  *
661  * See gst_gl_value_set_texture_target_from_mask() for what entails a mask
662  *
663  * Returns: the mask of #GstGLTextureTarget's in @value
664  */
665 GstGLTextureTarget
666 gst_gl_value_get_texture_target_mask (const GValue * targets)
667 {
668   guint new_targets = 0;
669
670   g_return_val_if_fail (targets != NULL, GST_GL_TEXTURE_TARGET_NONE);
671
672   if (G_TYPE_CHECK_VALUE_TYPE (targets, G_TYPE_STRING)) {
673     GstGLTextureTarget target;
674     const gchar *str;
675
676     str = g_value_get_string (targets);
677     target = gst_gl_texture_target_from_string (str);
678
679     if (target)
680       new_targets |= 1 << target;
681   } else if (G_TYPE_CHECK_VALUE_TYPE (targets, GST_TYPE_LIST)) {
682     gint j, m;
683
684     m = gst_value_list_get_size (targets);
685     for (j = 0; j < m; j++) {
686       const GValue *val = gst_value_list_get_value (targets, j);
687       GstGLTextureTarget target;
688       const gchar *str;
689
690       str = g_value_get_string (val);
691       target = gst_gl_texture_target_from_string (str);
692       if (target)
693         new_targets |= 1 << target;
694     }
695   }
696
697   return new_targets;
698 }
699
700 /**
701  * gst_gl_value_set_texture_target:
702  * @value: an initialized #GValue of type G_TYPE_STRING
703  * @target: a #GstGLTextureTarget's
704  *
705  * Returns: whether the @target could be set on @value
706  */
707 gboolean
708 gst_gl_value_set_texture_target (GValue * value, GstGLTextureTarget target)
709 {
710   g_return_val_if_fail (value != NULL, FALSE);
711   g_return_val_if_fail (target != GST_GL_TEXTURE_TARGET_NONE, FALSE);
712
713   if (target == GST_GL_TEXTURE_TARGET_2D) {
714     g_value_set_static_string (value, GST_GL_TEXTURE_TARGET_2D_STR);
715   } else if (target == GST_GL_TEXTURE_TARGET_RECTANGLE) {
716     g_value_set_static_string (value, GST_GL_TEXTURE_TARGET_RECTANGLE_STR);
717   } else if (target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) {
718     g_value_set_static_string (value, GST_GL_TEXTURE_TARGET_EXTERNAL_OES_STR);
719   } else {
720     return FALSE;
721   }
722
723   return TRUE;
724 }
725
726 static guint64
727 _gst_gl_log2_int64 (guint64 value)
728 {
729   guint64 ret = 0;
730
731   while (value >>= 1)
732     ret++;
733
734   return ret;
735 }
736
737 /**
738  * gst_gl_value_set_texture_target_from_mask:
739  * @value: an uninitialized #GValue
740  * @target_mask: a bitwise mask of #GstGLTextureTarget's
741  *
742  * A mask is a bitwise OR of (1 << target) where target is a valid
743  * #GstGLTextureTarget
744  *
745  * Returns: whether the @target_mask could be set on @value
746  */
747 gboolean
748 gst_gl_value_set_texture_target_from_mask (GValue * value,
749     GstGLTextureTarget target_mask)
750 {
751   g_return_val_if_fail (value != NULL, FALSE);
752   g_return_val_if_fail (target_mask != GST_GL_TEXTURE_TARGET_NONE, FALSE);
753
754   if ((target_mask & (target_mask - 1)) == 0) {
755     /* only one texture target set */
756     g_value_init (value, G_TYPE_STRING);
757     return gst_gl_value_set_texture_target (value,
758         _gst_gl_log2_int64 (target_mask));
759   } else {
760     GValue item = G_VALUE_INIT;
761     gboolean ret = FALSE;
762
763     g_value_init (value, GST_TYPE_LIST);
764     g_value_init (&item, G_TYPE_STRING);
765     if (target_mask & (1 << GST_GL_TEXTURE_TARGET_2D)) {
766       gst_gl_value_set_texture_target (&item, GST_GL_TEXTURE_TARGET_2D);
767       gst_value_list_append_value (value, &item);
768       ret = TRUE;
769     }
770     if (target_mask & (1 << GST_GL_TEXTURE_TARGET_RECTANGLE)) {
771       gst_gl_value_set_texture_target (&item, GST_GL_TEXTURE_TARGET_RECTANGLE);
772       gst_value_list_append_value (value, &item);
773       ret = TRUE;
774     }
775     if (target_mask & (1 << GST_GL_TEXTURE_TARGET_EXTERNAL_OES)) {
776       gst_gl_value_set_texture_target (&item,
777           GST_GL_TEXTURE_TARGET_EXTERNAL_OES);
778       gst_value_list_append_value (value, &item);
779       ret = TRUE;
780     }
781
782     g_value_unset (&item);
783     return ret;
784   }
785 }
786
787 static const gfloat identity_matrix[] = {
788   1.0, 0.0, 0.0, 0.0,
789   0.0, 1.0, 0.0, 0.0,
790   0.0, 0.0, 1.0, 0.0,
791   0.0, 0.0, 0.0, 1.0,
792 };
793
794 static const gfloat from_ndc_matrix[] = {
795   0.5, 0.0, 0.0, 0.0,
796   0.0, 0.5, 0.0, 0.0,
797   0.0, 0.0, 0.5, 0.0,
798   0.5, 0.5, 0.5, 1.0,
799 };
800
801 static const gfloat to_ndc_matrix[] = {
802   2.0, 0.0, 0.0, 0.0,
803   0.0, 2.0, 0.0, 0.0,
804   0.0, 0.0, 2.0, 0.0,
805   -1.0, -1.0, -1.0, 1.0,
806 };
807
808 /* multiplies two 4x4 matrices, @a X @b, and stores the result in @result
809  * https://en.wikipedia.org/wiki/Matrix_multiplication
810  */
811 static void
812 gst_gl_multiply_matrix4 (const gfloat * a, const gfloat * b, gfloat * result)
813 {
814   int i, j, k;
815   gfloat tmp[16] = { 0.0f };
816
817   if (!a || !b || !result)
818     return;
819   for (i = 0; i < 4; i++) {     /* column */
820     for (j = 0; j < 4; j++) {   /* row */
821       for (k = 0; k < 4; k++) {
822         tmp[j + (i * 4)] += a[k + (i * 4)] * b[j + (k * 4)];
823       }
824     }
825   }
826
827   for (i = 0; i < 16; i++)
828     result[i] = tmp[i];
829 }
830
831 /*
832  * gst_gl_get_affine_transformation_meta_as_ndc:
833  * @meta: (nullable): a #GstVideoAffineTransformationMeta
834  * @matrix: (out): result of the 4x4 matrix
835  *
836  * Retrieves the stored 4x4 affine transformation matrix stored in @meta in
837  * NDC coordinates. if @meta is NULL, an identity matrix is returned.
838  *
839  * NDC is a left-handed coordinate sytem
840  * - x - [-1, 1] - +ve X moves right
841  * - y - [-1, 1] - +ve Y moves up
842  * - z - [-1, 1] - +ve Z moves into
843  */
844 void
845 gst_gl_get_affine_transformation_meta_as_ndc (GstVideoAffineTransformationMeta *
846     meta, gfloat * matrix)
847 {
848   if (!meta) {
849     int i;
850
851     for (i = 0; i < 16; i++) {
852       matrix[i] = identity_matrix[i];
853     }
854   } else {
855     float tmp[16];
856
857     /* change of basis multiplications */
858     gst_gl_multiply_matrix4 (to_ndc_matrix, meta->matrix, tmp);
859     gst_gl_multiply_matrix4 (tmp, from_ndc_matrix, matrix);
860   }
861 }
862
863 void gst_gl_set_affine_transformation_meta_from_ndc
864     (GstVideoAffineTransformationMeta * meta, const gfloat * matrix)
865 {
866   float tmp[16];
867
868   g_return_if_fail (meta != NULL);
869
870   /* change of basis multiplications */
871   gst_gl_multiply_matrix4 (from_ndc_matrix, matrix, tmp);
872   gst_gl_multiply_matrix4 (tmp, to_ndc_matrix, meta->matrix);
873 }