7a9a3aaf2a3c18c1fa1c636cc9c979841ad70b73
[platform/upstream/gstreamer.git] / gst-libs / gst / gl / gstglfilter.c
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 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <gst/video/gstvideometa.h>
28
29 #include "gstglfilter.h"
30
31 #if GST_GL_HAVE_PLATFORM_EGL
32 #include "egl/gsteglimagememory.h"
33 #endif
34
35 #define GST_CAT_DEFAULT gst_gl_filter_debug
36 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
37
38
39 static GstStaticPadTemplate gst_gl_filter_src_pad_template =
40     GST_STATIC_PAD_TEMPLATE ("src",
41     GST_PAD_SRC,
42     GST_PAD_ALWAYS,
43     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
44         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
45             "RGBA") "; "
46 #if GST_GL_HAVE_PLATFORM_EGL
47         GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_EGL_IMAGE,
48             "RGBA") "; "
49 #endif
50         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
51         (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META,
52             "RGBA") "; " GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS))
53     );
54
55 static GstStaticPadTemplate gst_gl_filter_sink_pad_template =
56     GST_STATIC_PAD_TEMPLATE ("sink",
57     GST_PAD_SINK,
58     GST_PAD_ALWAYS,
59     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
60         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
61             "RGBA") "; "
62 #if GST_GL_HAVE_PLATFORM_EGL
63         GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_EGL_IMAGE,
64             "RGBA") "; "
65 #endif
66         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
67         (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, "RGBA") "; "
68         GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS))
69     );
70
71 /* Properties */
72 enum
73 {
74   PROP_0,
75   PROP_CONTEXT
76 };
77
78 #define DEBUG_INIT \
79   GST_DEBUG_CATEGORY_INIT (gst_gl_filter_debug, "glfilter", 0, "glfilter element");
80 #define gst_gl_filter_parent_class parent_class
81 G_DEFINE_TYPE_WITH_CODE (GstGLFilter, gst_gl_filter, GST_TYPE_BASE_TRANSFORM,
82     DEBUG_INIT);
83
84 static void gst_gl_filter_set_property (GObject * object, guint prop_id,
85     const GValue * value, GParamSpec * pspec);
86 static void gst_gl_filter_get_property (GObject * object, guint prop_id,
87     GValue * value, GParamSpec * pspec);
88
89 static void gst_gl_filter_set_context (GstElement * element,
90     GstContext * context);
91 static gboolean gst_gl_filter_query (GstBaseTransform * trans,
92     GstPadDirection direction, GstQuery * query);
93 static GstCaps *gst_gl_filter_transform_caps (GstBaseTransform * bt,
94     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
95 static GstCaps *gst_gl_filter_fixate_caps (GstBaseTransform * bt,
96     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
97 static void gst_gl_filter_reset (GstGLFilter * filter);
98 static gboolean gst_gl_filter_start (GstBaseTransform * bt);
99 static gboolean gst_gl_filter_stop (GstBaseTransform * bt);
100 static gboolean gst_gl_filter_get_unit_size (GstBaseTransform * trans,
101     GstCaps * caps, gsize * size);
102 static GstFlowReturn gst_gl_filter_transform (GstBaseTransform * bt,
103     GstBuffer * inbuf, GstBuffer * outbuf);
104 static gboolean gst_gl_filter_propose_allocation (GstBaseTransform * trans,
105     GstQuery * decide_query, GstQuery * query);
106 static gboolean gst_gl_filter_decide_allocation (GstBaseTransform * trans,
107     GstQuery * query);
108 static gboolean gst_gl_filter_set_caps (GstBaseTransform * bt, GstCaps * incaps,
109     GstCaps * outcaps);
110
111 /* GstGLContextThreadFunc */
112 static void gst_gl_filter_start_gl (GstGLContext * context, gpointer data);
113 static void gst_gl_filter_stop_gl (GstGLContext * context, gpointer data);
114
115 static void
116 gst_gl_filter_class_init (GstGLFilterClass * klass)
117 {
118   GObjectClass *gobject_class;
119   GstElementClass *element_class;
120
121   gobject_class = (GObjectClass *) klass;
122   element_class = GST_ELEMENT_CLASS (klass);
123
124   gobject_class->set_property = gst_gl_filter_set_property;
125   gobject_class->get_property = gst_gl_filter_get_property;
126
127   GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
128       gst_gl_filter_transform_caps;
129   GST_BASE_TRANSFORM_CLASS (klass)->fixate_caps = gst_gl_filter_fixate_caps;
130   GST_BASE_TRANSFORM_CLASS (klass)->transform = gst_gl_filter_transform;
131   GST_BASE_TRANSFORM_CLASS (klass)->query = gst_gl_filter_query;
132   GST_BASE_TRANSFORM_CLASS (klass)->start = gst_gl_filter_start;
133   GST_BASE_TRANSFORM_CLASS (klass)->stop = gst_gl_filter_stop;
134   GST_BASE_TRANSFORM_CLASS (klass)->set_caps = gst_gl_filter_set_caps;
135   GST_BASE_TRANSFORM_CLASS (klass)->propose_allocation =
136       gst_gl_filter_propose_allocation;
137   GST_BASE_TRANSFORM_CLASS (klass)->decide_allocation =
138       gst_gl_filter_decide_allocation;
139   GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size = gst_gl_filter_get_unit_size;
140
141   element_class->set_context = gst_gl_filter_set_context;
142
143   gst_element_class_add_pad_template (element_class,
144       gst_static_pad_template_get (&gst_gl_filter_src_pad_template));
145   gst_element_class_add_pad_template (element_class,
146       gst_static_pad_template_get (&gst_gl_filter_sink_pad_template));
147
148   g_object_class_install_property (gobject_class, PROP_CONTEXT,
149       g_param_spec_object ("context",
150           "OpenGL context",
151           "Get OpenGL context",
152           GST_GL_TYPE_CONTEXT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
153
154   klass->supported_gl_api = GST_GL_API_ANY;
155   klass->set_caps = NULL;
156   klass->filter = NULL;
157   klass->display_init_cb = NULL;
158   klass->display_reset_cb = NULL;
159   klass->onInitFBO = NULL;
160   klass->onStart = NULL;
161   klass->onStop = NULL;
162   klass->onReset = NULL;
163   klass->filter_texture = NULL;
164 }
165
166 static void
167 gst_gl_filter_init (GstGLFilter * filter)
168 {
169   gst_gl_filter_reset (filter);
170 }
171
172 static void
173 gst_gl_filter_set_property (GObject * object, guint prop_id,
174     const GValue * value, GParamSpec * pspec)
175 {
176   switch (prop_id) {
177     default:
178       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
179       break;
180   }
181 }
182
183 static void
184 gst_gl_filter_get_property (GObject * object, guint prop_id,
185     GValue * value, GParamSpec * pspec)
186 {
187   GstGLFilter *filter = GST_GL_FILTER (object);
188
189   switch (prop_id) {
190     case PROP_CONTEXT:
191       g_value_set_object (value, filter->context);
192       break;
193     default:
194       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
195       break;
196   }
197 }
198
199 static void
200 gst_gl_filter_set_context (GstElement * element, GstContext * context)
201 {
202   GstGLFilter *filter = GST_GL_FILTER (element);
203   GstGLFilterClass *filter_class = GST_GL_FILTER_GET_CLASS (filter);
204
205   gst_gl_handle_set_context (element, context, &filter->display,
206       &filter->other_context);
207   if (filter->display)
208     gst_gl_display_filter_gl_api (filter->display,
209         filter_class->supported_gl_api);
210 }
211
212 static gboolean
213 gst_gl_filter_query (GstBaseTransform * trans, GstPadDirection direction,
214     GstQuery * query)
215 {
216   GstGLFilter *filter = GST_GL_FILTER (trans);
217   GstGLFilterClass *filter_class = GST_GL_FILTER_GET_CLASS (filter);
218
219   switch (GST_QUERY_TYPE (query)) {
220     case GST_QUERY_ALLOCATION:
221     {
222       if (direction == GST_PAD_SINK
223           && gst_base_transform_is_passthrough (trans))
224         return gst_pad_peer_query (GST_BASE_TRANSFORM_SRC_PAD (trans), query);
225       break;
226     }
227     case GST_QUERY_CONTEXT:
228     {
229       gboolean ret = gst_gl_handle_context_query ((GstElement *) filter, query,
230           &filter->display, &filter->other_context);
231       if (filter->display)
232         gst_gl_display_filter_gl_api (filter->display,
233             filter_class->supported_gl_api);
234       return ret;
235     }
236     default:
237       break;
238   }
239
240   return GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
241       query);;
242 }
243
244 static void
245 gst_gl_filter_reset (GstGLFilter * filter)
246 {
247   GstGLFilterClass *filter_class = GST_GL_FILTER_GET_CLASS (filter);
248
249   gst_caps_replace (&filter->out_caps, NULL);
250
251   if (filter->upload) {
252     gst_object_unref (filter->upload);
253     filter->upload = NULL;
254   }
255
256   if (filter->in_convert) {
257     gst_object_unref (filter->in_convert);
258     filter->in_convert = NULL;
259   }
260
261   if (filter->download) {
262     gst_object_unref (filter->download);
263     filter->download = NULL;
264   }
265
266   if (filter->uploaded_buffer) {
267     gst_buffer_unref (filter->uploaded_buffer);
268     filter->uploaded_buffer = NULL;
269   }
270
271   if (filter->pool) {
272     gst_object_unref (filter->pool);
273     filter->pool = NULL;
274   }
275
276   if (filter->context) {
277     if (filter_class->onReset)
278       filter_class->onReset (filter);
279
280     if (filter_class->display_reset_cb != NULL) {
281       gst_gl_context_thread_add (filter->context, gst_gl_filter_stop_gl,
282           filter);
283     }
284     /* blocking call, delete the FBO */
285     if (filter->fbo != 0) {
286       gst_gl_context_del_fbo (filter->context, filter->fbo,
287           filter->depthbuffer);
288     }
289
290     if (filter->in_tex_id) {
291       gst_gl_context_del_texture (filter->context, &filter->in_tex_id);
292       filter->in_tex_id = 0;
293     }
294
295     if (filter->out_tex_id) {
296       gst_gl_context_del_texture (filter->context, &filter->out_tex_id);
297       filter->out_tex_id = 0;
298     }
299
300     gst_object_unref (filter->context);
301     filter->context = NULL;
302   }
303
304   if (filter->display) {
305     gst_object_unref (filter->display);
306     filter->display = NULL;
307   }
308
309   filter->fbo = 0;
310   filter->depthbuffer = 0;
311   filter->default_shader = NULL;
312   filter->draw_attr_position_loc = -1;
313   filter->draw_attr_texture_loc = -1;
314   if (filter->other_context)
315     gst_object_unref (filter->other_context);
316   filter->other_context = NULL;
317
318   if (filter->context)
319     gst_object_unref (filter->context);
320   filter->context = NULL;
321
322   if (filter->in_converted_caps) {
323     gst_caps_unref (filter->in_converted_caps);
324     filter->in_converted_caps = NULL;
325   }
326 }
327
328 static gboolean
329 gst_gl_filter_start (GstBaseTransform * bt)
330 {
331   GstGLFilter *filter = GST_GL_FILTER (bt);
332   GstGLFilterClass *filter_class = GST_GL_FILTER_GET_CLASS (filter);
333
334   if (!gst_gl_ensure_element_data (filter, &filter->display,
335           &filter->other_context))
336     return FALSE;
337
338   gst_gl_display_filter_gl_api (filter->display,
339       filter_class->supported_gl_api);
340
341   if (filter_class->onStart)
342     filter_class->onStart (filter);
343
344   return TRUE;
345 }
346
347 static gboolean
348 gst_gl_filter_stop (GstBaseTransform * bt)
349 {
350   GstGLFilter *filter = GST_GL_FILTER (bt);
351   GstGLFilterClass *filter_class = GST_GL_FILTER_GET_CLASS (filter);
352
353   if (filter_class->onStop)
354     filter_class->onStop (filter);
355
356   gst_gl_filter_reset (filter);
357
358   return TRUE;
359 }
360
361 static void
362 gst_gl_filter_start_gl (GstGLContext * context, gpointer data)
363 {
364   GstGLFilter *filter = GST_GL_FILTER (data);
365   GstGLFilterClass *filter_class = GST_GL_FILTER_GET_CLASS (filter);
366
367   filter_class->display_init_cb (filter);
368 }
369
370 static void
371 gst_gl_filter_stop_gl (GstGLContext * context, gpointer data)
372 {
373   const GstGLFuncs *gl = context->gl_vtable;
374   GstGLFilter *filter = GST_GL_FILTER (data);
375   GstGLFilterClass *filter_class = GST_GL_FILTER_GET_CLASS (filter);
376
377   filter_class->display_reset_cb (filter);
378
379   if (filter->vao) {
380     gl->DeleteVertexArrays (1, &filter->vao);
381     filter->vao = 0;
382   }
383
384   if (filter->vertex_buffer) {
385     gl->DeleteBuffers (1, &filter->vertex_buffer);
386     filter->vertex_buffer = 0;
387   }
388 }
389
390 static GstCaps *
391 gst_gl_filter_fixate_caps (GstBaseTransform * bt,
392     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
393 {
394   GstStructure *ins, *outs;
395   const GValue *from_par, *to_par;
396   GValue fpar = { 0, }, tpar = {
397   0,};
398
399   othercaps = gst_caps_truncate (othercaps);
400   othercaps = gst_caps_make_writable (othercaps);
401
402   GST_DEBUG_OBJECT (bt, "trying to fixate othercaps %" GST_PTR_FORMAT
403       " based on caps %" GST_PTR_FORMAT, othercaps, caps);
404
405   ins = gst_caps_get_structure (caps, 0);
406   outs = gst_caps_get_structure (othercaps, 0);
407
408   from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
409   to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
410
411   /* If we're fixating from the sinkpad we always set the PAR and
412    * assume that missing PAR on the sinkpad means 1/1 and
413    * missing PAR on the srcpad means undefined
414    */
415   if (direction == GST_PAD_SINK) {
416     if (!from_par) {
417       g_value_init (&fpar, GST_TYPE_FRACTION);
418       gst_value_set_fraction (&fpar, 1, 1);
419       from_par = &fpar;
420     }
421     if (!to_par) {
422       g_value_init (&tpar, GST_TYPE_FRACTION);
423       gst_value_set_fraction (&tpar, 1, 1);
424       to_par = &tpar;
425     }
426   } else {
427     if (!to_par) {
428       g_value_init (&tpar, GST_TYPE_FRACTION);
429       gst_value_set_fraction (&tpar, 1, 1);
430       to_par = &tpar;
431
432       gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
433           NULL);
434     }
435     if (!from_par) {
436       g_value_init (&fpar, GST_TYPE_FRACTION);
437       gst_value_set_fraction (&fpar, 1, 1);
438       from_par = &fpar;
439     }
440   }
441
442   /* we have both PAR but they might not be fixated */
443   {
444     gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
445     gint w = 0, h = 0;
446     gint from_dar_n, from_dar_d;
447     gint num, den;
448
449     /* from_par should be fixed */
450     g_return_val_if_fail (gst_value_is_fixed (from_par), othercaps);
451
452     from_par_n = gst_value_get_fraction_numerator (from_par);
453     from_par_d = gst_value_get_fraction_denominator (from_par);
454
455     gst_structure_get_int (ins, "width", &from_w);
456     gst_structure_get_int (ins, "height", &from_h);
457
458     gst_structure_get_int (outs, "width", &w);
459     gst_structure_get_int (outs, "height", &h);
460
461     /* if both width and height are already fixed, we can't do anything
462      * about it anymore */
463     if (w && h) {
464       GST_DEBUG_OBJECT (bt, "dimensions already set to %dx%d, not fixating",
465           w, h);
466       if (!gst_value_is_fixed (to_par)) {
467         GST_DEBUG_OBJECT (bt, "fixating to_par to %dx%d", 1, 1);
468         if (gst_structure_has_field (outs, "pixel-aspect-ratio"))
469           gst_structure_fixate_field_nearest_fraction (outs,
470               "pixel-aspect-ratio", 1, 1);
471       }
472       goto done;
473     }
474
475     /* Calculate input DAR */
476     if (!gst_util_fraction_multiply (from_w, from_h, from_par_n, from_par_d,
477             &from_dar_n, &from_dar_d)) {
478       GST_ELEMENT_ERROR (bt, CORE, NEGOTIATION, (NULL),
479           ("Error calculating the output scaled size - integer overflow"));
480       goto done;
481     }
482
483     GST_DEBUG_OBJECT (bt, "Input DAR is %d/%d", from_dar_n, from_dar_d);
484
485     /* If either width or height are fixed there's not much we
486      * can do either except choosing a height or width and PAR
487      * that matches the DAR as good as possible
488      */
489     if (h) {
490       gint num, den;
491
492       GST_DEBUG_OBJECT (bt, "height is fixed (%d)", h);
493
494       if (!gst_value_is_fixed (to_par)) {
495         gst_value_set_fraction (&tpar, 1, 1);
496       }
497
498       /* PAR is fixed, choose the height that is nearest to the
499        * height with the same DAR */
500       to_par_n = gst_value_get_fraction_numerator (to_par);
501       to_par_d = gst_value_get_fraction_denominator (to_par);
502
503       GST_DEBUG_OBJECT (bt, "PAR is fixed %d/%d", to_par_n, to_par_d);
504
505       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
506               to_par_n, &num, &den)) {
507         GST_ELEMENT_ERROR (bt, CORE, NEGOTIATION, (NULL),
508             ("Error calculating the output scaled size - integer overflow"));
509         goto done;
510       }
511
512       w = (guint) gst_util_uint64_scale_int (h, num, den);
513       gst_structure_fixate_field_nearest_int (outs, "width", w);
514
515       goto done;
516     } else if (w) {
517       gint num, den;
518
519       GST_DEBUG_OBJECT (bt, "width is fixed (%d)", w);
520
521       if (!gst_value_is_fixed (to_par)) {
522         gst_value_set_fraction (&tpar, 1, 1);
523       }
524
525       /* PAR is fixed, choose the height that is nearest to the
526        * height with the same DAR */
527       to_par_n = gst_value_get_fraction_numerator (to_par);
528       to_par_d = gst_value_get_fraction_denominator (to_par);
529
530       GST_DEBUG_OBJECT (bt, "PAR is fixed %d/%d", to_par_n, to_par_d);
531
532       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
533               to_par_n, &num, &den)) {
534         GST_ELEMENT_ERROR (bt, CORE, NEGOTIATION, (NULL),
535             ("Error calculating the output scaled size - integer overflow"));
536         goto done;
537       }
538
539       h = (guint) gst_util_uint64_scale_int (w, den, num);
540       gst_structure_fixate_field_nearest_int (outs, "height", h);
541
542       goto done;
543     } else if (gst_value_is_fixed (to_par)) {
544       GstStructure *tmp;
545       gint set_h, set_w, f_h, f_w;
546
547       to_par_n = gst_value_get_fraction_numerator (to_par);
548       to_par_d = gst_value_get_fraction_denominator (to_par);
549
550       /* Calculate scale factor for the PAR change */
551       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
552               to_par_d, &num, &den)) {
553         GST_ELEMENT_ERROR (bt, CORE, NEGOTIATION, (NULL),
554             ("Error calculating the output scaled size - integer overflow"));
555         goto done;
556       }
557
558       /* Try to keep the input height */
559       tmp = gst_structure_copy (outs);
560       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
561       gst_structure_get_int (tmp, "height", &set_h);
562
563       /* This might have failed but try to scale the width
564        * to keep the DAR nonetheless */
565       w = (guint) gst_util_uint64_scale_int (set_h, num, den);
566       gst_structure_fixate_field_nearest_int (tmp, "width", w);
567       gst_structure_get_int (tmp, "width", &set_w);
568       gst_structure_free (tmp);
569
570       /* We kept the DAR and the height is nearest to the original height */
571       if (set_w == w) {
572         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
573             G_TYPE_INT, set_h, NULL);
574         goto done;
575       }
576
577       f_h = set_h;
578       f_w = set_w;
579
580       /* If the former failed, try to keep the input width at least */
581       tmp = gst_structure_copy (outs);
582       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
583       gst_structure_get_int (tmp, "width", &set_w);
584
585       /* This might have failed but try to scale the width
586        * to keep the DAR nonetheless */
587       h = (guint) gst_util_uint64_scale_int (set_w, den, num);
588       gst_structure_fixate_field_nearest_int (tmp, "height", h);
589       gst_structure_get_int (tmp, "height", &set_h);
590       gst_structure_free (tmp);
591
592       /* We kept the DAR and the width is nearest to the original width */
593       if (set_h == h) {
594         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
595             G_TYPE_INT, set_h, NULL);
596         goto done;
597       }
598
599       /* If all this failed, keep the height that was nearest to the orignal
600        * height and the nearest possible width. This changes the DAR but
601        * there's not much else to do here.
602        */
603       gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
604           f_h, NULL);
605       goto done;
606     } else {
607       GstStructure *tmp;
608       gint set_h, set_w, set_par_n, set_par_d, tmp2;
609
610       /* width, height and PAR are not fixed */
611
612       /* First try to keep the height and width as good as possible
613        * and scale PAR */
614       tmp = gst_structure_copy (outs);
615       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
616       gst_structure_get_int (tmp, "height", &set_h);
617       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
618       gst_structure_get_int (tmp, "width", &set_w);
619
620       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, set_w,
621               &to_par_n, &to_par_d)) {
622         GST_ELEMENT_ERROR (bt, CORE, NEGOTIATION, (NULL),
623             ("Error calculating the output scaled size - integer overflow"));
624         gst_structure_free (tmp);
625         goto done;
626       }
627
628       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
629         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
630       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
631           to_par_n, to_par_d);
632       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
633           &set_par_d);
634       gst_structure_free (tmp);
635
636       if (set_par_n == to_par_n && set_par_d == to_par_d) {
637         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
638             G_TYPE_INT, set_h, NULL);
639
640         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
641             set_par_n != set_par_d)
642           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
643               set_par_n, set_par_d, NULL);
644         goto done;
645       }
646
647       /* Otherwise try to scale width to keep the DAR with the set
648        * PAR and height */
649       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
650               set_par_n, &num, &den)) {
651         GST_ELEMENT_ERROR (bt, CORE, NEGOTIATION, (NULL),
652             ("Error calculating the output scaled size - integer overflow"));
653         goto done;
654       }
655
656       w = (guint) gst_util_uint64_scale_int (set_h, num, den);
657       tmp = gst_structure_copy (outs);
658       gst_structure_fixate_field_nearest_int (tmp, "width", w);
659       gst_structure_get_int (tmp, "width", &tmp2);
660       gst_structure_free (tmp);
661
662       if (tmp2 == w) {
663         gst_structure_set (outs, "width", G_TYPE_INT, tmp2, "height",
664             G_TYPE_INT, set_h, NULL);
665         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
666             set_par_n != set_par_d)
667           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
668               set_par_n, set_par_d, NULL);
669         goto done;
670       }
671
672       /* ... or try the same with the height */
673       h = (guint) gst_util_uint64_scale_int (set_w, den, num);
674       tmp = gst_structure_copy (outs);
675       gst_structure_fixate_field_nearest_int (tmp, "height", h);
676       gst_structure_get_int (tmp, "height", &tmp2);
677       gst_structure_free (tmp);
678
679       if (tmp2 == h) {
680         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
681             G_TYPE_INT, tmp2, NULL);
682         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
683             set_par_n != set_par_d)
684           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
685               set_par_n, set_par_d, NULL);
686         goto done;
687       }
688
689       /* If all fails we can't keep the DAR and take the nearest values
690        * for everything from the first try */
691       gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
692           G_TYPE_INT, set_h, NULL);
693       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
694           set_par_n != set_par_d)
695         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
696             set_par_n, set_par_d, NULL);
697     }
698   }
699
700
701 done:
702   othercaps = gst_caps_fixate (othercaps);
703
704   GST_DEBUG_OBJECT (bt, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
705
706   if (from_par == &fpar)
707     g_value_unset (&fpar);
708   if (to_par == &tpar)
709     g_value_unset (&tpar);
710
711   return othercaps;
712 }
713
714 static GstCaps *
715 gst_gl_filter_set_caps_features (const GstCaps * caps,
716     const gchar * feature_name)
717 {
718   GstCaps *ret = gst_gl_caps_replace_all_caps_features (caps, feature_name);
719   gst_caps_set_simple (ret, "format", G_TYPE_STRING, "RGBA", NULL);
720   return ret;
721 }
722
723 static GstCaps *
724 gst_gl_filter_transform_caps (GstBaseTransform * bt,
725     GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps)
726 {
727   GstGLFilter *filter = GST_GL_FILTER (bt);
728   GstCaps *tmp = NULL;
729   GstCaps *result = NULL;
730
731   /* The following is the list of caps transformations performed.  When
732    * direction == GST_PAD_SINK we start at the sinkpad and work toward the src
733    * pad and vice versa for direction == GST_PAD_SRC.
734    *
735    * sinkpad <-> (upload <-> convert) <-> filter (possible resize) <->
736    * (convert <-> download/output) <-> srcpad
737    */
738   if (direction == GST_PAD_SINK) {
739     result =
740         gst_gl_upload_transform_caps (filter->context, direction, caps, NULL);
741     tmp = result;
742
743     result =
744         gst_gl_color_convert_transform_caps (filter->context, direction, tmp,
745         NULL);
746     gst_caps_unref (tmp);
747   } else {
748     GstCaps *gl_caps = gst_caps_merge (gst_gl_filter_set_caps_features (caps,
749             GST_CAPS_FEATURE_MEMORY_GL_MEMORY),
750         gst_gl_filter_set_caps_features (caps,
751             GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META));
752
753     result =
754         gst_gl_download_transform_caps (filter->context, direction, caps, NULL);
755
756     result = gst_caps_merge (gl_caps, result);
757   }
758   tmp = result;
759   GST_DEBUG_OBJECT (bt, "transfer returned caps %" GST_PTR_FORMAT, tmp);
760
761   if (direction == GST_PAD_SRC) {
762     result =
763         gst_gl_color_convert_transform_caps (filter->context, direction, tmp,
764         NULL);
765     gst_caps_unref (tmp);
766     tmp = result;
767     result =
768         gst_gl_upload_transform_caps (filter->context, direction, tmp, NULL);
769   } else {
770     GstCaps *gl_caps = gst_caps_merge (gst_gl_filter_set_caps_features (tmp,
771             GST_CAPS_FEATURE_MEMORY_GL_MEMORY),
772         gst_gl_filter_set_caps_features (tmp,
773             GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META));
774
775     result =
776         gst_gl_download_transform_caps (filter->context, direction, tmp, NULL);
777
778     result = gst_caps_merge (gl_caps, result);
779   }
780   gst_caps_unref (tmp);
781   tmp = result;
782   GST_DEBUG_OBJECT (bt, "transfer returned caps %" GST_PTR_FORMAT, tmp);
783
784   if (filter_caps) {
785     result =
786         gst_caps_intersect_full (filter_caps, tmp, GST_CAPS_INTERSECT_FIRST);
787     gst_caps_unref (tmp);
788   } else {
789     result = tmp;
790   }
791
792   GST_DEBUG_OBJECT (bt, "returning caps: %" GST_PTR_FORMAT, result);
793
794   return result;
795 }
796
797
798 static gboolean
799 gst_gl_filter_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
800     gsize * size)
801 {
802   gboolean ret = FALSE;
803   GstVideoInfo info;
804
805   ret = gst_video_info_from_caps (&info, caps);
806   if (ret)
807     *size = GST_VIDEO_INFO_SIZE (&info);
808
809   return TRUE;
810 }
811
812 static gboolean
813 gst_gl_filter_set_caps (GstBaseTransform * bt, GstCaps * incaps,
814     GstCaps * outcaps)
815 {
816   GstGLFilter *filter;
817   GstGLFilterClass *filter_class;
818
819   filter = GST_GL_FILTER (bt);
820   filter_class = GST_GL_FILTER_GET_CLASS (filter);
821
822   if (!gst_video_info_from_caps (&filter->in_info, incaps))
823     goto wrong_caps;
824   if (!gst_video_info_from_caps (&filter->out_info, outcaps))
825     goto wrong_caps;
826
827   if (filter_class->set_caps) {
828     if (!filter_class->set_caps (filter, incaps, outcaps))
829       goto error;
830   }
831
832   if (filter->out_caps && !gst_caps_is_equal (filter->out_caps, outcaps)) {
833     if (filter->upload) {
834       gst_object_unref (filter->upload);
835       filter->upload = NULL;
836     }
837   }
838
839   gst_caps_replace (&filter->out_caps, outcaps);
840
841   GST_DEBUG ("set_caps %dx%d", GST_VIDEO_INFO_WIDTH (&filter->out_info),
842       GST_VIDEO_INFO_HEIGHT (&filter->out_info));
843
844   return TRUE;
845
846 /* ERRORS */
847 wrong_caps:
848   {
849     GST_WARNING ("Wrong caps");
850     return FALSE;
851   }
852 error:
853   {
854     return FALSE;
855   }
856 }
857
858 static gboolean
859 _ensure_input_chain (GstGLFilter * filter)
860 {
861   GstBaseTransform *bt = GST_BASE_TRANSFORM (filter);
862
863   if (!filter->upload) {
864     GstCapsFeatures *uploaded_features;
865     GstCaps *uploaded_caps;
866     GstCapsFeatures *converted_features;
867     GstVideoInfo converted_info;
868     GstCaps *in_caps = gst_pad_get_current_caps (bt->sinkpad);
869
870     filter->upload = gst_gl_upload_new (filter->context);
871
872     uploaded_caps = gst_caps_copy (in_caps);
873     uploaded_features =
874         gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
875     gst_caps_set_features (uploaded_caps, 0, uploaded_features);
876
877     if (!gst_gl_upload_set_caps (filter->upload, in_caps, uploaded_caps)) {
878       gst_caps_unref (uploaded_caps);
879       gst_caps_unref (in_caps);
880       return FALSE;
881     }
882     gst_caps_unref (in_caps);
883
884     if (!filter->in_convert) {
885       filter->in_convert = gst_gl_color_convert_new (filter->context);
886     }
887
888     gst_video_info_set_format (&converted_info, GST_VIDEO_FORMAT_RGBA,
889         GST_VIDEO_INFO_WIDTH (&filter->in_info),
890         GST_VIDEO_INFO_HEIGHT (&filter->in_info));
891     converted_features =
892         gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
893
894     if (filter->in_converted_caps)
895       gst_caps_unref (filter->in_converted_caps);
896     filter->in_converted_caps = gst_video_info_to_caps (&converted_info);
897     gst_caps_set_features (filter->in_converted_caps, 0, converted_features);
898
899     if (!gst_gl_color_convert_set_caps (filter->in_convert, uploaded_caps,
900             filter->in_converted_caps)) {
901       gst_caps_unref (uploaded_caps);
902       return FALSE;
903     }
904     gst_caps_unref (uploaded_caps);
905   }
906
907   return TRUE;
908 }
909
910 static gboolean
911 gst_gl_filter_propose_allocation (GstBaseTransform * trans,
912     GstQuery * decide_query, GstQuery * query)
913 {
914   GstGLFilter *filter = GST_GL_FILTER (trans);
915   GstStructure *config;
916   GstCaps *caps;
917   guint size;
918   gboolean need_pool;
919
920   gst_query_parse_allocation (query, &caps, &need_pool);
921
922   if (caps == NULL)
923     goto no_caps;
924
925   if (need_pool) {
926     if (filter->pool) {
927       GstCaps *pcaps;
928
929       /* we had a pool, check caps */
930       GST_DEBUG_OBJECT (filter, "check existing pool caps");
931       config = gst_buffer_pool_get_config (filter->pool);
932       gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
933
934       if (!gst_caps_is_equal (caps, pcaps)) {
935         GST_DEBUG_OBJECT (filter, "pool has different caps");
936         /* different caps, we can't use this pool */
937         gst_object_unref (filter->pool);
938         filter->pool = NULL;
939       }
940       gst_structure_free (config);
941     }
942
943     if (filter->pool == NULL) {
944       GstVideoInfo info;
945
946       if (!gst_video_info_from_caps (&info, caps))
947         goto invalid_caps;
948
949       /* the normal size of a frame */
950       size = info.size;
951
952       GST_DEBUG_OBJECT (filter, "create new pool");
953       filter->pool = gst_gl_buffer_pool_new (filter->context);
954
955       config = gst_buffer_pool_get_config (filter->pool);
956       gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
957
958       if (!gst_buffer_pool_set_config (filter->pool, config))
959         goto config_failed;
960     }
961
962     gst_query_add_allocation_pool (query, filter->pool, size, 1, 0);
963   }
964
965   if (!_ensure_input_chain (filter))
966     return FALSE;
967
968   gst_gl_upload_propose_allocation (filter->upload, decide_query, query);
969
970   if (filter->context->gl_vtable->FenceSync)
971     gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
972
973   return TRUE;
974
975   /* ERRORS */
976 no_caps:
977   {
978     GST_DEBUG_OBJECT (trans, "no caps specified");
979     return FALSE;
980   }
981 invalid_caps:
982   {
983     GST_DEBUG_OBJECT (trans, "invalid caps specified");
984     return FALSE;
985   }
986 config_failed:
987   {
988     GST_DEBUG_OBJECT (trans, "failed setting config");
989     return FALSE;
990   }
991 }
992
993 static gboolean
994 gst_gl_filter_decide_allocation (GstBaseTransform * trans, GstQuery * query)
995 {
996   GstGLFilter *filter = GST_GL_FILTER (trans);
997   GstGLFilterClass *filter_class = GST_GL_FILTER_GET_CLASS (filter);
998   GstBufferPool *pool = NULL;
999   GstStructure *config;
1000   GstCaps *caps;
1001   guint min, max, size;
1002   gboolean update_pool;
1003   guint idx;
1004   GError *error = NULL;
1005   guint in_width, in_height, out_width, out_height;
1006   GstGLContext *other_context = NULL;
1007   gboolean same_downstream_gl_context = FALSE;
1008
1009   gst_query_parse_allocation (query, &caps, NULL);
1010   if (!caps)
1011     return FALSE;
1012
1013   if (!gst_gl_ensure_element_data (filter, &filter->display,
1014           &filter->other_context)) {
1015     return FALSE;
1016   }
1017
1018   gst_gl_display_filter_gl_api (filter->display,
1019       filter_class->supported_gl_api);
1020
1021   if (gst_query_find_allocation_meta (query,
1022           GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, &idx)) {
1023     GstGLContext *context;
1024     const GstStructure *upload_meta_params;
1025     gpointer handle;
1026     gchar *type;
1027     gchar *apis;
1028
1029     gst_query_parse_nth_allocation_meta (query, idx, &upload_meta_params);
1030     if (upload_meta_params) {
1031       if (gst_structure_get (upload_meta_params, "gst.gl.GstGLContext",
1032               GST_GL_TYPE_CONTEXT, &context, NULL) && context) {
1033         GstGLContext *old = filter->context;
1034
1035         filter->context = context;
1036         if (old)
1037           gst_object_unref (old);
1038         same_downstream_gl_context = TRUE;
1039       } else if (gst_structure_get (upload_meta_params, "gst.gl.context.handle",
1040               G_TYPE_POINTER, &handle, "gst.gl.context.type", G_TYPE_STRING,
1041               &type, "gst.gl.context.apis", G_TYPE_STRING, &apis, NULL)
1042           && handle) {
1043         GstGLPlatform platform = GST_GL_PLATFORM_NONE;
1044         GstGLAPI gl_apis;
1045
1046         GST_DEBUG ("got GL context handle 0x%p with type %s and apis %s",
1047             handle, type, apis);
1048
1049         platform = gst_gl_platform_from_string (type);
1050         gl_apis = gst_gl_api_from_string (apis);
1051
1052         if (gl_apis && platform)
1053           other_context =
1054               gst_gl_context_new_wrapped (filter->display, (guintptr) handle,
1055               platform, gl_apis);
1056       }
1057     }
1058   }
1059
1060   if (filter->other_context) {
1061     if (!other_context) {
1062       other_context = filter->other_context;
1063     } else {
1064       GST_ELEMENT_WARNING (filter, LIBRARY, SETTINGS,
1065           ("%s", "Cannot share with more than one GL context"),
1066           ("%s", "Cannot share with more than one GL context"));
1067     }
1068   }
1069
1070   if (!filter->context) {
1071     filter->context = gst_gl_context_new (filter->display);
1072     if (!gst_gl_context_create (filter->context, other_context, &error))
1073       goto context_error;
1074   }
1075
1076   in_width = GST_VIDEO_INFO_WIDTH (&filter->in_info);
1077   in_height = GST_VIDEO_INFO_HEIGHT (&filter->in_info);
1078   out_width = GST_VIDEO_INFO_WIDTH (&filter->out_info);
1079   out_height = GST_VIDEO_INFO_HEIGHT (&filter->out_info);
1080
1081   if (filter->fbo) {
1082     gst_gl_context_del_fbo (filter->context, filter->fbo, filter->depthbuffer);
1083     filter->fbo = 0;
1084     filter->depthbuffer = 0;
1085   }
1086
1087   if (filter->in_tex_id) {
1088     gst_gl_context_del_texture (filter->context, &filter->in_tex_id);
1089     filter->in_tex_id = 0;
1090   }
1091
1092   if (filter->out_tex_id) {
1093     gst_gl_context_del_texture (filter->context, &filter->out_tex_id);
1094     filter->out_tex_id = 0;
1095   }
1096   //blocking call, generate a FBO
1097   if (!gst_gl_context_gen_fbo (filter->context, out_width, out_height,
1098           &filter->fbo, &filter->depthbuffer))
1099     goto context_error;
1100
1101   gst_gl_context_gen_texture (filter->context, &filter->in_tex_id,
1102       GST_VIDEO_FORMAT_RGBA, in_width, in_height);
1103
1104   gst_gl_context_gen_texture (filter->context, &filter->out_tex_id,
1105       GST_VIDEO_FORMAT_RGBA, out_width, out_height);
1106
1107   if (filter_class->display_init_cb != NULL) {
1108     gst_gl_context_thread_add (filter->context, gst_gl_filter_start_gl, filter);
1109   }
1110
1111   if (filter_class->onInitFBO) {
1112     if (!filter_class->onInitFBO (filter))
1113       goto error;
1114   }
1115
1116   if (gst_query_get_n_allocation_pools (query) > 0) {
1117     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
1118
1119     update_pool = TRUE;
1120   } else {
1121     GstVideoInfo vinfo;
1122
1123     gst_video_info_init (&vinfo);
1124     gst_video_info_from_caps (&vinfo, caps);
1125     size = vinfo.size;
1126     min = max = 0;
1127     update_pool = FALSE;
1128   }
1129
1130   if (!pool || (!same_downstream_gl_context
1131           && gst_query_find_allocation_meta (query, GST_GL_SYNC_META_API_TYPE,
1132               NULL)
1133           && !gst_buffer_pool_has_option (pool,
1134               GST_BUFFER_POOL_OPTION_GL_SYNC_META))) {
1135     /* can't use this pool */
1136     if (pool)
1137       gst_object_unref (pool);
1138     pool = gst_gl_buffer_pool_new (filter->context);
1139   }
1140   config = gst_buffer_pool_get_config (pool);
1141
1142   gst_buffer_pool_config_set_params (config, caps, size, min, max);
1143   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
1144   if (gst_query_find_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, NULL))
1145     gst_buffer_pool_config_add_option (config,
1146         GST_BUFFER_POOL_OPTION_GL_SYNC_META);
1147   gst_buffer_pool_config_add_option (config,
1148       GST_BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META);
1149
1150   gst_buffer_pool_set_config (pool, config);
1151
1152   if (update_pool)
1153     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
1154   else
1155     gst_query_add_allocation_pool (query, pool, size, min, max);
1156
1157   gst_object_unref (pool);
1158
1159   return TRUE;
1160
1161 context_error:
1162   {
1163     GST_ELEMENT_ERROR (trans, RESOURCE, NOT_FOUND, ("%s", error->message),
1164         (NULL));
1165     return FALSE;
1166   }
1167 error:
1168   {
1169     GST_ELEMENT_ERROR (trans, LIBRARY, INIT,
1170         ("Subclass failed to initialize."), (NULL));
1171     return FALSE;
1172   }
1173 }
1174
1175 /**
1176  * gst_gl_filter_filter_texture:
1177  * @filter: a #GstGLFilter
1178  * @inbuf: an input buffer
1179  * @outbuf: an output buffer
1180  *
1181  * Perform automatic upload if needed, call filter_texture vfunc and then an
1182  * automatic download if needed.
1183  *
1184  * Returns: whether the transformation succeeded
1185  */
1186 gboolean
1187 gst_gl_filter_filter_texture (GstGLFilter * filter, GstBuffer * inbuf,
1188     GstBuffer * outbuf)
1189 {
1190   GstGLFilterClass *filter_class;
1191   guint in_tex, out_tex, out_tex_target;
1192   GstVideoFrame gl_frame, out_frame;
1193   GstVideoInfo gl_info;
1194   gboolean ret;
1195   gboolean to_download =
1196       gst_caps_features_is_equal (GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY,
1197       gst_caps_get_features (filter->out_caps, 0));
1198   GstMapFlags out_map_flags = GST_MAP_WRITE;
1199   GstBuffer *upload_buffer;
1200
1201   filter_class = GST_GL_FILTER_GET_CLASS (filter);
1202
1203   if (gst_gl_upload_perform_with_buffer (filter->upload, inbuf,
1204           &upload_buffer) != GST_GL_UPLOAD_DONE) {
1205     return FALSE;
1206   }
1207
1208   if (filter->uploaded_buffer) {
1209     gst_buffer_unref (filter->uploaded_buffer);
1210     filter->uploaded_buffer = NULL;
1211   }
1212
1213   filter->uploaded_buffer =
1214       gst_gl_color_convert_perform (filter->in_convert, upload_buffer);
1215
1216   if (!filter->uploaded_buffer) {
1217     ret = FALSE;
1218     goto inbuf_error;
1219   }
1220
1221   gst_video_info_from_caps (&gl_info, filter->in_converted_caps);
1222
1223   if (!gst_video_frame_map (&gl_frame, &gl_info, filter->uploaded_buffer,
1224           GST_MAP_READ | GST_MAP_GL)) {
1225     ret = FALSE;
1226     goto inbuf_error;
1227   }
1228
1229   in_tex = *(guint *) gl_frame.data[0];
1230   to_download |= !gst_is_gl_memory (gst_buffer_peek_memory (outbuf, 0));
1231
1232   if (!to_download)
1233     out_map_flags |= GST_MAP_GL;
1234
1235   if (!gst_video_frame_map (&out_frame, &filter->out_info, outbuf,
1236           out_map_flags)) {
1237     ret = FALSE;
1238     goto unmap_out_error;
1239   }
1240
1241   if (!to_download) {
1242     out_tex = *(guint *) out_frame.data[0];
1243     out_tex_target =
1244         ((GstGLMemory *) gst_buffer_peek_memory (outbuf, 0))->tex_target;
1245   } else {
1246     GST_LOG ("Output Buffer does not contain correct memory, "
1247         "attempting to wrap for download");
1248
1249     if (!filter->download)
1250       filter->download = gst_gl_download_new (filter->context);
1251
1252     gst_gl_download_set_format (filter->download, &out_frame.info);
1253
1254     out_tex = filter->out_tex_id;
1255     out_tex_target = GL_TEXTURE_2D;
1256   }
1257
1258   GST_DEBUG ("calling filter_texture with textures in:%i out:%i", in_tex,
1259       out_tex);
1260
1261   g_assert (filter_class->filter_texture);
1262   ret = filter_class->filter_texture (filter, in_tex, out_tex);
1263
1264   if (to_download) {
1265     if (!gst_gl_download_perform_with_data (filter->download,
1266             out_tex, out_tex_target, out_frame.data)) {
1267       GST_ELEMENT_ERROR (filter, RESOURCE, NOT_FOUND,
1268           ("%s", "Failed to download video frame"), (NULL));
1269       ret = FALSE;
1270     }
1271   }
1272
1273   gst_video_frame_unmap (&gl_frame);
1274 unmap_out_error:
1275   gst_video_frame_unmap (&out_frame);
1276 inbuf_error:
1277   gst_gl_upload_release_buffer (filter->upload);
1278   gst_buffer_unref (upload_buffer);
1279
1280   return ret;
1281 }
1282
1283 static GstFlowReturn
1284 gst_gl_filter_transform (GstBaseTransform * bt, GstBuffer * inbuf,
1285     GstBuffer * outbuf)
1286 {
1287   GstGLFilter *filter;
1288   GstGLFilterClass *filter_class;
1289   GstGLSyncMeta *out_sync_meta, *in_sync_meta;
1290
1291   filter = GST_GL_FILTER (bt);
1292   filter_class = GST_GL_FILTER_GET_CLASS (bt);
1293
1294   if (!filter->display)
1295     return GST_FLOW_NOT_NEGOTIATED;
1296
1297   g_assert (filter_class->filter || filter_class->filter_texture);
1298
1299   in_sync_meta = gst_buffer_get_gl_sync_meta (inbuf);
1300   if (in_sync_meta)
1301     gst_gl_sync_meta_wait (in_sync_meta);
1302
1303   if (!_ensure_input_chain (filter))
1304     return GST_FLOW_ERROR;
1305
1306   if (filter_class->filter)
1307     filter_class->filter (filter, inbuf, outbuf);
1308   else if (filter_class->filter_texture)
1309     gst_gl_filter_filter_texture (filter, inbuf, outbuf);
1310
1311   out_sync_meta = gst_buffer_get_gl_sync_meta (outbuf);
1312   if (out_sync_meta)
1313     gst_gl_sync_meta_set_sync_point (out_sync_meta, filter->context);
1314
1315   return GST_FLOW_OK;
1316 }
1317
1318 struct glcb2
1319 {
1320   GLCB func;
1321   gpointer data;
1322   guint texture;
1323   guint width;
1324   guint height;
1325 };
1326
1327 /* convenience functions to simplify filter development */
1328 static void
1329 _glcb2 (gpointer data)
1330 {
1331   struct glcb2 *cb = data;
1332
1333   cb->func (cb->width, cb->height, cb->texture, cb->data);
1334 }
1335
1336 /**
1337  * gst_gl_filter_render_to_target:
1338  * @filter: a #GstGLFilter
1339  * @resize: whether to automatically resize the texture between the input size
1340  *          and the output size
1341  * @input: the input texture
1342  * @target: the output texture
1343  * @func: the function to transform @input into @output. called with @data
1344  * @data: the data associated with @func
1345  *
1346  * Transforms @input into @output using @func on through FBO.  @resize should
1347  * only ever be %TRUE whenever @input is the input texture of @filter.
1348  */
1349 void
1350 gst_gl_filter_render_to_target (GstGLFilter * filter, gboolean resize,
1351     GLuint input, GLuint target, GLCB func, gpointer data)
1352 {
1353   guint in_width, in_height, out_width, out_height;
1354   struct glcb2 cb;
1355
1356   out_width = GST_VIDEO_INFO_WIDTH (&filter->out_info);
1357   out_height = GST_VIDEO_INFO_HEIGHT (&filter->out_info);
1358   if (resize) {
1359     in_width = GST_VIDEO_INFO_WIDTH (&filter->in_info);
1360     in_height = GST_VIDEO_INFO_HEIGHT (&filter->in_info);
1361   } else {
1362     in_width = out_width;
1363     in_height = out_height;
1364   }
1365
1366   GST_LOG ("rendering to target. in %u, %ux%u out %u, %ux%u", input, in_width,
1367       in_height, target, out_width, out_height);
1368
1369   cb.func = func;
1370   cb.data = data;
1371   cb.texture = input;
1372   cb.width = in_width;
1373   cb.height = in_height;
1374
1375   gst_gl_context_use_fbo_v2 (filter->context, out_width, out_height,
1376       filter->fbo, filter->depthbuffer, target, _glcb2, &cb);
1377 }
1378
1379 static void
1380 _draw_with_shader_cb (gint width, gint height, guint texture, gpointer stuff)
1381 {
1382   GstGLFilter *filter = GST_GL_FILTER (stuff);
1383   GstGLFuncs *gl = filter->context->gl_vtable;
1384
1385 #if GST_GL_HAVE_OPENGL
1386   if (gst_gl_context_get_gl_api (filter->context) & GST_GL_API_OPENGL) {
1387     gl->MatrixMode (GL_PROJECTION);
1388     gl->LoadIdentity ();
1389   }
1390 #endif
1391
1392   gst_gl_shader_use (filter->default_shader);
1393
1394   gl->ActiveTexture (GL_TEXTURE1);
1395   gl->BindTexture (GL_TEXTURE_2D, texture);
1396
1397   gst_gl_shader_set_uniform_1i (filter->default_shader, "tex", 1);
1398   gst_gl_shader_set_uniform_1f (filter->default_shader, "width", width);
1399   gst_gl_shader_set_uniform_1f (filter->default_shader, "height", height);
1400
1401   gst_gl_filter_draw_texture (filter, texture, width, height);
1402 }
1403
1404 static void
1405 _get_attributes (GstGLFilter * filter)
1406 {
1407   if (!filter->default_shader)
1408     return;
1409
1410   if (filter->draw_attr_position_loc == -1)
1411     filter->draw_attr_position_loc =
1412         gst_gl_shader_get_attribute_location (filter->default_shader,
1413         "a_position");
1414
1415   if (filter->draw_attr_texture_loc == -1)
1416     filter->draw_attr_texture_loc =
1417         gst_gl_shader_get_attribute_location (filter->default_shader,
1418         "a_texcoord");
1419 }
1420
1421 /**
1422  * gst_gl_filter_render_to_target_with_shader:
1423  * @filter: a #GstGLFilter
1424  * @resize: whether to automatically resize the texture between the input size
1425  *          and the output size
1426  * @input: the input texture
1427  * @target: the output texture
1428  * @shader: the shader to use.
1429  *
1430  * Transforms @input into @output using @shader on FBO.  @resize should
1431  * only ever be %TRUE whenever @input is the input texture of @filter.
1432  *
1433  * See also: gst_gl_filter_render_to_target()
1434  */
1435 /* attach target to a FBO, use shader, pass input as "tex" uniform to
1436  * the shader, render input to a quad */
1437 void
1438 gst_gl_filter_render_to_target_with_shader (GstGLFilter * filter,
1439     gboolean resize, GLuint input, GLuint target, GstGLShader * shader)
1440 {
1441   filter->default_shader = shader;
1442   _get_attributes (filter);
1443
1444   gst_gl_filter_render_to_target (filter, resize, input, target,
1445       _draw_with_shader_cb, filter);
1446 }
1447
1448 /* *INDENT-OFF* */
1449 static const GLfloat vertices[] = {
1450   -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1451    1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
1452    1.0f,  1.0f, 0.0f, 1.0f, 1.0f,
1453   -1.0f,  1.0f, 0.0f, 0.0f, 1.0f
1454 };
1455 /* *INDENT-ON* */
1456
1457 static void
1458 _bind_buffer (GstGLFilter * filter)
1459 {
1460   const GstGLFuncs *gl = filter->context->gl_vtable;
1461
1462   gl->BindBuffer (GL_ARRAY_BUFFER, filter->vertex_buffer);
1463
1464   _get_attributes (filter);
1465   /* Load the vertex position */
1466   gl->VertexAttribPointer (filter->draw_attr_position_loc, 3, GL_FLOAT,
1467       GL_FALSE, 5 * sizeof (GLfloat), (void *) 0);
1468
1469   /* Load the texture coordinate */
1470   gl->VertexAttribPointer (filter->draw_attr_texture_loc, 2, GL_FLOAT, GL_FALSE,
1471       5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
1472
1473
1474   gl->EnableVertexAttribArray (filter->draw_attr_position_loc);
1475   gl->EnableVertexAttribArray (filter->draw_attr_texture_loc);
1476 }
1477
1478 static void
1479 _unbind_buffer (GstGLFilter * filter)
1480 {
1481   const GstGLFuncs *gl = filter->context->gl_vtable;
1482
1483   gl->BindBuffer (GL_ARRAY_BUFFER, 0);
1484
1485   gl->DisableVertexAttribArray (filter->draw_attr_position_loc);
1486   gl->DisableVertexAttribArray (filter->draw_attr_texture_loc);
1487 }
1488
1489 /**
1490  * gst_gl_filter_draw_texture:
1491  * @filter: a #GstGLFilter
1492  * @texture: the texture to draw
1493  * @width: width of @texture
1494  * @height: height of texture
1495  *
1496  * Draws @texture into the OpenGL scene at the specified @width and @height.
1497  */
1498 void
1499 gst_gl_filter_draw_texture (GstGLFilter * filter, GLuint texture,
1500     guint width, guint height)
1501 {
1502   GstGLContext *context = filter->context;
1503   GstGLFuncs *gl = context->gl_vtable;
1504
1505   GST_DEBUG ("drawing texture:%u dimensions:%ux%u", texture, width, height);
1506
1507 #if GST_GL_HAVE_OPENGL
1508   if (gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL) {
1509     GLfloat verts[] = { -1.0f, -1.0f,
1510       1.0f, -1.0f,
1511       1.0f, 1.0f,
1512       -1.0f, 1.0f
1513     };
1514     GLfloat texcoords[] = { 0.0f, 0.0f,
1515       1.0f, 0.0f,
1516       1.0f, 1.0f,
1517       0.0f, 1.0f
1518     };
1519
1520     gl->ActiveTexture (GL_TEXTURE0);
1521
1522     gl->Enable (GL_TEXTURE_2D);
1523     gl->BindTexture (GL_TEXTURE_2D, texture);
1524
1525     gl->ClientActiveTexture (GL_TEXTURE0);
1526
1527     gl->EnableClientState (GL_VERTEX_ARRAY);
1528     gl->EnableClientState (GL_TEXTURE_COORD_ARRAY);
1529
1530     gl->VertexPointer (2, GL_FLOAT, 0, &verts);
1531     gl->TexCoordPointer (2, GL_FLOAT, 0, &texcoords);
1532
1533     gl->DrawArrays (GL_TRIANGLE_FAN, 0, 4);
1534
1535     gl->DisableClientState (GL_VERTEX_ARRAY);
1536     gl->DisableClientState (GL_TEXTURE_COORD_ARRAY);
1537   }
1538 #endif
1539   if (gst_gl_context_get_gl_api (context) & (GST_GL_API_GLES2 |
1540           GST_GL_API_OPENGL3)) {
1541     GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
1542
1543     if (!filter->vertex_buffer) {
1544       if (gl->GenVertexArrays) {
1545         gl->GenVertexArrays (1, &filter->vao);
1546         gl->BindVertexArray (filter->vao);
1547       }
1548
1549       gl->GenBuffers (1, &filter->vertex_buffer);
1550       gl->BindBuffer (GL_ARRAY_BUFFER, filter->vertex_buffer);
1551       gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices,
1552           GL_STATIC_DRAW);
1553
1554       if (gl->GenVertexArrays) {
1555         _bind_buffer (filter);
1556         gl->BindBuffer (GL_ARRAY_BUFFER, 0);
1557       }
1558     }
1559
1560     if (gl->GenVertexArrays)
1561       gl->BindVertexArray (filter->vao);
1562     else
1563       _bind_buffer (filter);
1564
1565     gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
1566
1567     if (gl->GenVertexArrays)
1568       gl->BindVertexArray (0);
1569     else
1570       _unbind_buffer (filter);
1571   }
1572 }