• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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