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