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-glmosaic
23 * @title: glmosaic
24 *
25 * glmixer sub element. N gl sink pads to 1 source pad.
26 * N + 1 OpenGL contexts shared together.
27 * N <= 6 because the rendering is more a like a cube than a mosaic
28 * Each opengl input stream is rendered on a cube face
29 *
30 * ## Examples
31 * |[
32 * gst-launch-1.0 videotestsrc ! video/x-raw, format=YUY2 ! glupload ! glcolorconvert ! queue ! glmosaic name=m ! glimagesink \
33 * videotestsrc pattern=12 ! video/x-raw, format=I420, framerate=5/1, width=100, height=200 ! glupload ! glcolorconvert ! queue ! m. \
34 * videotestsrc ! video/x-raw, framerate=15/1, width=1500, height=1500 ! glupload ! gleffects effect=3 ! queue ! 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 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44
45 #include "gstglelements.h"
46 #include "gstglmosaic.h"
47 #include "gstglutils.h"
48
49 #define GST_CAT_DEFAULT gst_gl_mosaic_debug
50 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
51
52 enum
53 {
54 PROP_0,
55 };
56
57 static void gst_gl_mosaic_child_proxy_init (gpointer g_iface,
58 gpointer iface_data);
59
60 #define DEBUG_INIT \
61 GST_DEBUG_CATEGORY_INIT (gst_gl_mosaic_debug, "glmosaic", 0, "glmosaic element");
62
63 G_DEFINE_TYPE_WITH_CODE (GstGLMosaic, gst_gl_mosaic, GST_TYPE_GL_MIXER,
64 G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY,
65 gst_gl_mosaic_child_proxy_init);
66 DEBUG_INIT);
67 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (glmosaic, "glmosaic",
68 GST_RANK_NONE, GST_TYPE_GL_MOSAIC, gl_element_init (plugin));
69
70 static GstPad *gst_gl_mosaic_request_new_pad (GstElement * element,
71 GstPadTemplate * temp, const gchar * req_name, const GstCaps * caps);
72 static void gst_gl_mosaic_release_pad (GstElement * element, GstPad * pad);
73
74 static void gst_gl_mosaic_reset (GstGLMixer * mixer);
75 static gboolean gst_gl_mosaic_set_caps (GstGLMixer * mixer, GstCaps * outcaps);
76
77 static gboolean gst_gl_mosaic_process_textures (GstGLMixer * mixer,
78 GstGLMemory * out_tex);
79 static gboolean gst_gl_mosaic_callback (gpointer stuff);
80
81 /* vertex source */
82 static const gchar *mosaic_v_src =
83 "uniform mat4 u_matrix; \n"
84 "uniform float xrot_degree, yrot_degree, zrot_degree; \n"
85 "attribute vec4 a_position; \n"
86 "attribute vec2 a_texCoord; \n"
87 "varying vec2 v_texCoord; \n"
88 "void main() \n"
89 "{ \n"
90 " float PI = 3.14159265; \n"
91 " float xrot = xrot_degree*2.0*PI/360.0; \n"
92 " float yrot = yrot_degree*2.0*PI/360.0; \n"
93 " float zrot = zrot_degree*2.0*PI/360.0; \n"
94 " mat4 matX = mat4 ( \n"
95 " 1.0, 0.0, 0.0, 0.0, \n"
96 " 0.0, cos(xrot), sin(xrot), 0.0, \n"
97 " 0.0, -sin(xrot), cos(xrot), 0.0, \n"
98 " 0.0, 0.0, 0.0, 1.0 ); \n"
99 " mat4 matY = mat4 ( \n"
100 " cos(yrot), 0.0, -sin(yrot), 0.0, \n"
101 " 0.0, 1.0, 0.0, 0.0, \n"
102 " sin(yrot), 0.0, cos(yrot), 0.0, \n"
103 " 0.0, 0.0, 0.0, 1.0 ); \n"
104 " mat4 matZ = mat4 ( \n"
105 " cos(zrot), sin(zrot), 0.0, 0.0, \n"
106 " -sin(zrot), cos(zrot), 0.0, 0.0, \n"
107 " 0.0, 0.0, 1.0, 0.0, \n"
108 " 0.0, 0.0, 0.0, 1.0 ); \n"
109 " gl_Position = u_matrix * matZ * matY * matX * a_position; \n"
110 " v_texCoord = a_texCoord; \n"
111 "} \n";
112
113 /* fragment source */
114 static const gchar *mosaic_f_src =
115 "uniform sampler2D s_texture; \n"
116 "varying vec2 v_texCoord; \n"
117 "void main() \n"
118 "{ \n"
119 " gl_FragColor = texture2D( s_texture, v_texCoord );\n"
120 "} \n";
121
122 static void
gst_gl_mosaic_class_init(GstGLMosaicClass * klass)123 gst_gl_mosaic_class_init (GstGLMosaicClass * klass)
124 {
125 GstElementClass *element_class;
126
127 element_class = GST_ELEMENT_CLASS (klass);
128
129 element_class->request_new_pad =
130 GST_DEBUG_FUNCPTR (gst_gl_mosaic_request_new_pad);
131 element_class->release_pad = GST_DEBUG_FUNCPTR (gst_gl_mosaic_release_pad);
132
133 gst_element_class_set_metadata (element_class, "OpenGL mosaic",
134 "Filter/Effect/Video", "OpenGL mosaic",
135 "Julien Isorce <julien.isorce@gmail.com>");
136
137 GST_GL_MIXER_CLASS (klass)->set_caps = gst_gl_mosaic_set_caps;
138 GST_GL_MIXER_CLASS (klass)->reset = gst_gl_mosaic_reset;
139 GST_GL_MIXER_CLASS (klass)->process_textures = gst_gl_mosaic_process_textures;
140 }
141
142 static void
gst_gl_mosaic_init(GstGLMosaic * mosaic)143 gst_gl_mosaic_init (GstGLMosaic * mosaic)
144 {
145 mosaic->shader = NULL;
146
147 mosaic->attr_position_loc = -1;
148 mosaic->attr_texture_loc = -1;
149 }
150
151 static GstPad *
gst_gl_mosaic_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * req_name,const GstCaps * caps)152 gst_gl_mosaic_request_new_pad (GstElement * element, GstPadTemplate * templ,
153 const gchar * req_name, const GstCaps * caps)
154 {
155 GstPad *newpad;
156
157 newpad = (GstPad *)
158 GST_ELEMENT_CLASS (gst_gl_mosaic_parent_class)->request_new_pad (element,
159 templ, req_name, caps);
160
161 if (newpad == NULL)
162 goto could_not_create;
163
164 gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad),
165 GST_OBJECT_NAME (newpad));
166
167 return newpad;
168
169 could_not_create:
170 {
171 GST_DEBUG_OBJECT (element, "could not create/add pad");
172 return NULL;
173 }
174 }
175
176 static void
gst_gl_mosaic_release_pad(GstElement * element,GstPad * pad)177 gst_gl_mosaic_release_pad (GstElement * element, GstPad * pad)
178 {
179 GstGLMosaic *gl_mosaic = GST_GL_MOSAIC (element);
180
181 GST_DEBUG_OBJECT (gl_mosaic, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));
182
183 gst_child_proxy_child_removed (GST_CHILD_PROXY (gl_mosaic), G_OBJECT (pad),
184 GST_OBJECT_NAME (pad));
185
186 GST_ELEMENT_CLASS (gst_gl_mosaic_parent_class)->release_pad (element, pad);
187 }
188
189 static void
gst_gl_mosaic_reset(GstGLMixer * mixer)190 gst_gl_mosaic_reset (GstGLMixer * mixer)
191 {
192 GstGLMosaic *mosaic = GST_GL_MOSAIC (mixer);
193
194 if (mosaic->shader)
195 gst_object_unref (mosaic->shader);
196 mosaic->shader = NULL;
197
198 mosaic->attr_position_loc = -1;
199 mosaic->attr_texture_loc = -1;
200 mosaic->xrot = 0.0;
201 mosaic->yrot = 0.0;
202 mosaic->zrot = 0.0;
203 }
204
205 static gboolean
gst_gl_mosaic_set_caps(GstGLMixer * mixer,GstCaps * outcaps)206 gst_gl_mosaic_set_caps (GstGLMixer * mixer, GstCaps * outcaps)
207 {
208 GstGLMosaic *mosaic = GST_GL_MOSAIC (mixer);
209
210 g_clear_object (&mosaic->shader);
211 return TRUE;
212 }
213
214 static void
_mosaic_render(GstGLContext * context,GstGLMosaic * mosaic)215 _mosaic_render (GstGLContext * context, GstGLMosaic * mosaic)
216 {
217 GstGLMixer *mixer = GST_GL_MIXER (mosaic);
218
219 if (!mosaic->shader) {
220 gchar *frag_str = g_strdup_printf ("%s%s",
221 gst_gl_shader_string_get_highest_precision (GST_GL_BASE_MIXER
222 (mixer)->context, GST_GLSL_VERSION_NONE,
223 GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY),
224 mosaic_f_src);
225
226 gst_gl_context_gen_shader (GST_GL_BASE_MIXER (mixer)->context,
227 mosaic_v_src, frag_str, &mosaic->shader);
228 g_free (frag_str);
229 }
230
231 gst_gl_framebuffer_draw_to_texture (mixer->fbo, mosaic->out_tex,
232 gst_gl_mosaic_callback, mosaic);
233 }
234
235 static gboolean
gst_gl_mosaic_process_textures(GstGLMixer * mix,GstGLMemory * out_tex)236 gst_gl_mosaic_process_textures (GstGLMixer * mix, GstGLMemory * out_tex)
237 {
238 GstGLMosaic *mosaic = GST_GL_MOSAIC (mix);
239 GstGLContext *context = GST_GL_BASE_MIXER (mix)->context;
240
241 mosaic->out_tex = out_tex;
242
243 gst_gl_context_thread_add (context, (GstGLContextThreadFunc) _mosaic_render,
244 mosaic);
245
246 return TRUE;
247 }
248
249 static void
_bind_buffer(GstGLMosaic * mosaic)250 _bind_buffer (GstGLMosaic * mosaic)
251 {
252 GstGLContext *context = GST_GL_BASE_MIXER (mosaic)->context;
253 const GstGLFuncs *gl = context->gl_vtable;
254
255 gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, mosaic->vbo_indices);
256 gl->BindBuffer (GL_ARRAY_BUFFER, mosaic->vertex_buffer);
257
258 /* Load the vertex position */
259 gl->VertexAttribPointer (mosaic->attr_position_loc, 3, GL_FLOAT,
260 GL_FALSE, 5 * sizeof (GLfloat), (void *) 0);
261
262 /* Load the texture coordinate */
263 gl->VertexAttribPointer (mosaic->attr_texture_loc, 2, GL_FLOAT, GL_FALSE,
264 5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
265
266
267 gl->EnableVertexAttribArray (mosaic->attr_position_loc);
268 gl->EnableVertexAttribArray (mosaic->attr_texture_loc);
269 }
270
271 static void
_unbind_buffer(GstGLMosaic * mosaic)272 _unbind_buffer (GstGLMosaic * mosaic)
273 {
274 GstGLContext *context = GST_GL_BASE_MIXER (mosaic)->context;
275 const GstGLFuncs *gl = context->gl_vtable;
276
277 gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
278 gl->BindBuffer (GL_ARRAY_BUFFER, 0);
279
280 gl->DisableVertexAttribArray (mosaic->attr_position_loc);
281 gl->DisableVertexAttribArray (mosaic->attr_texture_loc);
282 }
283
284 /* opengl scene, params: input texture (not the output mixer->texture) */
285 static gboolean
gst_gl_mosaic_callback(gpointer stuff)286 gst_gl_mosaic_callback (gpointer stuff)
287 {
288 GstGLMosaic *mosaic = GST_GL_MOSAIC (stuff);
289 GstGLMixer *mixer = GST_GL_MIXER (mosaic);
290 GstGLFuncs *gl = GST_GL_BASE_MIXER (mixer)->context->gl_vtable;
291 GList *walk;
292
293 const GLfloat matrix[] = {
294 0.5f, 0.0f, 0.0f, 0.0f,
295 0.0f, 0.5f, 0.0f, 0.0f,
296 0.0f, 0.0f, 0.5f, 0.0f,
297 0.0f, 0.0f, 0.0f, 1.0f
298 };
299
300 guint count = 0;
301
302 gst_gl_context_clear_shader (GST_GL_BASE_MIXER (mixer)->context);
303 gl->BindTexture (GL_TEXTURE_2D, 0);
304
305 gl->Enable (GL_DEPTH_TEST);
306
307 gl->ClearColor (0.0, 0.0, 0.0, 0.0);
308 gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
309
310 gst_gl_shader_use (mosaic->shader);
311
312 if (mosaic->attr_position_loc == -1) {
313 mosaic->attr_position_loc =
314 gst_gl_shader_get_attribute_location (mosaic->shader, "a_position");
315 }
316 if (mosaic->attr_texture_loc == -1) {
317 mosaic->attr_texture_loc =
318 gst_gl_shader_get_attribute_location (mosaic->shader, "a_texCoord");
319 }
320
321 gst_gl_shader_set_uniform_1i (mosaic->shader, "s_texture", 0);
322 gst_gl_shader_set_uniform_1f (mosaic->shader, "xrot_degree", mosaic->xrot);
323 gst_gl_shader_set_uniform_1f (mosaic->shader, "yrot_degree", mosaic->yrot);
324 gst_gl_shader_set_uniform_1f (mosaic->shader, "zrot_degree", mosaic->zrot);
325 gst_gl_shader_set_uniform_matrix_4fv (mosaic->shader, "u_matrix", 1,
326 GL_FALSE, matrix);
327
328 if (!mosaic->vertex_buffer) {
329 /* *INDENT-OFF* */
330 gfloat vertices[] = {
331 /* front face */
332 1.0f, 1.0f,-1.0f, 1.0f, 0.0f,
333 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,
334 -1.0f,-1.0f,-1.0f, 0.0f, 1.0f,
335 -1.0f, 1.0f,-1.0f, 0.0f, 0.0f,
336 /* right face */
337 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
338 1.0f,-1.0f, 1.0f, 0.0f, 0.0f,
339 1.0f,-1.0f,-1.0f, 0.0f, 1.0f,
340 1.0f, 1.0f,-1.0f, 1.0f, 1.0f,
341 /* left face */
342 -1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
343 -1.0f, 1.0f,-1.0f, 1.0f, 1.0f,
344 -1.0f,-1.0f,-1.0f, 0.0f, 1.0f,
345 -1.0f,-1.0f, 1.0f, 0.0f, 0.0f,
346 /* top face */
347 1.0f,-1.0f, 1.0f, 1.0f, 0.0f,
348 -1.0f,-1.0f, 1.0f, 0.0f, 0.0f,
349 -1.0f,-1.0f,-1.0f, 0.0f, 1.0f,
350 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,
351 /* bottom face */
352 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
353 1.0f, 1.0f,-1.0f, 1.0f, 1.0f,
354 -1.0f, 1.0f,-1.0f, 0.0f, 1.0f,
355 -1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
356 /* back face */
357 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
358 -1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
359 -1.0f,-1.0f, 1.0f, 0.0f, 1.0f,
360 1.0f,-1.0f, 1.0f, 1.0f, 1.0f
361 };
362 const GLushort indices[] = {
363 0, 1, 2,
364 0, 2, 3,
365 4, 5, 6,
366 4, 6, 7,
367 8, 9, 10,
368 8, 10, 11,
369 12, 13, 14,
370 12, 14, 15,
371 16, 17, 18,
372 16, 18, 19,
373 20, 21, 22,
374 20, 22, 23,
375 };
376 /* *INDENT-ON* */
377
378 if (gl->GenVertexArrays) {
379 gl->GenVertexArrays (1, &mosaic->vao);
380 gl->BindVertexArray (mosaic->vao);
381 }
382
383 gl->GenBuffers (1, &mosaic->vertex_buffer);
384 gl->BindBuffer (GL_ARRAY_BUFFER, mosaic->vertex_buffer);
385 gl->BufferData (GL_ARRAY_BUFFER, sizeof (vertices), vertices,
386 GL_STATIC_DRAW);
387
388 gl->GenBuffers (1, &mosaic->vbo_indices);
389 gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, mosaic->vbo_indices);
390 gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indices), indices,
391 GL_STATIC_DRAW);
392 }
393
394 if (gl->GenVertexArrays)
395 gl->BindVertexArray (mosaic->vao);
396 _bind_buffer (mosaic);
397
398 GST_OBJECT_LOCK (mosaic);
399 walk = GST_ELEMENT (mosaic)->sinkpads;
400 while (walk) {
401 GstGLMixerPad *pad = walk->data;
402 guint in_tex;
403 guint width, height;
404
405 in_tex = pad->current_texture;
406 width = GST_VIDEO_INFO_WIDTH (&GST_VIDEO_AGGREGATOR_PAD (pad)->info);
407 height = GST_VIDEO_INFO_HEIGHT (&GST_VIDEO_AGGREGATOR_PAD (pad)->info);
408
409 if (count >= 6) {
410 GST_FIXME_OBJECT (mosaic, "Skipping 7th pad (and all subsequent pads)");
411 break;
412 }
413
414 if (!in_tex || width <= 0 || height <= 0) {
415 GST_DEBUG ("skipping texture:%u pad:%p width:%u height %u",
416 in_tex, pad, width, height);
417 count++;
418 walk = g_list_next (walk);
419 continue;
420 }
421
422 GST_TRACE ("processing texture:%u dimensions:%ux%u", in_tex, width, height);
423
424 gl->ActiveTexture (GL_TEXTURE0);
425 gl->BindTexture (GL_TEXTURE_2D, in_tex);
426
427 gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
428 (void *) (6 * sizeof (GLushort) * count));
429
430 ++count;
431
432 walk = g_list_next (walk);
433 }
434 GST_OBJECT_UNLOCK (mosaic);
435
436 if (gl->GenVertexArrays)
437 gl->BindVertexArray (0);
438 else
439 _unbind_buffer (mosaic);
440
441 gl->BindTexture (GL_TEXTURE_2D, 0);
442
443 gl->Disable (GL_DEPTH_TEST);
444
445 gst_gl_context_clear_shader (GST_GL_BASE_MIXER (mixer)->context);
446
447 mosaic->xrot += 0.6f;
448 mosaic->yrot += 0.4f;
449 mosaic->zrot += 0.8f;
450
451 return TRUE;
452 }
453
454 /* GstChildProxy implementation */
455 static GObject *
gst_gl_mosaic_child_proxy_get_child_by_index(GstChildProxy * child_proxy,guint index)456 gst_gl_mosaic_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
457 guint index)
458 {
459 GstGLMosaic *gl_mosaic = GST_GL_MOSAIC (child_proxy);
460 GObject *obj = NULL;
461
462 GST_OBJECT_LOCK (gl_mosaic);
463 obj = g_list_nth_data (GST_ELEMENT_CAST (gl_mosaic)->sinkpads, index);
464 if (obj)
465 gst_object_ref (obj);
466 GST_OBJECT_UNLOCK (gl_mosaic);
467
468 return obj;
469 }
470
471 static guint
gst_gl_mosaic_child_proxy_get_children_count(GstChildProxy * child_proxy)472 gst_gl_mosaic_child_proxy_get_children_count (GstChildProxy * child_proxy)
473 {
474 guint count = 0;
475 GstGLMosaic *gl_mosaic = GST_GL_MOSAIC (child_proxy);
476
477 GST_OBJECT_LOCK (gl_mosaic);
478 count = GST_ELEMENT_CAST (gl_mosaic)->numsinkpads;
479 GST_OBJECT_UNLOCK (gl_mosaic);
480 GST_INFO_OBJECT (gl_mosaic, "Children Count: %d", count);
481
482 return count;
483 }
484
485 static void
gst_gl_mosaic_child_proxy_init(gpointer g_iface,gpointer iface_data)486 gst_gl_mosaic_child_proxy_init (gpointer g_iface, gpointer iface_data)
487 {
488 GstChildProxyInterface *iface = g_iface;
489
490 iface->get_child_by_index = gst_gl_mosaic_child_proxy_get_child_by_index;
491 iface->get_children_count = gst_gl_mosaic_child_proxy_get_children_count;
492 }
493