1 /*
2 * GStreamer
3 * Copyright (C) 2008 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-glfiltercube
23 * @title: glfiltercube
24 *
25 * The resize and redraw callbacks can be set from a client code.
26 *
27 * ## Examples
28 * |[
29 * gst-launch-1.0 -v videotestsrc ! glfiltercube ! glimagesink
30 * ]| A pipeline to mpa textures on the 6 cube faces..
31 * FBO is required.
32 * |[
33 * gst-launch-1.0 -v videotestsrc ! glfiltercube ! video/x-raw, width=640, height=480 ! glimagesink
34 * ]| Resize scene after drawing the cube.
35 * The scene size is greater than the input video size.
36 |[
37 * gst-launch-1.0 -v videotestsrc ! video/x-raw, width=640, height=480 ! glfiltercube ! glimagesink
38 * ]| Resize scene before drawing the cube.
39 * The scene size is greater than the input video size.
40 *
41 */
42
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46
47 #include <gst/gl/gstglapi.h>
48 #include "gstglfiltercube.h"
49 #include "gstglutils.h"
50
51 #define GST_CAT_DEFAULT gst_gl_filter_cube_debug
52 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
53
54 enum
55 {
56 PROP_0,
57 PROP_RED,
58 PROP_GREEN,
59 PROP_BLUE,
60 PROP_FOVY,
61 PROP_ASPECT,
62 PROP_ZNEAR,
63 PROP_ZFAR
64 };
65
66 #define DEBUG_INIT \
67 GST_DEBUG_CATEGORY_INIT (gst_gl_filter_cube_debug, "glfiltercube", 0, "glfiltercube element");
68 #define gst_gl_filter_cube_parent_class parent_class
69 G_DEFINE_TYPE_WITH_CODE (GstGLFilterCube, gst_gl_filter_cube,
70 GST_TYPE_GL_FILTER, DEBUG_INIT);
71
72 static void gst_gl_filter_cube_set_property (GObject * object, guint prop_id,
73 const GValue * value, GParamSpec * pspec);
74 static void gst_gl_filter_cube_get_property (GObject * object, guint prop_id,
75 GValue * value, GParamSpec * pspec);
76
77 static gboolean gst_gl_filter_cube_set_caps (GstGLFilter * filter,
78 GstCaps * incaps, GstCaps * outcaps);
79 static gboolean gst_gl_filter_cube_gl_start (GstGLBaseFilter * filter);
80 static void gst_gl_filter_cube_gl_stop (GstGLBaseFilter * filter);
81 static gboolean _callback (gpointer stuff);
82 static gboolean gst_gl_filter_cube_filter_texture (GstGLFilter * filter,
83 GstGLMemory * in_tex, GstGLMemory * out_tex);
84
85 /* vertex source */
86 static const gchar *cube_v_src =
87 "attribute vec4 a_position; \n"
88 "attribute vec2 a_texcoord; \n"
89 "uniform mat4 u_matrix; \n"
90 "uniform float xrot_degree, yrot_degree, zrot_degree; \n"
91 "varying vec2 v_texcoord; \n"
92 "void main() \n"
93 "{ \n"
94 " float PI = 3.14159265; \n"
95 " float xrot = xrot_degree*2.0*PI/360.0; \n"
96 " float yrot = yrot_degree*2.0*PI/360.0; \n"
97 " float zrot = zrot_degree*2.0*PI/360.0; \n"
98 " mat4 matX = mat4 ( \n"
99 " 1.0, 0.0, 0.0, 0.0, \n"
100 " 0.0, cos(xrot), sin(xrot), 0.0, \n"
101 " 0.0, -sin(xrot), cos(xrot), 0.0, \n"
102 " 0.0, 0.0, 0.0, 1.0 ); \n"
103 " mat4 matY = mat4 ( \n"
104 " cos(yrot), 0.0, -sin(yrot), 0.0, \n"
105 " 0.0, 1.0, 0.0, 0.0, \n"
106 " sin(yrot), 0.0, cos(yrot), 0.0, \n"
107 " 0.0, 0.0, 0.0, 1.0 ); \n"
108 " mat4 matZ = mat4 ( \n"
109 " cos(zrot), sin(zrot), 0.0, 0.0, \n"
110 " -sin(zrot), cos(zrot), 0.0, 0.0, \n"
111 " 0.0, 0.0, 1.0, 0.0, \n"
112 " 0.0, 0.0, 0.0, 1.0 ); \n"
113 " gl_Position = u_matrix * matZ * matY * matX * a_position; \n"
114 " v_texcoord = a_texcoord; \n"
115 "} \n";
116
117 /* fragment source */
118 static const gchar *cube_f_src =
119 "varying vec2 v_texcoord; \n"
120 "uniform sampler2D s_texture; \n"
121 "void main() \n"
122 "{ \n"
123 " gl_FragColor = texture2D( s_texture, v_texcoord );\n"
124 "} \n";
125
126 static void
gst_gl_filter_cube_class_init(GstGLFilterCubeClass * klass)127 gst_gl_filter_cube_class_init (GstGLFilterCubeClass * klass)
128 {
129 GObjectClass *gobject_class;
130 GstElementClass *element_class;
131
132 gobject_class = (GObjectClass *) klass;
133 element_class = GST_ELEMENT_CLASS (klass);
134
135 gst_gl_filter_add_rgba_pad_templates (GST_GL_FILTER_CLASS (klass));
136
137 gobject_class->set_property = gst_gl_filter_cube_set_property;
138 gobject_class->get_property = gst_gl_filter_cube_get_property;
139
140 GST_GL_BASE_FILTER_CLASS (klass)->gl_start = gst_gl_filter_cube_gl_start;
141 GST_GL_BASE_FILTER_CLASS (klass)->gl_stop = gst_gl_filter_cube_gl_stop;
142
143 GST_GL_FILTER_CLASS (klass)->set_caps = gst_gl_filter_cube_set_caps;
144 GST_GL_FILTER_CLASS (klass)->filter_texture =
145 gst_gl_filter_cube_filter_texture;
146
147 g_object_class_install_property (gobject_class, PROP_RED,
148 g_param_spec_float ("red", "Red", "Background red color",
149 0.0f, 1.0f, 0.0f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
150
151 g_object_class_install_property (gobject_class, PROP_GREEN,
152 g_param_spec_float ("green", "Green", "Background green color",
153 0.0f, 1.0f, 0.0f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
154
155 g_object_class_install_property (gobject_class, PROP_BLUE,
156 g_param_spec_float ("blue", "Blue", "Background blue color",
157 0.0f, 1.0f, 0.0f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
158
159 g_object_class_install_property (gobject_class, PROP_FOVY,
160 g_param_spec_double ("fovy", "Fovy", "Field of view angle in degrees",
161 0.0, 180.0, 45.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
162
163 g_object_class_install_property (gobject_class, PROP_ASPECT,
164 g_param_spec_double ("aspect", "Aspect",
165 "Field of view in the x direction", 0.0, 100, 0.0,
166 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
167
168 g_object_class_install_property (gobject_class, PROP_ZNEAR,
169 g_param_spec_double ("znear", "Znear",
170 "Specifies the distance from the viewer to the near clipping plane",
171 0.0, 100.0, 0.1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
172
173 g_object_class_install_property (gobject_class, PROP_ZFAR,
174 g_param_spec_double ("zfar", "Zfar",
175 "Specifies the distance from the viewer to the far clipping plane",
176 0.0, 1000.0, 100.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
177
178 gst_element_class_set_metadata (element_class, "OpenGL cube filter",
179 "Filter/Effect/Video", "Map input texture on the 6 cube faces",
180 "Julien Isorce <julien.isorce@gmail.com>");
181
182 GST_GL_BASE_FILTER_CLASS (klass)->supported_gl_api =
183 GST_GL_API_OPENGL | GST_GL_API_GLES2 | GST_GL_API_OPENGL3;
184 }
185
186 static void
gst_gl_filter_cube_init(GstGLFilterCube * filter)187 gst_gl_filter_cube_init (GstGLFilterCube * filter)
188 {
189 filter->shader = NULL;
190 filter->fovy = 45;
191 filter->aspect = 0;
192 filter->znear = 0.1;
193 filter->zfar = 100;
194 }
195
196 static void
gst_gl_filter_cube_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)197 gst_gl_filter_cube_set_property (GObject * object, guint prop_id,
198 const GValue * value, GParamSpec * pspec)
199 {
200 GstGLFilterCube *filter = GST_GL_FILTER_CUBE (object);
201
202 switch (prop_id) {
203 case PROP_RED:
204 filter->red = g_value_get_float (value);
205 break;
206 case PROP_GREEN:
207 filter->green = g_value_get_float (value);
208 break;
209 case PROP_BLUE:
210 filter->blue = g_value_get_float (value);
211 break;
212 case PROP_FOVY:
213 filter->fovy = g_value_get_double (value);
214 break;
215 case PROP_ASPECT:
216 filter->aspect = g_value_get_double (value);
217 break;
218 case PROP_ZNEAR:
219 filter->znear = g_value_get_double (value);
220 break;
221 case PROP_ZFAR:
222 filter->zfar = g_value_get_double (value);
223 break;
224 default:
225 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
226 break;
227 }
228 }
229
230 static void
gst_gl_filter_cube_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)231 gst_gl_filter_cube_get_property (GObject * object, guint prop_id,
232 GValue * value, GParamSpec * pspec)
233 {
234 GstGLFilterCube *filter = GST_GL_FILTER_CUBE (object);
235
236 switch (prop_id) {
237 case PROP_RED:
238 g_value_set_float (value, filter->red);
239 break;
240 case PROP_GREEN:
241 g_value_set_float (value, filter->green);
242 break;
243 case PROP_BLUE:
244 g_value_set_float (value, filter->blue);
245 break;
246 case PROP_FOVY:
247 g_value_set_double (value, filter->fovy);
248 break;
249 case PROP_ASPECT:
250 g_value_set_double (value, filter->aspect);
251 break;
252 case PROP_ZNEAR:
253 g_value_set_double (value, filter->znear);
254 break;
255 case PROP_ZFAR:
256 g_value_set_double (value, filter->zfar);
257 break;
258 default:
259 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
260 break;
261 }
262 }
263
264 static gboolean
gst_gl_filter_cube_set_caps(GstGLFilter * filter,GstCaps * incaps,GstCaps * outcaps)265 gst_gl_filter_cube_set_caps (GstGLFilter * filter, GstCaps * incaps,
266 GstCaps * outcaps)
267 {
268 GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter);
269
270 if (cube_filter->aspect == 0)
271 cube_filter->aspect = (gdouble) GST_VIDEO_INFO_WIDTH (&filter->out_info) /
272 (gdouble) GST_VIDEO_INFO_HEIGHT (&filter->out_info);
273
274 return TRUE;
275 }
276
277 static void
gst_gl_filter_cube_gl_stop(GstGLBaseFilter * base_filter)278 gst_gl_filter_cube_gl_stop (GstGLBaseFilter * base_filter)
279 {
280 GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (base_filter);
281 const GstGLFuncs *gl = base_filter->context->gl_vtable;
282
283 if (cube_filter->vao) {
284 gl->DeleteVertexArrays (1, &cube_filter->vao);
285 cube_filter->vao = 0;
286 }
287
288 if (cube_filter->vertex_buffer) {
289 gl->DeleteBuffers (1, &cube_filter->vertex_buffer);
290 cube_filter->vertex_buffer = 0;
291 }
292
293 if (cube_filter->vbo_indices) {
294 gl->DeleteBuffers (1, &cube_filter->vbo_indices);
295 cube_filter->vbo_indices = 0;
296 }
297
298 if (cube_filter->shader) {
299 gst_object_unref (cube_filter->shader);
300 cube_filter->shader = NULL;
301 }
302
303 GST_GL_BASE_FILTER_CLASS (parent_class)->gl_stop (base_filter);
304 }
305
306 static gboolean
gst_gl_filter_cube_gl_start(GstGLBaseFilter * filter)307 gst_gl_filter_cube_gl_start (GstGLBaseFilter * filter)
308 {
309 GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter);
310 GstGLContext *context = GST_GL_BASE_FILTER (filter)->context;
311 gchar *frag_str;
312 gboolean ret;
313
314 cube_filter->xrot = 0.0;
315 cube_filter->yrot = 0.0;
316 cube_filter->zrot = 0.0;
317
318 frag_str =
319 g_strdup_printf ("%s%s",
320 gst_gl_shader_string_get_highest_precision (context,
321 GST_GLSL_VERSION_NONE,
322 GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY), cube_f_src);
323
324 /* blocking call, wait the opengl thread has compiled the shader */
325 ret = gst_gl_context_gen_shader (context, cube_v_src, frag_str,
326 &cube_filter->shader);
327 g_free (frag_str);
328
329 return ret;
330 }
331
332 static gboolean
gst_gl_filter_cube_filter_texture(GstGLFilter * filter,GstGLMemory * in_tex,GstGLMemory * out_tex)333 gst_gl_filter_cube_filter_texture (GstGLFilter * filter, GstGLMemory * in_tex,
334 GstGLMemory * out_tex)
335 {
336 GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter);
337
338 cube_filter->in_tex = in_tex;
339
340 return gst_gl_framebuffer_draw_to_texture (filter->fbo, out_tex, _callback,
341 cube_filter);
342 }
343
344 /* *INDENT-OFF* */
345 static const GLfloat vertices[] = {
346 /*| Vertex | TexCoord |*/
347 /* front face */
348 1.0, 1.0, -1.0, 1.0, 0.0,
349 1.0, -1.0, -1.0, 1.0, 1.0,
350 -1.0, -1.0, -1.0, 0.0, 1.0,
351 -1.0, 1.0, -1.0, 0.0, 0.0,
352 /* back face */
353 1.0, 1.0, 1.0, 1.0, 0.0,
354 -1.0, 1.0, 1.0, 0.0, 0.0,
355 -1.0, -1.0, 1.0, 0.0, 1.0,
356 1.0, -1.0, 1.0, 1.0, 1.0,
357 /* right face */
358 1.0, 1.0, 1.0, 1.0, 0.0,
359 1.0, -1.0, 1.0, 0.0, 0.0,
360 1.0, -1.0, -1.0, 0.0, 1.0,
361 1.0, 1.0, -1.0, 1.0, 1.0,
362 /* left face */
363 -1.0, 1.0, 1.0, 1.0, 0.0,
364 -1.0, 1.0, -1.0, 1.0, 1.0,
365 -1.0, -1.0, -1.0, 0.0, 1.0,
366 -1.0, -1.0, 1.0, 0.0, 0.0,
367 /* top face */
368 1.0, -1.0, 1.0, 1.0, 0.0,
369 -1.0, -1.0, 1.0, 0.0, 0.0,
370 -1.0, -1.0, -1.0, 0.0, 1.0,
371 1.0, -1.0, -1.0, 1.0, 1.0,
372 /* bottom face */
373 1.0, 1.0, 1.0, 1.0, 0.0,
374 1.0, 1.0, -1.0, 1.0, 1.0,
375 -1.0, 1.0, -1.0, 0.0, 1.0,
376 -1.0, 1.0, 1.0, 0.0, 0.0
377 };
378
379 static const GLushort indices[] = {
380 0, 1, 2,
381 0, 2, 3,
382 4, 5, 6,
383 4, 6, 7,
384 8, 9, 10,
385 8, 10, 11,
386 12, 13, 14,
387 12, 14, 15,
388 16, 17, 18,
389 16, 18, 19,
390 20, 21, 22,
391 20, 22, 23
392 };
393 /* *INDENT-ON* */
394
395 static void
_bind_buffer(GstGLFilterCube * cube_filter)396 _bind_buffer (GstGLFilterCube * cube_filter)
397 {
398 const GstGLFuncs *gl = GST_GL_BASE_FILTER (cube_filter)->context->gl_vtable;
399
400 gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, cube_filter->vbo_indices);
401 gl->BindBuffer (GL_ARRAY_BUFFER, cube_filter->vertex_buffer);
402
403 cube_filter->attr_position =
404 gst_gl_shader_get_attribute_location (cube_filter->shader, "a_position");
405
406 cube_filter->attr_texture =
407 gst_gl_shader_get_attribute_location (cube_filter->shader, "a_texcoord");
408
409 /* Load the vertex position */
410 gl->VertexAttribPointer (cube_filter->attr_position, 3, GL_FLOAT, GL_FALSE,
411 5 * sizeof (GLfloat), (void *) 0);
412
413 /* Load the texture coordinate */
414 gl->VertexAttribPointer (cube_filter->attr_texture, 2, GL_FLOAT, GL_FALSE,
415 5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
416
417 gl->EnableVertexAttribArray (cube_filter->attr_position);
418 gl->EnableVertexAttribArray (cube_filter->attr_texture);
419 }
420
421 static void
_unbind_buffer(GstGLFilterCube * cube_filter)422 _unbind_buffer (GstGLFilterCube * cube_filter)
423 {
424 const GstGLFuncs *gl = GST_GL_BASE_FILTER (cube_filter)->context->gl_vtable;
425
426 gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
427 gl->BindBuffer (GL_ARRAY_BUFFER, 0);
428
429 gl->DisableVertexAttribArray (cube_filter->attr_position);
430 gl->DisableVertexAttribArray (cube_filter->attr_texture);
431 }
432
433 static gboolean
_callback(gpointer stuff)434 _callback (gpointer stuff)
435 {
436 GstGLFilter *filter = GST_GL_FILTER (stuff);
437 GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter);
438 GstGLFuncs *gl = GST_GL_BASE_FILTER (filter)->context->gl_vtable;
439
440 const GLfloat matrix[] = {
441 0.5f, 0.0f, 0.0f, 0.0f,
442 0.0f, 0.5f, 0.0f, 0.0f,
443 0.0f, 0.0f, 0.5f, 0.0f,
444 0.0f, 0.0f, 0.0f, 1.0f
445 };
446
447 gl->Enable (GL_DEPTH_TEST);
448
449 gl->ClearColor (cube_filter->red, cube_filter->green, cube_filter->blue, 0.0);
450 gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
451
452 gst_gl_shader_use (cube_filter->shader);
453
454 gl->ActiveTexture (GL_TEXTURE0);
455 gl->BindTexture (GL_TEXTURE_2D, cube_filter->in_tex->tex_id);
456 gst_gl_shader_set_uniform_1i (cube_filter->shader, "s_texture", 0);
457 gst_gl_shader_set_uniform_1f (cube_filter->shader, "xrot_degree",
458 cube_filter->xrot);
459 gst_gl_shader_set_uniform_1f (cube_filter->shader, "yrot_degree",
460 cube_filter->yrot);
461 gst_gl_shader_set_uniform_1f (cube_filter->shader, "zrot_degree",
462 cube_filter->zrot);
463 gst_gl_shader_set_uniform_matrix_4fv (cube_filter->shader, "u_matrix", 1,
464 GL_FALSE, matrix);
465
466 if (!cube_filter->vertex_buffer) {
467 if (gl->GenVertexArrays) {
468 gl->GenVertexArrays (1, &cube_filter->vao);
469 gl->BindVertexArray (cube_filter->vao);
470 }
471
472 gl->GenBuffers (1, &cube_filter->vertex_buffer);
473 gl->BindBuffer (GL_ARRAY_BUFFER, cube_filter->vertex_buffer);
474 gl->BufferData (GL_ARRAY_BUFFER, 6 * 4 * 5 * sizeof (GLfloat), vertices,
475 GL_STATIC_DRAW);
476
477 gl->GenBuffers (1, &cube_filter->vbo_indices);
478 gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, cube_filter->vbo_indices);
479 gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indices), indices,
480 GL_STATIC_DRAW);
481
482 if (gl->GenVertexArrays) {
483 _bind_buffer (cube_filter);
484 gl->BindVertexArray (0);
485 }
486
487 gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
488 gl->BindBuffer (GL_ARRAY_BUFFER, 0);
489 }
490
491 if (gl->GenVertexArrays)
492 gl->BindVertexArray (cube_filter->vao);
493 _bind_buffer (cube_filter);
494
495 gl->DrawElements (GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);
496
497 if (gl->GenVertexArrays)
498 gl->BindVertexArray (0);
499 else
500 _unbind_buffer (cube_filter);
501
502 gl->Disable (GL_DEPTH_TEST);
503
504 cube_filter->xrot += 0.3f;
505 cube_filter->yrot += 0.2f;
506 cube_filter->zrot += 0.4f;
507
508 return TRUE;
509 }
510