1 /*
2 * GStreamer
3 * Copyright (C) 2007 David Schleef <ds@schleef.org>
4 * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
5 * Copyright (C) 2008 Filippo Argiolas <filippo.argiolas@gmail.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 /**
24 * SECTION:gstglfilter
25 * @title: GstGLFilter
26 * @short_description: GstBaseTransform subclass for dealing with RGBA textures
27 * @see_also: #GstBaseTransform, #GstGLContext, #GstGLFramebuffer
28 *
29 * #GstGLFilter helps to implement simple OpenGL filter elements taking a
30 * single input and producing a single output with a #GstGLFramebuffer
31 */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include <gst/video/gstvideometa.h>
38
39 #include "gstglfilter.h"
40
41 #include "gstglfuncs.h"
42
43 #define GST_CAT_DEFAULT gst_gl_filter_debug
44 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
45
46 /* *INDENT-OFF* */
47 static GstStaticPadTemplate gst_gl_filter_src_pad_template =
48 GST_STATIC_PAD_TEMPLATE ("src",
49 GST_PAD_SRC,
50 GST_PAD_ALWAYS,
51 GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), "
52 "format = (string) RGBA, "
53 "width = " GST_VIDEO_SIZE_RANGE ", "
54 "height = " GST_VIDEO_SIZE_RANGE ", "
55 "framerate = " GST_VIDEO_FPS_RANGE ","
56 "texture-target = (string) 2D ; "
57 "video/x-raw(ANY), "
58 "format = (string) RGBA, "
59 "width = " GST_VIDEO_SIZE_RANGE ", "
60 "height = " GST_VIDEO_SIZE_RANGE ", "
61 "framerate = " GST_VIDEO_FPS_RANGE ","
62 "texture-target = (string) 2D"
63 ));
64
65 static GstStaticPadTemplate gst_gl_filter_sink_pad_template =
66 GST_STATIC_PAD_TEMPLATE ("sink",
67 GST_PAD_SINK,
68 GST_PAD_ALWAYS,
69 GST_STATIC_CAPS ("video/x-raw(ANY), "
70 "format = (string) RGBA, "
71 "width = " GST_VIDEO_SIZE_RANGE ", "
72 "height = " GST_VIDEO_SIZE_RANGE ", "
73 "framerate = " GST_VIDEO_FPS_RANGE ","
74 "texture-target = (string) 2D ; "
75 "video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), "
76 "format = (string) RGBA, "
77 "width = " GST_VIDEO_SIZE_RANGE ", "
78 "height = " GST_VIDEO_SIZE_RANGE ", "
79 "framerate = " GST_VIDEO_FPS_RANGE ","
80 "texture-target = (string) 2D"
81 ));
82 /* *INDENT-ON* */
83
84 /* Properties */
85 enum
86 {
87 PROP_0,
88 };
89
90 #define gst_gl_filter_parent_class parent_class
91 G_DEFINE_TYPE_WITH_CODE (GstGLFilter, gst_gl_filter, GST_TYPE_GL_BASE_FILTER,
92 GST_DEBUG_CATEGORY_INIT (gst_gl_filter_debug, "glfilter", 0,
93 "glfilter element");
94 );
95
96 static void gst_gl_filter_set_property (GObject * object, guint prop_id,
97 const GValue * value, GParamSpec * pspec);
98 static void gst_gl_filter_get_property (GObject * object, guint prop_id,
99 GValue * value, GParamSpec * pspec);
100
101 static GstCaps *gst_gl_filter_transform_caps (GstBaseTransform * bt,
102 GstPadDirection direction, GstCaps * caps, GstCaps * filter);
103 static GstCaps *default_transform_internal_caps (GstGLFilter * filter,
104 GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps);
105 static GstCaps *gst_gl_filter_fixate_caps (GstBaseTransform * bt,
106 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
107 static void gst_gl_filter_reset (GstGLFilter * filter);
108 static gboolean gst_gl_filter_stop (GstBaseTransform * bt);
109 static gboolean gst_gl_filter_get_unit_size (GstBaseTransform * trans,
110 GstCaps * caps, gsize * size);
111 static GstFlowReturn gst_gl_filter_transform (GstBaseTransform * bt,
112 GstBuffer * inbuf, GstBuffer * outbuf);
113 static gboolean gst_gl_filter_propose_allocation (GstBaseTransform * trans,
114 GstQuery * decide_query, GstQuery * query);
115 static gboolean gst_gl_filter_decide_allocation (GstBaseTransform * trans,
116 GstQuery * query);
117 static gboolean gst_gl_filter_set_caps (GstBaseTransform * bt, GstCaps * incaps,
118 GstCaps * outcaps);
119 static void gst_gl_filter_gl_stop (GstGLBaseFilter * filter);
120 static gboolean gst_gl_filter_gl_set_caps (GstGLBaseFilter * bt,
121 GstCaps * incaps, GstCaps * outcaps);
122
123 void
gst_gl_filter_add_rgba_pad_templates(GstGLFilterClass * klass)124 gst_gl_filter_add_rgba_pad_templates (GstGLFilterClass * klass)
125 {
126 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
127 gst_element_class_add_static_pad_template (element_class,
128 &gst_gl_filter_src_pad_template);
129 gst_element_class_add_static_pad_template (element_class,
130 &gst_gl_filter_sink_pad_template);
131 }
132
133 static void
gst_gl_filter_class_init(GstGLFilterClass * klass)134 gst_gl_filter_class_init (GstGLFilterClass * klass)
135 {
136 GObjectClass *gobject_class;
137
138 gobject_class = (GObjectClass *) klass;
139
140 gobject_class->set_property = gst_gl_filter_set_property;
141 gobject_class->get_property = gst_gl_filter_get_property;
142
143 GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
144 gst_gl_filter_transform_caps;
145 GST_BASE_TRANSFORM_CLASS (klass)->fixate_caps = gst_gl_filter_fixate_caps;
146 GST_BASE_TRANSFORM_CLASS (klass)->transform = gst_gl_filter_transform;
147 GST_BASE_TRANSFORM_CLASS (klass)->stop = gst_gl_filter_stop;
148 GST_BASE_TRANSFORM_CLASS (klass)->set_caps = gst_gl_filter_set_caps;
149 GST_BASE_TRANSFORM_CLASS (klass)->propose_allocation =
150 gst_gl_filter_propose_allocation;
151 GST_BASE_TRANSFORM_CLASS (klass)->decide_allocation =
152 gst_gl_filter_decide_allocation;
153 GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size = gst_gl_filter_get_unit_size;
154
155 GST_GL_BASE_FILTER_CLASS (klass)->gl_stop = gst_gl_filter_gl_stop;
156 GST_GL_BASE_FILTER_CLASS (klass)->gl_set_caps = gst_gl_filter_gl_set_caps;
157
158 klass->transform_internal_caps = default_transform_internal_caps;
159 }
160
161 static void
gst_gl_filter_init(GstGLFilter * filter)162 gst_gl_filter_init (GstGLFilter * filter)
163 {
164 filter->draw_attr_position_loc = -1;
165 filter->draw_attr_texture_loc = -1;
166 }
167
168 static void
gst_gl_filter_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)169 gst_gl_filter_set_property (GObject * object, guint prop_id,
170 const GValue * value, GParamSpec * pspec)
171 {
172 switch (prop_id) {
173 default:
174 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
175 break;
176 }
177 }
178
179 static void
gst_gl_filter_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)180 gst_gl_filter_get_property (GObject * object, guint prop_id,
181 GValue * value, GParamSpec * pspec)
182 {
183 switch (prop_id) {
184 default:
185 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
186 break;
187 }
188 }
189
190 static void
gst_gl_filter_reset(GstGLFilter * filter)191 gst_gl_filter_reset (GstGLFilter * filter)
192 {
193 gst_caps_replace (&filter->out_caps, NULL);
194 }
195
196 static gboolean
gst_gl_filter_stop(GstBaseTransform * bt)197 gst_gl_filter_stop (GstBaseTransform * bt)
198 {
199 GstGLFilter *filter = GST_GL_FILTER (bt);
200
201 gst_gl_filter_reset (filter);
202
203 return GST_BASE_TRANSFORM_CLASS (parent_class)->stop (bt);
204 }
205
206 static void
gst_gl_filter_gl_stop(GstGLBaseFilter * base_filter)207 gst_gl_filter_gl_stop (GstGLBaseFilter * base_filter)
208 {
209 GstGLFilter *filter = GST_GL_FILTER (base_filter);
210 GstGLContext *context = base_filter->context;
211 const GstGLFuncs *gl = context->gl_vtable;
212
213 if (filter->vao) {
214 gl->DeleteVertexArrays (1, &filter->vao);
215 filter->vao = 0;
216 }
217
218 if (filter->vertex_buffer) {
219 gl->DeleteBuffers (1, &filter->vertex_buffer);
220 filter->vertex_buffer = 0;
221 }
222
223 if (filter->vbo_indices) {
224 gl->DeleteBuffers (1, &filter->vbo_indices);
225 filter->vbo_indices = 0;
226 }
227
228 if (filter->fbo != NULL) {
229 gst_object_unref (filter->fbo);
230 filter->fbo = NULL;
231 }
232
233 filter->default_shader = NULL;
234 filter->draw_attr_position_loc = -1;
235 filter->draw_attr_texture_loc = -1;
236
237 GST_GL_BASE_FILTER_CLASS (parent_class)->gl_stop (base_filter);
238 }
239
240 static GstCaps *
gst_gl_filter_fixate_caps(GstBaseTransform * bt,GstPadDirection direction,GstCaps * caps,GstCaps * othercaps)241 gst_gl_filter_fixate_caps (GstBaseTransform * bt,
242 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
243 {
244 GstStructure *ins, *outs;
245 const GValue *from_par, *to_par;
246 GValue fpar = { 0, }, tpar = {
247 0,};
248
249 othercaps = gst_caps_make_writable (othercaps);
250 othercaps = gst_caps_truncate (othercaps);
251
252 GST_DEBUG_OBJECT (bt, "trying to fixate othercaps %" GST_PTR_FORMAT
253 " based on caps %" GST_PTR_FORMAT, othercaps, caps);
254
255 ins = gst_caps_get_structure (caps, 0);
256 outs = gst_caps_get_structure (othercaps, 0);
257
258 from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
259 to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
260
261 /* If we're fixating from the sinkpad we always set the PAR and
262 * assume that missing PAR on the sinkpad means 1/1 and
263 * missing PAR on the srcpad means undefined
264 */
265 if (direction == GST_PAD_SINK) {
266 if (!from_par) {
267 g_value_init (&fpar, GST_TYPE_FRACTION);
268 gst_value_set_fraction (&fpar, 1, 1);
269 from_par = &fpar;
270 }
271 if (!to_par) {
272 g_value_init (&tpar, GST_TYPE_FRACTION);
273 gst_value_set_fraction (&tpar, 1, 1);
274 to_par = &tpar;
275 }
276 } else {
277 if (!to_par) {
278 g_value_init (&tpar, GST_TYPE_FRACTION);
279 gst_value_set_fraction (&tpar, 1, 1);
280 to_par = &tpar;
281
282 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
283 NULL);
284 }
285 if (!from_par) {
286 g_value_init (&fpar, GST_TYPE_FRACTION);
287 gst_value_set_fraction (&fpar, 1, 1);
288 from_par = &fpar;
289 }
290 }
291
292 /* we have both PAR but they might not be fixated */
293 {
294 gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
295 gint w = 0, h = 0;
296 gint from_dar_n, from_dar_d;
297 gint num, den;
298
299 /* from_par should be fixed */
300 g_return_val_if_fail (gst_value_is_fixed (from_par), othercaps);
301
302 from_par_n = gst_value_get_fraction_numerator (from_par);
303 from_par_d = gst_value_get_fraction_denominator (from_par);
304
305 gst_structure_get_int (ins, "width", &from_w);
306 gst_structure_get_int (ins, "height", &from_h);
307
308 gst_structure_get_int (outs, "width", &w);
309 gst_structure_get_int (outs, "height", &h);
310
311 /* if both width and height are already fixed, we can't do anything
312 * about it anymore */
313 if (w && h) {
314 GST_DEBUG_OBJECT (bt, "dimensions already set to %dx%d, not fixating",
315 w, h);
316 if (!gst_value_is_fixed (to_par)) {
317 GST_DEBUG_OBJECT (bt, "fixating to_par to %dx%d", 1, 1);
318 if (gst_structure_has_field (outs, "pixel-aspect-ratio"))
319 gst_structure_fixate_field_nearest_fraction (outs,
320 "pixel-aspect-ratio", 1, 1);
321 }
322 goto done;
323 }
324
325 /* Calculate input DAR */
326 if (!gst_util_fraction_multiply (from_w, from_h, from_par_n, from_par_d,
327 &from_dar_n, &from_dar_d)) {
328 GST_ELEMENT_ERROR (bt, CORE, NEGOTIATION, (NULL),
329 ("Error calculating the output scaled size - integer overflow"));
330 goto done;
331 }
332
333 GST_DEBUG_OBJECT (bt, "Input DAR is %d/%d", from_dar_n, from_dar_d);
334
335 /* If either width or height are fixed there's not much we
336 * can do either except choosing a height or width and PAR
337 * that matches the DAR as good as possible
338 */
339 if (h) {
340 gint num, den;
341
342 GST_DEBUG_OBJECT (bt, "height is fixed (%d)", h);
343
344 if (!gst_value_is_fixed (to_par)) {
345 /* (shortcut) copy-paste (??) of videoscale seems to aim for 1/1,
346 * so let's make it so ...
347 * especially if following code assumes fixed */
348 GST_DEBUG_OBJECT (bt, "fixating to_par to 1x1");
349 gst_structure_fixate_field_nearest_fraction (outs,
350 "pixel-aspect-ratio", 1, 1);
351 to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
352 }
353
354 /* PAR is fixed, choose the height that is nearest to the
355 * height with the same DAR */
356 to_par_n = gst_value_get_fraction_numerator (to_par);
357 to_par_d = gst_value_get_fraction_denominator (to_par);
358
359 GST_DEBUG_OBJECT (bt, "PAR is fixed %d/%d", to_par_n, to_par_d);
360
361 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
362 to_par_n, &num, &den)) {
363 GST_ELEMENT_ERROR (bt, CORE, NEGOTIATION, (NULL),
364 ("Error calculating the output scaled size - integer overflow"));
365 goto done;
366 }
367
368 w = (guint) gst_util_uint64_scale_int (h, num, den);
369 gst_structure_fixate_field_nearest_int (outs, "width", w);
370
371 goto done;
372 } else if (w) {
373 gint num, den;
374
375 GST_DEBUG_OBJECT (bt, "width is fixed (%d)", w);
376
377 if (!gst_value_is_fixed (to_par)) {
378 /* (shortcut) copy-paste (??) of videoscale seems to aim for 1/1,
379 * so let's make it so ...
380 * especially if following code assumes fixed */
381 GST_DEBUG_OBJECT (bt, "fixating to_par to 1x1");
382 gst_structure_fixate_field_nearest_fraction (outs,
383 "pixel-aspect-ratio", 1, 1);
384 to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
385 }
386
387 /* PAR is fixed, choose the height that is nearest to the
388 * height with the same DAR */
389 to_par_n = gst_value_get_fraction_numerator (to_par);
390 to_par_d = gst_value_get_fraction_denominator (to_par);
391
392 GST_DEBUG_OBJECT (bt, "PAR is fixed %d/%d", to_par_n, to_par_d);
393
394 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
395 to_par_n, &num, &den)) {
396 GST_ELEMENT_ERROR (bt, CORE, NEGOTIATION, (NULL),
397 ("Error calculating the output scaled size - integer overflow"));
398 goto done;
399 }
400
401 h = (guint) gst_util_uint64_scale_int (w, den, num);
402 gst_structure_fixate_field_nearest_int (outs, "height", h);
403
404 goto done;
405 } else if (gst_value_is_fixed (to_par)) {
406 GstStructure *tmp;
407 gint set_h, set_w, f_h, f_w;
408
409 to_par_n = gst_value_get_fraction_numerator (to_par);
410 to_par_d = gst_value_get_fraction_denominator (to_par);
411
412 /* Calculate scale factor for the PAR change */
413 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
414 to_par_d, &num, &den)) {
415 GST_ELEMENT_ERROR (bt, CORE, NEGOTIATION, (NULL),
416 ("Error calculating the output scaled size - integer overflow"));
417 goto done;
418 }
419
420 /* Try to keep the input height */
421 tmp = gst_structure_copy (outs);
422 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
423 gst_structure_get_int (tmp, "height", &set_h);
424
425 /* This might have failed but try to scale the width
426 * to keep the DAR nonetheless */
427 w = (guint) gst_util_uint64_scale_int (set_h, num, den);
428 gst_structure_fixate_field_nearest_int (tmp, "width", w);
429 gst_structure_get_int (tmp, "width", &set_w);
430 gst_structure_free (tmp);
431
432 /* We kept the DAR and the height is nearest to the original height */
433 if (set_w == w) {
434 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
435 G_TYPE_INT, set_h, NULL);
436 goto done;
437 }
438
439 f_h = set_h;
440 f_w = set_w;
441
442 /* If the former failed, try to keep the input width at least */
443 tmp = gst_structure_copy (outs);
444 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
445 gst_structure_get_int (tmp, "width", &set_w);
446
447 /* This might have failed but try to scale the width
448 * to keep the DAR nonetheless */
449 h = (guint) gst_util_uint64_scale_int (set_w, den, num);
450 gst_structure_fixate_field_nearest_int (tmp, "height", h);
451 gst_structure_get_int (tmp, "height", &set_h);
452 gst_structure_free (tmp);
453
454 /* We kept the DAR and the width is nearest to the original width */
455 if (set_h == h) {
456 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
457 G_TYPE_INT, set_h, NULL);
458 goto done;
459 }
460
461 /* If all this failed, keep the height that was nearest to the original
462 * height and the nearest possible width. This changes the DAR but
463 * there's not much else to do here.
464 */
465 gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
466 f_h, NULL);
467 goto done;
468 } else {
469 GstStructure *tmp;
470 gint set_h, set_w, set_par_n, set_par_d, tmp2;
471
472 /* width, height and PAR are not fixed */
473
474 /* First try to keep the height and width as good as possible
475 * and scale PAR */
476 tmp = gst_structure_copy (outs);
477 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
478 gst_structure_get_int (tmp, "height", &set_h);
479 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
480 gst_structure_get_int (tmp, "width", &set_w);
481
482 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, set_w,
483 &to_par_n, &to_par_d)) {
484 GST_ELEMENT_ERROR (bt, CORE, NEGOTIATION, (NULL),
485 ("Error calculating the output scaled size - integer overflow"));
486 gst_structure_free (tmp);
487 goto done;
488 }
489
490 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
491 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
492 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
493 to_par_n, to_par_d);
494 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
495 &set_par_d);
496 gst_structure_free (tmp);
497
498 if (set_par_n == to_par_n && set_par_d == to_par_d) {
499 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
500 G_TYPE_INT, set_h, NULL);
501
502 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
503 set_par_n != set_par_d)
504 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
505 set_par_n, set_par_d, NULL);
506 goto done;
507 }
508
509 /* Otherwise try to scale width to keep the DAR with the set
510 * PAR and height */
511 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
512 set_par_n, &num, &den)) {
513 GST_ELEMENT_ERROR (bt, CORE, NEGOTIATION, (NULL),
514 ("Error calculating the output scaled size - integer overflow"));
515 goto done;
516 }
517
518 w = (guint) gst_util_uint64_scale_int (set_h, num, den);
519 tmp = gst_structure_copy (outs);
520 gst_structure_fixate_field_nearest_int (tmp, "width", w);
521 gst_structure_get_int (tmp, "width", &tmp2);
522 gst_structure_free (tmp);
523
524 if (tmp2 == w) {
525 gst_structure_set (outs, "width", G_TYPE_INT, tmp2, "height",
526 G_TYPE_INT, set_h, NULL);
527 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
528 set_par_n != set_par_d)
529 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
530 set_par_n, set_par_d, NULL);
531 goto done;
532 }
533
534 /* ... or try the same with the height */
535 h = (guint) gst_util_uint64_scale_int (set_w, den, num);
536 tmp = gst_structure_copy (outs);
537 gst_structure_fixate_field_nearest_int (tmp, "height", h);
538 gst_structure_get_int (tmp, "height", &tmp2);
539 gst_structure_free (tmp);
540
541 if (tmp2 == h) {
542 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
543 G_TYPE_INT, tmp2, NULL);
544 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
545 set_par_n != set_par_d)
546 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
547 set_par_n, set_par_d, NULL);
548 goto done;
549 }
550
551 /* If all fails we can't keep the DAR and take the nearest values
552 * for everything from the first try */
553 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
554 G_TYPE_INT, set_h, NULL);
555 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
556 set_par_n != set_par_d)
557 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
558 set_par_n, set_par_d, NULL);
559 }
560 }
561
562
563 done:
564 othercaps = gst_caps_fixate (othercaps);
565
566 GST_DEBUG_OBJECT (bt, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
567
568 if (from_par == &fpar)
569 g_value_unset (&fpar);
570 if (to_par == &tpar)
571 g_value_unset (&tpar);
572
573 return othercaps;
574 }
575
576 /* copies the given caps */
577 static GstCaps *
gst_gl_filter_caps_remove_size(GstCaps * caps)578 gst_gl_filter_caps_remove_size (GstCaps * caps)
579 {
580 GstStructure *st;
581 GstCapsFeatures *f;
582 gint i, n;
583 GstCaps *res;
584
585 res = gst_caps_new_empty ();
586
587 n = gst_caps_get_size (caps);
588 for (i = 0; i < n; i++) {
589 st = gst_caps_get_structure (caps, i);
590 f = gst_caps_get_features (caps, i);
591
592 /* If this is already expressed by the existing caps
593 * skip this structure */
594 if (i > 0 && gst_caps_is_subset_structure_full (res, st, f))
595 continue;
596
597 st = gst_structure_copy (st);
598 gst_structure_set (st, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
599 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
600
601 /* if pixel aspect ratio, make a range of it */
602 if (gst_structure_has_field (st, "pixel-aspect-ratio")) {
603 gst_structure_set (st, "pixel-aspect-ratio",
604 GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1, NULL);
605 }
606
607 gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
608 }
609
610 return res;
611 }
612
613 static GstCaps *
gst_gl_filter_ensure_caps_contains_features(const GstCaps * caps,const gchar * feature_name)614 gst_gl_filter_ensure_caps_contains_features (const GstCaps * caps,
615 const gchar * feature_name)
616 {
617 GstCaps *ret = gst_caps_copy (caps);
618 guint n = gst_caps_get_size (ret);
619 guint i = 0;
620
621 for (i = 0; i < n; i++) {
622 GstCapsFeatures *f = gst_caps_get_features (ret, i);
623 if (!gst_caps_features_is_any (f)) {
624 if (!gst_caps_features_contains (f, feature_name)) {
625 gst_caps_features_add (f, GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
626 }
627 }
628 }
629
630 return ret;
631 }
632
633 static GstCaps *
default_transform_internal_caps(GstGLFilter * filter,GstPadDirection direction,GstCaps * caps,GstCaps * filter_caps)634 default_transform_internal_caps (GstGLFilter * filter,
635 GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps)
636 {
637 GstCaps *tmp = gst_gl_filter_caps_remove_size (caps);
638
639 GST_DEBUG_OBJECT (filter, "size removal returned caps %" GST_PTR_FORMAT, tmp);
640 return tmp;
641 }
642
643 static GstCaps *
gst_gl_filter_transform_caps(GstBaseTransform * bt,GstPadDirection direction,GstCaps * caps,GstCaps * filter_caps)644 gst_gl_filter_transform_caps (GstBaseTransform * bt,
645 GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps)
646 {
647 GstGLFilter *filter = GST_GL_FILTER (bt);
648 GstCaps *tmp = NULL;
649 GstCaps *result = NULL;
650
651 if (gst_base_transform_is_passthrough (bt)) {
652 tmp = gst_caps_ref (caps);
653 } else {
654 tmp = GST_GL_FILTER_GET_CLASS (filter)->transform_internal_caps (filter,
655 direction, caps, NULL);
656
657 result =
658 gst_gl_filter_ensure_caps_contains_features (tmp,
659 GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
660 gst_caps_unref (tmp);
661 tmp = result;
662 }
663
664 if (filter_caps) {
665 result =
666 gst_caps_intersect_full (filter_caps, tmp, GST_CAPS_INTERSECT_FIRST);
667 gst_caps_unref (tmp);
668 } else {
669 result = tmp;
670 }
671
672 GST_DEBUG_OBJECT (bt, "returning caps: %" GST_PTR_FORMAT, result);
673
674 return result;
675 }
676
677 static gboolean
gst_gl_filter_get_unit_size(GstBaseTransform * trans,GstCaps * caps,gsize * size)678 gst_gl_filter_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
679 gsize * size)
680 {
681 gboolean ret = FALSE;
682 GstVideoInfo info;
683
684 ret = gst_video_info_from_caps (&info, caps);
685 if (ret)
686 *size = GST_VIDEO_INFO_SIZE (&info);
687
688 return TRUE;
689 }
690
691 static gboolean
gst_gl_filter_gl_set_caps(GstGLBaseFilter * bt,GstCaps * incaps,GstCaps * outcaps)692 gst_gl_filter_gl_set_caps (GstGLBaseFilter * bt, GstCaps * incaps,
693 GstCaps * outcaps)
694 {
695 GstGLFilter *filter = GST_GL_FILTER (bt);
696 GstGLFilterClass *filter_class = GST_GL_FILTER_GET_CLASS (filter);
697 GstGLContext *context = GST_GL_BASE_FILTER (filter)->context;
698 gint out_width, out_height;
699
700 out_width = GST_VIDEO_INFO_WIDTH (&filter->out_info);
701 out_height = GST_VIDEO_INFO_HEIGHT (&filter->out_info);
702
703 if (filter->fbo)
704 gst_object_unref (filter->fbo);
705
706 if (!(filter->fbo =
707 gst_gl_framebuffer_new_with_default_depth (context, out_width,
708 out_height)))
709 goto context_error;
710
711 if (filter_class->init_fbo) {
712 if (!filter_class->init_fbo (filter))
713 goto error;
714 }
715
716 return TRUE;
717
718 context_error:
719 {
720 GST_ELEMENT_ERROR (filter, RESOURCE, NOT_FOUND, ("Could not generate FBO"),
721 (NULL));
722 return FALSE;
723 }
724 error:
725 {
726 GST_ELEMENT_ERROR (filter, LIBRARY, INIT,
727 ("Subclass failed to initialize."), (NULL));
728 return FALSE;
729 }
730 }
731
732 static gboolean
gst_gl_filter_set_caps(GstBaseTransform * bt,GstCaps * incaps,GstCaps * outcaps)733 gst_gl_filter_set_caps (GstBaseTransform * bt, GstCaps * incaps,
734 GstCaps * outcaps)
735 {
736 GstGLFilter *filter;
737 GstGLFilterClass *filter_class;
738 GstGLTextureTarget from_target, to_target;
739
740 filter = GST_GL_FILTER (bt);
741 filter_class = GST_GL_FILTER_GET_CLASS (filter);
742
743 if (!gst_video_info_from_caps (&filter->in_info, incaps))
744 goto wrong_caps;
745 if (!gst_video_info_from_caps (&filter->out_info, outcaps))
746 goto wrong_caps;
747
748 {
749 GstStructure *in_s = gst_caps_get_structure (incaps, 0);
750 GstStructure *out_s = gst_caps_get_structure (outcaps, 0);
751
752 if (gst_structure_has_field_typed (in_s, "texture-target", G_TYPE_STRING))
753 from_target =
754 gst_gl_texture_target_from_string (gst_structure_get_string (in_s,
755 "texture-target"));
756 else
757 from_target = GST_GL_TEXTURE_TARGET_2D;
758
759 if (gst_structure_has_field_typed (out_s, "texture-target", G_TYPE_STRING))
760 to_target =
761 gst_gl_texture_target_from_string (gst_structure_get_string (out_s,
762 "texture-target"));
763 else
764 to_target = GST_GL_TEXTURE_TARGET_2D;
765
766 if (to_target == GST_GL_TEXTURE_TARGET_NONE
767 || from_target == GST_GL_TEXTURE_TARGET_NONE)
768 /* invalid caps */
769 goto wrong_caps;
770 }
771
772 if (filter_class->set_caps) {
773 if (!filter_class->set_caps (filter, incaps, outcaps))
774 goto error;
775 }
776
777 gst_caps_replace (&filter->out_caps, outcaps);
778 filter->in_texture_target = from_target;
779 filter->out_texture_target = to_target;
780
781 GST_DEBUG_OBJECT (filter, "set_caps %dx%d in %" GST_PTR_FORMAT
782 " out %" GST_PTR_FORMAT,
783 GST_VIDEO_INFO_WIDTH (&filter->out_info),
784 GST_VIDEO_INFO_HEIGHT (&filter->out_info), incaps, outcaps);
785
786 return GST_BASE_TRANSFORM_CLASS (parent_class)->set_caps (bt, incaps,
787 outcaps);
788
789 /* ERRORS */
790 wrong_caps:
791 {
792 GST_WARNING ("Wrong caps - could not understand input or output caps");
793 return FALSE;
794 }
795 error:
796 {
797 return FALSE;
798 }
799 }
800
801 static gboolean
gst_gl_filter_propose_allocation(GstBaseTransform * trans,GstQuery * decide_query,GstQuery * query)802 gst_gl_filter_propose_allocation (GstBaseTransform * trans,
803 GstQuery * decide_query, GstQuery * query)
804 {
805 GstGLFilter *filter = GST_GL_FILTER (trans);
806 GstGLContext *context = GST_GL_BASE_FILTER (filter)->context;
807 GstCaps *caps;
808 GstVideoInfo info;
809 guint size;
810 GstBufferPool *pool = NULL;
811 gboolean need_pool;
812
813 gst_query_parse_allocation (query, &caps, &need_pool);
814
815 if (caps == NULL)
816 goto no_caps;
817
818 if (!gst_video_info_from_caps (&info, caps))
819 goto invalid_caps;
820
821 /* the normal size of a frame */
822 size = info.size;
823
824 if (need_pool) {
825 GstStructure *config;
826
827 GST_DEBUG_OBJECT (filter, "create new pool");
828 pool = gst_gl_buffer_pool_new (context);
829
830 config = gst_buffer_pool_get_config (pool);
831 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
832
833 if (!gst_buffer_pool_set_config (pool, config)) {
834 gst_object_unref (pool);
835 goto config_failed;
836 }
837 }
838
839 gst_query_add_allocation_pool (query, pool, size, 1, 0);
840 if (pool)
841 gst_object_unref (pool);
842
843 if (context->gl_vtable->FenceSync)
844 gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
845
846 return TRUE;
847
848 /* ERRORS */
849 no_caps:
850 {
851 GST_DEBUG_OBJECT (trans, "no caps specified");
852 return FALSE;
853 }
854 invalid_caps:
855 {
856 GST_DEBUG_OBJECT (trans, "invalid caps specified");
857 return FALSE;
858 }
859 config_failed:
860 {
861 GST_DEBUG_OBJECT (trans, "failed setting config");
862 return FALSE;
863 }
864 }
865
866 static gboolean
gst_gl_filter_decide_allocation(GstBaseTransform * trans,GstQuery * query)867 gst_gl_filter_decide_allocation (GstBaseTransform * trans, GstQuery * query)
868 {
869 GstGLContext *context;
870 GstBufferPool *pool = NULL;
871 GstStructure *config;
872 GstCaps *caps;
873 guint min, max, size;
874 gboolean update_pool;
875
876 gst_query_parse_allocation (query, &caps, NULL);
877 if (!caps)
878 return FALSE;
879
880 /* get gl context */
881 if (!GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
882 query))
883 return FALSE;
884
885 context = GST_GL_BASE_FILTER (trans)->context;
886
887 if (gst_query_get_n_allocation_pools (query) > 0) {
888 gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
889
890 update_pool = TRUE;
891 } else {
892 GstVideoInfo vinfo;
893
894 gst_video_info_init (&vinfo);
895 gst_video_info_from_caps (&vinfo, caps);
896 size = vinfo.size;
897 min = max = 0;
898 update_pool = FALSE;
899 }
900
901 if (!pool || !GST_IS_GL_BUFFER_POOL (pool)) {
902 if (pool)
903 gst_object_unref (pool);
904 pool = gst_gl_buffer_pool_new (context);
905 }
906
907 config = gst_buffer_pool_get_config (pool);
908
909 gst_buffer_pool_config_set_params (config, caps, size, min, max);
910 gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
911 if (gst_query_find_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, NULL))
912 gst_buffer_pool_config_add_option (config,
913 GST_BUFFER_POOL_OPTION_GL_SYNC_META);
914
915 gst_buffer_pool_set_config (pool, config);
916
917 if (update_pool)
918 gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
919 else
920 gst_query_add_allocation_pool (query, pool, size, min, max);
921
922 gst_object_unref (pool);
923
924 return TRUE;
925 }
926
927 /**
928 * gst_gl_filter_filter_texture:
929 * @filter: a #GstGLFilter
930 * @input: an input buffer
931 * @output: an output buffer
932 *
933 * Calls filter_texture vfunc with correctly mapped #GstGLMemorys
934 *
935 * Returns: whether the transformation succeeded
936 *
937 * Since: 1.4
938 */
939 gboolean
gst_gl_filter_filter_texture(GstGLFilter * filter,GstBuffer * input,GstBuffer * output)940 gst_gl_filter_filter_texture (GstGLFilter * filter, GstBuffer * input,
941 GstBuffer * output)
942 {
943 GstGLFilterClass *filter_class;
944 GstMemory *in_tex, *out_tex;
945 GstVideoFrame gl_frame, out_frame;
946 gboolean ret;
947
948 filter_class = GST_GL_FILTER_GET_CLASS (filter);
949
950 if (!gst_video_frame_map (&gl_frame, &filter->in_info, input,
951 GST_MAP_READ | GST_MAP_GL)) {
952 ret = FALSE;
953 goto inbuf_error;
954 }
955
956 in_tex = gl_frame.map[0].memory;
957 if (!gst_is_gl_memory (in_tex)) {
958 ret = FALSE;
959 GST_ERROR_OBJECT (filter, "Input memory must be GstGLMemory");
960 goto unmap_out_error;
961 }
962
963 if (!gst_video_frame_map (&out_frame, &filter->out_info, output,
964 GST_MAP_WRITE | GST_MAP_GL)) {
965 ret = FALSE;
966 goto unmap_out_error;
967 }
968
969 out_tex = out_frame.map[0].memory;
970 g_return_val_if_fail (gst_is_gl_memory (out_tex), FALSE);
971
972 GST_DEBUG ("calling filter_texture with textures in:%i out:%i",
973 GST_GL_MEMORY_CAST (in_tex)->tex_id,
974 GST_GL_MEMORY_CAST (out_tex)->tex_id);
975
976 g_assert (filter_class->filter_texture);
977
978 ret = filter_class->filter_texture (filter, GST_GL_MEMORY_CAST (in_tex),
979 GST_GL_MEMORY_CAST (out_tex));
980
981 gst_video_frame_unmap (&out_frame);
982 unmap_out_error:
983 gst_video_frame_unmap (&gl_frame);
984 inbuf_error:
985
986 return ret;
987 }
988
989 static void
_filter_gl(GstGLContext * context,GstGLFilter * filter)990 _filter_gl (GstGLContext * context, GstGLFilter * filter)
991 {
992 GstGLFilterClass *filter_class = GST_GL_FILTER_GET_CLASS (filter);
993
994 gst_gl_insert_debug_marker (context,
995 "processing in element %s", GST_OBJECT_NAME (filter));
996
997 if (filter_class->filter)
998 filter->gl_result =
999 filter_class->filter (filter, filter->inbuf, filter->outbuf);
1000 else
1001 filter->gl_result =
1002 gst_gl_filter_filter_texture (filter, filter->inbuf, filter->outbuf);
1003 }
1004
1005 static GstFlowReturn
gst_gl_filter_transform(GstBaseTransform * bt,GstBuffer * inbuf,GstBuffer * outbuf)1006 gst_gl_filter_transform (GstBaseTransform * bt, GstBuffer * inbuf,
1007 GstBuffer * outbuf)
1008 {
1009 GstGLFilter *filter = GST_GL_FILTER (bt);
1010 GstGLFilterClass *filter_class = GST_GL_FILTER_GET_CLASS (bt);
1011 GstGLDisplay *display = GST_GL_BASE_FILTER (bt)->display;
1012 GstGLContext *context = GST_GL_BASE_FILTER (bt)->context;
1013 GstGLSyncMeta *out_sync_meta, *in_sync_meta;
1014 gboolean ret;
1015
1016 if (!display)
1017 return GST_FLOW_NOT_NEGOTIATED;
1018
1019 g_assert (filter_class->filter || filter_class->filter_texture);
1020
1021 in_sync_meta = gst_buffer_get_gl_sync_meta (inbuf);
1022 if (in_sync_meta)
1023 gst_gl_sync_meta_wait (in_sync_meta, context);
1024
1025 filter->inbuf = inbuf;
1026 filter->outbuf = outbuf;
1027 gst_gl_context_thread_add (context, (GstGLContextThreadFunc) _filter_gl,
1028 filter);
1029 ret = filter->gl_result;
1030
1031 out_sync_meta = gst_buffer_get_gl_sync_meta (outbuf);
1032 if (out_sync_meta)
1033 gst_gl_sync_meta_set_sync_point (out_sync_meta, context);
1034
1035 return ret ? GST_FLOW_OK : GST_FLOW_ERROR;
1036 }
1037
1038 struct glcb
1039 {
1040 GstGLFilter *filter;
1041 GstGLFilterRenderFunc func;
1042 GstGLMemory *in_tex;
1043 gpointer data;
1044 };
1045
1046 static gboolean
_glcb(gpointer data)1047 _glcb (gpointer data)
1048 {
1049 struct glcb *cb = data;
1050
1051 return cb->func (cb->filter, cb->in_tex, cb->data);
1052 }
1053
1054 /**
1055 * gst_gl_filter_render_to_target:
1056 * @filter: a #GstGLFilter
1057 * @input: the input texture
1058 * @output: the output texture
1059 * @func: (scope call): the function to transform @input into @output. called with @data
1060 * @data: (allow-none): the data associated with @func
1061 *
1062 * Transforms @input into @output using @func on through FBO.
1063 *
1064 * Returns: the return value of @func
1065 *
1066 * Since: 1.10
1067 */
1068 gboolean
gst_gl_filter_render_to_target(GstGLFilter * filter,GstGLMemory * input,GstGLMemory * output,GstGLFilterRenderFunc func,gpointer data)1069 gst_gl_filter_render_to_target (GstGLFilter * filter, GstGLMemory * input,
1070 GstGLMemory * output, GstGLFilterRenderFunc func, gpointer data)
1071 {
1072 struct glcb cb;
1073
1074 cb.filter = filter;
1075 cb.func = func;
1076 cb.in_tex = input;
1077 cb.data = data;
1078
1079 return gst_gl_framebuffer_draw_to_texture (filter->fbo, output, _glcb, &cb);
1080 }
1081
1082 static void
_get_attributes(GstGLFilter * filter)1083 _get_attributes (GstGLFilter * filter)
1084 {
1085 if (!filter->default_shader)
1086 return;
1087
1088 if (filter->valid_attributes)
1089 return;
1090
1091 if (filter->draw_attr_position_loc == -1)
1092 filter->draw_attr_position_loc =
1093 gst_gl_shader_get_attribute_location (filter->default_shader,
1094 "a_position");
1095
1096 if (filter->draw_attr_texture_loc == -1)
1097 filter->draw_attr_texture_loc =
1098 gst_gl_shader_get_attribute_location (filter->default_shader,
1099 "a_texcoord");
1100
1101 filter->valid_attributes = TRUE;
1102 }
1103
1104 static gboolean
_draw_with_shader_cb(GstGLFilter * filter,GstGLMemory * in_tex,gpointer unused)1105 _draw_with_shader_cb (GstGLFilter * filter, GstGLMemory * in_tex,
1106 gpointer unused)
1107 {
1108 GstGLContext *context = GST_GL_BASE_FILTER (filter)->context;
1109 GstGLFuncs *gl = context->gl_vtable;
1110 guint gl_target;
1111
1112 #if GST_GL_HAVE_OPENGL
1113 if (gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL) {
1114 gl->MatrixMode (GL_PROJECTION);
1115 gl->LoadIdentity ();
1116 }
1117 #endif
1118
1119 _get_attributes (filter);
1120 gst_gl_shader_use (filter->default_shader);
1121 gl_target = gst_gl_texture_target_to_gl (filter->in_texture_target);
1122
1123 gl->ActiveTexture (GL_TEXTURE1);
1124 gl->BindTexture (gl_target, gst_gl_memory_get_texture_id (in_tex));
1125
1126 gst_gl_shader_set_uniform_1i (filter->default_shader, "tex", 1);
1127 gst_gl_shader_set_uniform_1f (filter->default_shader, "width",
1128 GST_VIDEO_INFO_WIDTH (&filter->out_info));
1129 gst_gl_shader_set_uniform_1f (filter->default_shader, "height",
1130 GST_VIDEO_INFO_HEIGHT (&filter->out_info));
1131
1132 gst_gl_filter_draw_fullscreen_quad (filter);
1133
1134 return TRUE;
1135 }
1136
1137 /**
1138 * gst_gl_filter_render_to_target_with_shader:
1139 * @filter: a #GstGLFilter
1140 * @input: the input texture
1141 * @output: the output texture
1142 * @shader: the shader to use.
1143 *
1144 * Transforms @input into @output using @shader with a FBO.
1145 *
1146 * See also: gst_gl_filter_render_to_target()
1147 *
1148 * Since: 1.4
1149 */
1150 /* attach target to a FBO, use shader, pass input as "tex" uniform to
1151 * the shader, render input to a quad */
1152 void
gst_gl_filter_render_to_target_with_shader(GstGLFilter * filter,GstGLMemory * input,GstGLMemory * output,GstGLShader * shader)1153 gst_gl_filter_render_to_target_with_shader (GstGLFilter * filter,
1154 GstGLMemory * input, GstGLMemory * output, GstGLShader * shader)
1155 {
1156 if (filter->default_shader != shader)
1157 filter->valid_attributes = FALSE;
1158 filter->default_shader = shader;
1159
1160 gst_gl_filter_render_to_target (filter, input, output, _draw_with_shader_cb,
1161 NULL);
1162 }
1163
1164 /* *INDENT-OFF* */
1165 static const GLfloat vertices[] = {
1166 -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1167 1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
1168 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
1169 -1.0f, 1.0f, 0.0f, 0.0f, 1.0f
1170 };
1171
1172 static const GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
1173 /* *INDENT-ON* */
1174
1175 static void
_bind_buffer(GstGLFilter * filter)1176 _bind_buffer (GstGLFilter * filter)
1177 {
1178 GstGLContext *context = GST_GL_BASE_FILTER (filter)->context;
1179 const GstGLFuncs *gl = context->gl_vtable;
1180
1181 gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, filter->vbo_indices);
1182 gl->BindBuffer (GL_ARRAY_BUFFER, filter->vertex_buffer);
1183
1184 _get_attributes (filter);
1185 /* Load the vertex position */
1186 gl->VertexAttribPointer (filter->draw_attr_position_loc, 3, GL_FLOAT,
1187 GL_FALSE, 5 * sizeof (GLfloat), (void *) 0);
1188
1189 /* Load the texture coordinate */
1190 gl->VertexAttribPointer (filter->draw_attr_texture_loc, 2, GL_FLOAT, GL_FALSE,
1191 5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
1192
1193
1194 gl->EnableVertexAttribArray (filter->draw_attr_position_loc);
1195 gl->EnableVertexAttribArray (filter->draw_attr_texture_loc);
1196 }
1197
1198 static void
_unbind_buffer(GstGLFilter * filter)1199 _unbind_buffer (GstGLFilter * filter)
1200 {
1201 GstGLContext *context = GST_GL_BASE_FILTER (filter)->context;
1202 const GstGLFuncs *gl = context->gl_vtable;
1203
1204 gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
1205 gl->BindBuffer (GL_ARRAY_BUFFER, 0);
1206
1207 gl->DisableVertexAttribArray (filter->draw_attr_position_loc);
1208 gl->DisableVertexAttribArray (filter->draw_attr_texture_loc);
1209 }
1210
1211 /**
1212 * gst_gl_filter_draw_fullscreen_quad:
1213 * @filter: a #GstGLFilter
1214 *
1215 * Render a fullscreen quad using the current GL state. The only GL state this
1216 * modifies is the necessary vertex/index buffers and, if necessary, a
1217 * Vertex Array Object for drawing a fullscreen quad. Framebuffer state,
1218 * any shaders, viewport state, etc must be setup by the caller.
1219 *
1220 * Since: 1.10
1221 */
1222 void
gst_gl_filter_draw_fullscreen_quad(GstGLFilter * filter)1223 gst_gl_filter_draw_fullscreen_quad (GstGLFilter * filter)
1224 {
1225 GstGLContext *context = GST_GL_BASE_FILTER (filter)->context;
1226 GstGLFuncs *gl = context->gl_vtable;
1227
1228 {
1229 if (!filter->vertex_buffer) {
1230 if (gl->GenVertexArrays) {
1231 gl->GenVertexArrays (1, &filter->vao);
1232 gl->BindVertexArray (filter->vao);
1233 }
1234
1235 gl->GenBuffers (1, &filter->vertex_buffer);
1236 gl->BindBuffer (GL_ARRAY_BUFFER, filter->vertex_buffer);
1237 gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices,
1238 GL_STATIC_DRAW);
1239
1240 gl->GenBuffers (1, &filter->vbo_indices);
1241 gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, filter->vbo_indices);
1242 gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indices), indices,
1243 GL_STATIC_DRAW);
1244 }
1245
1246 if (gl->GenVertexArrays)
1247 gl->BindVertexArray (filter->vao);
1248 _bind_buffer (filter);
1249
1250 gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
1251
1252 if (gl->GenVertexArrays)
1253 gl->BindVertexArray (0);
1254 else
1255 _unbind_buffer (filter);
1256 }
1257 }
1258