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 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 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: (type Gst.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 neighbor, 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: (out) (transfer full): location of a #GstGLDisplay
316 * @other_context: (out) (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(const GstVideoInfo * info,const GstVideoAlignment * align,guint plane)613 gst_gl_get_plane_data_size (const GstVideoInfo * info,
614 const GstVideoAlignment * align, guint plane)
615 {
616 gint comp[GST_VIDEO_MAX_COMPONENTS];
617 gint padded_height;
618 gsize plane_size;
619
620 gst_video_format_info_component (info->finfo, plane, comp);
621
622 padded_height = info->height;
623
624 if (align)
625 padded_height += align->padding_top + align->padding_bottom;
626
627 padded_height =
628 GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, comp[0], padded_height);
629
630 plane_size = GST_VIDEO_INFO_PLANE_STRIDE (info, plane) * padded_height;
631
632 return plane_size;
633 }
634
635 /**
636 * gst_gl_get_plane_start:
637 * @info: a #GstVideoInfo
638 * @valign: a #GstVideoAlignment or %NULL
639 * @plane: plane number in @info to retrieve the data size of
640 *
641 * Returns: difference between the supposed start of the plane from the @info
642 * and where the data from the previous plane ends.
643 */
644 gsize
gst_gl_get_plane_start(const GstVideoInfo * info,const GstVideoAlignment * valign,guint plane)645 gst_gl_get_plane_start (const GstVideoInfo * info,
646 const GstVideoAlignment * valign, guint plane)
647 {
648 gsize plane_start;
649 gint i;
650
651 /* find the start of the plane data including padding */
652 plane_start = 0;
653 for (i = 0; i < plane; i++) {
654 plane_start += gst_gl_get_plane_data_size (info, valign, i);
655 }
656
657 /* offset between the plane data start and where the video frame starts */
658 return (GST_VIDEO_INFO_PLANE_OFFSET (info, plane)) - plane_start;
659 }
660
661 /**
662 * gst_gl_value_get_texture_target_mask:
663 * @value: an initialized #GValue of type G_TYPE_STRING
664 *
665 * See gst_gl_value_set_texture_target_from_mask() for what entails a mask
666 *
667 * Returns: the mask of #GstGLTextureTarget's in @value or
668 * %GST_GL_TEXTURE_TARGET_NONE on failure
669 */
670 GstGLTextureTarget
gst_gl_value_get_texture_target_mask(const GValue * targets)671 gst_gl_value_get_texture_target_mask (const GValue * targets)
672 {
673 guint new_targets = 0;
674
675 g_return_val_if_fail (targets != NULL, GST_GL_TEXTURE_TARGET_NONE);
676
677 if (G_TYPE_CHECK_VALUE_TYPE (targets, G_TYPE_STRING)) {
678 GstGLTextureTarget target;
679 const gchar *str;
680
681 str = g_value_get_string (targets);
682 target = gst_gl_texture_target_from_string (str);
683
684 if (target)
685 new_targets |= 1 << target;
686 } else if (G_TYPE_CHECK_VALUE_TYPE (targets, GST_TYPE_LIST)) {
687 gint j, m;
688
689 m = gst_value_list_get_size (targets);
690 for (j = 0; j < m; j++) {
691 const GValue *val = gst_value_list_get_value (targets, j);
692 GstGLTextureTarget target;
693 const gchar *str;
694
695 str = g_value_get_string (val);
696 target = gst_gl_texture_target_from_string (str);
697 if (target)
698 new_targets |= 1 << target;
699 }
700 }
701
702 return new_targets;
703 }
704
705 /**
706 * gst_gl_value_set_texture_target:
707 * @value: an initialized #GValue of type G_TYPE_STRING
708 * @target: a #GstGLTextureTarget's
709 *
710 * Returns: whether the @target could be set on @value
711 */
712 gboolean
gst_gl_value_set_texture_target(GValue * value,GstGLTextureTarget target)713 gst_gl_value_set_texture_target (GValue * value, GstGLTextureTarget target)
714 {
715 g_return_val_if_fail (value != NULL, FALSE);
716 g_return_val_if_fail (target != GST_GL_TEXTURE_TARGET_NONE, FALSE);
717
718 if (target == GST_GL_TEXTURE_TARGET_2D) {
719 g_value_set_static_string (value, GST_GL_TEXTURE_TARGET_2D_STR);
720 } else if (target == GST_GL_TEXTURE_TARGET_RECTANGLE) {
721 g_value_set_static_string (value, GST_GL_TEXTURE_TARGET_RECTANGLE_STR);
722 } else if (target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) {
723 g_value_set_static_string (value, GST_GL_TEXTURE_TARGET_EXTERNAL_OES_STR);
724 } else {
725 return FALSE;
726 }
727
728 return TRUE;
729 }
730
731 static guint64
_gst_gl_log2_int64(guint64 value)732 _gst_gl_log2_int64 (guint64 value)
733 {
734 guint64 ret = 0;
735
736 while (value >>= 1)
737 ret++;
738
739 return ret;
740 }
741
742 /**
743 * gst_gl_value_set_texture_target_from_mask:
744 * @value: an uninitialized #GValue
745 * @target_mask: a bitwise mask of #GstGLTextureTarget's
746 *
747 * A mask is a bitwise OR of (1 << target) where target is a valid
748 * #GstGLTextureTarget
749 *
750 * Returns: whether the @target_mask could be set on @value
751 */
752 gboolean
gst_gl_value_set_texture_target_from_mask(GValue * value,GstGLTextureTarget target_mask)753 gst_gl_value_set_texture_target_from_mask (GValue * value,
754 GstGLTextureTarget target_mask)
755 {
756 g_return_val_if_fail (value != NULL, FALSE);
757 g_return_val_if_fail (target_mask != GST_GL_TEXTURE_TARGET_NONE, FALSE);
758
759 if ((target_mask & (target_mask - 1)) == 0) {
760 /* only one texture target set */
761 g_value_init (value, G_TYPE_STRING);
762 return gst_gl_value_set_texture_target (value,
763 _gst_gl_log2_int64 (target_mask));
764 } else {
765 GValue item = G_VALUE_INIT;
766 gboolean ret = FALSE;
767
768 g_value_init (value, GST_TYPE_LIST);
769 g_value_init (&item, G_TYPE_STRING);
770 if (target_mask & (1 << GST_GL_TEXTURE_TARGET_2D)) {
771 gst_gl_value_set_texture_target (&item, GST_GL_TEXTURE_TARGET_2D);
772 gst_value_list_append_value (value, &item);
773 ret = TRUE;
774 }
775 if (target_mask & (1 << GST_GL_TEXTURE_TARGET_RECTANGLE)) {
776 gst_gl_value_set_texture_target (&item, GST_GL_TEXTURE_TARGET_RECTANGLE);
777 gst_value_list_append_value (value, &item);
778 ret = TRUE;
779 }
780 if (target_mask & (1 << GST_GL_TEXTURE_TARGET_EXTERNAL_OES)) {
781 gst_gl_value_set_texture_target (&item,
782 GST_GL_TEXTURE_TARGET_EXTERNAL_OES);
783 gst_value_list_append_value (value, &item);
784 ret = TRUE;
785 }
786
787 g_value_unset (&item);
788 return ret;
789 }
790 }
791
792 static const gfloat identity_matrix[] = {
793 1.0, 0.0, 0.0, 0.0,
794 0.0, 1.0, 0.0, 0.0,
795 0.0, 0.0, 1.0, 0.0,
796 0.0, 0.0, 0.0, 1.0,
797 };
798
799 static const gfloat from_ndc_matrix[] = {
800 0.5, 0.0, 0.0, 0.0,
801 0.0, 0.5, 0.0, 0.0,
802 0.0, 0.0, 0.5, 0.0,
803 0.5, 0.5, 0.5, 1.0,
804 };
805
806 static const gfloat to_ndc_matrix[] = {
807 2.0, 0.0, 0.0, 0.0,
808 0.0, 2.0, 0.0, 0.0,
809 0.0, 0.0, 2.0, 0.0,
810 -1.0, -1.0, -1.0, 1.0,
811 };
812
813 /**
814 * gst_gl_multiply_matrix4:
815 * @a: (array fixed-size=16): a 2-dimensional 4x4 array of #gfloat
816 * @b: (array fixed-size=16): another 2-dimensional 4x4 array of #gfloat
817 * @result: (out caller-allocates) (array fixed-size=16): the result of the multiplication
818 *
819 * Multiplies two 4x4 matrices, @a and @b, and stores the result, a
820 * 2-dimensional array of #gfloat, in @result.
821 *
822 * Since: 1.20
823 */
824 /* https://en.wikipedia.org/wiki/Matrix_multiplication */
825 void
gst_gl_multiply_matrix4(const gfloat * a,const gfloat * b,gfloat * result)826 gst_gl_multiply_matrix4 (const gfloat * a, const gfloat * b, gfloat * result)
827 {
828 int i, j, k;
829 gfloat tmp[16] = { 0.0f };
830
831 g_return_if_fail (a != NULL);
832 g_return_if_fail (b != NULL);
833 g_return_if_fail (result != NULL);
834
835 for (i = 0; i < 4; i++) { /* column */
836 for (j = 0; j < 4; j++) { /* row */
837 for (k = 0; k < 4; k++) {
838 tmp[j + (i * 4)] += a[k + (i * 4)] * b[j + (k * 4)];
839 }
840 }
841 }
842
843 for (i = 0; i < 16; i++)
844 result[i] = tmp[i];
845 }
846
847 /**
848 * gst_gl_get_affine_transformation_meta_as_ndc:
849 * @meta: (nullable): a #GstVideoAffineTransformationMeta
850 * @matrix: (array fixed-size=16) (out caller-allocates): result of the 4x4 matrix
851 *
852 * Retrieves the stored 4x4 affine transformation matrix stored in @meta in
853 * NDC coordinates. if @meta is NULL, an identity matrix is returned.
854 *
855 * NDC is a left-handed coordinate system
856 * - x - [-1, 1] - +ve X moves right
857 * - y - [-1, 1] - +ve Y moves up
858 * - z - [-1, 1] - +ve Z moves into
859 *
860 * Since: 1.20
861 */
862 void
gst_gl_get_affine_transformation_meta_as_ndc(GstVideoAffineTransformationMeta * meta,gfloat * matrix)863 gst_gl_get_affine_transformation_meta_as_ndc (GstVideoAffineTransformationMeta *
864 meta, gfloat * matrix)
865 {
866 g_return_if_fail (matrix != NULL);
867
868 if (!meta) {
869 int i;
870
871 for (i = 0; i < 16; i++) {
872 matrix[i] = identity_matrix[i];
873 }
874 } else {
875 float tmp[16];
876
877 /* change of basis multiplications */
878 gst_gl_multiply_matrix4 (from_ndc_matrix, meta->matrix, tmp);
879 gst_gl_multiply_matrix4 (tmp, to_ndc_matrix, matrix);
880 }
881 }
882
883 /**
884 * gst_gl_set_affine_transformation_meta_from_ndc:
885 * @meta: a #GstVideoAffineTransformationMeta
886 * @matrix: (array fixed-size=16): a 4x4 matrix
887 *
888 * Set the 4x4 affine transformation matrix stored in @meta from the
889 * NDC coordinates in @matrix.
890 *
891 * Since: 1.20
892 */
gst_gl_set_affine_transformation_meta_from_ndc(GstVideoAffineTransformationMeta * meta,const gfloat * matrix)893 void gst_gl_set_affine_transformation_meta_from_ndc
894 (GstVideoAffineTransformationMeta * meta, const gfloat * matrix)
895 {
896 float tmp[16];
897
898 g_return_if_fail (meta != NULL);
899 g_return_if_fail (matrix != NULL);
900
901 /* change of basis multiplications */
902 gst_gl_multiply_matrix4 (to_ndc_matrix, matrix, tmp);
903 gst_gl_multiply_matrix4 (tmp, from_ndc_matrix, meta->matrix);
904 }
905