Merging gst-devtools
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-base / ext / gl / gstglvideomixer.c
1 /*
2  * GStreamer
3  * Copyright (C) 2009 Julien Isorce <julien.isorce@gmail.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * SECTION:element-glvideomixer
23  * @title: glvideomixer
24  *
25  * Composites a number of streams into a single output scene using OpenGL in
26  * a similar fashion to compositor and videomixer. See the compositor plugin
27  * for documentation about the #GstGLVideoMixerPad properties.
28  *
29  * ## Examples
30  * |[
31  * gst-launch-1.0  glvideomixer name=m ! glimagesink \
32  *     videotestsrc ! video/x-raw, format=YUY2 ! glupload ! glcolorconvert ! m. \
33  *     videotestsrc pattern=12 ! video/x-raw, format=I420, framerate=5/1, width=100, height=200 ! queue ! \
34  *     glupload ! glcolorconvert ! m. \
35  *     videotestsrc ! glupload ! gleffects effect=2 ! queue ! m.  \
36  *     videotestsrc ! glupload ! glfiltercube ! queue ! m. \
37  *     videotestsrc ! glupload ! gleffects effect=6 ! queue ! m.
38  * ]|
39  *
40  */
41
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45
46 #include <string.h>
47 #include <gst/controller/gstproxycontrolbinding.h>
48 #include <gst/gl/gstglfuncs.h>
49 #include <gst/video/gstvideoaffinetransformationmeta.h>
50
51 #include "gstglelements.h"
52 #include "gstglvideomixer.h"
53
54 #include "gstglmixerbin.h"
55 #include "gstglutils.h"
56
57 #define GST_CAT_DEFAULT gst_gl_video_mixer_debug
58 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
59
60 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%u",
61     GST_PAD_SINK,
62     GST_PAD_REQUEST,
63     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
64         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
65             "RGBA"))
66     );
67
68 #define GST_TYPE_GL_VIDEO_MIXER_BACKGROUND (gst_gl_video_mixer_background_get_type())
69 static GType
70 gst_gl_video_mixer_background_get_type (void)
71 {
72   static GType mixer_background_type = 0;
73
74   static const GEnumValue mixer_background[] = {
75     {GST_GL_VIDEO_MIXER_BACKGROUND_CHECKER, "Checker pattern", "checker"},
76     {GST_GL_VIDEO_MIXER_BACKGROUND_BLACK, "Black", "black"},
77     {GST_GL_VIDEO_MIXER_BACKGROUND_WHITE, "White", "white"},
78     {GST_GL_VIDEO_MIXER_BACKGROUND_TRANSPARENT,
79         "Transparent Background to enable further compositing", "transparent"},
80     {0, NULL, NULL},
81   };
82
83   if (!mixer_background_type) {
84     mixer_background_type =
85         g_enum_register_static ("GstGLVideoMixerBackground", mixer_background);
86   }
87   return mixer_background_type;
88 }
89
90 #define GST_TYPE_GL_VIDEO_MIXER_BLEND_EQUATION (gst_gl_video_mixer_blend_equation_get_type())
91 static GType
92 gst_gl_video_mixer_blend_equation_get_type (void)
93 {
94   static GType mixer_blend_equation_type = 0;
95
96   static const GEnumValue mixer_blend_equations[] = {
97     {GST_GL_VIDEO_MIXER_BLEND_EQUATION_ADD, "Add", "add"},
98     {GST_GL_VIDEO_MIXER_BLEND_EQUATION_SUBTRACT, "Subtract", "subtract"},
99     {GST_GL_VIDEO_MIXER_BLEND_EQUATION_REVERSE_SUBTRACT, "Reverse Subtract",
100         "reverse-subtract"},
101     {0, NULL, NULL},
102   };
103
104   if (!mixer_blend_equation_type) {
105     mixer_blend_equation_type =
106         g_enum_register_static ("GstGLVideoMixerBlendEquation",
107         mixer_blend_equations);
108   }
109   return mixer_blend_equation_type;
110 }
111
112 #define GST_TYPE_GL_VIDEO_MIXER_BLEND_FUNCTION (gst_gl_video_mixer_blend_function_get_type())
113 static GType
114 gst_gl_video_mixer_blend_function_get_type (void)
115 {
116   static GType mixer_blend_function_type = 0;
117
118   static const GEnumValue mixer_blend_funcs[] = {
119     {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ZERO, "Zero", "zero"},
120     {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE, "One", "one"},
121     {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_SRC_COLOR, "Source Color", "src-color"},
122     {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_SRC_COLOR,
123         "One Minus Source Color", "one-minus-src-color"},
124     {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_DST_COLOR, "Destination Color",
125         "dst-color"},
126     {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_DST_COLOR,
127         "One Minus Destination Color", "one-minus-dst-color"},
128     {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_SRC_ALPHA, "Source Alpha", "src-alpha"},
129     {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_SRC_ALPHA,
130         "One Minus Source Alpha", "one-minus-src-alpha"},
131     {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_DST_ALPHA, "Destination Alpha",
132         "dst-alpha"},
133     {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_DST_ALPHA,
134         "One Minus Destination Alpha", "one-minus-dst-alpha"},
135     {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_CONSTANT_COLOR, "Constant Color",
136         "constant-color"},
137     {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_CONSTANT_COLOR,
138         "One Minus Constant Color", "one-minus-contant-color"},
139     {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_CONSTANT_ALPHA, "Constant Alpha",
140         "constant-alpha"},
141     {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_CONSTANT_ALPHA,
142         "One Minus Constant Alpha", "one-minus-contant-alpha"},
143     {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_SRC_ALPHA_SATURATE,
144         "Source Alpha Saturate", "src-alpha-saturate"},
145     {0, NULL, NULL},
146   };
147
148   if (!mixer_blend_function_type) {
149     mixer_blend_function_type =
150         g_enum_register_static ("GstGLVideoMixerBlendFunction",
151         mixer_blend_funcs);
152   }
153   return mixer_blend_function_type;
154 }
155
156 #define DEFAULT_PAD_XPOS   0
157 #define DEFAULT_PAD_YPOS   0
158 #define DEFAULT_PAD_WIDTH  0
159 #define DEFAULT_PAD_HEIGHT 0
160 #define DEFAULT_PAD_ALPHA  1.0
161 #define DEFAULT_PAD_ZORDER 0
162 #define DEFAULT_PAD_REPEAT_AFTER_EOS FALSE
163 #define DEFAULT_PAD_BLEND_EQUATION_RGB GST_GL_VIDEO_MIXER_BLEND_EQUATION_ADD
164 #define DEFAULT_PAD_BLEND_EQUATION_ALPHA GST_GL_VIDEO_MIXER_BLEND_EQUATION_ADD
165 #define DEFAULT_PAD_BLEND_FUNCTION_SRC_RGB GST_GL_VIDEO_MIXER_BLEND_FUNCTION_SRC_ALPHA
166 #define DEFAULT_PAD_BLEND_FUNCTION_SRC_ALPHA GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE
167 #define DEFAULT_PAD_BLEND_FUNCTION_DST_RGB GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_SRC_ALPHA
168 #define DEFAULT_PAD_BLEND_FUNCTION_DST_ALPHA GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_SRC_ALPHA
169
170 enum
171 {
172   PROP_INPUT_0,
173   PROP_INPUT_XPOS,
174   PROP_INPUT_YPOS,
175   PROP_INPUT_WIDTH,
176   PROP_INPUT_HEIGHT,
177   PROP_INPUT_ALPHA,
178   PROP_INPUT_BLEND_EQUATION_RGB,
179   PROP_INPUT_BLEND_EQUATION_ALPHA,
180   PROP_INPUT_BLEND_FUNCTION_SRC_RGB,
181   PROP_INPUT_BLEND_FUNCTION_SRC_ALPHA,
182   PROP_INPUT_BLEND_FUNCTION_DST_RGB,
183   PROP_INPUT_BLEND_FUNCTION_DST_ALPHA,
184   PROP_INPUT_BLEND_FUNCTION_CONSTANT_COLOR_RED,
185   PROP_INPUT_BLEND_FUNCTION_CONSTANT_COLOR_GREEN,
186   PROP_INPUT_BLEND_FUNCTION_CONSTANT_COLOR_BLUE,
187   PROP_INPUT_BLEND_FUNCTION_CONSTANT_COLOR_ALPHA,
188   PROP_INPUT_ZORDER,
189   PROP_INPUT_REPEAT_AFTER_EOS,
190 };
191
192 static void gst_gl_video_mixer_input_get_property (GObject * object,
193     guint prop_id, GValue * value, GParamSpec * pspec);
194 static void gst_gl_video_mixer_input_set_property (GObject * object,
195     guint prop_id, const GValue * value, GParamSpec * pspec);
196
197 typedef struct _GstGLVideoMixerInput GstGLVideoMixerInput;
198 typedef GstGhostPadClass GstGLVideoMixerInputClass;
199
200 struct _GstGLVideoMixerInput
201 {
202   GstGhostPad parent;
203
204   GstSegment segment;
205
206   GstPad *mixer_pad;
207 };
208
209 GType gst_gl_video_mixer_input_get_type (void);
210 G_DEFINE_TYPE (GstGLVideoMixerInput, gst_gl_video_mixer_input,
211     GST_TYPE_GHOST_PAD);
212
213 static void
214 gst_gl_video_mixer_input_init (GstGLVideoMixerInput * self)
215 {
216 }
217
218 static void
219 gst_gl_video_mixer_input_class_init (GstGLVideoMixerInputClass * klass)
220 {
221   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
222
223   gobject_class->set_property = gst_gl_video_mixer_input_set_property;
224   gobject_class->get_property = gst_gl_video_mixer_input_get_property;
225
226   g_object_class_install_property (gobject_class, PROP_INPUT_ZORDER,
227       g_param_spec_uint ("zorder", "Z-Order", "Z Order of the picture",
228           0, 10000, DEFAULT_PAD_ZORDER,
229           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
230   g_object_class_install_property (gobject_class, PROP_INPUT_REPEAT_AFTER_EOS,
231       g_param_spec_boolean ("repeat-after-eos", "Repeat After EOS",
232           "Aggregate the last "
233           "frame on pads that are EOS till they are released",
234           DEFAULT_PAD_REPEAT_AFTER_EOS,
235           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
236   g_object_class_install_property (gobject_class, PROP_INPUT_XPOS,
237       g_param_spec_int ("xpos", "X Position", "X Position of the picture",
238           G_MININT, G_MAXINT, DEFAULT_PAD_XPOS,
239           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
240   g_object_class_install_property (gobject_class, PROP_INPUT_YPOS,
241       g_param_spec_int ("ypos", "Y Position", "Y Position of the picture",
242           G_MININT, G_MAXINT, DEFAULT_PAD_YPOS,
243           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
244   g_object_class_install_property (gobject_class, PROP_INPUT_WIDTH,
245       g_param_spec_int ("width", "Width", "Width of the picture", G_MININT,
246           G_MAXINT, DEFAULT_PAD_WIDTH,
247           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
248   g_object_class_install_property (gobject_class, PROP_INPUT_HEIGHT,
249       g_param_spec_int ("height", "Height", "Height of the picture", G_MININT,
250           G_MAXINT, DEFAULT_PAD_HEIGHT,
251           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
252   g_object_class_install_property (gobject_class, PROP_INPUT_ALPHA,
253       g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0,
254           DEFAULT_PAD_ALPHA,
255           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
256   g_object_class_install_property (gobject_class, PROP_INPUT_BLEND_EQUATION_RGB,
257       g_param_spec_enum ("blend-equation-rgb", "Blend Equation RGB",
258           "Blend Equation for RGB", GST_TYPE_GL_VIDEO_MIXER_BLEND_EQUATION,
259           DEFAULT_PAD_BLEND_EQUATION_RGB,
260           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
261   g_object_class_install_property (gobject_class,
262       PROP_INPUT_BLEND_EQUATION_ALPHA,
263       g_param_spec_enum ("blend-equation-alpha", "Blend Equation Alpha",
264           "Blend Equation for Alpha", GST_TYPE_GL_VIDEO_MIXER_BLEND_EQUATION,
265           DEFAULT_PAD_BLEND_EQUATION_ALPHA,
266           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
267   g_object_class_install_property (gobject_class,
268       PROP_INPUT_BLEND_FUNCTION_SRC_RGB,
269       g_param_spec_enum ("blend-function-src-rgb", "Blend Function Source RGB",
270           "Blend Function for Source RGB",
271           GST_TYPE_GL_VIDEO_MIXER_BLEND_FUNCTION,
272           DEFAULT_PAD_BLEND_FUNCTION_SRC_RGB,
273           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
274   g_object_class_install_property (gobject_class,
275       PROP_INPUT_BLEND_FUNCTION_SRC_ALPHA,
276       g_param_spec_enum ("blend-function-src-alpha",
277           "Blend Function Source Alpha", "Blend Function for Source Alpha",
278           GST_TYPE_GL_VIDEO_MIXER_BLEND_FUNCTION,
279           DEFAULT_PAD_BLEND_FUNCTION_SRC_ALPHA,
280           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
281   g_object_class_install_property (gobject_class,
282       PROP_INPUT_BLEND_FUNCTION_DST_RGB,
283       g_param_spec_enum ("blend-function-dst-rgb",
284           "Blend Function Destination RGB",
285           "Blend Function for Destination RGB",
286           GST_TYPE_GL_VIDEO_MIXER_BLEND_FUNCTION,
287           DEFAULT_PAD_BLEND_FUNCTION_DST_RGB,
288           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
289   g_object_class_install_property (gobject_class,
290       PROP_INPUT_BLEND_FUNCTION_DST_ALPHA,
291       g_param_spec_enum ("blend-function-dst-alpha",
292           "Blend Function Destination Alpha",
293           "Blend Function for Destination Alpha",
294           GST_TYPE_GL_VIDEO_MIXER_BLEND_FUNCTION,
295           DEFAULT_PAD_BLEND_FUNCTION_DST_ALPHA,
296           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
297   g_object_class_install_property (gobject_class,
298       PROP_INPUT_BLEND_FUNCTION_CONSTANT_COLOR_RED,
299       g_param_spec_double ("blend-constant-color-red",
300           "Blend Constant Color Red", "Blend Constant Color Red", 0.0, 1.0, 0.0,
301           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
302   g_object_class_install_property (gobject_class,
303       PROP_INPUT_BLEND_FUNCTION_CONSTANT_COLOR_GREEN,
304       g_param_spec_double ("blend-constant-color-green",
305           "Blend Constant Color Green", "Blend Constant Color Green", 0.0, 1.0,
306           0.0,
307           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
308   g_object_class_install_property (gobject_class,
309       PROP_INPUT_BLEND_FUNCTION_CONSTANT_COLOR_BLUE,
310       g_param_spec_double ("blend-constant-color-blue",
311           "Blend Constant Color Green", "Blend Constant Color Green", 0.0, 1.0,
312           0.0,
313           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
314   g_object_class_install_property (gobject_class,
315       PROP_INPUT_BLEND_FUNCTION_CONSTANT_COLOR_ALPHA,
316       g_param_spec_double ("blend-constant-color-alpha",
317           "Blend Constant Color Alpha", "Blend Constant Color Alpha", 0.0, 1.0,
318           0.0,
319           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
320 }
321
322 static void
323 gst_gl_video_mixer_input_get_property (GObject * object, guint prop_id,
324     GValue * value, GParamSpec * pspec)
325 {
326   GstGLVideoMixerInput *self = (GstGLVideoMixerInput *) object;
327
328   if (self->mixer_pad)
329     g_object_get_property (G_OBJECT (self->mixer_pad), pspec->name, value);
330 }
331
332 static void
333 gst_gl_video_mixer_input_set_property (GObject * object, guint prop_id,
334     const GValue * value, GParamSpec * pspec)
335 {
336   GstGLVideoMixerInput *self = (GstGLVideoMixerInput *) object;
337
338   if (self->mixer_pad)
339     g_object_set_property (G_OBJECT (self->mixer_pad), pspec->name, value);
340 }
341
342 static GstGhostPad *
343 _create_video_mixer_input (GstGLMixerBin * self, GstPad * mixer_pad)
344 {
345   GstGLVideoMixerInput *input =
346       g_object_new (gst_gl_video_mixer_input_get_type (), "name",
347       GST_OBJECT_NAME (mixer_pad), "direction", GST_PAD_DIRECTION (mixer_pad),
348       NULL);
349
350 #define ADD_BINDING(obj,ref,prop) \
351     gst_object_add_control_binding (GST_OBJECT (obj), \
352         gst_proxy_control_binding_new (GST_OBJECT (obj), prop, \
353             GST_OBJECT (ref), prop));
354   ADD_BINDING (mixer_pad, input, "zorder");
355   ADD_BINDING (mixer_pad, input, "xpos");
356   ADD_BINDING (mixer_pad, input, "ypos");
357   ADD_BINDING (mixer_pad, input, "width");
358   ADD_BINDING (mixer_pad, input, "height");
359   ADD_BINDING (mixer_pad, input, "alpha");
360   ADD_BINDING (mixer_pad, input, "blend-equation-rgb");
361   ADD_BINDING (mixer_pad, input, "blend-equation-alpha");
362   ADD_BINDING (mixer_pad, input, "blend-function-src-rgb");
363   ADD_BINDING (mixer_pad, input, "blend-function-src-alpha");
364   ADD_BINDING (mixer_pad, input, "blend-function-dst-rgb");
365   ADD_BINDING (mixer_pad, input, "blend-function-dst-alpha");
366   ADD_BINDING (mixer_pad, input, "blend-constant-color-red");
367   ADD_BINDING (mixer_pad, input, "blend-constant-color-green");
368   ADD_BINDING (mixer_pad, input, "blend-constant-color-blue");
369   ADD_BINDING (mixer_pad, input, "blend-constant-color-alpha");
370 #undef ADD_BINDING
371
372   input->mixer_pad = mixer_pad;
373
374   return GST_GHOST_PAD (input);
375 }
376
377 enum
378 {
379   PROP_BIN_0,
380   PROP_BIN_BACKGROUND,
381 };
382 #define DEFAULT_BACKGROUND GST_GL_VIDEO_MIXER_BACKGROUND_CHECKER
383
384 static void gst_gl_video_mixer_bin_get_property (GObject * object,
385     guint prop_id, GValue * value, GParamSpec * pspec);
386 static void gst_gl_video_mixer_bin_set_property (GObject * object,
387     guint prop_id, const GValue * value, GParamSpec * pspec);
388
389 typedef GstGLMixerBin GstGLVideoMixerBin;
390 typedef GstGLMixerBinClass GstGLVideoMixerBinClass;
391
392 G_DEFINE_TYPE (GstGLVideoMixerBin, gst_gl_video_mixer_bin,
393     GST_TYPE_GL_MIXER_BIN);
394 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (glvideomixer, "glvideomixer",
395     GST_RANK_NONE, gst_gl_video_mixer_bin_get_type (),
396     gl_element_init (plugin));
397
398 static void
399 gst_gl_video_mixer_bin_init (GstGLVideoMixerBin * self)
400 {
401   GstGLMixerBin *mix_bin = GST_GL_MIXER_BIN (self);
402
403   gst_gl_mixer_bin_finish_init_with_element (mix_bin,
404       g_object_new (GST_TYPE_GL_VIDEO_MIXER, NULL));
405 }
406
407 static void
408 gst_gl_video_mixer_bin_class_init (GstGLVideoMixerBinClass * klass)
409 {
410   GstGLMixerBinClass *mixer_class = GST_GL_MIXER_BIN_CLASS (klass);
411   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
412   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
413
414   mixer_class->create_input_pad = _create_video_mixer_input;
415
416   gobject_class->set_property = gst_gl_video_mixer_bin_set_property;
417   gobject_class->get_property = gst_gl_video_mixer_bin_get_property;
418
419   g_object_class_install_property (gobject_class, PROP_BIN_BACKGROUND,
420       g_param_spec_enum ("background", "Background", "Background type",
421           GST_TYPE_GL_VIDEO_MIXER_BACKGROUND,
422           DEFAULT_BACKGROUND, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
423
424   gst_element_class_set_metadata (element_class, "OpenGL video_mixer bin",
425       "Bin/Filter/Effect/Video/Compositor", "OpenGL video_mixer bin",
426       "Matthew Waters <matthew@centricular.com>");
427 }
428
429 static void
430 gst_gl_video_mixer_bin_get_property (GObject * object, guint prop_id,
431     GValue * value, GParamSpec * pspec)
432 {
433   GstGLMixerBin *self = GST_GL_MIXER_BIN (object);
434
435   if (self->mixer)
436     g_object_get_property (G_OBJECT (self->mixer), pspec->name, value);
437 }
438
439 static void
440 gst_gl_video_mixer_bin_set_property (GObject * object, guint prop_id,
441     const GValue * value, GParamSpec * pspec)
442 {
443   GstGLMixerBin *self = GST_GL_MIXER_BIN (object);
444
445   if (self->mixer)
446     g_object_set_property (G_OBJECT (self->mixer), pspec->name, value);
447 }
448
449 enum
450 {
451   PROP_0,
452   PROP_BACKGROUND,
453 };
454
455 static void gst_gl_video_mixer_child_proxy_init (gpointer g_iface,
456     gpointer iface_data);
457
458 #define DEBUG_INIT \
459     GST_DEBUG_CATEGORY_INIT (gst_gl_video_mixer_debug, "glvideomixer", 0, "glvideomixer element");
460
461 #define gst_gl_video_mixer_parent_class parent_class
462 G_DEFINE_TYPE_WITH_CODE (GstGLVideoMixer, gst_gl_video_mixer, GST_TYPE_GL_MIXER,
463     G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY,
464         gst_gl_video_mixer_child_proxy_init); DEBUG_INIT);
465 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (glvideomixerelement,
466     "glvideomixerelement", GST_RANK_NONE, gst_gl_video_mixer_get_type (),
467     gl_element_init (plugin));
468
469 static void gst_gl_video_mixer_set_property (GObject * object, guint prop_id,
470     const GValue * value, GParamSpec * pspec);
471 static void gst_gl_video_mixer_get_property (GObject * object, guint prop_id,
472     GValue * value, GParamSpec * pspec);
473
474 static GstCaps *_update_caps (GstVideoAggregator * vagg, GstCaps * caps);
475 static GstCaps *_fixate_caps (GstAggregator * agg, GstCaps * caps);
476 static gboolean gst_gl_video_mixer_propose_allocation (GstAggregator *
477     agg, GstAggregatorPad * agg_pad, GstQuery * decide_query, GstQuery * query);
478 static gboolean gst_gl_video_mixer_gl_start (GstGLBaseMixer * base_mix);
479 static void gst_gl_video_mixer_gl_stop (GstGLBaseMixer * base_mix);
480 static gboolean gst_gl_video_mixer_set_caps (GstGLMixer * mixer,
481     GstCaps * outcaps);
482
483 static gboolean gst_gl_video_mixer_process_textures (GstGLMixer * mixer,
484     GstGLMemory * out_tex);
485 static gboolean gst_gl_video_mixer_callback (gpointer stuff);
486
487 /* *INDENT-OFF* */
488
489 /* fragment source */
490 static const gchar *video_mixer_f_src =
491     "uniform sampler2D texture;                     \n"
492     "uniform float alpha;\n"
493     "varying vec2 v_texcoord;                            \n"
494     "void main()                                         \n"
495     "{                                                   \n"
496     "  vec4 rgba = texture2D(texture, v_texcoord);\n"
497     "  gl_FragColor = vec4(rgba.rgb, rgba.a * alpha);\n"
498     "}                                                   \n";
499
500 /* checker vertex source */
501 static const gchar *checker_v_src =
502     "attribute vec4 a_position;\n"
503     "void main()\n"
504     "{\n"
505     "   gl_Position = a_position;\n"
506     "}\n";
507
508 /* checker fragment source */
509 static const gchar *checker_f_src =
510     "const float blocksize = 8.0;\n"
511     "void main ()\n"
512     "{\n"
513     "  vec4 high = vec4(0.667, 0.667, 0.667, 1.0);\n"
514     "  vec4 low = vec4(0.333, 0.333, 0.333, 1.0);\n"
515     "  if (mod(gl_FragCoord.x, blocksize * 2.0) >= blocksize) {\n"
516     "    if (mod(gl_FragCoord.y, blocksize * 2.0) >= blocksize)\n"
517     "      gl_FragColor = low;\n"
518     "    else\n"
519     "      gl_FragColor = high;\n"
520     "  } else {\n"
521     "    if (mod(gl_FragCoord.y, blocksize * 2.0) < blocksize)\n"
522     "      gl_FragColor = low;\n"
523     "    else\n"
524     "      gl_FragColor = high;\n"
525     "  }\n"
526     "}\n";
527 /* *INDENT-ON* */
528
529 #define GST_TYPE_GL_VIDEO_MIXER_PAD (gst_gl_video_mixer_pad_get_type())
530 #define GST_GL_VIDEO_MIXER_PAD(obj) \
531         (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_VIDEO_MIXER_PAD, GstGLVideoMixerPad))
532 #define GST_GL_VIDEO_MIXER_PAD_CLASS(klass) \
533         (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GL_VIDEO_MIXER_PAD, GstGLVideoMixerPadClass))
534 #define GST_IS_GL_VIDEO_MIXER_PAD(obj) \
535         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_VIDEO_MIXER_PAD))
536 #define GST_IS_GL_VIDEO_MIXER_PAD_CLASS(klass) \
537         (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GL_VIDEO_MIXER_PAD))
538
539 typedef struct _GstGLVideoMixerPad GstGLVideoMixerPad;
540 typedef struct _GstGLVideoMixerPadClass GstGLVideoMixerPadClass;
541 typedef struct _GstGLVideoMixerCollect GstGLVideoMixerCollect;
542
543 /**
544  * GstGLVideoMixerPad:
545  *
546  * The opaque #GstGLVideoMixerPad structure.
547  */
548 struct _GstGLVideoMixerPad
549 {
550   GstGLMixerPad parent;
551
552   /* properties */
553   gint xpos, ypos;
554   gint width, height;
555   gdouble alpha;
556
557   GstGLVideoMixerBlendEquation blend_equation_rgb;
558   GstGLVideoMixerBlendEquation blend_equation_alpha;
559   GstGLVideoMixerBlendFunction blend_function_src_rgb;
560   GstGLVideoMixerBlendFunction blend_function_src_alpha;
561   GstGLVideoMixerBlendFunction blend_function_dst_rgb;
562   GstGLVideoMixerBlendFunction blend_function_dst_alpha;
563   gdouble blend_constant_color_red;
564   gdouble blend_constant_color_green;
565   gdouble blend_constant_color_blue;
566   gdouble blend_constant_color_alpha;
567
568   gboolean geometry_change;
569   GLuint vertex_buffer;
570   gfloat m_matrix[16];
571 };
572
573 struct _GstGLVideoMixerPadClass
574 {
575   GstGLMixerPadClass parent_class;
576 };
577
578 GType gst_gl_video_mixer_pad_get_type (void);
579 G_DEFINE_TYPE (GstGLVideoMixerPad, gst_gl_video_mixer_pad,
580     GST_TYPE_GL_MIXER_PAD);
581
582 static void gst_gl_video_mixer_pad_set_property (GObject * object,
583     guint prop_id, const GValue * value, GParamSpec * pspec);
584 static void gst_gl_video_mixer_pad_get_property (GObject * object,
585     guint prop_id, GValue * value, GParamSpec * pspec);
586
587 enum
588 {
589   PROP_PAD_0,
590   PROP_PAD_XPOS,
591   PROP_PAD_YPOS,
592   PROP_PAD_WIDTH,
593   PROP_PAD_HEIGHT,
594   PROP_PAD_ALPHA,
595   PROP_PAD_BLEND_EQUATION_RGB,
596   PROP_PAD_BLEND_EQUATION_ALPHA,
597   PROP_PAD_BLEND_FUNCTION_SRC_RGB,
598   PROP_PAD_BLEND_FUNCTION_SRC_ALPHA,
599   PROP_PAD_BLEND_FUNCTION_DST_RGB,
600   PROP_PAD_BLEND_FUNCTION_DST_ALPHA,
601   PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_RED,
602   PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_GREEN,
603   PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_BLUE,
604   PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_ALPHA,
605 };
606
607 static void
608 gst_gl_video_mixer_pad_init (GstGLVideoMixerPad * pad)
609 {
610   pad->alpha = DEFAULT_PAD_ALPHA;
611   pad->blend_equation_rgb = DEFAULT_PAD_BLEND_EQUATION_RGB;
612   pad->blend_equation_alpha = DEFAULT_PAD_BLEND_EQUATION_ALPHA;
613   pad->blend_function_src_rgb = DEFAULT_PAD_BLEND_FUNCTION_SRC_RGB;
614   pad->blend_function_src_alpha = DEFAULT_PAD_BLEND_FUNCTION_SRC_ALPHA;
615   pad->blend_function_dst_rgb = DEFAULT_PAD_BLEND_FUNCTION_DST_RGB;
616   pad->blend_function_dst_alpha = DEFAULT_PAD_BLEND_FUNCTION_DST_ALPHA;
617   memset (pad->m_matrix, 0, sizeof (gfloat) * 4 * 4);
618   pad->m_matrix[0] = 1.0;
619   pad->m_matrix[5] = 1.0;
620   pad->m_matrix[10] = 1.0;
621   pad->m_matrix[15] = 1.0;
622 }
623
624 static void
625 gst_gl_video_mixer_pad_class_init (GstGLVideoMixerPadClass * klass)
626 {
627   GObjectClass *gobject_class = (GObjectClass *) klass;
628
629   gobject_class->set_property = gst_gl_video_mixer_pad_set_property;
630   gobject_class->get_property = gst_gl_video_mixer_pad_get_property;
631
632   g_object_class_install_property (gobject_class, PROP_PAD_XPOS,
633       g_param_spec_int ("xpos", "X Position", "X Position of the picture",
634           G_MININT, G_MAXINT, DEFAULT_PAD_XPOS,
635           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
636   g_object_class_install_property (gobject_class, PROP_PAD_YPOS,
637       g_param_spec_int ("ypos", "Y Position", "Y Position of the picture",
638           G_MININT, G_MAXINT, DEFAULT_PAD_YPOS,
639           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
640   g_object_class_install_property (gobject_class, PROP_PAD_WIDTH,
641       g_param_spec_int ("width", "Width", "Width of the picture",
642           G_MININT, G_MAXINT, DEFAULT_PAD_WIDTH,
643           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
644   g_object_class_install_property (gobject_class, PROP_PAD_HEIGHT,
645       g_param_spec_int ("height", "Height", "Height of the picture",
646           G_MININT, G_MAXINT, DEFAULT_PAD_HEIGHT,
647           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
648   g_object_class_install_property (gobject_class, PROP_PAD_ALPHA,
649       g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0,
650           DEFAULT_PAD_ALPHA,
651           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
652   g_object_class_install_property (gobject_class, PROP_INPUT_BLEND_EQUATION_RGB,
653       g_param_spec_enum ("blend-equation-rgb", "Blend Equation RGB",
654           "Blend Equation for RGB",
655           GST_TYPE_GL_VIDEO_MIXER_BLEND_EQUATION,
656           DEFAULT_PAD_BLEND_EQUATION_RGB,
657           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
658   g_object_class_install_property (gobject_class,
659       PROP_INPUT_BLEND_EQUATION_ALPHA,
660       g_param_spec_enum ("blend-equation-alpha", "Blend Equation Alpha",
661           "Blend Equation for Alpha", GST_TYPE_GL_VIDEO_MIXER_BLEND_EQUATION,
662           DEFAULT_PAD_BLEND_EQUATION_ALPHA,
663           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
664   g_object_class_install_property (gobject_class,
665       PROP_INPUT_BLEND_FUNCTION_SRC_RGB,
666       g_param_spec_enum ("blend-function-src-rgb", "Blend Function Source RGB",
667           "Blend Function for Source RGB",
668           GST_TYPE_GL_VIDEO_MIXER_BLEND_FUNCTION,
669           DEFAULT_PAD_BLEND_FUNCTION_SRC_RGB,
670           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
671   g_object_class_install_property (gobject_class,
672       PROP_INPUT_BLEND_FUNCTION_SRC_ALPHA,
673       g_param_spec_enum ("blend-function-src-alpha",
674           "Blend Function Source Alpha", "Blend Function for Source Alpha",
675           GST_TYPE_GL_VIDEO_MIXER_BLEND_FUNCTION,
676           DEFAULT_PAD_BLEND_FUNCTION_SRC_ALPHA,
677           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
678   g_object_class_install_property (gobject_class,
679       PROP_INPUT_BLEND_FUNCTION_DST_RGB,
680       g_param_spec_enum ("blend-function-dst-rgb",
681           "Blend Function Destination RGB",
682           "Blend Function for Destination RGB",
683           GST_TYPE_GL_VIDEO_MIXER_BLEND_FUNCTION,
684           DEFAULT_PAD_BLEND_FUNCTION_DST_RGB,
685           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
686   g_object_class_install_property (gobject_class,
687       PROP_INPUT_BLEND_FUNCTION_DST_ALPHA,
688       g_param_spec_enum ("blend-function-dst-alpha",
689           "Blend Function Destination Alpha",
690           "Blend Function for Destination Alpha",
691           GST_TYPE_GL_VIDEO_MIXER_BLEND_FUNCTION,
692           DEFAULT_PAD_BLEND_FUNCTION_DST_ALPHA,
693           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
694   g_object_class_install_property (gobject_class,
695       PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_RED,
696       g_param_spec_double ("blend-constant-color-red",
697           "Blend Constant Color Red", "Blend Constant Color Red", 0.0, 1.0, 0.0,
698           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
699   g_object_class_install_property (gobject_class,
700       PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_GREEN,
701       g_param_spec_double ("blend-constant-color-green",
702           "Blend Constant Color Green", "Blend Constant Color Green", 0.0, 1.0,
703           0.0,
704           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
705   g_object_class_install_property (gobject_class,
706       PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_BLUE,
707       g_param_spec_double ("blend-constant-color-blue",
708           "Blend Constant Color Green", "Blend Constant Color Green", 0.0, 1.0,
709           0.0,
710           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
711   g_object_class_install_property (gobject_class,
712       PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_ALPHA,
713       g_param_spec_double ("blend-constant-color-alpha",
714           "Blend Constant Color Alpha", "Blend Constant Color Alpha", 0.0, 1.0,
715           0.0,
716           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
717 }
718
719 static void
720 gst_gl_video_mixer_pad_get_property (GObject * object, guint prop_id,
721     GValue * value, GParamSpec * pspec)
722 {
723   GstGLVideoMixerPad *pad = GST_GL_VIDEO_MIXER_PAD (object);
724
725   switch (prop_id) {
726     case PROP_PAD_XPOS:
727       g_value_set_int (value, pad->xpos);
728       break;
729     case PROP_PAD_YPOS:
730       g_value_set_int (value, pad->ypos);
731       break;
732     case PROP_PAD_WIDTH:
733       g_value_set_int (value, pad->width);
734       break;
735     case PROP_PAD_HEIGHT:
736       g_value_set_int (value, pad->height);
737       break;
738     case PROP_PAD_ALPHA:
739       g_value_set_double (value, pad->alpha);
740       break;
741     case PROP_PAD_BLEND_EQUATION_RGB:
742       g_value_set_enum (value, pad->blend_equation_rgb);
743       break;
744     case PROP_PAD_BLEND_EQUATION_ALPHA:
745       g_value_set_enum (value, pad->blend_equation_alpha);
746       break;
747     case PROP_PAD_BLEND_FUNCTION_SRC_RGB:
748       g_value_set_enum (value, pad->blend_function_src_rgb);
749       break;
750     case PROP_PAD_BLEND_FUNCTION_SRC_ALPHA:
751       g_value_set_enum (value, pad->blend_function_src_alpha);
752       break;
753     case PROP_PAD_BLEND_FUNCTION_DST_RGB:
754       g_value_set_enum (value, pad->blend_function_dst_rgb);
755       break;
756     case PROP_PAD_BLEND_FUNCTION_DST_ALPHA:
757       g_value_set_enum (value, pad->blend_function_dst_alpha);
758       break;
759     case PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_RED:
760       g_value_set_double (value, pad->blend_constant_color_red);
761       break;
762     case PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_GREEN:
763       g_value_set_double (value, pad->blend_constant_color_green);
764       break;
765     case PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_BLUE:
766       g_value_set_double (value, pad->blend_constant_color_blue);
767       break;
768     case PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_ALPHA:
769       g_value_set_double (value, pad->blend_constant_color_alpha);
770       break;
771     default:
772       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
773       break;
774   }
775 }
776
777 static void
778 gst_gl_video_mixer_pad_set_property (GObject * object, guint prop_id,
779     const GValue * value, GParamSpec * pspec)
780 {
781   GstGLVideoMixerPad *pad = GST_GL_VIDEO_MIXER_PAD (object);
782   GstGLMixer *mix = GST_GL_MIXER (gst_pad_get_parent (GST_PAD (pad)));
783
784   switch (prop_id) {
785     case PROP_PAD_XPOS:
786       pad->xpos = g_value_get_int (value);
787       pad->geometry_change = TRUE;
788       break;
789     case PROP_PAD_YPOS:
790       pad->ypos = g_value_get_int (value);
791       pad->geometry_change = TRUE;
792       break;
793     case PROP_PAD_WIDTH:
794       pad->width = g_value_get_int (value);
795       pad->geometry_change = TRUE;
796       break;
797     case PROP_PAD_HEIGHT:
798       pad->height = g_value_get_int (value);
799       pad->geometry_change = TRUE;
800       break;
801     case PROP_PAD_ALPHA:
802       pad->alpha = g_value_get_double (value);
803       break;
804     case PROP_PAD_BLEND_EQUATION_RGB:
805       pad->blend_equation_rgb = g_value_get_enum (value);
806       break;
807     case PROP_PAD_BLEND_EQUATION_ALPHA:
808       pad->blend_equation_alpha = g_value_get_enum (value);
809       break;
810     case PROP_PAD_BLEND_FUNCTION_SRC_RGB:
811       pad->blend_function_src_rgb = g_value_get_enum (value);
812       break;
813     case PROP_PAD_BLEND_FUNCTION_SRC_ALPHA:
814       pad->blend_function_src_alpha = g_value_get_enum (value);
815       break;
816     case PROP_PAD_BLEND_FUNCTION_DST_RGB:
817       pad->blend_function_dst_rgb = g_value_get_enum (value);
818       break;
819     case PROP_PAD_BLEND_FUNCTION_DST_ALPHA:
820       pad->blend_function_dst_alpha = g_value_get_enum (value);
821       break;
822     case PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_RED:
823       pad->blend_constant_color_red = g_value_get_double (value);
824       break;
825     case PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_GREEN:
826       pad->blend_constant_color_green = g_value_get_double (value);
827       break;
828     case PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_BLUE:
829       pad->blend_constant_color_blue = g_value_get_double (value);
830       break;
831     case PROP_PAD_BLEND_FUNCTION_CONSTANT_COLOR_ALPHA:
832       pad->blend_constant_color_alpha = g_value_get_double (value);
833       break;
834     default:
835       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
836       break;
837   }
838
839   gst_object_unref (mix);
840 }
841
842 static void
843 _del_buffer (GstGLContext * context, GLuint * pBuffer)
844 {
845   context->gl_vtable->DeleteBuffers (1, pBuffer);
846 }
847
848 static GstPad *
849 gst_gl_video_mixer_request_new_pad (GstElement * element,
850     GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
851 {
852   GstPad *newpad;
853
854   newpad = (GstPad *)
855       GST_ELEMENT_CLASS (parent_class)->request_new_pad (element,
856       templ, req_name, caps);
857
858   if (newpad == NULL)
859     goto could_not_create;
860
861   gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad),
862       GST_OBJECT_NAME (newpad));
863
864   return newpad;
865
866 could_not_create:
867   {
868     GST_DEBUG_OBJECT (element, "could not create/add  pad");
869     return NULL;
870   }
871 }
872
873 static void
874 gst_gl_video_mixer_release_pad (GstElement * element, GstPad * p)
875 {
876   GstGLVideoMixerPad *pad = GST_GL_VIDEO_MIXER_PAD (p);
877
878   gst_child_proxy_child_removed (GST_CHILD_PROXY (element), G_OBJECT (pad),
879       GST_OBJECT_NAME (pad));
880
881   /* we call the base class first as this will remove the pad from
882    * the aggregator, thus stopping misc callbacks from being called,
883    * one of which (process_textures) will recreate the vertex_buffer
884    * if it is destroyed.  Calling the parent may release the last ref to the pad
885    * so we need to keep the pad alive for the follow up clean up */
886   gst_object_ref (pad);
887   GST_ELEMENT_CLASS (g_type_class_peek_parent (G_OBJECT_GET_CLASS (element)))
888       ->release_pad (element, p);
889
890   if (pad->vertex_buffer) {
891     GstGLBaseMixer *mix = GST_GL_BASE_MIXER (element);
892     gst_gl_context_thread_add (mix->context, (GstGLContextThreadFunc)
893         _del_buffer, &pad->vertex_buffer);
894     pad->vertex_buffer = 0;
895   }
896   gst_object_unref (pad);
897 }
898
899 static void
900 gst_gl_video_mixer_class_init (GstGLVideoMixerClass * klass)
901 {
902   GObjectClass *gobject_class;
903   GstElementClass *element_class;
904   GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
905   GstVideoAggregatorClass *vagg_class = (GstVideoAggregatorClass *) klass;
906
907   gobject_class = (GObjectClass *) klass;
908   element_class = GST_ELEMENT_CLASS (klass);
909   element_class->request_new_pad = gst_gl_video_mixer_request_new_pad;
910   element_class->release_pad = gst_gl_video_mixer_release_pad;
911
912   gobject_class->set_property = gst_gl_video_mixer_set_property;
913   gobject_class->get_property = gst_gl_video_mixer_get_property;
914
915   gst_element_class_set_metadata (element_class, "OpenGL video_mixer",
916       "Filter/Effect/Video/Compositor", "OpenGL video_mixer",
917       "Matthew Waters <matthew@centricular.com>");
918
919   gst_element_class_add_static_pad_template_with_gtype (element_class,
920       &sink_factory, GST_TYPE_GL_VIDEO_MIXER_PAD);
921
922   g_object_class_install_property (gobject_class, PROP_BACKGROUND,
923       g_param_spec_enum ("background", "Background", "Background type",
924           GST_TYPE_GL_VIDEO_MIXER_BACKGROUND,
925           DEFAULT_BACKGROUND, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
926
927   GST_GL_MIXER_CLASS (klass)->set_caps = gst_gl_video_mixer_set_caps;
928   GST_GL_MIXER_CLASS (klass)->process_textures =
929       gst_gl_video_mixer_process_textures;
930
931   GST_GL_BASE_MIXER_CLASS (klass)->gl_stop = gst_gl_video_mixer_gl_stop;
932   GST_GL_BASE_MIXER_CLASS (klass)->gl_start = gst_gl_video_mixer_gl_start;
933
934   vagg_class->update_caps = _update_caps;
935
936   agg_class->fixate_src_caps = _fixate_caps;
937   agg_class->propose_allocation = gst_gl_video_mixer_propose_allocation;
938
939   GST_GL_BASE_MIXER_CLASS (klass)->supported_gl_api =
940       GST_GL_API_OPENGL | GST_GL_API_OPENGL3 | GST_GL_API_GLES2;
941
942   gst_type_mark_as_plugin_api (GST_TYPE_GL_VIDEO_MIXER_BACKGROUND, 0);
943   gst_type_mark_as_plugin_api (GST_TYPE_GL_VIDEO_MIXER_PAD, 0);
944   gst_type_mark_as_plugin_api (GST_TYPE_GL_VIDEO_MIXER_BLEND_EQUATION, 0);
945   gst_type_mark_as_plugin_api (GST_TYPE_GL_VIDEO_MIXER_BLEND_FUNCTION, 0);
946 }
947
948 static void
949 gst_gl_video_mixer_init (GstGLVideoMixer * video_mixer)
950 {
951   video_mixer->background = DEFAULT_BACKGROUND;
952   video_mixer->shader = NULL;
953 }
954
955 static void
956 gst_gl_video_mixer_set_property (GObject * object, guint prop_id,
957     const GValue * value, GParamSpec * pspec)
958 {
959   GstGLVideoMixer *mixer = GST_GL_VIDEO_MIXER (object);
960
961   switch (prop_id) {
962     case PROP_BACKGROUND:
963       mixer->background = g_value_get_enum (value);
964       break;
965     default:
966       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
967       break;
968   }
969 }
970
971 static void
972 gst_gl_video_mixer_get_property (GObject * object, guint prop_id,
973     GValue * value, GParamSpec * pspec)
974 {
975   GstGLVideoMixer *mixer = GST_GL_VIDEO_MIXER (object);
976
977   switch (prop_id) {
978     case PROP_BACKGROUND:
979       g_value_set_enum (value, mixer->background);
980       break;
981     default:
982       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
983       break;
984   }
985 }
986
987 static gboolean
988 gst_gl_video_mixer_propose_allocation (GstAggregator * agg,
989     GstAggregatorPad * agg_pad, GstQuery * decide_query, GstQuery * query)
990 {
991   if (!GST_AGGREGATOR_CLASS (parent_class)->propose_allocation (agg,
992           agg_pad, decide_query, query))
993     return FALSE;
994
995   gst_query_add_allocation_meta (query,
996       GST_VIDEO_AFFINE_TRANSFORMATION_META_API_TYPE, 0);
997
998   return TRUE;
999 }
1000
1001 static void
1002 _mixer_pad_get_output_size (GstGLVideoMixer * mix,
1003     GstGLVideoMixerPad * mix_pad, gint out_par_n, gint out_par_d, gint * width,
1004     gint * height)
1005 {
1006   GstVideoAggregatorPad *vagg_pad = GST_VIDEO_AGGREGATOR_PAD (mix_pad);
1007   gint pad_width, pad_height;
1008   guint dar_n, dar_d;
1009
1010   /* FIXME: Anything better we can do here? */
1011   if (!vagg_pad->info.finfo
1012       || vagg_pad->info.finfo->format == GST_VIDEO_FORMAT_UNKNOWN) {
1013     GST_DEBUG_OBJECT (mix_pad, "Have no caps yet");
1014     *width = 0;
1015     *height = 0;
1016     return;
1017   }
1018
1019   pad_width =
1020       mix_pad->width <=
1021       0 ? GST_VIDEO_INFO_WIDTH (&vagg_pad->info) : mix_pad->width;
1022   pad_height =
1023       mix_pad->height <=
1024       0 ? GST_VIDEO_INFO_HEIGHT (&vagg_pad->info) : mix_pad->height;
1025
1026   if (!gst_video_calculate_display_ratio (&dar_n, &dar_d, pad_width, pad_height,
1027           GST_VIDEO_INFO_PAR_N (&vagg_pad->info),
1028           GST_VIDEO_INFO_PAR_D (&vagg_pad->info), out_par_n, out_par_d)) {
1029     GST_WARNING_OBJECT (mix_pad, "Cannot calculate display aspect ratio");
1030     *width = *height = 0;
1031     return;
1032   }
1033   GST_LOG_OBJECT (mix_pad, "scaling %ux%u by %u/%u (%u/%u / %u/%u)", pad_width,
1034       pad_height, dar_n, dar_d, GST_VIDEO_INFO_PAR_N (&vagg_pad->info),
1035       GST_VIDEO_INFO_PAR_D (&vagg_pad->info), out_par_n, out_par_d);
1036
1037   if (pad_height % dar_n == 0) {
1038     pad_width = gst_util_uint64_scale_int (pad_height, dar_n, dar_d);
1039   } else if (pad_width % dar_d == 0) {
1040     pad_height = gst_util_uint64_scale_int (pad_width, dar_d, dar_n);
1041   } else {
1042     pad_width = gst_util_uint64_scale_int (pad_height, dar_n, dar_d);
1043   }
1044
1045   *width = pad_width;
1046   *height = pad_height;
1047 }
1048
1049 static GstCaps *
1050 _update_caps (GstVideoAggregator * vagg, GstCaps * caps)
1051 {
1052   GstCaps *template_caps, *ret;
1053   GList *l;
1054
1055   GST_OBJECT_LOCK (vagg);
1056   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1057     GstVideoAggregatorPad *vaggpad = l->data;
1058
1059     if (!vaggpad->info.finfo)
1060       continue;
1061
1062     if (GST_VIDEO_INFO_FORMAT (&vaggpad->info) == GST_VIDEO_FORMAT_UNKNOWN)
1063       continue;
1064
1065     if (GST_VIDEO_INFO_MULTIVIEW_MODE (&vaggpad->info) !=
1066         GST_VIDEO_MULTIVIEW_MODE_NONE
1067         && GST_VIDEO_INFO_MULTIVIEW_MODE (&vaggpad->info) !=
1068         GST_VIDEO_MULTIVIEW_MODE_MONO) {
1069       GST_FIXME_OBJECT (vaggpad, "Multiview support is not implemented yet");
1070       GST_OBJECT_UNLOCK (vagg);
1071       return NULL;
1072     }
1073   }
1074
1075   GST_OBJECT_UNLOCK (vagg);
1076
1077   template_caps = gst_pad_get_pad_template_caps (GST_AGGREGATOR_SRC_PAD (vagg));
1078   ret = gst_caps_intersect (caps, template_caps);
1079
1080   return ret;
1081 }
1082
1083 static GstCaps *
1084 _fixate_caps (GstAggregator * agg, GstCaps * caps)
1085 {
1086   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1087   GstGLVideoMixer *mix = GST_GL_VIDEO_MIXER (vagg);
1088   gint best_width = 0, best_height = 0;
1089   gint best_fps_n = 0, best_fps_d = 0;
1090   gint par_n, par_d;
1091   gdouble best_fps = 0.;
1092   GstCaps *ret = NULL;
1093   GstStructure *s;
1094   GList *l;
1095
1096   ret = gst_caps_make_writable (caps);
1097
1098   /* we need this to calculate how large to make the output frame */
1099   s = gst_caps_get_structure (ret, 0);
1100   if (!gst_structure_has_field (s, "pixel-aspect-ratio")) {
1101     gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL);
1102   }
1103   gst_structure_fixate_field_nearest_fraction (s, "pixel-aspect-ratio", 1, 1);
1104   gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d);
1105
1106   GST_OBJECT_LOCK (vagg);
1107   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1108     GstVideoAggregatorPad *vaggpad = l->data;
1109     GstGLVideoMixerPad *mixer_pad = GST_GL_VIDEO_MIXER_PAD (vaggpad);
1110     gint this_width, this_height;
1111     gint width, height;
1112     gint fps_n, fps_d;
1113     gdouble cur_fps;
1114
1115     fps_n = GST_VIDEO_INFO_FPS_N (&vaggpad->info);
1116     fps_d = GST_VIDEO_INFO_FPS_D (&vaggpad->info);
1117     _mixer_pad_get_output_size (mix, mixer_pad, par_n, par_d, &width, &height);
1118
1119     if (width == 0 || height == 0)
1120       continue;
1121
1122     this_width = width + MAX (mixer_pad->xpos, 0);
1123     this_height = height + MAX (mixer_pad->ypos, 0);
1124
1125     if (best_width < this_width)
1126       best_width = this_width;
1127     if (best_height < this_height)
1128       best_height = this_height;
1129
1130     if (fps_d == 0)
1131       cur_fps = 0.0;
1132     else
1133       gst_util_fraction_to_double (fps_n, fps_d, &cur_fps);
1134
1135     if (best_fps < cur_fps) {
1136       best_fps = cur_fps;
1137       best_fps_n = fps_n;
1138       best_fps_d = fps_d;
1139     }
1140   }
1141   GST_OBJECT_UNLOCK (vagg);
1142
1143   if (best_fps_n <= 0 || best_fps_d <= 0 || best_fps == 0.0) {
1144     best_fps_n = 25;
1145     best_fps_d = 1;
1146     best_fps = 25.0;
1147   }
1148
1149   s = gst_caps_get_structure (ret, 0);
1150   gst_structure_fixate_field_nearest_int (s, "width", best_width);
1151   gst_structure_fixate_field_nearest_int (s, "height", best_height);
1152   gst_structure_fixate_field_nearest_fraction (s, "framerate", best_fps_n,
1153       best_fps_d);
1154   ret = gst_caps_fixate (ret);
1155
1156   return ret;
1157 }
1158
1159 static gboolean
1160 _reset_pad_gl (GstElement * agg, GstPad * aggpad, gpointer udata)
1161 {
1162   const GstGLFuncs *gl = GST_GL_BASE_MIXER (agg)->context->gl_vtable;
1163   GstGLVideoMixerPad *pad = GST_GL_VIDEO_MIXER_PAD (aggpad);
1164
1165   if (pad->vertex_buffer) {
1166     gl->DeleteBuffers (1, &pad->vertex_buffer);
1167     pad->vertex_buffer = 0;
1168   }
1169
1170   return TRUE;
1171 }
1172
1173 static void
1174 _reset_gl (GstGLContext * context, GstGLVideoMixer * video_mixer)
1175 {
1176   const GstGLFuncs *gl = GST_GL_BASE_MIXER (video_mixer)->context->gl_vtable;
1177
1178   if (video_mixer->vao) {
1179     gl->DeleteVertexArrays (1, &video_mixer->vao);
1180     video_mixer->vao = 0;
1181   }
1182
1183   if (video_mixer->vbo_indices) {
1184     gl->DeleteBuffers (1, &video_mixer->vbo_indices);
1185     video_mixer->vbo_indices = 0;
1186   }
1187
1188   if (video_mixer->checker_vbo) {
1189     gl->DeleteBuffers (1, &video_mixer->checker_vbo);
1190     video_mixer->checker_vbo = 0;
1191   }
1192
1193   gst_element_foreach_sink_pad (GST_ELEMENT (video_mixer), _reset_pad_gl, NULL);
1194 }
1195
1196 static gboolean
1197 gst_gl_video_mixer_set_caps (GstGLMixer * mixer, GstCaps * outcaps)
1198 {
1199   GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (mixer);
1200
1201   /* need reconfigure output geometry */
1202   video_mixer->output_geo_change = TRUE;
1203
1204   return TRUE;
1205 }
1206
1207 static void
1208 gst_gl_video_mixer_gl_stop (GstGLBaseMixer * base_mix)
1209 {
1210   GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (base_mix);
1211
1212   gst_clear_object (&video_mixer->shader);
1213   gst_clear_object (&video_mixer->checker);
1214
1215   _reset_gl (base_mix->context, video_mixer);
1216
1217   GST_GL_BASE_MIXER_CLASS (parent_class)->gl_stop (base_mix);
1218 }
1219
1220 static gboolean
1221 gst_gl_video_mixer_gl_start (GstGLBaseMixer * base_mix)
1222 {
1223   GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (base_mix);
1224
1225   if (!video_mixer->shader) {
1226     gchar *frag_str = g_strdup_printf ("%s%s",
1227         gst_gl_shader_string_get_highest_precision (base_mix->context,
1228             GST_GLSL_VERSION_NONE,
1229             GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY),
1230         video_mixer_f_src);
1231
1232     gst_gl_context_gen_shader (base_mix->context,
1233         gst_gl_shader_string_vertex_mat4_vertex_transform,
1234         frag_str, &video_mixer->shader);
1235     g_free (frag_str);
1236   }
1237
1238   return GST_GL_BASE_MIXER_CLASS (parent_class)->gl_start (base_mix);
1239 }
1240
1241 static void
1242 _video_mixer_process_gl (GstGLContext * context, GstGLVideoMixer * video_mixer)
1243 {
1244   GstGLMixer *mixer = GST_GL_MIXER (video_mixer);
1245
1246   gst_gl_framebuffer_draw_to_texture (mixer->fbo, video_mixer->out_tex,
1247       gst_gl_video_mixer_callback, video_mixer);
1248 }
1249
1250 static gboolean
1251 gst_gl_video_mixer_process_textures (GstGLMixer * mix, GstGLMemory * out_tex)
1252 {
1253   GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (mix);
1254   GstGLContext *context = GST_GL_BASE_MIXER (mix)->context;
1255
1256   video_mixer->out_tex = out_tex;
1257
1258   gst_gl_context_thread_add (context,
1259       (GstGLContextThreadFunc) _video_mixer_process_gl, video_mixer);
1260
1261   return TRUE;
1262 }
1263
1264 static const GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
1265
1266 static void
1267 _init_vbo_indices (GstGLVideoMixer * mixer)
1268 {
1269   const GstGLFuncs *gl = GST_GL_BASE_MIXER (mixer)->context->gl_vtable;
1270
1271   if (!mixer->vbo_indices) {
1272     gl->GenBuffers (1, &mixer->vbo_indices);
1273     gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, mixer->vbo_indices);
1274     gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indices), indices,
1275         GL_STATIC_DRAW);
1276   }
1277 }
1278
1279 static gboolean
1280 _draw_checker_background (GstGLVideoMixer * video_mixer)
1281 {
1282   GstGLMixer *mixer = GST_GL_MIXER (video_mixer);
1283   const GstGLFuncs *gl = GST_GL_BASE_MIXER (mixer)->context->gl_vtable;
1284   gint attr_position_loc = 0;
1285
1286   /* *INDENT-OFF* */
1287   gfloat v_vertices[] = {
1288     -1.0,-1.0, 0.0f,
1289      1.0,-1.0, 0.0f,
1290      1.0, 1.0, 0.0f,
1291     -1.0, 1.0, 0.0f,
1292   };
1293   /* *INDENT-ON* */
1294
1295   if (!video_mixer->checker) {
1296     gchar *frag_str;
1297
1298     frag_str =
1299         g_strdup_printf ("%s%s",
1300         gst_gl_shader_string_get_highest_precision (GST_GL_BASE_MIXER
1301             (mixer)->context, GST_GLSL_VERSION_NONE,
1302             GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY),
1303         checker_f_src);
1304
1305     if (!gst_gl_context_gen_shader (GST_GL_BASE_MIXER (mixer)->context,
1306             checker_v_src, frag_str, &video_mixer->checker)) {
1307       g_free (frag_str);
1308       return FALSE;
1309     }
1310     g_free (frag_str);
1311   }
1312
1313   gst_gl_shader_use (video_mixer->checker);
1314   attr_position_loc =
1315       gst_gl_shader_get_attribute_location (video_mixer->checker, "a_position");
1316
1317   _init_vbo_indices (video_mixer);
1318
1319   if (!video_mixer->checker_vbo) {
1320     gl->GenBuffers (1, &video_mixer->checker_vbo);
1321     gl->BindBuffer (GL_ARRAY_BUFFER, video_mixer->checker_vbo);
1322     gl->BufferData (GL_ARRAY_BUFFER, 4 * 3 * sizeof (GLfloat), v_vertices,
1323         GL_STATIC_DRAW);
1324   } else {
1325     gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, video_mixer->vbo_indices);
1326     gl->BindBuffer (GL_ARRAY_BUFFER, video_mixer->checker_vbo);
1327   }
1328
1329   gl->VertexAttribPointer (attr_position_loc, 3, GL_FLOAT,
1330       GL_FALSE, 3 * sizeof (GLfloat), (void *) 0);
1331
1332   gl->EnableVertexAttribArray (attr_position_loc);
1333
1334   gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
1335
1336   gl->DisableVertexAttribArray (attr_position_loc);
1337   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
1338   gl->BindBuffer (GL_ARRAY_BUFFER, 0);
1339
1340   return TRUE;
1341 }
1342
1343 static gboolean
1344 _draw_background (GstGLVideoMixer * video_mixer)
1345 {
1346   GstGLMixer *mixer = GST_GL_MIXER (video_mixer);
1347   const GstGLFuncs *gl = GST_GL_BASE_MIXER (mixer)->context->gl_vtable;
1348
1349   switch (video_mixer->background) {
1350     case GST_GL_VIDEO_MIXER_BACKGROUND_BLACK:
1351       gl->ClearColor (0.0, 0.0, 0.0, 1.0);
1352       gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1353       break;
1354     case GST_GL_VIDEO_MIXER_BACKGROUND_WHITE:
1355       gl->ClearColor (1.0, 1.0, 1.0, 1.0);
1356       gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1357       break;
1358     case GST_GL_VIDEO_MIXER_BACKGROUND_TRANSPARENT:
1359       gl->ClearColor (0.0, 0.0, 0.0, 0.0);
1360       gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1361       break;
1362     case GST_GL_VIDEO_MIXER_BACKGROUND_CHECKER:
1363       return _draw_checker_background (video_mixer);
1364       break;
1365     default:
1366       break;
1367   }
1368
1369   return TRUE;
1370 }
1371
1372 static guint
1373 _blend_equation_to_gl (GstGLVideoMixerBlendEquation equation)
1374 {
1375   switch (equation) {
1376     case GST_GL_VIDEO_MIXER_BLEND_EQUATION_ADD:
1377       return GL_FUNC_ADD;
1378     case GST_GL_VIDEO_MIXER_BLEND_EQUATION_SUBTRACT:
1379       return GL_FUNC_SUBTRACT;
1380     case GST_GL_VIDEO_MIXER_BLEND_EQUATION_REVERSE_SUBTRACT:
1381       return GL_FUNC_REVERSE_SUBTRACT;
1382     default:
1383       g_assert_not_reached ();
1384       return 0;
1385   }
1386 }
1387
1388 static guint
1389 _blend_function_to_gl (GstGLVideoMixerBlendFunction equation)
1390 {
1391   switch (equation) {
1392     case GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ZERO:
1393       return GL_ZERO;
1394     case GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE:
1395       return GL_ONE;
1396     case GST_GL_VIDEO_MIXER_BLEND_FUNCTION_SRC_COLOR:
1397       return GL_SRC_COLOR;
1398     case GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_SRC_COLOR:
1399       return GL_ONE_MINUS_SRC_COLOR;
1400     case GST_GL_VIDEO_MIXER_BLEND_FUNCTION_DST_COLOR:
1401       return GL_DST_COLOR;
1402     case GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_DST_COLOR:
1403       return GL_ONE_MINUS_DST_COLOR;
1404     case GST_GL_VIDEO_MIXER_BLEND_FUNCTION_SRC_ALPHA:
1405       return GL_SRC_ALPHA;
1406     case GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_SRC_ALPHA:
1407       return GL_ONE_MINUS_SRC_ALPHA;
1408     case GST_GL_VIDEO_MIXER_BLEND_FUNCTION_DST_ALPHA:
1409       return GL_DST_ALPHA;
1410     case GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_DST_ALPHA:
1411       return GL_ONE_MINUS_DST_ALPHA;
1412     case GST_GL_VIDEO_MIXER_BLEND_FUNCTION_CONSTANT_COLOR:
1413       return GL_CONSTANT_COLOR;
1414     case GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_CONSTANT_COLOR:
1415       return GL_ONE_MINUS_CONSTANT_COLOR;
1416     case GST_GL_VIDEO_MIXER_BLEND_FUNCTION_CONSTANT_ALPHA:
1417       return GL_CONSTANT_ALPHA;
1418     case GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_CONSTANT_ALPHA:
1419       return GL_ONE_MINUS_CONSTANT_ALPHA;
1420     case GST_GL_VIDEO_MIXER_BLEND_FUNCTION_SRC_ALPHA_SATURATE:
1421       return GL_SRC_ALPHA_SATURATE;
1422     default:
1423       g_assert_not_reached ();
1424       return 0;
1425   }
1426 }
1427
1428 static gboolean
1429 _set_blend_state (GstGLVideoMixer * video_mixer, GstGLVideoMixerPad * mix_pad)
1430 {
1431   const GstGLFuncs *gl = GST_GL_BASE_MIXER (video_mixer)->context->gl_vtable;
1432   gboolean require_separate = FALSE;
1433   guint gl_func_src_rgb, gl_func_src_alpha, gl_func_dst_rgb, gl_func_dst_alpha;
1434   guint gl_equation_rgb, gl_equation_alpha;
1435
1436   require_separate =
1437       mix_pad->blend_equation_rgb != mix_pad->blend_equation_alpha
1438       || mix_pad->blend_function_src_rgb != mix_pad->blend_function_src_alpha
1439       || mix_pad->blend_function_dst_rgb != mix_pad->blend_function_dst_alpha;
1440
1441   if (require_separate && (!gl->BlendFuncSeparate
1442           || !gl->BlendEquationSeparate)) {
1443     GST_ERROR_OBJECT (mix_pad,
1444         "separated blend equations/functions requested however "
1445         "glBlendFuncSeparate or glBlendEquationSeparate not available");
1446     return FALSE;
1447   }
1448
1449   if (mix_pad->blend_function_dst_rgb ==
1450       GST_GL_VIDEO_MIXER_BLEND_FUNCTION_SRC_ALPHA_SATURATE) {
1451     GST_ERROR_OBJECT (mix_pad,
1452         "Destination RGB blend function cannot be \'SRC_ALPHA_SATURATE\'");
1453     return FALSE;
1454   }
1455
1456   if (mix_pad->blend_function_dst_alpha ==
1457       GST_GL_VIDEO_MIXER_BLEND_FUNCTION_SRC_ALPHA_SATURATE) {
1458     GST_ERROR_OBJECT (mix_pad,
1459         "Destination alpha blend function cannot be \'SRC_ALPHA_SATURATE\'");
1460     return FALSE;
1461   }
1462
1463   gl_equation_rgb = _blend_equation_to_gl (mix_pad->blend_equation_rgb);
1464   gl_equation_alpha = _blend_equation_to_gl (mix_pad->blend_equation_alpha);
1465
1466   gl_func_src_rgb = _blend_function_to_gl (mix_pad->blend_function_src_rgb);
1467   gl_func_src_alpha = _blend_function_to_gl (mix_pad->blend_function_src_alpha);
1468   gl_func_dst_rgb = _blend_function_to_gl (mix_pad->blend_function_dst_rgb);
1469   gl_func_dst_alpha = _blend_function_to_gl (mix_pad->blend_function_dst_alpha);
1470
1471   if (gl->BlendEquationSeparate)
1472     gl->BlendEquationSeparate (gl_equation_rgb, gl_equation_alpha);
1473   else
1474     gl->BlendEquation (gl_equation_rgb);
1475
1476   if (gl->BlendFuncSeparate)
1477     gl->BlendFuncSeparate (gl_func_src_rgb, gl_func_dst_rgb, gl_func_src_alpha,
1478         gl_func_dst_alpha);
1479   else
1480     gl->BlendFunc (gl_func_src_rgb, gl_func_dst_rgb);
1481
1482   gl->BlendColor (mix_pad->blend_constant_color_red,
1483       mix_pad->blend_constant_color_green, mix_pad->blend_constant_color_blue,
1484       mix_pad->blend_constant_color_alpha);
1485
1486   return TRUE;
1487 }
1488
1489 /* opengl scene, params: input texture (not the output mixer->texture) */
1490 static gboolean
1491 gst_gl_video_mixer_callback (gpointer stuff)
1492 {
1493   GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (stuff);
1494   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (stuff);
1495   GstGLMixer *mixer = GST_GL_MIXER (video_mixer);
1496   GstGLFuncs *gl = GST_GL_BASE_MIXER (mixer)->context->gl_vtable;
1497   GLint attr_position_loc = 0;
1498   GLint attr_texture_loc = 0;
1499   guint out_width, out_height;
1500   GList *walk;
1501
1502   out_width = GST_VIDEO_INFO_WIDTH (&vagg->info);
1503   out_height = GST_VIDEO_INFO_HEIGHT (&vagg->info);
1504
1505   gst_gl_context_clear_shader (GST_GL_BASE_MIXER (mixer)->context);
1506   gl->BindTexture (GL_TEXTURE_2D, 0);
1507
1508   gl->Disable (GL_DEPTH_TEST);
1509   gl->Disable (GL_CULL_FACE);
1510
1511   if (gl->GenVertexArrays) {
1512     if (!video_mixer->vao)
1513       gl->GenVertexArrays (1, &video_mixer->vao);
1514     gl->BindVertexArray (video_mixer->vao);
1515   }
1516
1517   if (!_draw_background (video_mixer))
1518     return FALSE;
1519
1520   gst_gl_shader_use (video_mixer->shader);
1521
1522   attr_position_loc =
1523       gst_gl_shader_get_attribute_location (video_mixer->shader, "a_position");
1524   attr_texture_loc =
1525       gst_gl_shader_get_attribute_location (video_mixer->shader, "a_texcoord");
1526
1527   gl->Enable (GL_BLEND);
1528
1529   GST_OBJECT_LOCK (video_mixer);
1530   walk = GST_ELEMENT (video_mixer)->sinkpads;
1531   while (walk) {
1532     GstGLMixerPad *mix_pad = walk->data;
1533     GstGLVideoMixerPad *pad = walk->data;
1534     GstVideoAggregatorPad *vagg_pad = walk->data;
1535     GstVideoInfo *v_info;
1536     guint in_tex;
1537     guint in_width, in_height;
1538
1539     /* *INDENT-OFF* */
1540     gfloat v_vertices[] = {
1541       -1.0,-1.0, 0.0f, 0.0f, 0.0f,
1542        1.0,-1.0, 0.0f, 1.0f, 0.0f,
1543        1.0, 1.0, 0.0f, 1.0f, 1.0f,
1544       -1.0, 1.0, 0.0f, 0.0f, 1.0f,
1545     };
1546     /* *INDENT-ON* */
1547
1548     v_info = &GST_VIDEO_AGGREGATOR_PAD (pad)->info;
1549     in_width = GST_VIDEO_INFO_WIDTH (v_info);
1550     in_height = GST_VIDEO_INFO_HEIGHT (v_info);
1551
1552     if (!mix_pad->current_texture || in_width <= 0 || in_height <= 0
1553         || pad->alpha == 0.0f) {
1554       GST_DEBUG ("skipping texture:%u pad:%p width:%u height:%u alpha:%f",
1555           mix_pad->current_texture, pad, in_width, in_height, pad->alpha);
1556       walk = g_list_next (walk);
1557       continue;
1558     }
1559
1560     if (!_set_blend_state (video_mixer, pad)) {
1561       GST_FIXME_OBJECT (pad, "skipping due to incorrect blend parameters");
1562       walk = g_list_next (walk);
1563       continue;
1564     }
1565
1566     in_tex = mix_pad->current_texture;
1567
1568     _init_vbo_indices (video_mixer);
1569
1570     if (video_mixer->output_geo_change
1571         || pad->geometry_change || !pad->vertex_buffer) {
1572       gint pad_width, pad_height;
1573       gfloat w, h;
1574
1575       _mixer_pad_get_output_size (video_mixer, pad,
1576           GST_VIDEO_INFO_PAR_N (&vagg->info),
1577           GST_VIDEO_INFO_PAR_D (&vagg->info), &pad_width, &pad_height);
1578
1579       w = ((gfloat) pad_width / (gfloat) out_width);
1580       h = ((gfloat) pad_height / (gfloat) out_height);
1581
1582       pad->m_matrix[0] = w;
1583       pad->m_matrix[5] = h;
1584       pad->m_matrix[12] =
1585           2. * (gfloat) pad->xpos / (gfloat) out_width - (1. - w);
1586       pad->m_matrix[13] =
1587           2. * (gfloat) pad->ypos / (gfloat) out_height - (1. - h);
1588
1589       GST_TRACE ("processing texture:%u dimensions:%ux%u, at %f,%f %fx%f with "
1590           "alpha:%f", in_tex, in_width, in_height, pad->m_matrix[12],
1591           pad->m_matrix[13], pad->m_matrix[0], pad->m_matrix[5], pad->alpha);
1592
1593       if (!pad->vertex_buffer)
1594         gl->GenBuffers (1, &pad->vertex_buffer);
1595
1596       gl->BindBuffer (GL_ARRAY_BUFFER, pad->vertex_buffer);
1597       gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), v_vertices,
1598           GL_STATIC_DRAW);
1599
1600       pad->geometry_change = FALSE;
1601     } else {
1602       gl->BindBuffer (GL_ARRAY_BUFFER, pad->vertex_buffer);
1603     }
1604     gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, video_mixer->vbo_indices);
1605
1606     gl->ActiveTexture (GL_TEXTURE0);
1607     gl->BindTexture (GL_TEXTURE_2D, in_tex);
1608     gst_gl_shader_set_uniform_1i (video_mixer->shader, "texture", 0);
1609     gst_gl_shader_set_uniform_1f (video_mixer->shader, "alpha", pad->alpha);
1610
1611     {
1612       GstVideoAffineTransformationMeta *af_meta;
1613       gfloat matrix[16];
1614       gfloat af_matrix[16];
1615       GstBuffer *buffer =
1616           gst_video_aggregator_pad_get_current_buffer (vagg_pad);
1617
1618       af_meta = gst_buffer_get_video_affine_transformation_meta (buffer);
1619       gst_gl_get_affine_transformation_meta_as_ndc_ext (af_meta, af_matrix);
1620       gst_gl_multiply_matrix4 (af_matrix, pad->m_matrix, matrix);
1621       gst_gl_shader_set_uniform_matrix_4fv (video_mixer->shader,
1622           "u_transformation", 1, FALSE, matrix);
1623     }
1624
1625     gl->EnableVertexAttribArray (attr_position_loc);
1626     gl->EnableVertexAttribArray (attr_texture_loc);
1627
1628     gl->VertexAttribPointer (attr_position_loc, 3, GL_FLOAT,
1629         GL_FALSE, 5 * sizeof (GLfloat), (void *) 0);
1630
1631     gl->VertexAttribPointer (attr_texture_loc, 2, GL_FLOAT,
1632         GL_FALSE, 5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
1633
1634     gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
1635
1636     walk = g_list_next (walk);
1637   }
1638
1639   video_mixer->output_geo_change = FALSE;
1640   GST_OBJECT_UNLOCK (video_mixer);
1641
1642   if (gl->GenVertexArrays) {
1643     gl->BindVertexArray (0);
1644   } else {
1645     gl->DisableVertexAttribArray (attr_position_loc);
1646     gl->DisableVertexAttribArray (attr_texture_loc);
1647
1648     gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
1649     gl->BindBuffer (GL_ARRAY_BUFFER, 0);
1650     gl->BindTexture (GL_TEXTURE_2D, 0);
1651   }
1652
1653   gl->Disable (GL_BLEND);
1654
1655   gst_gl_context_clear_shader (GST_GL_BASE_MIXER (mixer)->context);
1656
1657   return TRUE;
1658 }
1659
1660 /* GstChildProxy implementation */
1661 static GObject *
1662 gst_gl_video_mixer_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
1663     guint index)
1664 {
1665   GstGLVideoMixer *gl_video_mixer = GST_GL_VIDEO_MIXER (child_proxy);
1666   GObject *obj = NULL;
1667
1668   GST_OBJECT_LOCK (gl_video_mixer);
1669   obj = g_list_nth_data (GST_ELEMENT_CAST (gl_video_mixer)->sinkpads, index);
1670   if (obj)
1671     gst_object_ref (obj);
1672   GST_OBJECT_UNLOCK (gl_video_mixer);
1673
1674   return obj;
1675 }
1676
1677 static guint
1678 gst_gl_video_mixer_child_proxy_get_children_count (GstChildProxy * child_proxy)
1679 {
1680   guint count = 0;
1681   GstGLVideoMixer *gl_video_mixer = GST_GL_VIDEO_MIXER (child_proxy);
1682
1683   GST_OBJECT_LOCK (gl_video_mixer);
1684   count = GST_ELEMENT_CAST (gl_video_mixer)->numsinkpads;
1685   GST_OBJECT_UNLOCK (gl_video_mixer);
1686   GST_INFO_OBJECT (gl_video_mixer, "Children Count: %d", count);
1687
1688   return count;
1689 }
1690
1691 static void
1692 gst_gl_video_mixer_child_proxy_init (gpointer g_iface, gpointer iface_data)
1693 {
1694   GstChildProxyInterface *iface = g_iface;
1695
1696   iface->get_child_by_index = gst_gl_video_mixer_child_proxy_get_child_by_index;
1697   iface->get_children_count = gst_gl_video_mixer_child_proxy_get_children_count;
1698 }