3 * Copyright (C) 2013 Matthew Waters <ystreet00@gmail.com>
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.
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.
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.
24 * @short_description: some miscellaneous utilities for OpenGL
25 * @see_also: #GstGLContext
35 #include <glib/gprintf.h>
38 #include "gstglutils.h"
39 #include "gstglutils_private.h"
41 #if GST_GL_HAVE_WINDOW_X11
42 #include <gst/gl/x11/gstgldisplay_x11.h>
44 #if GST_GL_HAVE_WINDOW_WAYLAND
45 #include <gst/gl/wayland/gstgldisplay_wayland.h>
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))
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)
59 static volatile gsize _init = 0;
61 if (g_once_init_enter (&_init)) {
62 GST_DEBUG_CATEGORY_INIT (gst_gl_utils_debug, "glutils", 0,
64 g_once_init_leave (&_init, 1);
67 return gst_gl_utils_debug;
70 #define GST_CAT_DEFAULT _init_gl_utils_debug_category()
74 gst_gl_display_found (GstElement * element, GstGLDisplay * display)
77 GST_LOG_OBJECT (element, "already have a display (%p)", display);
84 GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
87 _init_context_debug (void)
89 #ifndef GST_DISABLE_GST_DEBUG
90 static volatile gsize _init = 0;
92 if (g_once_init_enter (&_init)) {
93 GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
94 g_once_init_leave (&_init, 1);
100 pad_query (const GValue * item, GValue * value, gpointer user_data)
102 GstPad *pad = g_value_get_object (item);
103 GstQuery *query = user_data;
106 _init_context_debug ();
108 res = gst_pad_peer_query (pad, query);
111 g_value_set_boolean (value, TRUE);
115 GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "pad peer query failed");
120 gst_gl_run_query (GstElement * element, GstQuery * query,
121 GstPadDirection direction)
124 GstIteratorFoldFunction func = pad_query;
127 g_value_init (&res, G_TYPE_BOOLEAN);
128 g_value_set_boolean (&res, FALSE);
131 if (direction == GST_PAD_SRC)
132 it = gst_element_iterate_src_pads (element);
134 it = gst_element_iterate_sink_pads (element);
136 while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
137 gst_iterator_resync (it);
139 gst_iterator_free (it);
141 return g_value_get_boolean (&res);
145 _gst_context_query (GstElement * element, const gchar * display_type)
150 _init_context_debug ();
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.
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);
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
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),
180 gst_element_post_message (element, msg);
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().
189 gst_query_unref (query);
193 gst_gl_display_context_query (GstElement * element, GstGLDisplay ** display_ptr)
195 _gst_context_query (element, GST_GL_DISPLAY_CONTEXT_TYPE);
199 #if GST_GL_HAVE_WINDOW_X11
200 _gst_context_query (element, "gst.x11.display.handle");
205 #if GST_GL_HAVE_WINDOW_WAYLAND
206 _gst_context_query (element, "GstWaylandDisplayHandleContextType");
213 gst_gl_context_query (GstElement * element)
215 _gst_context_query (element, "gst.gl.app_context");
218 /* 4) Create a context by itself and post a GST_MESSAGE_HAVE_CONTEXT
222 gst_gl_display_context_propagate (GstElement * element, GstGLDisplay * display)
228 GST_ERROR_OBJECT (element, "Could not get GL display connection");
232 _init_context_debug ();
234 context = gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
235 gst_context_set_gl_display (context, display);
237 gst_element_set_context (element, context);
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);
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
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.
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
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
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.
268 * Returns: whether a #GstGLDisplay exists in @display_ptr
271 gst_gl_ensure_element_data (gpointer element, GstGLDisplay ** display_ptr,
272 GstGLContext ** other_context_ptr)
274 GstGLDisplay *display;
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);
280 /* 1) Check if the element already has a context of the specific
283 display = *display_ptr;
284 if (gst_gl_display_found (element, display))
287 gst_gl_display_context_query (element, display_ptr);
289 /* Neighbour found and it updated the display */
290 if (gst_gl_display_found (element, *display_ptr))
293 /* If no neighboor, or application not interested, use system default */
294 display = gst_gl_display_new ();
296 *display_ptr = display;
298 gst_gl_display_context_propagate (element, display);
301 if (*other_context_ptr)
304 gst_gl_context_query (element);
307 return *display_ptr != NULL;
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
317 * Helper function for implementing GstElement::set_context() in OpenGL capable
320 * Retrieve's the #GstGLDisplay or #GstGLContext in @context and places the
321 * result in @display or @other_context respectively.
323 * Returns: whether the @display or @other_context could be set successfully
326 gst_gl_handle_set_context (GstElement * element, GstContext * context,
327 GstGLDisplay ** display, GstGLContext ** other_context)
329 GstGLDisplay *display_replacement = NULL;
330 GstGLContext *context_replacement = NULL;
331 const gchar *context_type;
333 g_return_val_if_fail (display != NULL, FALSE);
334 g_return_val_if_fail (other_context != NULL, FALSE);
339 context_type = gst_context_get_context_type (context);
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");
347 #if GST_GL_HAVE_WINDOW_X11
348 else if (g_strcmp0 (context_type, "gst.x11.display.handle") == 0) {
349 const GstStructure *s;
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);
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;
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);
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;
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;
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;
387 gst_object_unref (context_display);
391 if (display_replacement) {
392 GstGLDisplay *old = *display;
393 *display = display_replacement;
396 gst_object_unref (old);
399 if (context_replacement) {
400 GstGLContext *old = *other_context;
401 *other_context = context_replacement;
404 gst_object_unref (old);
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
418 * Returns: Whether the @query was successfully responded to from the passed
419 * @display, @context, and @other_context.
422 gst_gl_handle_context_query (GstElement * element, GstQuery * query,
423 GstGLDisplay * display, GstGLContext * gl_context,
424 GstGLContext * other_context)
426 const gchar *context_type;
427 GstContext *context, *old_context;
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);
437 GST_LOG_OBJECT (element, "handle context query %" GST_PTR_FORMAT, query);
438 gst_query_parse_context_type (query, &context_type);
440 if (display && g_strcmp0 (context_type, GST_GL_DISPLAY_CONTEXT_TYPE) == 0) {
441 gst_query_parse_context (query, &old_context);
444 context = gst_context_copy (old_context);
446 context = gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
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);
456 #if GST_GL_HAVE_WINDOW_X11
457 else if (display && g_strcmp0 (context_type, "gst.x11.display.handle") == 0) {
460 gst_query_parse_context (query, &old_context);
463 context = gst_context_copy (old_context);
465 context = gst_context_new ("gst.x11.display.handle", TRUE);
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);
471 s = gst_context_writable_structure (context);
472 gst_structure_set (s, "display", G_TYPE_POINTER, x11_display, NULL);
474 gst_query_set_context (query, context);
475 gst_context_unref (context);
477 GST_DEBUG_OBJECT (element, "successfully set x11 display %p (from %"
478 GST_PTR_FORMAT ") on %" GST_PTR_FORMAT, x11_display, display,
486 #if GST_GL_HAVE_WINDOW_WAYLAND
488 && g_strcmp0 (context_type, "GstWaylandDisplayHandleContextType") == 0) {
491 gst_query_parse_context (query, &old_context);
494 context = gst_context_copy (old_context);
496 context = gst_context_new ("GstWaylandDisplayHandleContextType", TRUE);
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);
502 if (wayland_display) {
503 s = gst_context_writable_structure (context);
504 gst_structure_set (s, "display", G_TYPE_POINTER, wayland_display, NULL);
506 gst_query_set_context (query, context);
507 gst_context_unref (context);
509 GST_DEBUG_OBJECT (element, "successfully set wayland display %p (from %"
510 GST_PTR_FORMAT ") on %" GST_PTR_FORMAT, wayland_display, display,
518 else if (other_context && g_strcmp0 (context_type, "gst.gl.app_context") == 0) {
521 gst_query_parse_context (query, &old_context);
524 context = gst_context_copy (old_context);
526 context = gst_context_new ("gst.gl.app_context", TRUE);
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);
533 GST_DEBUG_OBJECT (element, "successfully set application GL context %"
534 GST_PTR_FORMAT " on %" GST_PTR_FORMAT, other_context, query);
537 } else if (gl_context
538 && g_strcmp0 (context_type, "gst.gl.local_context") == 0) {
541 gst_query_parse_context (query, &old_context);
544 context = gst_context_copy (old_context);
546 context = gst_context_new ("gst.gl.local_context", TRUE);
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);
553 GST_DEBUG_OBJECT (element, "successfully set GL context %"
554 GST_PTR_FORMAT " on %" GST_PTR_FORMAT, gl_context, query);
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
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.
573 * Returns: whether @context_ptr contains a #GstGLContext
576 gst_gl_query_local_gl_context (GstElement * element, GstPadDirection direction,
577 GstGLContext ** context_ptr)
581 const GstStructure *s;
583 g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
584 g_return_val_if_fail (context_ptr != NULL, FALSE);
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);
593 s = gst_context_get_structure (context);
594 gst_structure_get (s, "context", GST_TYPE_GL_CONTEXT, context_ptr, NULL);
598 gst_query_unref (query);
600 return *context_ptr != NULL;
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
609 * Retrieve the size in bytes of a video plane of data with a certain alignment
612 gst_gl_get_plane_data_size (GstVideoInfo * info, GstVideoAlignment * align,
618 padded_height = info->height;
621 padded_height += align->padding_top + align->padding_bottom;
624 GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, plane, padded_height);
626 plane_size = GST_VIDEO_INFO_PLANE_STRIDE (info, plane) * padded_height;
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
637 * Returns: difference between the supposed start of the plane from the @info
638 * and where the data from the previous plane ends.
641 gst_gl_get_plane_start (GstVideoInfo * info, GstVideoAlignment * valign,
647 /* find the start of the plane data including padding */
649 for (i = 0; i < plane; i++) {
650 plane_start += gst_gl_get_plane_data_size (info, valign, i);
653 /* offset between the plane data start and where the video frame starts */
654 return (GST_VIDEO_INFO_PLANE_OFFSET (info, plane)) - plane_start;
658 * gst_gl_value_get_texture_target_mask:
659 * @value: an initialized #GValue of type G_TYPE_STRING
661 * See gst_gl_value_set_texture_target_from_mask() for what entails a mask
663 * Returns: the mask of #GstGLTextureTarget's in @value
666 gst_gl_value_get_texture_target_mask (const GValue * targets)
668 guint new_targets = 0;
670 g_return_val_if_fail (targets != NULL, GST_GL_TEXTURE_TARGET_NONE);
672 if (G_TYPE_CHECK_VALUE_TYPE (targets, G_TYPE_STRING)) {
673 GstGLTextureTarget target;
676 str = g_value_get_string (targets);
677 target = gst_gl_texture_target_from_string (str);
680 new_targets |= 1 << target;
681 } else if (G_TYPE_CHECK_VALUE_TYPE (targets, GST_TYPE_LIST)) {
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;
690 str = g_value_get_string (val);
691 target = gst_gl_texture_target_from_string (str);
693 new_targets |= 1 << target;
701 * gst_gl_value_set_texture_target:
702 * @value: an initialized #GValue of type G_TYPE_STRING
703 * @target: a #GstGLTextureTarget's
705 * Returns: whether the @target could be set on @value
708 gst_gl_value_set_texture_target (GValue * value, GstGLTextureTarget target)
710 g_return_val_if_fail (value != NULL, FALSE);
711 g_return_val_if_fail (target != GST_GL_TEXTURE_TARGET_NONE, FALSE);
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);
727 _gst_gl_log2_int64 (guint64 value)
738 * gst_gl_value_set_texture_target_from_mask:
739 * @value: an uninitialized #GValue
740 * @target_mask: a bitwise mask of #GstGLTextureTarget's
742 * A mask is a bitwise OR of (1 << target) where target is a valid
743 * #GstGLTextureTarget
745 * Returns: whether the @target_mask could be set on @value
748 gst_gl_value_set_texture_target_from_mask (GValue * value,
749 GstGLTextureTarget target_mask)
751 g_return_val_if_fail (value != NULL, FALSE);
752 g_return_val_if_fail (target_mask != GST_GL_TEXTURE_TARGET_NONE, FALSE);
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));
760 GValue item = G_VALUE_INIT;
761 gboolean ret = FALSE;
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);
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);
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);
782 g_value_unset(&item);
787 static const gfloat identity_matrix[] = {
788 1.0f, 0.0f, 0.0, 0.0f,
789 0.0f, 1.0f, 0.0, 0.0f,
790 0.0f, 0.0f, 1.0, 0.0f,
791 0.0f, 0.0f, 0.0, 1.0f,
794 static const gfloat from_ndc_matrix[] = {
795 0.5f, 0.0f, 0.0, 0.5f,
796 0.0f, 0.5f, 0.0, 0.5f,
797 0.0f, 0.0f, 0.5, 0.5f,
798 0.0f, 0.0f, 0.0, 1.0f,
801 static const gfloat to_ndc_matrix[] = {
802 2.0f, 0.0f, 0.0, -1.0f,
803 0.0f, 2.0f, 0.0, -1.0f,
804 0.0f, 0.0f, 2.0, -1.0f,
805 0.0f, 0.0f, 0.0, 1.0f,
809 gst_gl_multiply_matrix4 (const gfloat * a, const gfloat * b, gfloat * result)
812 gfloat tmp[16] = { 0.0f };
814 if (!a || !b || !result)
817 for (i = 0; i < 4; i++) {
818 for (j = 0; j < 4; j++) {
819 for (k = 0; k < 4; k++) {
820 tmp[i + (j * 4)] += a[i + (k * 4)] * b[k + (j * 4)];
825 for (i = 0; i < 16; i++)
830 gst_gl_get_affine_transformation_meta_as_ndc (GstVideoAffineTransformationMeta *
831 meta, gfloat * matrix)
836 for (i = 0; i < 16; i++) {
837 matrix[i] = identity_matrix[i];
840 gfloat tmp[16] = { 0.0f };
842 gst_gl_multiply_matrix4 (from_ndc_matrix, meta->matrix, tmp);
843 gst_gl_multiply_matrix4 (tmp, to_ndc_matrix, matrix);