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
gst_gl_video_mixer_background_get_type(void)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
gst_gl_video_mixer_blend_equation_get_type(void)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
gst_gl_video_mixer_blend_function_get_type(void)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
gst_gl_video_mixer_input_init(GstGLVideoMixerInput * self)214 gst_gl_video_mixer_input_init (GstGLVideoMixerInput * self)
215 {
216 }
217
218 static void
gst_gl_video_mixer_input_class_init(GstGLVideoMixerInputClass * klass)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
gst_gl_video_mixer_input_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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
gst_gl_video_mixer_input_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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 *
_create_video_mixer_input(GstGLMixerBin * self,GstPad * mixer_pad)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
gst_gl_video_mixer_bin_init(GstGLVideoMixerBin * self)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
gst_gl_video_mixer_bin_class_init(GstGLVideoMixerBinClass * klass)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
gst_gl_video_mixer_bin_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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
gst_gl_video_mixer_bin_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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
gst_gl_video_mixer_pad_init(GstGLVideoMixerPad * pad)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
gst_gl_video_mixer_pad_class_init(GstGLVideoMixerPadClass * klass)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
gst_gl_video_mixer_pad_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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
gst_gl_video_mixer_pad_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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
_del_buffer(GstGLContext * context,GLuint * pBuffer)843 _del_buffer (GstGLContext * context, GLuint * pBuffer)
844 {
845 context->gl_vtable->DeleteBuffers (1, pBuffer);
846 }
847
848 static GstPad *
gst_gl_video_mixer_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * req_name,const GstCaps * caps)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
gst_gl_video_mixer_release_pad(GstElement * element,GstPad * p)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
gst_gl_video_mixer_class_init(GstGLVideoMixerClass * klass)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
gst_gl_video_mixer_init(GstGLVideoMixer * video_mixer)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
gst_gl_video_mixer_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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
gst_gl_video_mixer_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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
gst_gl_video_mixer_propose_allocation(GstAggregator * agg,GstAggregatorPad * agg_pad,GstQuery * decide_query,GstQuery * query)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
_mixer_pad_get_output_size(GstGLVideoMixer * mix,GstGLVideoMixerPad * mix_pad,gint out_par_n,gint out_par_d,gint * width,gint * height)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 *
_update_caps(GstVideoAggregator * vagg,GstCaps * caps)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 *
_fixate_caps(GstAggregator * agg,GstCaps * caps)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
_reset_pad_gl(GstElement * agg,GstPad * aggpad,gpointer udata)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
_reset_gl(GstGLContext * context,GstGLVideoMixer * video_mixer)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
gst_gl_video_mixer_set_caps(GstGLMixer * mixer,GstCaps * outcaps)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
gst_gl_video_mixer_gl_stop(GstGLBaseMixer * base_mix)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
gst_gl_video_mixer_gl_start(GstGLBaseMixer * base_mix)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
_video_mixer_process_gl(GstGLContext * context,GstGLVideoMixer * video_mixer)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
gst_gl_video_mixer_process_textures(GstGLMixer * mix,GstGLMemory * out_tex)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
_init_vbo_indices(GstGLVideoMixer * mixer)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
_draw_checker_background(GstGLVideoMixer * video_mixer)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
_draw_background(GstGLVideoMixer * video_mixer)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
_blend_equation_to_gl(GstGLVideoMixerBlendEquation equation)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
_blend_function_to_gl(GstGLVideoMixerBlendFunction equation)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
_set_blend_state(GstGLVideoMixer * video_mixer,GstGLVideoMixerPad * mix_pad)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
gst_gl_video_mixer_callback(gpointer stuff)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 (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 *
gst_gl_video_mixer_child_proxy_get_child_by_index(GstChildProxy * child_proxy,guint index)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
gst_gl_video_mixer_child_proxy_get_children_count(GstChildProxy * child_proxy)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
gst_gl_video_mixer_child_proxy_init(gpointer g_iface,gpointer iface_data)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 }
1699