From a884d6feee4bd2ee377ecdc5458a90e9f707b7bf Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Sun, 16 Mar 2014 11:23:16 +0100 Subject: [PATCH] move gl elements to ext subdirectory --- ext/gl/BUGS | 3 + ext/gl/Makefile.am | 96 +++ ext/gl/effects/gstgleffectbulge.c | 75 ++ ext/gl/effects/gstgleffectfisheye.c | 75 ++ ext/gl/effects/gstgleffectglow.c | 223 ++++++ ext/gl/effects/gstgleffectidentity.c | 95 +++ ext/gl/effects/gstgleffectlumatocurve.c | 148 ++++ ext/gl/effects/gstgleffectlumatocurve.h | 35 + ext/gl/effects/gstgleffectmirror.c | 120 +++ ext/gl/effects/gstgleffectrgbtocurve.c | 110 +++ ext/gl/effects/gstgleffectscurves.h | 212 ++++++ ext/gl/effects/gstgleffectsin.c | 72 ++ ext/gl/effects/gstgleffectsquare.c | 75 ++ ext/gl/effects/gstgleffectsqueeze.c | 119 +++ ext/gl/effects/gstgleffectssources.c | 481 ++++++++++++ ext/gl/effects/gstgleffectssources.h | 58 ++ ext/gl/effects/gstgleffectstretch.c | 75 ++ ext/gl/effects/gstgleffecttunnel.c | 75 ++ ext/gl/effects/gstgleffecttwirl.c | 75 ++ ext/gl/effects/gstgleffectxray.c | 380 ++++++++++ ext/gl/gltestsrc.c | 507 +++++++++++++ ext/gl/gltestsrc.h | 58 ++ ext/gl/gstglbumper.c | 546 ++++++++++++++ ext/gl/gstglbumper.h | 57 ++ ext/gl/gstglcolorscale.c | 156 ++++ ext/gl/gstglcolorscale.h | 56 ++ ext/gl/gstgldeinterlace.c | 351 +++++++++ ext/gl/gstgldeinterlace.h | 55 ++ ext/gl/gstgldifferencematte.c | 581 +++++++++++++++ ext/gl/gstgldifferencematte.h | 59 ++ ext/gl/gstgleffects.c | 398 ++++++++++ ext/gl/gstgleffects.h | 105 +++ ext/gl/gstglfilterapp.c | 214 ++++++ ext/gl/gstglfilterapp.h | 55 ++ ext/gl/gstglfilterblur.c | 257 +++++++ ext/gl/gstglfilterblur.h | 53 ++ ext/gl/gstglfiltercube.c | 561 ++++++++++++++ ext/gl/gstglfiltercube.h | 65 ++ ext/gl/gstglfilterglass.c | 406 +++++++++++ ext/gl/gstglfilterglass.h | 56 ++ ext/gl/gstglfilterlaplacian.c | 222 ++++++ ext/gl/gstglfilterlaplacian.h | 53 ++ ext/gl/gstglfilterreflectedscreen.c | 482 ++++++++++++ ext/gl/gstglfilterreflectedscreen.h | 61 ++ ext/gl/gstglfiltershader.c | 382 ++++++++++ ext/gl/gstglfiltershader.h | 55 ++ ext/gl/gstglfiltersobel.c | 269 +++++++ ext/gl/gstglfiltersobel.h | 56 ++ ext/gl/gstglimagesink.c | 1206 +++++++++++++++++++++++++++++++ ext/gl/gstglimagesink.h | 102 +++ ext/gl/gstglmosaic.c | 365 ++++++++++ ext/gl/gstglmosaic.h | 55 ++ ext/gl/gstgloverlay.c | 809 +++++++++++++++++++++ ext/gl/gstgloverlay.h | 80 ++ ext/gl/gstgltestsrc.c | 708 ++++++++++++++++++ ext/gl/gstgltestsrc.h | 125 ++++ ext/gl/gstglvideomixer.c | 294 ++++++++ ext/gl/gstglvideomixer.h | 55 ++ ext/gl/gstopengl.c | 212 ++++++ 59 files changed, 12789 insertions(+) create mode 100644 ext/gl/BUGS create mode 100644 ext/gl/Makefile.am create mode 100644 ext/gl/effects/gstgleffectbulge.c create mode 100644 ext/gl/effects/gstgleffectfisheye.c create mode 100644 ext/gl/effects/gstgleffectglow.c create mode 100644 ext/gl/effects/gstgleffectidentity.c create mode 100644 ext/gl/effects/gstgleffectlumatocurve.c create mode 100644 ext/gl/effects/gstgleffectlumatocurve.h create mode 100644 ext/gl/effects/gstgleffectmirror.c create mode 100644 ext/gl/effects/gstgleffectrgbtocurve.c create mode 100644 ext/gl/effects/gstgleffectscurves.h create mode 100644 ext/gl/effects/gstgleffectsin.c create mode 100644 ext/gl/effects/gstgleffectsquare.c create mode 100644 ext/gl/effects/gstgleffectsqueeze.c create mode 100644 ext/gl/effects/gstgleffectssources.c create mode 100644 ext/gl/effects/gstgleffectssources.h create mode 100644 ext/gl/effects/gstgleffectstretch.c create mode 100644 ext/gl/effects/gstgleffecttunnel.c create mode 100644 ext/gl/effects/gstgleffecttwirl.c create mode 100644 ext/gl/effects/gstgleffectxray.c create mode 100644 ext/gl/gltestsrc.c create mode 100644 ext/gl/gltestsrc.h create mode 100644 ext/gl/gstglbumper.c create mode 100644 ext/gl/gstglbumper.h create mode 100644 ext/gl/gstglcolorscale.c create mode 100644 ext/gl/gstglcolorscale.h create mode 100644 ext/gl/gstgldeinterlace.c create mode 100644 ext/gl/gstgldeinterlace.h create mode 100644 ext/gl/gstgldifferencematte.c create mode 100644 ext/gl/gstgldifferencematte.h create mode 100644 ext/gl/gstgleffects.c create mode 100644 ext/gl/gstgleffects.h create mode 100644 ext/gl/gstglfilterapp.c create mode 100644 ext/gl/gstglfilterapp.h create mode 100644 ext/gl/gstglfilterblur.c create mode 100644 ext/gl/gstglfilterblur.h create mode 100644 ext/gl/gstglfiltercube.c create mode 100644 ext/gl/gstglfiltercube.h create mode 100644 ext/gl/gstglfilterglass.c create mode 100644 ext/gl/gstglfilterglass.h create mode 100644 ext/gl/gstglfilterlaplacian.c create mode 100644 ext/gl/gstglfilterlaplacian.h create mode 100644 ext/gl/gstglfilterreflectedscreen.c create mode 100644 ext/gl/gstglfilterreflectedscreen.h create mode 100644 ext/gl/gstglfiltershader.c create mode 100644 ext/gl/gstglfiltershader.h create mode 100644 ext/gl/gstglfiltersobel.c create mode 100644 ext/gl/gstglfiltersobel.h create mode 100644 ext/gl/gstglimagesink.c create mode 100644 ext/gl/gstglimagesink.h create mode 100644 ext/gl/gstglmosaic.c create mode 100644 ext/gl/gstglmosaic.h create mode 100644 ext/gl/gstgloverlay.c create mode 100644 ext/gl/gstgloverlay.h create mode 100644 ext/gl/gstgltestsrc.c create mode 100644 ext/gl/gstgltestsrc.h create mode 100644 ext/gl/gstglvideomixer.c create mode 100644 ext/gl/gstglvideomixer.h create mode 100644 ext/gl/gstopengl.c diff --git a/ext/gl/BUGS b/ext/gl/BUGS new file mode 100644 index 0000000..7110c92 --- /dev/null +++ b/ext/gl/BUGS @@ -0,0 +1,3 @@ + +known issues: + diff --git a/ext/gl/Makefile.am b/ext/gl/Makefile.am new file mode 100644 index 0000000..d87f881 --- /dev/null +++ b/ext/gl/Makefile.am @@ -0,0 +1,96 @@ + +plugin_LTLIBRARIES = libgstopengl.la + +AM_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) +AM_LIBS = $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) + +# full opengl required +if USE_OPENGL +OPENGL_SOURCES = \ + gstglfiltershader.c \ + gstglfiltershader.h \ + gstglfilterblur.c \ + gstglfilterblur.h \ + gstglfiltersobel.c \ + gstglfiltersobel.h \ + gstglfilterlaplacian.c \ + gstglfilterlaplacian.h \ + gstglfilterglass.c \ + gstglfilterglass.h \ + gstglfilterapp.c \ + gstglfilterapp.h \ + gstglfilterreflectedscreen.c \ + gstglfilterreflectedscreen.h \ + gstgldeinterlace.c \ + gstgldeinterlace.h \ + gltestsrc.c \ + gltestsrc.h \ + gstgltestsrc.c \ + gstgltestsrc.h \ + gstglmosaic.c \ + gstglmosaic.h \ + gstglvideomixer.c \ + gstglvideomixer.h \ + effects/gstgleffectscurves.h \ + effects/gstgleffectstretch.c \ + effects/gstgleffecttunnel.c \ + effects/gstgleffectfisheye.c \ + effects/gstgleffecttwirl.c \ + effects/gstgleffectbulge.c \ + effects/gstgleffectsquare.c \ + effects/gstgleffectlumatocurve.c \ + effects/gstgleffectlumatocurve.h \ + effects/gstgleffectrgbtocurve.c \ + effects/gstgleffectsin.c \ + effects/gstgleffectglow.c \ + effects/gstgleffectxray.c + +if HAVE_PNG +OPENGL_SOURCES += \ + gstglbumper.c \ + gstglbumper.h \ + gstgldifferencematte.c \ + gstgldifferencematte.h +if HAVE_JPEG +OPENGL_SOURCES += \ + gstgloverlay.c \ + gstgloverlay.h +endif +endif +endif + +libgstopengl_la_SOURCES = \ + gstopengl.c \ + gstglimagesink.c \ + gstglimagesink.h \ + gstglfiltercube.c \ + gstglfiltercube.h \ + gstgleffects.c \ + gstgleffects.h \ + effects/gstgleffectssources.c \ + effects/gstgleffectssources.h \ + effects/gstgleffectidentity.c \ + effects/gstgleffectmirror.c \ + effects/gstgleffectsqueeze.c \ + gstglcolorscale.c \ + gstglcolorscale.h \ + $(OPENGL_SOURCES) + +# check order of CFLAGS and LIBS, shouldn't the order be the other way around +# (like in AM_CFLAGS)? +libgstopengl_la_CFLAGS = -I$(top_srcdir)/gst-libs $(GST_CFLAGS) $(GST_BASE_CFLAGS) \ + $(GST_PLUGINS_BASE_CFLAGS) $(GL_CFLAGS) $(LIBPNG_CFLAGS) + +libgstopengl_la_LIBADD = \ + $(top_builddir)/gst-libs/gst/gl/libgstgl-$(GST_API_VERSION).la \ + $(GST_BASE_LIBS) \ + $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_API_VERSION) \ + -lgstpbutils-$(GST_API_VERSION) \ + $(GL_LIBS) \ + $(LIBPNG_LIBS) \ + $(JPEG_LIBS) \ + $(LIBM) + +libgstopengl_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstopengl_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + diff --git a/ext/gl/effects/gstgleffectbulge.c b/ext/gl/effects/gstgleffectbulge.c new file mode 100644 index 0000000..b0c1b41 --- /dev/null +++ b/ext/gl/effects/gstgleffectbulge.c @@ -0,0 +1,75 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../gstgleffects.h" + +static void +gst_gl_effects_bulge_callback (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "bulge0"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "bulge0", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + bulge_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, "Failed to initialize bulge shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gst_gl_shader_set_uniform_1i (shader, "tex", 0); + + gst_gl_shader_set_uniform_1f (shader, "width", (gfloat) width / 2.0f); + gst_gl_shader_set_uniform_1f (shader, "height", (gfloat) height / 2.0f); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +void +gst_gl_effects_bulge (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->outtexture, gst_gl_effects_bulge_callback, effects); +} diff --git a/ext/gl/effects/gstgleffectfisheye.c b/ext/gl/effects/gstgleffectfisheye.c new file mode 100644 index 0000000..4ee6416 --- /dev/null +++ b/ext/gl/effects/gstgleffectfisheye.c @@ -0,0 +1,75 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../gstgleffects.h" + +static void +gst_gl_effects_fisheye_callback (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "fisheye0"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "fisheye0", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + fisheye_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, "Failed to initialize fisheye shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gst_gl_shader_set_uniform_1i (shader, "tex", 0); + + gst_gl_shader_set_uniform_1f (shader, "width", (gfloat) width / 2.0f); + gst_gl_shader_set_uniform_1f (shader, "height", (gfloat) height / 2.0f); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +void +gst_gl_effects_fisheye (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->outtexture, gst_gl_effects_fisheye_callback, effects); +} diff --git a/ext/gl/effects/gstgleffectglow.c b/ext/gl/effects/gstgleffectglow.c new file mode 100644 index 0000000..b79f54f --- /dev/null +++ b/ext/gl/effects/gstgleffectglow.c @@ -0,0 +1,223 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../gstgleffects.h" + +static gboolean kernel_ready = FALSE; +static float gauss_kernel[7]; + +static void +gst_gl_effects_glow_step_one (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "glow0"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "glow0", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + luma_threshold_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, + "Failed to initialize luma threshold shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gst_gl_shader_set_uniform_1i (shader, "tex", 0); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +static void +gst_gl_effects_glow_step_two (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "glow1"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "glow1", shader); + } + + if (!kernel_ready) { + fill_gaussian_kernel (gauss_kernel, 7, 10.0); + kernel_ready = TRUE; + } + + if (!gst_gl_shader_compile_and_check (shader, + hconv7_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, "Failed to initialize hconv7 shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (shader, "tex", 1); + gst_gl_shader_set_uniform_1fv (shader, "kernel", 7, gauss_kernel); + gst_gl_shader_set_uniform_1f (shader, "height", height); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +void +gst_gl_effects_glow_step_three (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "glow2"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "glow2", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + vconv7_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, "Failed to initialize vcon7 shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (shader, "tex", 1); + gst_gl_shader_set_uniform_1fv (shader, "kernel", 7, gauss_kernel); + gst_gl_shader_set_uniform_1f (shader, "width", width); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +void +gst_gl_effects_glow_step_four (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "glow3"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "glow3", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + sum_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, "Failed to initialize sum shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE2); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, effects->intexture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1f (shader, "alpha", 1.0); + gst_gl_shader_set_uniform_1i (shader, "base", 2); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1f (shader, "beta", (gfloat) 1 / 3.5f); + gst_gl_shader_set_uniform_1i (shader, "blend", 1); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +void +gst_gl_effects_glow (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + /* threshold */ + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->midtexture[0], gst_gl_effects_glow_step_one, effects); + /* blur */ + gst_gl_filter_render_to_target (filter, FALSE, effects->midtexture[0], + effects->midtexture[1], gst_gl_effects_glow_step_two, effects); + gst_gl_filter_render_to_target (filter, FALSE, effects->midtexture[1], + effects->midtexture[2], gst_gl_effects_glow_step_three, effects); + /* add blurred luma to intexture */ + gst_gl_filter_render_to_target (filter, FALSE, effects->midtexture[2], + effects->outtexture, gst_gl_effects_glow_step_four, effects); +} diff --git a/ext/gl/effects/gstgleffectidentity.c b/ext/gl/effects/gstgleffectidentity.c new file mode 100644 index 0000000..9814fec --- /dev/null +++ b/ext/gl/effects/gstgleffectidentity.c @@ -0,0 +1,95 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../gstgleffects.h" + +#define USING_OPENGL(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL) +#define USING_OPENGL3(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL3) +#define USING_GLES(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES) +#define USING_GLES2(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES2) +#define USING_GLES3(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES3) + +static void +gst_gl_effects_identity_callback (gint width, gint height, guint texture, + gpointer data) +{ + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + +#if GST_GL_HAVE_OPENGL + if (USING_OPENGL (context)) { + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + } +#endif +#if GST_GL_HAVE_GLES2 + if (USING_GLES2 (context)) { + GstGLShader *shader = + g_hash_table_lookup (effects->shaderstable, "identity0"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "identity0", shader); + + if (shader) { + GError *error = NULL; + gst_gl_shader_set_vertex_source (shader, vertex_shader_source); + gst_gl_shader_set_fragment_source (shader, identity_fragment_source); + + gst_gl_shader_compile (shader, &error); + if (error) { + GST_ERROR ("%s", error->message); + g_error_free (error); + error = NULL; + gst_gl_shader_use (NULL); + } else { + filter->draw_attr_position_loc = + gst_gl_shader_get_attribute_location (shader, "a_position"); + filter->draw_attr_texture_loc = + gst_gl_shader_get_attribute_location (shader, "a_texCoord"); + } + } + } + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gst_gl_shader_set_uniform_1i (shader, "tex", 0); + } +#endif + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +void +gst_gl_effects_identity (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->outtexture, gst_gl_effects_identity_callback, effects); +} diff --git a/ext/gl/effects/gstgleffectlumatocurve.c b/ext/gl/effects/gstgleffectlumatocurve.c new file mode 100644 index 0000000..e45a81b --- /dev/null +++ b/ext/gl/effects/gstgleffectlumatocurve.c @@ -0,0 +1,148 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../gstgleffects.h" +#include "gstgleffectlumatocurve.h" + +void +gst_gl_effects_luma_to_curve (GstGLEffects * effects, + GstGLEffectsCurve curve, + gint curve_index, gint width, gint height, GLuint texture) +{ + GstGLShader *shader; + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "lumamap0"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "lumamap0", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + luma_to_curve_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, + "Failed to initialize luma to curve shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + if (effects->curve[curve_index] == 0) { + /* this parameters are needed to have a right, predictable, mapping */ + gl->GenTextures (1, &effects->curve[curve_index]); + gl->Enable (GL_TEXTURE_1D); + gl->BindTexture (GL_TEXTURE_1D, effects->curve[curve_index]); + gl->TexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl->TexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl->TexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP); + gl->TexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + gl->TexImage1D (GL_TEXTURE_1D, 0, curve.bytes_per_pixel, + curve.width, 0, GL_RGB, GL_UNSIGNED_BYTE, curve.pixel_data); + + gl->Disable (GL_TEXTURE_1D); + } + + gl->ActiveTexture (GL_TEXTURE2); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gst_gl_shader_set_uniform_1i (shader, "tex", 2); + + gl->Disable (GL_TEXTURE_2D); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_1D); + gl->BindTexture (GL_TEXTURE_1D, effects->curve[curve_index]); + + gst_gl_shader_set_uniform_1i (shader, "curve", 1); + + gl->Disable (GL_TEXTURE_1D); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +static void +gst_gl_effects_heat_callback (gint width, gint height, guint texture, + gpointer data) +{ + GstGLEffects *effects = GST_GL_EFFECTS (data); + + gst_gl_effects_luma_to_curve (effects, heat_curve, GST_GL_EFFECTS_CURVE_HEAT, + width, height, texture); +} + +void +gst_gl_effects_heat (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->outtexture, gst_gl_effects_heat_callback, effects); +} + +static void +gst_gl_effects_sepia_callback (gint width, gint height, guint texture, + gpointer data) +{ + GstGLEffects *effects = GST_GL_EFFECTS (data); + + gst_gl_effects_luma_to_curve (effects, sepia_curve, + GST_GL_EFFECTS_CURVE_SEPIA, width, height, texture); +} + +void +gst_gl_effects_sepia (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->outtexture, gst_gl_effects_sepia_callback, effects); +} + +static void +gst_gl_effects_luma_xpro_callback (gint width, gint height, guint texture, + gpointer data) +{ + GstGLEffects *effects = GST_GL_EFFECTS (data); + + gst_gl_effects_luma_to_curve (effects, luma_xpro_curve, + GST_GL_EFFECTS_CURVE_LUMA_XPRO, width, height, texture); +} + +void +gst_gl_effects_luma_xpro (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->outtexture, gst_gl_effects_luma_xpro_callback, effects); +} diff --git a/ext/gl/effects/gstgleffectlumatocurve.h b/ext/gl/effects/gstgleffectlumatocurve.h new file mode 100644 index 0000000..1260416 --- /dev/null +++ b/ext/gl/effects/gstgleffectlumatocurve.h @@ -0,0 +1,35 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_GL_LUMA_TO_CURVE_H__ +#define __GST_GL_LUMA_TO_CURVE_H__ + +#include "gstgleffectscurves.h" + +G_BEGIN_DECLS + +void gst_gl_effects_luma_to_curve (GstGLEffects *effects, + GstGLEffectsCurve curve, + gint curve_index, + gint width, gint height, + GLuint texture); +G_END_DECLS + +#endif /* __GST_GL_LUMA_TO_CURVE_H__ */ diff --git a/ext/gl/effects/gstgleffectmirror.c b/ext/gl/effects/gstgleffectmirror.c new file mode 100644 index 0000000..5d77fa9 --- /dev/null +++ b/ext/gl/effects/gstgleffectmirror.c @@ -0,0 +1,120 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../gstgleffects.h" + +#define USING_OPENGL(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL) +#define USING_OPENGL3(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL3) +#define USING_GLES(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES) +#define USING_GLES2(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES2) +#define USING_GLES3(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES3) + +static void +gst_gl_effects_mirror_callback (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLFilter *filter = GST_GL_FILTER (data); + GstGLEffects *effects = GST_GL_EFFECTS (filter); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "mirror0"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "mirror0", shader); + +#if GST_GL_HAVE_GLES2 + if (USING_GLES2 (context)) { + if (shader) { + GError *error = NULL; + gst_gl_shader_set_vertex_source (shader, vertex_shader_source); + gst_gl_shader_set_fragment_source (shader, + mirror_fragment_source_gles2); + + gst_gl_shader_compile (shader, &error); + if (error) { + gst_gl_context_set_error (context, + "Failed to initialize mirror shader, %s", error->message); + g_error_free (error); + error = NULL; + gst_gl_shader_use (NULL); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + } else { + filter->draw_attr_position_loc = + gst_gl_shader_get_attribute_location (shader, "a_position"); + filter->draw_attr_texture_loc = + gst_gl_shader_get_attribute_location (shader, "a_texCoord"); + } + } + } +#endif +#if GST_GL_HAVE_OPENGL + if (USING_OPENGL (context)) { + if (!gst_gl_shader_compile_and_check (shader, + mirror_fragment_source_opengl, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, + "Failed to initialize mirror shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + } +#endif + } +#if GST_GL_HAVE_OPENGL + if (USING_OPENGL (context)) { + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + } +#endif + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gst_gl_shader_set_uniform_1i (shader, "tex", 0); + +#if GST_GL_HAVE_OPENGL + if (USING_OPENGL (filter->context)) { + gst_gl_shader_set_uniform_1f (shader, "width", (gfloat) width / 2.0f); + gst_gl_shader_set_uniform_1f (shader, "height", (gfloat) height / 2.0f); + } +#endif + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +void +gst_gl_effects_mirror (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->outtexture, gst_gl_effects_mirror_callback, effects); +} diff --git a/ext/gl/effects/gstgleffectrgbtocurve.c b/ext/gl/effects/gstgleffectrgbtocurve.c new file mode 100644 index 0000000..293875f --- /dev/null +++ b/ext/gl/effects/gstgleffectrgbtocurve.c @@ -0,0 +1,110 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../gstgleffects.h" +#include "gstgleffectscurves.h" + +static void +gst_gl_effects_rgb_to_curve (GstGLEffects * effects, + GstGLEffectsCurve curve, + gint curve_index, gint width, gint height, GLuint texture) +{ + GstGLShader *shader; + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "rgbmap0"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "rgbmap0", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + rgb_to_curve_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, + "Failed to initialize rgb to curve shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + if (effects->curve[curve_index] == 0) { + /* this parameters are needed to have a right, predictable, mapping */ + gl->GenTextures (1, &effects->curve[curve_index]); + gl->Enable (GL_TEXTURE_1D); + gl->BindTexture (GL_TEXTURE_1D, effects->curve[curve_index]); + gl->TexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl->TexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl->TexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP); + gl->TexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + gl->TexImage1D (GL_TEXTURE_1D, 0, curve.bytes_per_pixel, + curve.width, 0, GL_RGB, GL_UNSIGNED_BYTE, curve.pixel_data); + + gl->Disable (GL_TEXTURE_1D); + } + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gst_gl_shader_set_uniform_1i (shader, "tex", 0); + + gl->Disable (GL_TEXTURE_2D); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_1D); + gl->BindTexture (GL_TEXTURE_1D, effects->curve[curve_index]); + + gst_gl_shader_set_uniform_1i (shader, "curve", 1); + + gl->Disable (GL_TEXTURE_1D); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +static void +gst_gl_effects_xpro_callback (gint width, gint height, guint texture, + gpointer data) +{ + GstGLEffects *effects = GST_GL_EFFECTS (data); + + gst_gl_effects_rgb_to_curve (effects, xpro_curve, GST_GL_EFFECTS_CURVE_XPRO, + width, height, texture); +} + +void +gst_gl_effects_xpro (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->outtexture, gst_gl_effects_xpro_callback, effects); +} diff --git a/ext/gl/effects/gstgleffectscurves.h b/ext/gl/effects/gstgleffectscurves.h new file mode 100644 index 0000000..e0e98a5 --- /dev/null +++ b/ext/gl/effects/gstgleffectscurves.h @@ -0,0 +1,212 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_GL_EFFECTS_TEXTURES_H__ +#define __GST_GL_EFFECTS_TEXTURES_H__ + + +struct _GstGLEffectsCurve { + guint width; + guint height; + guint bytes_per_pixel; /* 3:RGB */ + guint8 pixel_data[256 * 1 * 3 + 1]; +}; + +typedef struct _GstGLEffectsCurve GstGLEffectsCurve; + +/* CURVE for the heat signature effect */ +static const GstGLEffectsCurve xpro_curve = { + 256, 1, 3, + "\0\0\37\0\0\37\0\1\40\0\2!\0\2\"\0\3\"\1\4%\1\4%\1\5%\1\5'\1\7'\1\7(\1\7" + "(\1\10*\1\11+\1\11,\1\12,\1\13/\1\14/\1\14""1\2\15""1\2\15""1\2\16""4\2\17" + """4\3\17""5\3\22""7\3\22""7\3\23""8\3\24""9\3\25;\3\26;\3\27<\3\27=\4\31" + "=\4\33?\4\34@\5\34B\5\35C\5\36D\5\40D\5\40G\5!G\6\"H\6$H\7&J\7&K\7*M\7*M" + "\10+N\10-P\11-P\11/R\11""3R\11""3T\12""4U\12""5U\13""7W\14""8Y\14""9Y\14" + "\324S?\327P@\332LA\335IB\337FC\342CE\344@F\347=G\351;I\3538I\3558M\357" + "3P\3610S\363.V\365+Y\366)\\\370'`\371%d\372#g\373\"l\374\40p\374\37t\374" + "\35t\375\34}\376\33\202\376\32\202\375\31\213\375\30\220\375\27\225\375\27" + "\232\373\26\237\372\25\244\371\24\251\370\23\256\367\23\262\367\22\267\364" + "\21\274\362\20\300\361\20\305\357\17\311\355\16\311\353\16\322\351\15\326" + "\346\15\332\346\14\336\344\14\341\337\13\341\335\13\350\332\12\353\330\11" + "\356\330\11\360\322\10\362\320\10\364\320\10\364\312\7\366\307\7\366\304" + "\7\367\302\6\367\277\6\370\274\5\367\271\5\367\271\5\367\263\4\365\260\4" + "\364\255\4\363\253\3\362\250\3\361\245\3\360\242\3\357\240\3\357\235\2\355" + "\232\2\355\227\2\354\225\2\353\221\1\353\216\1\353\216\1\353\213\1\353\204" + "\1\353\201\1\354}\1\354y\0\354v\0\355r\0\355n\0\355j\0\356f\0\356b\0\357" + "_\0\357[\0\357W\0\357S\0\360O\0\360O\0\361K\0\361C\0\362@\0\363<\0\3638\0" + "\3648\0\3641\0\365.\0\366+\0\366'\0\367'\0\370!\0\370\36\0\370\33\0\371\30" + "\0\371\26\0\373\26\0\373\23\0\374\15\0\374\13\0\375\10\0\375\5\0\376\3\0", +}; + +static const GstGLEffectsCurve sepia_curve = { + 256, 1, 3, + "\0\0\0\0\0\0\0\0\0\0\1\0\1\1\0\1\1\0\1\1\1\2\1\1\2\2\1\3\2\1\3\2\1\3\2\1" + "\4\3\2\4\3\2\4\3\2\6\4\2\6\4\2\6\4\2\7\5\2\7\5\3\11\6\3\11\6\3\12\7\3\13" + "\10\3\15\10\4\16\11\4\17\11\4\21\12\4\22\13\4\22\13\5\23\14\5\24\15\5\26" + "\16\6\31\20\6\31\21\6\32\22\7\34\22\7\35\23\7\40\24\10\40\26\10!\26\11#\30" + "\11&\31\12&\32\12'\34\13)\34\13*\37\13,\37\13-\40\14.\"\15""0\"\15""2#\17" + """3&\17""4&\17""5'\20""8(\21""9)\21:*\23<,\23=-\23A.\24A0\25B0\25C2\26D3" + "\30H4\30H7\31K7\32K8\32L9\33M:\34P<\34Q=\35S>\37T?\37UA\40VB!XC!ZD#\\F#^" + "G#^J$`J&bK'bM'eM(fO)gP)iQ*kS,mT-mU-nV.oX/rY0sZ2u]2v]3w^3x`4za5{c7|c8~e8\177" + "f9\200i:\203i<\204j<\206k=\207m>\210n?\211o?\213qA\214rC\215sC\217uD\220" + "vD\221wF\223xG\224zH\225{J\227|K\230~K\231\177L\232\200M\234\202O\235\203" + "P\236\204Q\240\206Q\241\207S\242\210T\243\211U\245\213V\246\214X\247\215" + "Y\250\217Y\252\220Z\253\221\\\254\223]\254\224^\255\225`\257\227a\260\230" + "b\261\231c\262\232e\264\234e\265\235f\266\236g\267\240i\267\241i\272\242" + "k\273\243m\274\245n\274\246o\276\247q\277\250r\300\252s\301\253u\302\254" + "v\304\255w\305\257x\306\257z\306\261{\307\262|\310\264~\310\265\177\313\266" + "\200\314\267\202\315\267\203\316\272\204\317\273\206\317\274\207\320\276" + "\210\322\277\211\323\277\213\324\301\214\325\302\215\326\304\217\326\305" + "\220\327\306\221\327\307\223\331\310\224\333\311\225\334\311\227\334\313" + "\227\335\315\231\335\316\231\337\317\234\340\320\235\341\320\235\341\323" + "\240\342\324\241\343\324\242\343\326\243\345\327\245\345\330\245\346\331" + "\250\346\333\252\347\334\253\351\335\254\351\335\255\351\337\257\352\340" + "\260\353\341\260\354\342\262\355\343\264\355\344\265\355\345\266\356\346" + "\266\356\347\272\357\350\273\360\351\274\360\351\276\361\352\277\361\353" + "\300\362\353\301\362\354\302\362\355\304\362\356\305\364\357\305\364\357" + "\310\364\360\311\365\361\313\365\361\314\366\362\315\366\362\316\366\363" + "\316\367\364\320\367\364\320\367\365\324\367\365\324\370\366\326\370\366" + "\327\371\366\330\371\367\331\371\367\333\371\370\333\372\370\336\372\370" + "\336\372\371\340\373\371\341\373\372\342\373\372\343\374\372\344\374\373" + "\344\374\373\347\374\374\350\375\374\351\375\374\351\375\374\352\375\375" + "\352\376\375\353\376\376\355\376\376\356\376\376\357\377\377\357", +}; + +static const GstGLEffectsCurve xray_curve = { + 256, 1, 3, + "\377\377\377\377\377\377\376\376\376\375\375\376\374\375\375\373\374\375" + "\372\374\374\371\374\374\370\373\373\366\373\372\366\372\372\365\372\371" + "\363\371\371\363\371\370\362\370\370\360\370\367\360\367\366\357\367\365" + "\356\366\365\355\366\364\353\365\363\353\365\363\352\364\362\351\363\362" + "\347\363\361\346\362\361\345\362\361\344\362\360\343\361\357\343\361\356" + "\342\360\356\341\360\356\340\357\355\336\356\354\336\356\354\335\355\353" + "\334\355\353\333\355\352\331\354\351\331\353\351\330\353\350\327\353\350" + "\325\352\347\325\351\347\324\350\346\323\350\345\322\347\344\321\347\344" + "\320\347\344\317\346\343\316\346\342\315\345\341\314\344\341\313\344\340" + "\312\344\340\311\343\337\310\342\337\307\342\335\306\341\335\305\341\335" + "\303\340\334\303\337\333\302\337\333\301\337\332\300\336\331\276\335\331" + "\276\334\330\274\334\330\274\334\327\273\333\327\272\333\326\271\332\325" + "\270\332\325\267\331\324\266\330\323\265\330\323\264\327\322\263\327\321" + "\262\326\320\261\325\320\257\325\317\257\324\317\256\324\316\254\323\315" + "\254\322\315\253\322\314\252\321\313\251\321\313\250\320\312\246\317\311" + "\245\317\311\245\316\310\244\316\307\243\315\307\242\314\306\241\314\305" + "\240\312\305\237\312\304\236\312\303\235\311\303\234\311\302\233\307\301" + "\232\307\300\231\307\300\230\306\277\227\305\276\226\305\276\225\304\275" + "\224\303\274\223\303\273\222\302\273\221\301\272\220\301\271\217\300\270" + "\216\277\270\215\277\267\214\276\266\213\275\265\212\275\265\211\274\264" + "\210\273\263\207\273\262\206\272\262\205\271\261\204\270\260\203\270\257" + "\202\267\257\201\266\256\200\266\255\177\265\254~\264\253}\263\253|\263\252" + "{\262\251z\261\250y\260\247x\260\247w\257\246v\256\245u\255\244t\255\243" + "s\254\243r\253\242q\252\241p\252\240o\251\237n\250\236m\247\235l\246\235" + "l\246\235j\245\233i\244\232h\243\231g\242\230f\242\227e\241\226d\240\226" + "c\237\225b\236\224a\235\223`\234\222_\234\221_\233\220]\232\217\\\231\216" + "\\\230\215Z\227\214Y\226\214X\226\213W\225\212V\224\211U\223\210T\222\207" + "S\221\206R\221\205Q\217\204P\216\203O\215\202N\215\201M\214\200M\213\177" + "K\212~J\211}I\211|H\210|G\206zG\205zE\204xD\203vC\203vB\201tA\200s@\200q" + "@~p>}o>|o<{l5\31<2\31<0\27" + ":.\27""5,\26""3*\24""1*\23.&\22.&\22*\"\21'\40\17%\36\16\"\34\15\"\32\14" + "\36\32\13\33\26\13\31\24\11\26\22\11\24\20\7\24\16\6\21\16\5\14\14\4\12\10" + "\3\7\6\3\5\4\1\2\2", +}; + +#endif diff --git a/ext/gl/effects/gstgleffectsin.c b/ext/gl/effects/gstgleffectsin.c new file mode 100644 index 0000000..1b229d2 --- /dev/null +++ b/ext/gl/effects/gstgleffectsin.c @@ -0,0 +1,72 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../gstgleffects.h" + +static void +gst_gl_effects_sin_callback (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "sin0"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "sin0", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + sin_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, "Failed to initialize sin shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gst_gl_shader_set_uniform_1i (shader, "tex", 0); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +void +gst_gl_effects_sin (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->outtexture, gst_gl_effects_sin_callback, effects); +} diff --git a/ext/gl/effects/gstgleffectsquare.c b/ext/gl/effects/gstgleffectsquare.c new file mode 100644 index 0000000..127e39f --- /dev/null +++ b/ext/gl/effects/gstgleffectsquare.c @@ -0,0 +1,75 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../gstgleffects.h" + +static void +gst_gl_effects_square_callback (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "square0"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "square0", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + square_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, "Failed to initialize square shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gst_gl_shader_set_uniform_1i (shader, "tex", 0); + + gst_gl_shader_set_uniform_1f (shader, "width", (gfloat) width / 2.0f); + gst_gl_shader_set_uniform_1f (shader, "height", (gfloat) height / 2.0f); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +void +gst_gl_effects_square (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->outtexture, gst_gl_effects_square_callback, effects); +} diff --git a/ext/gl/effects/gstgleffectsqueeze.c b/ext/gl/effects/gstgleffectsqueeze.c new file mode 100644 index 0000000..14a9bcb --- /dev/null +++ b/ext/gl/effects/gstgleffectsqueeze.c @@ -0,0 +1,119 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../gstgleffects.h" + +#define USING_OPENGL(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL) +#define USING_OPENGL3(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL3) +#define USING_GLES(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES) +#define USING_GLES2(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES2) +#define USING_GLES3(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES3) + +static void +gst_gl_effects_squeeze_callback (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLFilter *filter = GST_GL_FILTER (data); + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "squeeze0"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "squeeze0", shader); + +#if GST_GL_HAVE_GLES2 + if (USING_GLES2 (context)) { + if (shader) { + GError *error = NULL; + gst_gl_shader_set_vertex_source (shader, vertex_shader_source); + gst_gl_shader_set_fragment_source (shader, + squeeze_fragment_source_gles2); + + gst_gl_shader_compile (shader, &error); + if (error) { + gst_gl_context_set_error (context, + "Failed to initialize squeeze shader, %s", error->message); + g_error_free (error); + error = NULL; + gst_gl_shader_use (NULL); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + } else { + filter->draw_attr_position_loc = + gst_gl_shader_get_attribute_location (shader, "a_position"); + filter->draw_attr_texture_loc = + gst_gl_shader_get_attribute_location (shader, "a_texCoord"); + } + } + } +#endif +#if GST_GL_HAVE_OPENGL + if (USING_OPENGL (context)) { + if (!gst_gl_shader_compile_and_check (shader, + squeeze_fragment_source_opengl, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, + "Failed to initialize squeeze shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + } +#endif + } +#if GST_GL_HAVE_OPENGL + if (USING_OPENGL (context)) { + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + } +#endif + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gst_gl_shader_set_uniform_1i (shader, "tex", 0); + +#if GST_GL_HAVE_OPENGL + if (USING_OPENGL (filter->context)) { + gst_gl_shader_set_uniform_1f (shader, "width", (gfloat) width / 2.0f); + gst_gl_shader_set_uniform_1f (shader, "height", (gfloat) height / 2.0f); + } +#endif + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +void +gst_gl_effects_squeeze (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->outtexture, gst_gl_effects_squeeze_callback, effects); +} diff --git a/ext/gl/effects/gstgleffectssources.c b/ext/gl/effects/gstgleffectssources.c new file mode 100644 index 0000000..eadcccd --- /dev/null +++ b/ext/gl/effects/gstgleffectssources.c @@ -0,0 +1,481 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "../gstgleffects.h" +#include "gstgleffectssources.h" +#include + +/* A common file for sources is needed since shader sources can be + * generic and reused by several effects */ + +/* FIXME */ +/* Move sooner or later into single .frag .vert files and either bake + * them into a c file at compile time or load them at run time */ + + +/* fill a normalized and zero centered gaussian vector for separable + * gaussian convolution */ + +void +fill_gaussian_kernel (float *kernel, int size, float sigma) +{ + int i; + float sum; + int l; + + /* need an odd sized vector to center it at zero */ + g_return_if_fail ((size % 2) != 0); + + sum = 0.0; + l = (size - 1) / 2; + + for (i = 0; i < size; i++) { + kernel[i] = expf (-0.5 * pow ((i - l) / sigma, 2.0)); + sum += kernel[i]; + } + + for (i = 0; i < size; i++) { + kernel[i] /= sum; + } +} + +/* *INDENT-OFF* */ + +/* Vertex shader */ +const gchar *vertex_shader_source = + "attribute vec4 a_position;" + "attribute vec2 a_texCoord;" + "varying vec2 v_texCoord;" + "void main()" + "{" + " gl_Position = a_position;" + " v_texCoord = a_texCoord;" + "}"; + +/* Identity effect */ +const gchar *identity_fragment_source = + "precision mediump float;" + "varying vec2 v_texCoord;" + "uniform sampler2D tex;" + "void main()" + "{" + " gl_FragColor = texture2D(tex, v_texCoord);" + "}"; + +/* Mirror effect */ +#if GST_GL_HAVE_OPENGL +const gchar *mirror_fragment_source_opengl = + "uniform sampler2D tex;" + "void main () {" + " vec2 texturecoord = gl_TexCoord[0].xy;" + " vec2 normcoord;" + " normcoord = texturecoord - 0.5;" + " normcoord.x *= sign (normcoord.x);" + " texturecoord = normcoord + 0.5;" + " vec4 color = texture2D (tex, texturecoord);" + " gl_FragColor = color * gl_Color;" + "}"; +#endif +#if GST_GL_HAVE_GLES2 +const gchar *mirror_fragment_source_gles2 = + "precision mediump float;" + "varying vec2 v_texCoord;" + "uniform sampler2D tex;" + "void main () {" + " vec2 texturecoord = v_texCoord.xy;" + " float normcoord = texturecoord.x - 0.5;" + " normcoord *= sign (normcoord);" + " texturecoord.x = normcoord + 0.5;" + " gl_FragColor = texture2D (tex, texturecoord);" + "}"; +#endif + +/* Squeeze effect */ +#if GST_GL_HAVE_OPENGL +const gchar *squeeze_fragment_source_opengl = + "uniform sampler2D tex;" + "void main () {" + " vec2 texturecoord = gl_TexCoord[0].xy;" + " vec2 normcoord = texturecoord - 0.5;" + " float r = length (normcoord);" + " r = pow(r, 0.40)*1.3;" + " normcoord = normcoord / r;" + " texturecoord = (normcoord + 0.5);" + " gl_FragColor = texture2D (tex, texturecoord);" + "}"; +#endif +#if GST_GL_HAVE_GLES2 +const gchar *squeeze_fragment_source_gles2 = + "precision mediump float;" + "varying vec2 v_texCoord;" + "uniform sampler2D tex;" + "void main () {" + " vec2 texturecoord = v_texCoord.xy;" + " vec2 normcoord = texturecoord - 0.5;" + " float r = length (normcoord);" + " r = pow(r, 0.40)*1.3;" + " normcoord = normcoord / r;" + " texturecoord = (normcoord + 0.5);" + " gl_FragColor = texture2D (tex, texturecoord);" + "}"; +#endif + +/* Stretch Effect */ +const gchar *stretch_fragment_source = + "uniform sampler2D tex;" + "void main () {" + " vec2 texturecoord = gl_TexCoord[0].xy;" + " vec2 normcoord;" + " normcoord = texturecoord - 0.5;" + " float r = length (normcoord);" + " normcoord *= 2.0 - smoothstep(0.0, 0.35, r);" + " texturecoord = normcoord + 0.5;" + " vec4 color = texture2D (tex, texturecoord);" + " gl_FragColor = color * gl_Color;" + "}"; + +/* Light Tunnel effect */ +const gchar *tunnel_fragment_source = + "uniform sampler2D tex;" + "void main () {" + " vec2 texturecoord = gl_TexCoord[0].xy;" + " vec2 normcoord;" + /* little trick with normalized coords to obtain a circle with + * rect textures */ + " normcoord = (texturecoord - 0.5);" + " float r = length(normcoord);" + " normcoord *= clamp (r, 0.0, 0.275) / r;" + " texturecoord = normcoord + 0.5;" + " vec4 color = texture2D (tex, texturecoord); " + " gl_FragColor = color;" + "}"; + +/* FishEye effect */ +const gchar *fisheye_fragment_source = + "uniform sampler2D tex;" + "void main () {" + " vec2 texturecoord = gl_TexCoord[0].xy;" + " vec2 normcoord;" + " normcoord = texturecoord - 0.5;" + " float r = length (normcoord);" + " normcoord *= r * sqrt(2);" + " texturecoord = normcoord + 0.5;" + " vec4 color = texture2D (tex, texturecoord);" + " gl_FragColor = color;" + "}"; + + +/* Twirl effect */ +const gchar *twirl_fragment_source = + "uniform sampler2D tex;" + "void main () {" + " vec2 texturecoord = gl_TexCoord[0].xy;" + " vec2 normcoord;" + " normcoord = texturecoord - 0.5;" + " float r = length (normcoord);" + /* calculate rotation angle: maximum (about pi/2) at the origin and + * gradually decrease it up to 0.6 of each quadrant */ + " float phi = (1.0 - smoothstep (0.0, 0.3, r)) * 1.6;" + /* precalculate sin phi and cos phi, save some alu */ + " float s = sin(phi);" + " float c = cos(phi);" + /* rotate */ + " normcoord *= mat2(c, s, -s, c);" + " texturecoord = normcoord + 0.5;" + " vec4 color = texture2D (tex, texturecoord); " + " gl_FragColor = color;" + "}"; + + +/* Bulge effect */ +const gchar *bulge_fragment_source = + "uniform sampler2D tex;" + "void main () {" + " vec2 texturecoord = gl_TexCoord[0].xy;" + " vec2 normcoord;" + " normcoord = texturecoord - 0.5;" + " float r = length (normcoord);" + " normcoord *= smoothstep (-0.05, 0.25, r);" + " texturecoord = normcoord + 0.5;" + " vec4 color = texture2D (tex, texturecoord);" + " gl_FragColor = color;" + "}"; + + +/* Square Effect */ +const gchar *square_fragment_source = + "uniform sampler2D tex;" + "void main () {" + " vec2 texturecoord = gl_TexCoord[0].xy;" + " vec2 normcoord;" + " normcoord = texturecoord - 0.5;" + " float r = length (normcoord);" + " normcoord *= 1.0 + smoothstep(0.125, 0.25, abs(normcoord));" + " normcoord /= 2.0; /* zoom amount */" + " texturecoord = normcoord + 0.5;" + " vec4 color = texture2D (tex, texturecoord);" + " gl_FragColor = color * gl_Color;" + "}"; + + +const gchar *luma_threshold_fragment_source = + "uniform sampler2D tex;" + "void main () {" + " vec2 texturecoord = gl_TexCoord[0].st;" + " vec4 color = texture2D(tex, texturecoord);" + " float luma = dot(color.rgb, vec3(0.2125, 0.7154, 0.0721));" /* BT.709 (from orange book) */ + " gl_FragColor = vec4 (vec3 (smoothstep (0.30, 0.50, luma)), color.a);" + "}"; + +const gchar *sep_sobel_length_fragment_source = + "uniform sampler2D tex;" + "uniform bool invert;" + "void main () {" + " vec4 g = texture2D (tex, gl_TexCoord[0].st);" + /* restore black background with grey edges */ + " g -= vec4(0.5, 0.5, 0.0, 0.0);" + " float len = length (g);" + /* little trick to avoid IF operator */ + /* TODO: test if a standalone inverting pass is worth */ + " gl_FragColor = abs(vec4(vec3(float(invert) - len), 1.0));" + "}"; + +const gchar *desaturate_fragment_source = + "uniform sampler2D tex;" + "void main () {" + " vec4 color = texture2D (tex, gl_TexCoord[0].st);" + " float luma = dot(color.rgb, vec3(0.2125, 0.7154, 0.0721));" + " gl_FragColor = vec4(vec3(luma), color.a);" + "}"; + +const gchar *sep_sobel_hconv3_fragment_source = + "uniform sampler2D tex;" + "uniform float width;" + "void main () {" + " float w = 1.0 / width;" + " vec2 texturecoord[3];" + " texturecoord[1] = gl_TexCoord[0].st;" + " texturecoord[0] = texturecoord[1] - vec2(w, 0.0);" + " texturecoord[2] = texturecoord[1] + vec2(w, 0.0);" + " float grad_kern[3];" + " grad_kern[0] = 1.0;" + " grad_kern[1] = 0.0;" + " grad_kern[2] = -1.0;" + " float blur_kern[3];" + " blur_kern[0] = 0.25;" + " blur_kern[1] = 0.5;" + " blur_kern[2] = 0.25;" + " int i;" + " vec4 sum = vec4 (0.0);" + " for (i = 0; i < 3; i++) { " + " vec4 neighbor = texture2D(tex, texturecoord[i]); " + " sum.r = neighbor.r * blur_kern[i] + sum.r;" + " sum.g = neighbor.g * grad_kern[i] + sum.g;" + " }" + " gl_FragColor = sum + vec4(0.0, 0.5, 0.0, 0.0);" + "}"; + +const gchar *sep_sobel_vconv3_fragment_source = + "uniform sampler2D tex;" + "uniform float height;" + "void main () {" + " float h = 1.0 / height;" + " vec2 texturecoord[3];" + " texturecoord[1] = gl_TexCoord[0].st;" + " texturecoord[0] = texturecoord[1] - vec2(0.0, h);" + " texturecoord[2] = texturecoord[1] + vec2(0.0, h);" + " float grad_kern[3];" + " grad_kern[0] = 1.0;" + " grad_kern[1] = 0.0;" + " grad_kern[2] = -1.0;" + " float blur_kern[3];" + " blur_kern[0] = 0.25;" + " blur_kern[1] = 0.5;" + " blur_kern[2] = 0.25;" + " int i;" + " vec4 sum = vec4 (0.0);" + " for (i = 0; i < 3; i++) { " + " vec4 neighbor = texture2D(tex, texturecoord[i]); " + " sum.r = neighbor.r * grad_kern[i] + sum.r;" + " sum.g = neighbor.g * blur_kern[i] + sum.g;" + " }" + " gl_FragColor = sum + vec4(0.5, 0.0, 0.0, 0.0);" + "}"; + +/* horizontal convolution 7x7 */ +const gchar *hconv7_fragment_source = + "uniform sampler2D tex;" + "uniform float kernel[7];" + "uniform float width;" + "void main () {" + " float w = 1.0 / width;" + " vec2 texturecoord[7];" + " texturecoord[3] = gl_TexCoord[0].st;" + " texturecoord[2] = texturecoord[3] - vec2(w, 0.0);" + " texturecoord[1] = texturecoord[2] - vec2(w, 0.0);" + " texturecoord[0] = texturecoord[1] - vec2(w, 0.0);" + " texturecoord[4] = texturecoord[3] + vec2(w, 0.0);" + " texturecoord[5] = texturecoord[4] + vec2(w, 0.0);" + " texturecoord[6] = texturecoord[5] + vec2(w, 0.0);" + " int i;" + " vec4 sum = vec4 (0.0);" + " for (i = 0; i < 7; i++) { " + " vec4 neighbor = texture2D(tex, texturecoord[i]); " + " sum += neighbor * kernel[i];" + " }" + " gl_FragColor = sum;" + "}"; + +/* vertical convolution 7x7 */ +const gchar *vconv7_fragment_source = + "uniform sampler2D tex;" + "uniform float kernel[7];" + "uniform float height;" + "void main () {" + " float h = 1.0 / height;" + " vec2 texturecoord[7];" + " texturecoord[3] = gl_TexCoord[0].st;" + " texturecoord[2] = texturecoord[3] - vec2(0.0, h);" + " texturecoord[1] = texturecoord[2] - vec2(0.0, h);" + " texturecoord[0] = texturecoord[1] - vec2(0.0, h);" + " texturecoord[4] = texturecoord[3] + vec2(0.0, h);" + " texturecoord[5] = texturecoord[4] + vec2(0.0, h);" + " texturecoord[6] = texturecoord[5] + vec2(0.0, h);" + " int i;" + " vec4 sum = vec4 (0.0);" + " for (i = 0; i < 7; i++) { " + " vec4 neighbor = texture2D(tex, texturecoord[i]);" + " sum += neighbor * kernel[i];" + " }" + " gl_FragColor = sum;" + "}"; + + +/* TODO: support several blend modes */ +const gchar *sum_fragment_source = + "uniform sampler2D base;" + "uniform sampler2D blend;" + "uniform float alpha;" + "uniform float beta;" + "void main () {" + " vec4 basecolor = texture2D (base, gl_TexCoord[0].st);" + " vec4 blendcolor = texture2D (blend, gl_TexCoord[0].st);" + " gl_FragColor = alpha * basecolor + beta * blendcolor;" + "}"; + +const gchar *multiply_fragment_source = + "uniform sampler2D base;" + "uniform sampler2D blend;" + "uniform float alpha;" + "void main () {" + " vec4 basecolor = texture2D (base, gl_TexCoord[0].st);" + " vec4 blendcolor = texture2D (blend, gl_TexCoord[0].st);" + " gl_FragColor = (1.0 - alpha) * basecolor + alpha * basecolor * blendcolor;" + "}"; + +/* lut operations, map luma to tex1d, see orange book (chapter 19) */ +const gchar *luma_to_curve_fragment_source = + "uniform sampler2D tex;" + "uniform sampler1D curve;" + "void main () {" + " vec2 texturecoord = gl_TexCoord[0].st;" + " vec4 color = texture2D (tex, texturecoord);" + " float luma = dot(color.rgb, vec3(0.2125, 0.7154, 0.0721));" + " color = texture1D(curve, luma);" + " gl_FragColor = color;" + "}"; + + +/* lut operations, map rgb to tex1d, see orange book (chapter 19) */ +const gchar *rgb_to_curve_fragment_source = + "uniform sampler2D tex;" + "uniform sampler1D curve;" + "void main () {" + " vec4 color = texture2D (tex, gl_TexCoord[0].st);" + " vec4 outcolor;" + " outcolor.r = texture1D(curve, color.r).r;" + " outcolor.g = texture1D(curve, color.g).g;" + " outcolor.b = texture1D(curve, color.b).b;" + " outcolor.a = color.a;" + " gl_FragColor = outcolor;" + "}"; + +const gchar *sin_fragment_source = + "uniform sampler2D tex;" + "void main () {" + " vec4 color = texture2D (tex, vec2(gl_TexCoord[0].st));" + " float luma = dot(color.rgb, vec3(0.2125, 0.7154, 0.0721));" +/* calculate hue with the Preucil formula */ + " float cosh = color.r - 0.5*(color.g + color.b);" +/* sqrt(3)/2 = 0.866 */ + " float sinh = 0.866*(color.g - color.b);" +/* hue = atan2 h */ + " float sch = (1.0-sinh)*cosh;" +/* ok this is a little trick I came up because I didn't find any + * detailed proof of the Preucil formula. The issue is that tan(h) is + * pi-periodic so the smoothstep thing gives both reds (h = 0) and + * cyans (h = 180). I don't want to use atan since it requires + * branching and doesn't work on i915. So take only the right half of + * the circle where cosine is positive */ +/* take a slightly purple color trying to get rid of human skin reds */ +/* tanh = +-1.0 for h = +-45, where yellow=60, magenta=-60 */ + " float a = smoothstep (0.3, 1.0, sch);" + " float b = smoothstep (-0.4, -0.1, sinh);" + " float mix = a * b;" + " gl_FragColor = color * mix + luma * (1.0 - mix);" + "}"; + +const gchar *interpolate_fragment_source = + "uniform sampler2D base;" + "uniform sampler2D blend;" + "void main () {" + "vec4 basecolor = texture2D (base, gl_TexCoord[0].st);" + "vec4 blendcolor = texture2D (blend, gl_TexCoord[0].st);" + "vec4 white = vec4(1.0);" + "gl_FragColor = blendcolor + (1.0 - blendcolor.a) * basecolor;" + "}"; + +const gchar *texture_interp_fragment_source = + "uniform sampler2D base;" + "uniform sampler2D blend;" + "uniform sampler2D alpha;" + "void main () {" + " vec4 basecolor = texture2D (base, gl_TexCoord[0].st);" + " vec4 blendcolor = texture2D (blend, gl_TexCoord[0].st);" + " vec4 alphacolor = texture2D (alpha, gl_TexCoord[0].st);" + " gl_FragColor = (alphacolor * blendcolor) + (1.0 - alphacolor) * basecolor;" + "}"; + +const gchar *difference_fragment_source = + "uniform sampler2D saved;" + "uniform sampler2D current;" + "void main () {" + "vec4 savedcolor = texture2D (saved, gl_TexCoord[0].st);" + "vec4 currentcolor = texture2D (current, gl_TexCoord[0].st);" + "gl_FragColor = vec4 (step (0.12, length (savedcolor - currentcolor)));" + "}"; + +/* *INDENT-ON* */ diff --git a/ext/gl/effects/gstgleffectssources.h b/ext/gl/effects/gstgleffectssources.h new file mode 100644 index 0000000..ba05de1 --- /dev/null +++ b/ext/gl/effects/gstgleffectssources.h @@ -0,0 +1,58 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_GL_EFFECTS_SOURCES_H__ +#define __GST_GL_EFFECTS_SOURCES_H__ + +extern const gchar *vertex_shader_source; +extern const gchar *identity_fragment_source; +#if GST_GL_HAVE_OPENGL +extern const gchar *mirror_fragment_source_opengl; +extern const gchar *squeeze_fragment_source_opengl; +#endif +#if GST_GL_HAVE_GLES2 +extern const gchar *mirror_fragment_source_gles2; +extern const gchar *squeeze_fragment_source_gles2; +#endif +extern const gchar *stretch_fragment_source; +extern const gchar *tunnel_fragment_source; +extern const gchar *fisheye_fragment_source; +extern const gchar *twirl_fragment_source; +extern const gchar *bulge_fragment_source; +extern const gchar *square_fragment_source; +extern const gchar *luma_threshold_fragment_source; +extern const gchar *sep_sobel_length_fragment_source; +extern const gchar *desaturate_fragment_source; +extern const gchar *sep_sobel_hconv3_fragment_source; +extern const gchar *sep_sobel_vconv3_fragment_source; +extern const gchar *hconv7_fragment_source; +extern const gchar *vconv7_fragment_source; +extern const gchar *sum_fragment_source; +extern const gchar *luma_to_curve_fragment_source; +extern const gchar *rgb_to_curve_fragment_source; +extern const gchar *sin_fragment_source; +extern const gchar *interpolate_fragment_source; +extern const gchar *texture_interp_fragment_source; +extern const gchar *difference_fragment_source; +extern const gchar *multiply_fragment_source; + +void fill_gaussian_kernel (float *kernel, int size, float sigma); + +#endif /* __GST_GL_EFFECTS_SOURCES_H__ */ diff --git a/ext/gl/effects/gstgleffectstretch.c b/ext/gl/effects/gstgleffectstretch.c new file mode 100644 index 0000000..0b8b9d1 --- /dev/null +++ b/ext/gl/effects/gstgleffectstretch.c @@ -0,0 +1,75 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../gstgleffects.h" + +static void +gst_gl_effects_stretch_callback (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "stretch0"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "stretch0", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + stretch_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, "Failed to initialize stretch shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gst_gl_shader_set_uniform_1i (shader, "tex", 0); + + gst_gl_shader_set_uniform_1f (shader, "width", (gfloat) width / 2.0f); + gst_gl_shader_set_uniform_1f (shader, "height", (gfloat) height / 2.0f); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +void +gst_gl_effects_stretch (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->outtexture, gst_gl_effects_stretch_callback, effects); +} diff --git a/ext/gl/effects/gstgleffecttunnel.c b/ext/gl/effects/gstgleffecttunnel.c new file mode 100644 index 0000000..2188633 --- /dev/null +++ b/ext/gl/effects/gstgleffecttunnel.c @@ -0,0 +1,75 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../gstgleffects.h" + +static void +gst_gl_effects_tunnel_callback (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "tunnel0"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "tunnel0", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + tunnel_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, "Failed to initialize tunnel shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gst_gl_shader_set_uniform_1i (shader, "tex", 0); + + gst_gl_shader_set_uniform_1f (shader, "width", (gfloat) width / 2.0f); + gst_gl_shader_set_uniform_1f (shader, "height", (gfloat) height / 2.0f); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +void +gst_gl_effects_tunnel (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->outtexture, gst_gl_effects_tunnel_callback, effects); +} diff --git a/ext/gl/effects/gstgleffecttwirl.c b/ext/gl/effects/gstgleffecttwirl.c new file mode 100644 index 0000000..22ce874 --- /dev/null +++ b/ext/gl/effects/gstgleffecttwirl.c @@ -0,0 +1,75 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../gstgleffects.h" + +static void +gst_gl_effects_twirl_callback (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "twirl0"); + + if (!shader) { + shader = gst_gl_shader_new (GST_GL_FILTER (effects)->context); + g_hash_table_insert (effects->shaderstable, "twirl0", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + twirl_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, "Failed to initialize twirl shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gst_gl_shader_set_uniform_1i (shader, "tex", 0); + + gst_gl_shader_set_uniform_1f (shader, "width", (gfloat) width / 2.0f); + gst_gl_shader_set_uniform_1f (shader, "height", (gfloat) height / 2.0f); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +void +gst_gl_effects_twirl (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->outtexture, gst_gl_effects_twirl_callback, effects); +} diff --git a/ext/gl/effects/gstgleffectxray.c b/ext/gl/effects/gstgleffectxray.c new file mode 100644 index 0000000..bd70abf --- /dev/null +++ b/ext/gl/effects/gstgleffectxray.c @@ -0,0 +1,380 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../gstgleffects.h" +#include "gstgleffectscurves.h" +#include "gstgleffectlumatocurve.h" + +static gboolean kernel_ready = FALSE; +static float gauss_kernel[7]; + +static void +gst_gl_effects_xray_step_one (gint width, gint height, guint texture, + gpointer data) +{ + GstGLEffects *effects = GST_GL_EFFECTS (data); + + gst_gl_effects_luma_to_curve (effects, xray_curve, GST_GL_EFFECTS_CURVE_XRAY, + width, height, texture); +} + +static void +gst_gl_effects_xray_step_two (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "xray1"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "xray1", shader); + } + + if (!kernel_ready) { + fill_gaussian_kernel (gauss_kernel, 7, 1.5); + kernel_ready = TRUE; + } + + if (!gst_gl_shader_compile_and_check (shader, + hconv7_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, "Failed to initialize hconv7 shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (shader, "tex", 1); + gst_gl_shader_set_uniform_1fv (shader, "kernel", 9, gauss_kernel); + gst_gl_shader_set_uniform_1f (shader, "width", width); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +static void +gst_gl_effects_xray_step_three (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "xray2"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "xray2", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + vconv7_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, "Failed to initialize vconv7 shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (shader, "tex", 1); + gst_gl_shader_set_uniform_1fv (shader, "kernel", 9, gauss_kernel); + gst_gl_shader_set_uniform_1f (shader, "height", height); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +/* multipass separable sobel */ +static void +gst_gl_effects_xray_desaturate (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "xray_desat"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "xray_desat", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + desaturate_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, + "Failed to initialize desaturate shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (shader, "tex", 1); + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +static void +gst_gl_effects_xray_sobel_hconv (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "xray_sob_hconv"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "xray_sob_hconv", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + sep_sobel_hconv3_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, + "Failed to initialize sobel hvonc3 shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (shader, "tex", 1); + gst_gl_shader_set_uniform_1f (shader, "width", width); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +static void +gst_gl_effects_xray_sobel_vconv (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "xray_sob_vconv"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "xray_sob_vconv", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + sep_sobel_vconv3_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, + "Failed to initialize sobel vconv3 shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (shader, "tex", 1); + gst_gl_shader_set_uniform_1f (shader, "height", height); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +static void +gst_gl_effects_xray_sobel_length (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "xray_sob_len"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "xray_sob_len", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + sep_sobel_length_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, + "Failed to initialize seobel length shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (shader, "tex", 1); + gst_gl_shader_set_uniform_1i (shader, "invert", TRUE); + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +/* end of sobel passes */ + +void +gst_gl_effects_xray_step_five (gint width, gint height, guint texture, + gpointer data) +{ + GstGLShader *shader; + GstGLEffects *effects = GST_GL_EFFECTS (data); + GstGLFilter *filter = GST_GL_FILTER (effects); + GstGLContext *context = filter->context; + GstGLFuncs *gl = context->gl_vtable; + + shader = g_hash_table_lookup (effects->shaderstable, "xray4"); + + if (!shader) { + shader = gst_gl_shader_new (context); + g_hash_table_insert (effects->shaderstable, "xray4", shader); + } + + if (!gst_gl_shader_compile_and_check (shader, + multiply_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (context, "Failed to initialize multiply shader"); + GST_ELEMENT_ERROR (effects, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (shader); + + gl->ActiveTexture (GL_TEXTURE2); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, effects->midtexture[2]); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (shader, "base", 2); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1f (shader, "alpha", (gfloat) 0.5f); + gst_gl_shader_set_uniform_1i (shader, "blend", 1); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +void +gst_gl_effects_xray (GstGLEffects * effects) +{ + GstGLFilter *filter = GST_GL_FILTER (effects); + + /* map luma to xray curve */ + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->midtexture[0], gst_gl_effects_xray_step_one, effects); + /* horizontal blur */ + gst_gl_filter_render_to_target (filter, FALSE, effects->midtexture[0], + effects->midtexture[1], gst_gl_effects_xray_step_two, effects); + /* vertical blur */ + gst_gl_filter_render_to_target (filter, FALSE, effects->midtexture[1], + effects->midtexture[2], gst_gl_effects_xray_step_three, effects); + /* detect edges with Sobel */ + /* the old version used edges from the blurred texture, this uses + * the ones from original texture, still not sure what I like + * more. This one gives better edges obviously but behaves badly + * with noise */ + /* desaturate */ + gst_gl_filter_render_to_target (filter, TRUE, effects->intexture, + effects->midtexture[3], gst_gl_effects_xray_desaturate, effects); + /* horizonal convolution */ + gst_gl_filter_render_to_target (filter, FALSE, effects->midtexture[3], + effects->midtexture[4], gst_gl_effects_xray_sobel_hconv, effects); + /* vertical convolution */ + gst_gl_filter_render_to_target (filter, FALSE, effects->midtexture[4], + effects->midtexture[3], gst_gl_effects_xray_sobel_vconv, effects); + /* gradient length */ + gst_gl_filter_render_to_target (filter, FALSE, effects->midtexture[3], + effects->midtexture[4], gst_gl_effects_xray_sobel_length, effects); + /* multiply edges with the blurred image */ + gst_gl_filter_render_to_target (filter, FALSE, effects->midtexture[4], + effects->outtexture, gst_gl_effects_xray_step_five, effects); +} diff --git a/ext/gl/gltestsrc.c b/ext/gl/gltestsrc.c new file mode 100644 index 0000000..db28017 --- /dev/null +++ b/ext/gl/gltestsrc.c @@ -0,0 +1,507 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* non-GST-specific stuff */ + +#include "gltestsrc.h" + +#include +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +enum +{ + COLOR_WHITE = 0, + COLOR_YELLOW, + COLOR_CYAN, + COLOR_GREEN, + COLOR_MAGENTA, + COLOR_RED, + COLOR_BLUE, + COLOR_BLACK, + COLOR_NEG_I, + COLOR_POS_Q, + COLOR_SUPER_BLACK, + COLOR_DARK_GREY +}; + +static const struct vts_color_struct vts_colors[] = { + /* 100% white */ + {255, 128, 128, 255, 255, 255, 255}, + /* yellow */ + {226, 0, 155, 255, 255, 0, 255}, + /* cyan */ + {179, 170, 0, 0, 255, 255, 255}, + /* green */ + {150, 46, 21, 0, 255, 0, 255}, + /* magenta */ + {105, 212, 235, 255, 0, 255, 255}, + /* red */ + {76, 85, 255, 255, 0, 0, 255}, + /* blue */ + {29, 255, 107, 0, 0, 255, 255}, + /* black */ + {16, 128, 128, 0, 0, 0, 255}, + /* -I */ + {16, 198, 21, 0, 0, 128, 255}, + /* +Q */ + {16, 235, 198, 0, 128, 255, 255}, + /* superblack */ + {0, 128, 128, 0, 0, 0, 255}, + /* 5% grey */ + {32, 128, 128, 32, 32, 32, 255}, +}; + +static void +gst_gl_test_src_unicolor (GstGLTestSrc * v, GstBuffer * buffer, int w, + int h, const struct vts_color_struct *color); + +void +gst_gl_test_src_smpte (GstGLTestSrc * v, GstBuffer * buffer, int w, int h) +{ +#if GST_GL_HAVE_OPENGL + int i; + + if (gst_gl_context_get_gl_api (v->context) & GST_GL_API_OPENGL) { + + glClearColor (0.0, 0.0, 0.0, 1.0); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glDisable (GL_CULL_FACE); + glDisable (GL_TEXTURE_2D); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + + for (i = 0; i < 7; i++) { + glColor4f (vts_colors[i].R * (1 / 255.0f), vts_colors[i].G * (1 / 255.0f), + vts_colors[i].B * (1 / 255.0f), 1.0f); + glBegin (GL_QUADS); + glVertex3f (-1.0f + i * (2.0f / 7.0f), -1.0f + 2.0 * (2.0f / 3.0f), 0); + glVertex3f (-1.0f + (i + 1.0f) * (2.0f / 7.0f), + -1.0f + 2.0f * (2.0f / 3.0f), 0); + glVertex3f (-1.0f + (i + 1.0f) * (2.0f / 7.0f), -1.0f, 0); + glVertex3f (-1.0f + i * (2.0f / 7.0f), -1.0f, 0); + glEnd (); + } + + for (i = 0; i < 7; i++) { + int k; + + if (i & 1) { + k = 7; + } else { + k = 6 - i; + } + + glColor4f (vts_colors[k].R * (1 / 255.0f), vts_colors[k].G * (1 / 255.0f), + vts_colors[k].B * (1 / 255.0f), 1.0f); + glBegin (GL_QUADS); + glVertex3f (-1.0f + i * (2.0f / 7.0f), -1.0f + 2.0f * (3.0f / 4.0f), 0); + glVertex3f (-1.0f + (i + 1) * (2.0f / 7.0f), -1.0f + 2.0f * (3.0f / 4.0f), + 0); + glVertex3f (-1.0f + (i + 1) * (2.0f / 7.0f), -1.0f + 2.0f * (2.0f / 3.0f), + 0); + glVertex3f (-1.0f + i * (2.0f / 7.0f), -1.0f + 2.0f * (2.0f / 3.0f), 0); + glEnd (); + } + + for (i = 0; i < 3; i++) { + int k; + + if (i == 0) { + k = 8; + } else if (i == 1) { + k = 0; + } else { + k = 9; + } + + glColor4f (vts_colors[k].R * (1 / 255.0f), vts_colors[k].G * (1 / 255.0f), + vts_colors[k].B * (1 / 255.0f), 1.0f); + glBegin (GL_QUADS); + glVertex3f (-1.0f + i * (2.0f / 6.0f), -1.0f + 2.0f * 1, 0); + glVertex3f (-1.0f + (i + 1) * (2.0f / 6.0f), -1.0f + 2.0f * 1, 0); + glVertex3f (-1.0f + (i + 1) * (2.0f / 6.0f), -1.0f + 2.0f * (3.0f / 4.0f), + 0); + glVertex3f (-1.0f + i * (2.0f / 6.0f), -1.0f + 2.0f * (3.0f / 4.0f), 0); + glEnd (); + } + + for (i = 0; i < 3; i++) { + int k; + + if (i == 0) { + k = COLOR_SUPER_BLACK; + } else if (i == 1) { + k = COLOR_BLACK; + } else { + k = COLOR_DARK_GREY; + } + + glColor4f (vts_colors[k].R * (1 / 255.0f), vts_colors[k].G * (1 / 255.0f), + vts_colors[k].B * (1 / 255.0f), 1.0f); + glBegin (GL_QUADS); + glVertex3f (-1.0f + 2.0f * (0.5f + i * (1.0f / 12.0f)), -1.0 + 2.0f * 1, + 0); + glVertex3f (-1.0f + 2.0f * (0.5f + (i + 1) * (1.0f / 12.0f)), + -1.0f + 2.0f * 1, 0); + glVertex3f (-1.0f + 2.0f * (0.5f + (i + 1) * (1.0f / 12.0f)), + -1.0f + 2.0f * (3.0f / 4.0f), 0); + glVertex3f (-1.0f + 2.0f * (0.5f + i * (1.0f / 12.0f)), + -1.0f + 2.0f * (3.0f / 4.0f), 0); + glEnd (); + } + + glColor4f (1.0, 1.0, 1.0, 1.0); + glBegin (GL_QUADS); + glVertex3f (-1.0 + 2.0 * (0.75), -1.0 + 2.0 * 1, 0); + glVertex3f (-1.0 + 2.0 * (1.0), -1.0 + 2.0 * 1, 0); + glVertex3f (-1.0 + 2.0 * (1.0), -1.0 + 2.0 * (3.0 / 4.0), 0); + glVertex3f (-1.0 + 2.0 * (0.75), -1.0 + 2.0 * (3.0 / 4.0), 0); + glEnd (); + } +#endif +} + +void +gst_gl_test_src_snow (GstGLTestSrc * v, GstBuffer * buffer, int w, int h) +{ +#if GST_GL_HAVE_OPENGL + if (gst_gl_context_get_gl_api (v->context) & GST_GL_API_OPENGL) { + glClearColor (0.0, 0.0, 0.0, 1.0); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + + /* FIXME snow requires a fragment shader. Please write. */ + glColor4f (0.5, 0.5, 0.5, 1.0); + glBegin (GL_QUADS); + glVertex3f (-1.0 + 2.0 * (0.0), -1.0 + 2.0 * 1, 0); + glVertex3f (-1.0 + 2.0 * (1.0), -1.0 + 2.0 * 1, 0); + glVertex3f (-1.0 + 2.0 * (1.0), -1.0 + 2.0 * (0.0), 0); + glVertex3f (-1.0 + 2.0 * (0.0), -1.0 + 2.0 * (0.0), 0); + glEnd (); + } +#endif +} + +static void +gst_gl_test_src_unicolor (GstGLTestSrc * v, GstBuffer * buffer, int w, + int h, const struct vts_color_struct *color) +{ +#if GST_GL_HAVE_OPENGL + if (gst_gl_context_get_gl_api (v->context) & GST_GL_API_OPENGL) { + glClearColor (color->R * (1 / 255.0f), color->G * (1 / 255.0f), + color->B * (1 / 255.0f), 1.0f); + glClear (GL_COLOR_BUFFER_BIT); + } +#endif +} + +void +gst_gl_test_src_black (GstGLTestSrc * v, GstBuffer * buffer, int w, int h) +{ + gst_gl_test_src_unicolor (v, buffer, w, h, vts_colors + COLOR_BLACK); +} + +void +gst_gl_test_src_white (GstGLTestSrc * v, GstBuffer * buffer, int w, int h) +{ + gst_gl_test_src_unicolor (v, buffer, w, h, vts_colors + COLOR_WHITE); +} + +void +gst_gl_test_src_red (GstGLTestSrc * v, GstBuffer * buffer, int w, int h) +{ + gst_gl_test_src_unicolor (v, buffer, w, h, vts_colors + COLOR_RED); +} + +void +gst_gl_test_src_green (GstGLTestSrc * v, GstBuffer * buffer, int w, int h) +{ + gst_gl_test_src_unicolor (v, buffer, w, h, vts_colors + COLOR_GREEN); +} + +void +gst_gl_test_src_blue (GstGLTestSrc * v, GstBuffer * buffer, int w, int h) +{ + gst_gl_test_src_unicolor (v, buffer, w, h, vts_colors + COLOR_BLUE); +} + +void +gst_gl_test_src_checkers1 (GstGLTestSrc * v, GstBuffer * buffer, int w, int h) +{ +#if 0 + int x, y; + paintinfo pi = { NULL, }; + paintinfo *p = π + struct fourcc_list_struct *fourcc; + + p->width = w; + p->height = h; + fourcc = v->fourcc; + if (fourcc == NULL) + return; + + fourcc->paint_setup (p, dest); + p->paint_hline = fourcc->paint_hline; + + for (y = 0; y < h; y++) { + p->color = vts_colors + COLOR_GREEN; + p->paint_hline (p, 0, y, w); + for (x = (y % 2); x < w; x += 2) { + p->color = vts_colors + COLOR_RED; + p->paint_hline (p, x, y, 1); + } + } +#endif +} + +void +gst_gl_test_src_checkers2 (GstGLTestSrc * v, GstBuffer * buffer, int w, int h) +{ +#if 0 + int x, y; + paintinfo pi = { NULL, }; + paintinfo *p = π + struct fourcc_list_struct *fourcc; + + p->width = w; + p->height = h; + fourcc = v->fourcc; + if (fourcc == NULL) + return; + + fourcc->paint_setup (p, dest); + p->paint_hline = fourcc->paint_hline; + + p->color = vts_colors + COLOR_GREEN; + for (y = 0; y < h; y++) { + p->paint_hline (p, 0, y, w); + } + + for (y = 0; y < h; y += 2) { + for (x = ((y % 4) == 0) ? 0 : 2; x < w; x += 4) { + guint len = (x < (w - 1)) ? 2 : (w - x); + + p->color = vts_colors + COLOR_RED; + p->paint_hline (p, x, y + 0, len); + if (G_LIKELY ((y + 1) < h)) { + p->paint_hline (p, x, y + 1, len); + } + } + } +#endif +} + +void +gst_gl_test_src_checkers4 (GstGLTestSrc * v, GstBuffer * buffer, int w, int h) +{ +#if 0 + int x, y; + paintinfo pi = { NULL, }; + paintinfo *p = π + struct fourcc_list_struct *fourcc; + + p->width = w; + p->height = h; + fourcc = v->fourcc; + if (fourcc == NULL) + return; + + fourcc->paint_setup (p, dest); + p->paint_hline = fourcc->paint_hline; + + p->color = vts_colors + COLOR_GREEN; + for (y = 0; y < h; y++) { + p->paint_hline (p, 0, y, w); + } + + for (y = 0; y < h; y += 4) { + for (x = ((y % 8) == 0) ? 0 : 4; x < w; x += 8) { + guint len = (x < (w - 3)) ? 4 : (w - x); + + p->color = vts_colors + COLOR_RED; + p->paint_hline (p, x, y + 0, len); + if (G_LIKELY ((y + 1) < h)) { + p->paint_hline (p, x, y + 1, len); + if (G_LIKELY ((y + 2) < h)) { + p->paint_hline (p, x, y + 2, len); + if (G_LIKELY ((y + 3) < h)) { + p->paint_hline (p, x, y + 3, len); + } + } + } + } + } +#endif +} + +void +gst_gl_test_src_checkers8 (GstGLTestSrc * v, GstBuffer * buffer, int w, int h) +{ +#if 0 + int x, y; + paintinfo pi = { NULL, }; + paintinfo *p = π + struct fourcc_list_struct *fourcc; + + p->width = w; + p->height = h; + fourcc = v->fourcc; + if (fourcc == NULL) + return; + + fourcc->paint_setup (p, dest); + p->paint_hline = fourcc->paint_hline; + + p->color = vts_colors + COLOR_GREEN; + for (y = 0; y < h; y++) { + p->paint_hline (p, 0, y, w); + } + + for (y = 0; y < h; y += 8) { + for (x = ((GST_ROUND_UP_8 (y) % 16) == 0) ? 0 : 8; x < w; x += 16) { + guint len = (x < (w - 7)) ? 8 : (w - x); + + p->color = vts_colors + COLOR_RED; + p->paint_hline (p, x, y + 0, len); + if (G_LIKELY ((y + 1) < h)) { + p->paint_hline (p, x, y + 1, len); + if (G_LIKELY ((y + 2) < h)) { + p->paint_hline (p, x, y + 2, len); + if (G_LIKELY ((y + 3) < h)) { + p->paint_hline (p, x, y + 3, len); + if (G_LIKELY ((y + 4) < h)) { + p->paint_hline (p, x, y + 4, len); + if (G_LIKELY ((y + 5) < h)) { + p->paint_hline (p, x, y + 5, len); + if (G_LIKELY ((y + 6) < h)) { + p->paint_hline (p, x, y + 6, len); + if (G_LIKELY ((y + 7) < h)) { + p->paint_hline (p, x, y + 7, len); + } + } + } + } + } + } + } + } + } +#endif +} + +void +gst_gl_test_src_circular (GstGLTestSrc * v, GstBuffer * buffer, int w, int h) +{ +#if 0 + int i; + int j; + paintinfo pi = { NULL, }; + paintinfo *p = π + struct fourcc_list_struct *fourcc; + struct vts_color_struct color; + static uint8_t sine_array[256]; + static int sine_array_inited = FALSE; + double freq[8]; + +#ifdef SCALE_AMPLITUDE + double ampl[8]; +#endif + int d; + + if (!sine_array_inited) { + for (i = 0; i < 256; i++) { + sine_array[i] = + floor (255 * (0.5 + 0.5 * sin (i * 2 * M_PI / 256)) + 0.5); + } + sine_array_inited = TRUE; + } + + p->width = w; + p->height = h; + fourcc = v->fourcc; + if (fourcc == NULL) + return; + + fourcc->paint_setup (p, dest); + p->paint_hline = fourcc->paint_hline; + + color = vts_colors[COLOR_BLACK]; + p->color = &color; + + for (i = 1; i < 8; i++) { + freq[i] = 200 * pow (2.0, -(i - 1) / 4.0); +#ifdef SCALE_AMPLITUDE + { + double x; + + x = 2 * M_PI * freq[i] / w; + ampl[i] = sin (x) / x; + } +#endif + } + + for (i = 0; i < w; i++) { + for (j = 0; j < h; j++) { + double dist; + int seg; + + dist = + sqrt ((2 * i - w) * (2 * i - w) + (2 * j - h) * (2 * j - + h)) / (2 * w); + seg = floor (dist * 16); + if (seg == 0 || seg >= 8) { + color.Y = 255; + } else { +#ifdef SCALE_AMPLITUDE + double a; +#endif + d = floor (256 * dist * freq[seg] + 0.5); +#ifdef SCALE_AMPLITUDE + a = ampl[seg]; + if (a < 0) + a = 0; + color.Y = 128 + a * (sine_array[d & 0xff] - 128); +#else + color.Y = sine_array[d & 0xff]; +#endif + } + color.R = color.Y; + color.G = color.Y; + color.B = color.Y; + p->paint_hline (p, i, j, 1); + } + } +#endif +} diff --git a/ext/gl/gltestsrc.h b/ext/gl/gltestsrc.h new file mode 100644 index 0000000..f79ffee --- /dev/null +++ b/ext/gl/gltestsrc.h @@ -0,0 +1,58 @@ +/* GStreamer + * Copyright (C) <2003> David A. Schleef + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GL_TEST_SRC_H__ +#define __GL_TEST_SRC_H__ + +#include + +#include "gstgltestsrc.h" + +struct vts_color_struct { + guint8 Y, U, V; + guint8 R, G, B; + guint8 A; +}; + +void gst_gl_test_src_smpte (GstGLTestSrc * v, + GstBuffer *buffer, int w, int h); +void gst_gl_test_src_snow (GstGLTestSrc * v, + GstBuffer *buffer, int w, int h); +void gst_gl_test_src_black (GstGLTestSrc * v, + GstBuffer *buffer, int w, int h); +void gst_gl_test_src_white (GstGLTestSrc * v, + GstBuffer *buffer, int w, int h); +void gst_gl_test_src_red (GstGLTestSrc * v, + GstBuffer *buffer, int w, int h); +void gst_gl_test_src_green (GstGLTestSrc * v, + GstBuffer *buffer, int w, int h); +void gst_gl_test_src_blue (GstGLTestSrc * v, + GstBuffer *buffer, int w, int h); +void gst_gl_test_src_checkers1 (GstGLTestSrc * v, + GstBuffer *buffer, int w, int h); +void gst_gl_test_src_checkers2 (GstGLTestSrc * v, + GstBuffer *buffer, int w, int h); +void gst_gl_test_src_checkers4 (GstGLTestSrc * v, + GstBuffer *buffer, int w, int h); +void gst_gl_test_src_checkers8 (GstGLTestSrc * v, + GstBuffer *buffer, int w, int h); +void gst_gl_test_src_circular (GstGLTestSrc * v, + GstBuffer *buffer, int w, int h); + +#endif diff --git a/ext/gl/gstglbumper.c b/ext/gl/gstglbumper.c new file mode 100644 index 0000000..cabc144 --- /dev/null +++ b/ext/gl/gstglbumper.c @@ -0,0 +1,546 @@ +/* + * GStreamer + * Copyright (C) 2008 Cyril Comparon + * Copyright (C) 2008 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-glbumper + * + * Bump mapping using the normal method. + * + * + * Examples + * |[ + * gst-launch -v videotestsrc ! glupload ! glbumper location=normalmap.bmp ! glimagesink + * ]| A pipeline to test normal mapping. + * FBO (Frame Buffer Object) and GLSL (OpenGL Shading Language) are required. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "gstglbumper.h" + +#if PNG_LIBPNG_VER >= 10400 +#define int_p_NULL NULL +#define png_infopp_NULL NULL +#endif + +#define GST_CAT_DEFAULT gst_gl_bumper_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +enum +{ + PROP_0, + PROP_LOCATION +}; + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_bumper_debug, "glbumper", 0, "glbumper element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLBumper, gst_gl_bumper, GST_TYPE_GL_FILTER, + DEBUG_INIT); + +static void gst_gl_bumper_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_bumper_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void gst_gl_bumper_reset (GstGLFilter * filter); +static gboolean gst_gl_bumper_init_shader (GstGLFilter * filter); +static gboolean gst_gl_bumper_filter_texture (GstGLFilter * filter, + guint in_tex, guint out_tex); +static void gst_gl_bumper_callback (gint width, gint height, guint texture, + gpointer stuff); + +//vertex source +static const gchar *bumper_v_src = + "attribute vec3 aTangent;\n" + "\n" + "varying vec3 vNormal;\n" + "varying vec3 vTangent;\n" + "varying vec3 vVertexToLight0;\n" + "varying vec3 vVertexToLight1;\n" + "\n" + "void main()\n" + "{\n" + " // transform the vertex\n" + " gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n" + "\n" + " // transform the normal and the tangent to scene coords\n" + " vNormal = normalize(gl_NormalMatrix * gl_Normal);\n" + " vTangent = normalize(gl_NormalMatrix * aTangent);\n" + "\n" + " // transforming the vertex position to modelview-space\n" + " //const vec4 vertexInSceneCoords = gl_ModelViewMatrix * gl_Vertex;\n" + "\n" + " // calculate the vector from the vertex position to the light position\n" + " vVertexToLight0 = normalize(gl_LightSource[0].position).xyz;\n" + " vVertexToLight1 = normalize(gl_LightSource[1].position).xyz;\n" + "\n" + " // transit vertex color\n" + " gl_FrontColor = gl_BackColor = gl_Color;\n" + "\n" + " // use the two first sets of texture coordinates in the fragment shader\n" + " gl_TexCoord[0] = gl_MultiTexCoord0;\n" + " gl_TexCoord[1] = gl_MultiTexCoord1;\n" "}\n"; + +//fragment source +static const gchar *bumper_f_src = + "uniform sampler2D texture0;\n" + "uniform sampler2D texture1;\n" + "\n" + "varying vec3 vNormal;\n" + "varying vec3 vTangent;\n" + "varying vec3 vVertexToLight0;\n" + "varying vec3 vVertexToLight1;\n" + "\n" + "void main()\n" + "{\n" + " // get the color of the textures\n" + " vec4 textureColor = texture2D(texture0, gl_TexCoord[0].st);\n" + " vec3 normalmapItem = texture2D(texture1, gl_TexCoord[1].st).xyz * 2.0 - 1.0;\n" + "\n" + " // calculate matrix that transform from tangent space to normalmap space (contrary of intuition)\n" + " vec3 binormal = cross(vNormal, vTangent);\n" + " mat3 tangentSpace2normalmapSpaceMat = mat3(vTangent, binormal, vNormal);\n" + "\n" + " // disturb the normal\n" + " vec3 disturbedNormal = tangentSpace2normalmapSpaceMat * normalmapItem;\n" + "\n" + " // calculate the diffuse term and clamping it to [0;1]\n" + " float diffuseTerm0 = clamp(dot(disturbedNormal, vVertexToLight0), 0.0, 1.0);\n" + " float diffuseTerm1 = clamp(dot(disturbedNormal, vVertexToLight1), 0.0, 1.0);\n" + "\n" + " vec3 irradiance = (diffuseTerm0 * gl_LightSource[0].diffuse.rgb + diffuseTerm1 * gl_LightSource[1].diffuse.rgb);\n" + "\n" + " // calculate the final color\n" + " gl_FragColor = vec4(irradiance * textureColor.rgb, textureColor.w);\n" + "}\n"; + +#define LOAD_ERROR(context, msg) { gst_gl_context_set_error (context, "unable to load %s: %s", bumper->location, msg); return; } + +//png reading error handler +static void +user_warning_fn (png_structp png_ptr, png_const_charp warning_msg) +{ + g_warning ("%s\n", warning_msg); +} + +//Called in the gl thread +static void +gst_gl_bumper_init_resources (GstGLFilter * filter) +{ + GstGLBumper *bumper = GST_GL_BUMPER (filter); + GstGLContext *context = filter->context; + const GstGLFuncs *gl = context->gl_vtable; + + png_structp png_ptr; + png_infop info_ptr; + png_uint_32 width = 0; + png_uint_32 height = 0; + gint bit_depth = 0; + gint color_type = 0; + gint interlace_type = 0; + png_FILE_p fp = NULL; + guint y = 0; + guchar *raw_data = NULL; + guchar **rows = NULL; + png_byte magic[8]; + gint n_read; + + if (!context) + return; + + if (!bumper->location) { + gst_gl_context_set_error (context, "A filename is required"); + return; + } + + /* BEGIN load png image file */ + + if ((fp = fopen (bumper->location, "rb")) == NULL) + LOAD_ERROR (context, "file not found"); + + /* Read magic number */ + n_read = fread (magic, 1, sizeof (magic), fp); + if (n_read != sizeof (magic)) { + fclose (fp); + LOAD_ERROR (context, "can't read PNG magic number"); + } + + /* Check for valid magic number */ + if (png_sig_cmp (magic, 0, sizeof (magic))) { + fclose (fp); + LOAD_ERROR (context, "not a valid PNG image"); + } + + png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (png_ptr == NULL) { + fclose (fp); + LOAD_ERROR (context, "failed to initialize the png_struct"); + } + + png_set_error_fn (png_ptr, NULL, NULL, user_warning_fn); + + info_ptr = png_create_info_struct (png_ptr); + if (info_ptr == NULL) { + fclose (fp); + png_destroy_read_struct (&png_ptr, png_infopp_NULL, png_infopp_NULL); + LOAD_ERROR (context, + "failed to initialize the memory for image information"); + } + + png_init_io (png_ptr, fp); + + png_set_sig_bytes (png_ptr, sizeof (magic)); + + png_read_info (png_ptr, info_ptr); + + png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, + &interlace_type, int_p_NULL, int_p_NULL); + + if (color_type != PNG_COLOR_TYPE_RGB) { + fclose (fp); + png_destroy_read_struct (&png_ptr, png_infopp_NULL, png_infopp_NULL); + LOAD_ERROR (context, "color type is not rgb"); + } + + raw_data = (guchar *) malloc (sizeof (guchar) * width * height * 3); + + rows = (guchar **) malloc (sizeof (guchar *) * height); + + for (y = 0; y < height; ++y) + rows[y] = (guchar *) (raw_data + y * width * 3); + + png_read_image (png_ptr, rows); + + free (rows); + + png_read_end (png_ptr, info_ptr); + png_destroy_read_struct (&png_ptr, &info_ptr, png_infopp_NULL); + fclose (fp); + + /* END load png image file */ + + bumper->bumpmap_width = width; + bumper->bumpmap_height = height; + + gl->GenTextures (1, &bumper->bumpmap); + gl->BindTexture (GL_TEXTURE_2D, bumper->bumpmap); + gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, + bumper->bumpmap_width, bumper->bumpmap_height, 0, + GL_RGB, GL_UNSIGNED_BYTE, raw_data); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + free (raw_data); +} + +//Called in the gl thread +static void +gst_gl_bumper_reset_resources (GstGLFilter * filter) +{ + GstGLBumper *bumper = GST_GL_BUMPER (filter); + + if (bumper->bumpmap) { + glDeleteTextures (1, &bumper->bumpmap); + bumper->bumpmap = 0; + } +} + +static void +gst_gl_bumper_class_init (GstGLBumperClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + gobject_class->set_property = gst_gl_bumper_set_property; + gobject_class->get_property = gst_gl_bumper_get_property; + + GST_GL_FILTER_CLASS (klass)->filter_texture = gst_gl_bumper_filter_texture; + GST_GL_FILTER_CLASS (klass)->display_init_cb = gst_gl_bumper_init_resources; + GST_GL_FILTER_CLASS (klass)->display_reset_cb = gst_gl_bumper_reset_resources; + GST_GL_FILTER_CLASS (klass)->onInitFBO = gst_gl_bumper_init_shader; + GST_GL_FILTER_CLASS (klass)->onReset = gst_gl_bumper_reset; + + g_object_class_install_property (gobject_class, + PROP_LOCATION, g_param_spec_string ("location", + "Normal map location", + "Normal map location", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_set_metadata (element_class, "OpenGL bumper filter", + "Filter/Effect/Video", "Bump mapping filter", + "Cyril Comparon , " + "Julien Isorce "); +} + +static void +gst_gl_bumper_init (GstGLBumper * bumper) +{ + bumper->shader = NULL; + bumper->bumpmap = 0; + bumper->bumpmap_width = 0; + bumper->bumpmap_height = 0; + bumper->location = NULL; +} + +static void +gst_gl_bumper_reset (GstGLFilter * filter) +{ + GstGLBumper *bumper_filter = GST_GL_BUMPER (filter); + + //blocking call, wait the opengl thread has destroyed the shader + if (bumper_filter->shader) + gst_gl_context_del_shader (filter->context, bumper_filter->shader); + bumper_filter->shader = NULL; +} + +static void +gst_gl_bumper_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGLBumper *bumper = GST_GL_BUMPER (object); + + switch (prop_id) { + case PROP_LOCATION: + if (bumper->location != NULL) + g_free (bumper->location); + bumper->location = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_bumper_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstGLBumper *bumper = GST_GL_BUMPER (object); + + switch (prop_id) { + case PROP_LOCATION: + g_value_set_string (value, bumper->location); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_gl_bumper_init_shader (GstGLFilter * filter) +{ + GstGLBumper *bumper = GST_GL_BUMPER (filter); + + //blocking call, wait the opengl thread has compiled the shader + return gst_gl_context_gen_shader (filter->context, bumper_v_src, bumper_f_src, + &bumper->shader); +} + +static gboolean +gst_gl_bumper_filter_texture (GstGLFilter * filter, guint in_tex, guint out_tex) +{ + gpointer bumper_filter = GST_GL_BUMPER (filter); + + //blocking call, use a FBO + gst_gl_context_use_fbo (filter->context, + GST_VIDEO_INFO_WIDTH (&filter->out_info), + GST_VIDEO_INFO_HEIGHT (&filter->out_info), + filter->fbo, filter->depthbuffer, out_tex, gst_gl_bumper_callback, + GST_VIDEO_INFO_WIDTH (&filter->in_info), + GST_VIDEO_INFO_HEIGHT (&filter->in_info), + in_tex, 45, + (gdouble) GST_VIDEO_INFO_WIDTH (&filter->out_info) / + (gdouble) GST_VIDEO_INFO_HEIGHT (&filter->out_info), 0.1, 50, + GST_GL_DISPLAY_PROJECTION_PERSPECTIVE, bumper_filter); + + return TRUE; +} + +typedef struct _MeshData +{ + float x, y, z; /* Vertex */ + float nx, ny, nz; /* Normal */ + float s0, t0; /* TexCoord0 */ + float s1, t1; /* TexCoord1 */ + float va0, vb0, vc0; /* VertexAttrib */ +} MeshData; + +//opengl scene, params: input texture (not the output filter->texture) +static void +gst_gl_bumper_callback (gint width, gint height, guint texture, gpointer stuff) +{ + static GLfloat xrot = 0; + static GLfloat yrot = 0; + static GLfloat zrot = 0; + + GstGLFuncs *gl; + GstGLBumper *bumper = GST_GL_BUMPER (stuff); + GstGLContext *context = GST_GL_FILTER (bumper)->context; + GLint locTangent = 0; + + //choose the lights + GLfloat light_direction0[] = { 1.0, 0.0, -1.0, 0.0 }; // light goes along -x + GLfloat light_direction1[] = { -1.0, 0.0, -1.0, 0.0 }; // light goes along x + GLfloat light_diffuse0[] = { 1.0, 1.0, 1.0, 1.0 }; + GLfloat light_diffuse1[] = { 1.0, 1.0, 1.0, 1.0 }; + GLfloat mat_diffuse[] = { 1.0, 1.0, 1.0, 1.0 }; + +/* *INDENT-OFF* */ + MeshData mesh[] = { + /* | Vertex | Normal |TexCoord0|TexCoord1| VertexAttrib | */ +/*F*/ { 1.0, 1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0}, +/*r*/ { 1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0}, +/*o*/ {-1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0}, + {-1.0, 1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0}, +/*R*/ {-1.0, 1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0}, +/*i*/ {-1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0}, +/*g*/ {-1.0, -1.0, 1.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0}, + {-1.0, 1.0, 1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0}, +/*B*/ {-1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0}, +/*a*/ {-1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0}, +/*c*/ { 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0}, + { 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0}, +/*L*/ { 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0}, +/*e*/ { 1.0, -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0}, +/*f*/ { 1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0}, + { 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0}, +/*T*/ { 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0}, +/*o*/ { 1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0}, +/*p*/ {-1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0}, + {-1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0}, +/*B*/ { 1.0, -1.0, -1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0}, +/*o*/ { 1.0, -1.0, 1.0, 0.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, -1.0}, +/*t*/ {-1.0, -1.0, 1.0, 0.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, -1.0}, + {-1.0, -1.0, -1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, -1.0}, + }; + + GLushort indices[] = { + 0, 1, 2, + 0, 2, 3, + 4, 5, 6, + 4, 6, 7, + 8, 9, 10, + 8, 10, 11, + 12, 13, 14, + 12, 14, 15, + 16, 17, 18, + 16, 18, 19, + 20, 21, 22, + 20, 22, 23 + }; + +/* *INDENT-ON* */ + + gl = GST_GL_FILTER (bumper)->context->gl_vtable; + + //eye point + gl->MatrixMode (GL_PROJECTION); + gluLookAt (0.0, 0.0, -6.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); + gl->MatrixMode (GL_MODELVIEW); + + //scene conf + gl->Enable (GL_DEPTH_TEST); + gl->DepthFunc (GL_LEQUAL); + gl->Hint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + + gl->ShadeModel (GL_SMOOTH); + + //set the lights + gl->Lightfv (GL_LIGHT0, GL_POSITION, light_direction0); + gl->Lightfv (GL_LIGHT0, GL_DIFFUSE, light_diffuse0); + gl->Lightfv (GL_LIGHT1, GL_POSITION, light_direction1); + gl->Lightfv (GL_LIGHT1, GL_DIFFUSE, light_diffuse1); + gl->Materialfv (GL_FRONT, GL_DIFFUSE, mat_diffuse); + gl->ColorMaterial (GL_FRONT_AND_BACK, GL_DIFFUSE); + gl->Enable (GL_COLOR_MATERIAL); + gl->Enable (GL_LIGHTING); + gl->Enable (GL_LIGHT0); + gl->Enable (GL_LIGHT1); + //configure shader + gst_gl_shader_use (bumper->shader); + locTangent = + gst_gl_shader_get_attribute_location (bumper->shader, "aTangent"); + + //set the normal map + gl->ActiveTexture (GL_TEXTURE1); + gst_gl_shader_set_uniform_1i (bumper->shader, "texture1", 1); + gl->BindTexture (GL_TEXTURE_2D, bumper->bumpmap); + + //set the video texture + gl->ActiveTexture (GL_TEXTURE0); + gst_gl_shader_set_uniform_1i (bumper->shader, "texture0", 0); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gl->Rotatef (xrot, 1.0f, 0.0f, 0.0f); + gl->Rotatef (yrot, 0.0f, 1.0f, 0.0f); + gl->Rotatef (zrot, 0.0f, 0.0f, 1.0f); + + gl->EnableVertexAttribArray (locTangent); + + gl->ClientActiveTexture (GL_TEXTURE0); + gl->EnableClientState (GL_TEXTURE_COORD_ARRAY); + gl->EnableClientState (GL_VERTEX_ARRAY); + gl->EnableClientState (GL_NORMAL_ARRAY); + + gl->VertexAttribPointer (locTangent, 3, GL_FLOAT, 0, sizeof (MeshData), + &mesh[0].va0); + gl->VertexPointer (3, GL_FLOAT, sizeof (MeshData), &mesh[0].x); + gl->NormalPointer (GL_FLOAT, sizeof (MeshData), &mesh[0].nx); + gl->TexCoordPointer (2, GL_FLOAT, sizeof (MeshData), &mesh[0].s0); + + gl->ClientActiveTexture (GL_TEXTURE1); + gl->EnableClientState (GL_TEXTURE_COORD_ARRAY); + gl->TexCoordPointer (2, GL_FLOAT, sizeof (MeshData), &mesh[0].s1); + + gl->DrawElements (GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, indices); + + gl->DisableClientState (GL_VERTEX_ARRAY); + gl->DisableClientState (GL_TEXTURE_COORD_ARRAY); + gl->DisableClientState (GL_NORMAL_ARRAY); + + gl->ClientActiveTexture (GL_TEXTURE0); + gl->DisableClientState (GL_TEXTURE_COORD_ARRAY); + + gl->DisableVertexAttribArray (locTangent); + + gst_gl_context_clear_shader (context); + + gl->Disable (GL_LIGHT0); + gl->Disable (GL_LIGHT1); + gl->Disable (GL_LIGHTING); + gl->Disable (GL_COLOR_MATERIAL); + + xrot += 1.0f; + yrot += 0.9f; + zrot += 0.6f; +} diff --git a/ext/gl/gstglbumper.h b/ext/gl/gstglbumper.h new file mode 100644 index 0000000..b1a1dc0 --- /dev/null +++ b/ext/gl/gstglbumper.h @@ -0,0 +1,57 @@ +/* + * GStreamer + * Copyright (C) 2008 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_GL_BUMPER_H_ +#define _GST_GL_BUMPER_H_ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_GL_BUMPER (gst_gl_bumper_get_type()) +#define GST_GL_BUMPER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_BUMPER,GstGLBumper)) +#define GST_IS_GL_BUMPER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_BUMPER)) +#define GST_GL_BUMPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_BUMPER,GstGLBumperClass)) +#define GST_IS_GL_BUMPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_BUMPER)) +#define GST_GL_BUMPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_BUMPER,GstGLBumperClass)) + +typedef struct _GstGLBumper GstGLBumper; +typedef struct _GstGLBumperClass GstGLBumperClass; + +struct _GstGLBumper +{ + GstGLFilter filter; + GstGLShader *shader; + GLuint bumpmap; + gint bumpmap_width; + gint bumpmap_height; + gchar *location; +}; + +struct _GstGLBumperClass +{ + GstGLFilterClass filter_class; +}; + +GType gst_gl_bumper_get_type (void); + +G_END_DECLS + +#endif /* _GST_GLBUMPER_H_ */ diff --git a/ext/gl/gstglcolorscale.c b/ext/gl/gstglcolorscale.c new file mode 100644 index 0000000..10ea7fc --- /dev/null +++ b/ext/gl/gstglcolorscale.c @@ -0,0 +1,156 @@ +/* + * GStreamer + * Copyright (C) 2008 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-glcolorscale + * + * video frame scaling and colorspace conversion. + * + * + * Scaling and Color space conversion + * + * Equivalent to glupload ! gldownload. + * + * + * + * Examples + * |[ + * gst-launch -v videotestsrc ! "video/x-raw-yuv" ! glcolorscale ! ximagesink + * ]| A pipeline to test colorspace conversion. + * FBO is required. + |[ + * gst-launch -v videotestsrc ! "video/x-raw-yuv, width=640, height=480, format=(fourcc)AYUV" ! glcolorscale ! \ + * "video/x-raw-yuv, width=320, height=240, format=(fourcc)YV12" ! autovideosink + * ]| A pipeline to test hardware scaling and colorspace conversion. + * FBO and GLSL are required. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstglcolorscale.h" + + +#define GST_CAT_DEFAULT gst_gl_colorscale_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +/* Properties */ +enum +{ + PROP_0 +}; + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_colorscale_debug, "glcolorscale", 0, "glcolorscale element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLColorscale, gst_gl_colorscale, + GST_TYPE_GL_FILTER, DEBUG_INIT); + +static void gst_gl_colorscale_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_colorscale_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_gl_colorscale_filter_texture (GstGLFilter * filter, + guint in_tex, guint out_tex); +static void gst_gl_colorscale_callback (gint width, gint height, + guint texture, gpointer stuff); + +static void +gst_gl_colorscale_class_init (GstGLColorscaleClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + GstGLFilterClass *filter_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + filter_class = GST_GL_FILTER_CLASS (klass); + + gobject_class->set_property = gst_gl_colorscale_set_property; + gobject_class->get_property = gst_gl_colorscale_get_property; + + gst_element_class_set_metadata (element_class, "OpenGL color scale", + "Filter/Effect/Video", "Colorspace converter and video scaler", + "Julien Isorce "); + + filter_class->filter_texture = gst_gl_colorscale_filter_texture; +} + +static void +gst_gl_colorscale_init (GstGLColorscale * colorscale) +{ +} + +static void +gst_gl_colorscale_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_colorscale_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_gl_colorscale_filter_texture (GstGLFilter * filter, guint in_tex, + guint out_tex) +{ + GstGLColorscale *colorscale; + + colorscale = GST_GL_COLORSCALE (filter); + + gst_gl_filter_render_to_target (filter, TRUE, in_tex, out_tex, + gst_gl_colorscale_callback, colorscale); + + return TRUE; +} + +static void +gst_gl_colorscale_callback (gint width, gint height, guint texture, + gpointer stuff) +{ + GstGLFilter *filter = GST_GL_FILTER (stuff); + +#if GST_GL_HAVE_OPENGL + if (gst_gl_context_get_gl_api (filter->context) & GST_GL_API_OPENGL) { + const GstGLFuncs *gl = filter->context->gl_vtable; + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + } +#endif + + gst_gl_filter_draw_texture (filter, texture, width, height); +} diff --git a/ext/gl/gstglcolorscale.h b/ext/gl/gstglcolorscale.h new file mode 100644 index 0000000..29e306e --- /dev/null +++ b/ext/gl/gstglcolorscale.h @@ -0,0 +1,56 @@ +/* + * GStreamer + * Copyright (C) 2008 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_GLCOLORSCALE_H_ +#define _GST_GLCOLORSCALE_H_ + +#include +#include + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_GL_COLORSCALE (gst_gl_colorscale_get_type()) +#define GST_GL_COLORSCALE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_COLORSCALE,GstGLColorscale)) +#define GST_IS_GL_COLORSCALE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_COLORSCALE)) +#define GST_GL_COLORSCALE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_COLORSCALE,GstGLColorscaleClass)) +#define GST_IS_GL_COLORSCALE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_COLORSCALE)) +#define GST_GL_COLORSCALE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_COLORSCALE,GstGLColorscaleClass)) + +typedef struct _GstGLColorscale GstGLColorscale; +typedef struct _GstGLColorscaleClass GstGLColorscaleClass; + + +struct _GstGLColorscale +{ + GstGLFilter filter; +}; + +struct _GstGLColorscaleClass +{ + GstGLFilterClass filter_class; +}; + +GType gst_gl_colorscale_get_type (void); + +G_END_DECLS + +#endif /* _GST_GLCOLORSCALE_H_ */ diff --git a/ext/gl/gstgldeinterlace.c b/ext/gl/gstgldeinterlace.c new file mode 100644 index 0000000..b420fa6 --- /dev/null +++ b/ext/gl/gstgldeinterlace.c @@ -0,0 +1,351 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-deinterlace + * + * Deinterlacing using based on fragment shaders. + * + * + * Examples + * |[ + * gst-launch videotestsrc ! glupload ! gldeinterlace ! glimagesink + * ]| + * FBO (Frame Buffer Object) and GLSL (OpenGL Shading Language) are required. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstgldeinterlace.h" + +#define GST_CAT_DEFAULT gst_gl_deinterlace_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +enum +{ + PROP_0 +}; + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_deinterlace_debug, "gldeinterlace", 0, "gldeinterlace element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLDeinterlace, gst_gl_deinterlace, + GST_TYPE_GL_FILTER, DEBUG_INIT); + +static void gst_gl_deinterlace_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_gl_deinterlace_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static void gst_gl_deinterlace_reset (GstGLFilter * filter); +static gboolean gst_gl_deinterlace_init_shader (GstGLFilter * filter); +static gboolean gst_gl_deinterlace_filter (GstGLFilter * filter, + GstBuffer * inbuf, GstBuffer * outbuf); +static gboolean gst_gl_deinterlace_filter_texture (GstGLFilter * filter, + guint in_tex, guint out_tex); +static void gst_gl_deinterlace_callback (gint width, gint height, + guint texture, gpointer stuff); + +/* *INDENT-OFF* */ +static const gchar *greedyh_fragment_source = + "uniform sampler2D tex;\n" + "uniform sampler2D tex_prev;\n" + "uniform float max_comb;\n" + "uniform float motion_threshold;\n" + "uniform float motion_sense;\n" + "uniform float width;\n" + "uniform float height;\n" + + "void main () {\n" + " vec2 texcoord = gl_TexCoord[0].xy;\n" + " if (int(mod(texcoord.y * height, 2.0)) == 0) {\n" + " gl_FragColor = vec4(texture2D(tex_prev, texcoord).rgb, 1.0);\n" + " } else {\n" + " vec2 texcoord_L1_a1, texcoord_L3_a1, texcoord_L1, texcoord_L3, texcoord_L1_1, texcoord_L3_1;\n" + " vec3 L1_a1, L3_a1, L1, L3, L1_1, L3_1;\n" + + " texcoord_L1 = vec2(texcoord.x, texcoord.y - 1.0 / height);\n" + " texcoord_L3 = vec2(texcoord.x, texcoord.y + 1.0 / height);\n" + " L1 = texture2D(tex_prev, texcoord_L1).rgb;\n" + " L3 = texture2D(tex_prev, texcoord_L3).rgb;\n" + " if (texcoord.x == 1.0 && texcoord.y == 1.0) {\n" + " L1_1 = L1;\n" + " L3_1 = L3;\n" + " } else {\n" + " texcoord_L1_1 = vec2(texcoord.x + 1.0 / width, texcoord.y - 1.0 / height);\n" + " texcoord_L3_1 = vec2(texcoord.x + 1.0 / width, texcoord.y + 1.0 / height);\n" + " L1_1 = texture2D(tex_prev, texcoord_L1_1).rgb;\n" + " L3_1 = texture2D(tex_prev, texcoord_L3_1).rgb;\n" + " }\n" + + " if (int(ceil(texcoord.x + texcoord.y)) == 0) {\n" + " L1_a1 = L1;\n" + " L3_a1 = L3;\n" + " } else {\n" + " texcoord_L1_a1 = vec2(texcoord.x - 1.0 / width, texcoord.y - 1.0 / height);\n" + " texcoord_L3_a1 = vec2(texcoord.x - 1.0 / width, texcoord.y + 1.0 / height);\n" + " L1_a1 = texture2D(tex_prev, texcoord_L1_a1).rgb;\n" + " L3_a1 = texture2D(tex_prev, texcoord_L3_a1).rgb;\n" + " }\n" + //STEP 1 + " vec3 avg_a1 = (L1_a1 + L3_a1) / 2.0;\n" + " vec3 avg = (L1 + L3) / 2.0;\n" + " vec3 avg_1 = (L1_1 + L3_1) / 2.0;\n" + " vec3 avg_s = (avg_a1 + avg_1) / 2.0;\n" + " vec3 avg_sc = (avg_s + avg) / 2.0;\n" + " vec3 L2 = texture2D(tex, texcoord).rgb;\n" + " vec3 LP2 = texture2D(tex_prev, texcoord).rgb;\n" + " vec3 best;\n" + " if (abs(L2.r - avg_sc.r) < abs(LP2.r - avg_sc.r)) {\n" + " best.r = L2.r;\n" " } else {\n" + " best.r = LP2.r;\n" + " }\n" + + " if (abs(L2.g - avg_sc.g) < abs(LP2.g - avg_sc.g)) {\n" + " best.g = L2.g;\n" + " } else {\n" + " best.g = LP2.g;\n" + " }\n" + + " if (abs(L2.b - avg_sc.b) < abs(LP2.b - avg_sc.b)) {\n" + " best.b = L2.b;\n" + " } else {\n" + " best.b = LP2.b;\n" + " }\n" + //STEP 2 + " vec3 last;\n" + " last.r = clamp(best.r, max(min(L1.r, L3.r) - max_comb, 0.0), min(max(L1.r, L3.r) + max_comb, 1.0));\n" + " last.g = clamp(best.g, max(min(L1.g, L3.g) - max_comb, 0.0), min(max(L1.g, L3.g) + max_comb, 1.0));\n" + " last.b = clamp(best.b, max(min(L1.b, L3.b) - max_comb, 0.0), min(max(L1.b, L3.b) + max_comb, 1.0));\n" + //STEP 3 + " const vec3 luma = vec3 (0.299011, 0.586987, 0.114001);" + " float mov = min(max(abs(dot(L2 - LP2, luma)) - motion_threshold, 0.0) * motion_sense, 1.0);\n" + " last = last * (1.0 - mov) + avg_sc * mov;\n" + " gl_FragColor = vec4(last, 1.0);\n" + " }\n" + "}\n"; +/* *INDENT-ON* */ + +static void +gst_gl_deinterlace_class_init (GstGLDeinterlaceClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_gl_deinterlace_set_property; + gobject_class->get_property = gst_gl_deinterlace_get_property; + + gst_element_class_set_metadata (element_class, + "OpenGL deinterlacing filter", "Deinterlace", + "Deinterlacing based on fragment shaders", + "Julien Isorce "); + + GST_GL_FILTER_CLASS (klass)->filter = gst_gl_deinterlace_filter; + GST_GL_FILTER_CLASS (klass)->filter_texture = + gst_gl_deinterlace_filter_texture; + GST_GL_FILTER_CLASS (klass)->onInitFBO = gst_gl_deinterlace_init_shader; + GST_GL_FILTER_CLASS (klass)->onReset = gst_gl_deinterlace_reset; +} + +static void +gst_gl_deinterlace_init (GstGLDeinterlace * filter) +{ + filter->shader = NULL; + filter->prev_buffer = NULL; + filter->prev_tex = 0; +} + +static void +gst_gl_deinterlace_reset (GstGLFilter * filter) +{ + GstGLDeinterlace *deinterlace_filter = GST_GL_DEINTERLACE (filter); + + if (deinterlace_filter->prev_buffer) { + gst_buffer_unref (deinterlace_filter->prev_buffer); + deinterlace_filter->prev_buffer = NULL; + } + //blocking call, wait the opengl thread has destroyed the shader + if (deinterlace_filter->shader) + gst_gl_context_del_shader (filter->context, deinterlace_filter->shader); + deinterlace_filter->shader = NULL; +} + +static void +gst_gl_deinterlace_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + //GstGLDeinterlace *filter = GST_GL_DEINTERLACE (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_deinterlace_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + //GstGLDeinterlace *filter = GST_GL_DEINTERLACE (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_gl_deinterlace_init_shader (GstGLFilter * filter) +{ + GstGLDeinterlace *deinterlace_filter = GST_GL_DEINTERLACE (filter); + + //blocking call, wait the opengl thread has compiled the shader + return gst_gl_context_gen_shader (filter->context, 0, greedyh_fragment_source, + &deinterlace_filter->shader); +} + +static gboolean +gst_gl_deinterlace_filter_texture (GstGLFilter * filter, guint in_tex, + guint out_tex) +{ + GstGLDeinterlace *deinterlace_filter = GST_GL_DEINTERLACE (filter); + + //blocking call, use a FBO + gst_gl_filter_render_to_target (filter, TRUE, in_tex, out_tex, + gst_gl_deinterlace_callback, deinterlace_filter); + + return TRUE; +} + +static gboolean +gst_gl_deinterlace_filter (GstGLFilter * filter, GstBuffer * inbuf, + GstBuffer * outbuf) +{ + GstGLDeinterlace *deinterlace_filter = GST_GL_DEINTERLACE (filter); + + gst_gl_filter_filter_texture (filter, inbuf, outbuf); + + if (deinterlace_filter->prev_buffer) { + gst_buffer_unref (deinterlace_filter->prev_buffer); + } + deinterlace_filter->prev_buffer = gst_buffer_ref (inbuf); + + return TRUE; +} + +//opengl scene, params: input texture (not the output filter->texture) +static void +gst_gl_deinterlace_callback (gint width, gint height, guint texture, + gpointer stuff) +{ + GstGLDeinterlace *deinterlace_filter = GST_GL_DEINTERLACE (stuff); + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFuncs *gl = filter->context->gl_vtable; + guint temp; + + GLfloat verts[] = { -1.0, -1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0 + }; + GLfloat texcoords0[] = { 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f + }; + GLfloat texcoords1[] = { 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f + }; + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (deinterlace_filter->shader); + + gl->Enable (GL_TEXTURE_2D); + + if (G_UNLIKELY (deinterlace_filter->prev_tex == 0)) { + gst_gl_context_gen_texture (filter->context, + &deinterlace_filter->prev_tex, + GST_VIDEO_INFO_FORMAT (&filter->out_info), + GST_VIDEO_INFO_WIDTH (&filter->out_info), + GST_VIDEO_INFO_HEIGHT (&filter->out_info)); + } else { + gl->ActiveTexture (GL_TEXTURE1); + gst_gl_shader_set_uniform_1i (deinterlace_filter->shader, "tex_prev", 1); + gl->BindTexture (GL_TEXTURE_2D, deinterlace_filter->prev_tex); + } + + gl->ActiveTexture (GL_TEXTURE0); + gst_gl_shader_set_uniform_1i (deinterlace_filter->shader, "tex", 0); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gst_gl_shader_set_uniform_1f (deinterlace_filter->shader, "max_comb", + 5.0f / 255.0f); + gst_gl_shader_set_uniform_1f (deinterlace_filter->shader, "motion_threshold", + 25.0f / 255.0f); + gst_gl_shader_set_uniform_1f (deinterlace_filter->shader, "motion_sense", + 30.0f / 255.0f); + + gst_gl_shader_set_uniform_1f (deinterlace_filter->shader, "width", + GST_VIDEO_INFO_WIDTH (&filter->out_info)); + gst_gl_shader_set_uniform_1f (deinterlace_filter->shader, "height", + GST_VIDEO_INFO_HEIGHT (&filter->out_info)); + + gl->ClientActiveTexture (GL_TEXTURE0); + + gl->EnableClientState (GL_TEXTURE_COORD_ARRAY); + gl->EnableClientState (GL_VERTEX_ARRAY); + + gl->VertexPointer (2, GL_FLOAT, 0, &verts); + gl->TexCoordPointer (2, GL_FLOAT, 0, &texcoords0); + + gl->ClientActiveTexture (GL_TEXTURE1); + gl->EnableClientState (GL_TEXTURE_COORD_ARRAY); + gl->TexCoordPointer (2, GL_FLOAT, 0, &texcoords1); + + gl->DrawArrays (GL_TRIANGLE_FAN, 0, 4); + + gl->DisableClientState (GL_VERTEX_ARRAY); + gl->DisableClientState (GL_TEXTURE_COORD_ARRAY); + + gl->ClientActiveTexture (GL_TEXTURE0); + gl->DisableClientState (GL_TEXTURE_COORD_ARRAY); + + gl->Disable (GL_TEXTURE_2D); + + if (texture == filter->in_tex_id) { + temp = filter->in_tex_id; + filter->in_tex_id = deinterlace_filter->prev_tex; + deinterlace_filter->prev_tex = temp; + } else { + deinterlace_filter->prev_tex = texture; + } +} diff --git a/ext/gl/gstgldeinterlace.h b/ext/gl/gstgldeinterlace.h new file mode 100644 index 0000000..a81a2e7 --- /dev/null +++ b/ext/gl/gstgldeinterlace.h @@ -0,0 +1,55 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_GL_DEINTERLACE_H_ +#define _GST_GL_DEINTERLACE_H_ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_GL_DEINTERLACE (gst_gl_deinterlace_get_type()) +#define GST_GL_DEINTERLACE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_DEINTERLACE,GstGLDeinterlace)) +#define GST_IS_GL_DEINTERLACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_DEINTERLACE)) +#define GST_GL_DEINTERLACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_DEINTERLACE,GstGLDeinterlaceClass)) +#define GST_IS_GL_DEINTERLACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_DEINTERLACE)) +#define GST_GL_DEINTERLACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_DEINTERLACE,GstGLDeinterlaceClass)) + +typedef struct _GstGLDeinterlace GstGLDeinterlace; +typedef struct _GstGLDeinterlaceClass GstGLDeinterlaceClass; + +struct _GstGLDeinterlace +{ + GstGLFilter filter; + GstGLShader *shader; + GstBuffer *prev_buffer; + guint prev_tex; +}; + +struct _GstGLDeinterlaceClass +{ + GstGLFilterClass filter_class; +}; + +GType gst_gl_deinterlace_get_type (void); + +G_END_DECLS + +#endif /* _GST_GLFILTERLAPLACIAN_H_ */ diff --git a/ext/gl/gstgldifferencematte.c b/ext/gl/gstgldifferencematte.c new file mode 100644 index 0000000..f04c4c4 --- /dev/null +++ b/ext/gl/gstgldifferencematte.c @@ -0,0 +1,581 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-gldifferencematte. + * + * Saves a background frame and replace it with a pixbuf. + * + * + * Examples + * |[ + * gst-launch videotestsrc ! glupload ! gldifferencemate location=backgroundimagefile ! glimagesink + * ]| + * FBO (Frame Buffer Object) and GLSL (OpenGL Shading Language) are required. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "gstgldifferencematte.h" +#include "effects/gstgleffectssources.h" + +#if PNG_LIBPNG_VER >= 10400 +#define int_p_NULL NULL +#define png_infopp_NULL NULL +#endif + +#define GST_CAT_DEFAULT gst_gl_differencematte_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_differencematte_debug, "gldifferencematte", 0, "gldifferencematte element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLDifferenceMatte, gst_gl_differencematte, + GST_TYPE_GL_FILTER, DEBUG_INIT); + +static void gst_gl_differencematte_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_gl_differencematte_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static void gst_gl_differencematte_init_resources (GstGLFilter * filter); +static void gst_gl_differencematte_reset_resources (GstGLFilter * filter); + +static gboolean gst_gl_differencematte_filter_texture (GstGLFilter * filter, + guint in_tex, guint out_tex); + +static gboolean gst_gl_differencematte_loader (GstGLFilter * filter); + +enum +{ + PROP_0, + PROP_LOCATION, +}; + + +/* init resources that need a gl context */ +static void +gst_gl_differencematte_init_gl_resources (GstGLFilter * filter) +{ + GstGLDifferenceMatte *differencematte = GST_GL_DIFFERENCEMATTE (filter); + GstGLFuncs *gl = filter->context->gl_vtable; + gint i; + + for (i = 0; i < 4; i++) { + gl->GenTextures (1, &differencematte->midtexture[i]); + gl->BindTexture (GL_TEXTURE_2D, differencematte->midtexture[i]); + gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, + GST_VIDEO_INFO_WIDTH (&filter->out_info), + GST_VIDEO_INFO_HEIGHT (&filter->out_info), + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + differencematte->shader[i] = gst_gl_shader_new (filter->context); + } + + if (!gst_gl_shader_compile_and_check (differencematte->shader[0], + difference_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (GST_GL_FILTER (differencematte)->context, + "Failed to initialize difference shader"); + GST_ELEMENT_ERROR (differencematte, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + if (!gst_gl_shader_compile_and_check (differencematte->shader[1], + hconv7_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (GST_GL_FILTER (differencematte)->context, + "Failed to initialize hconv7 shader"); + GST_ELEMENT_ERROR (differencematte, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + if (!gst_gl_shader_compile_and_check (differencematte->shader[2], + vconv7_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (GST_GL_FILTER (differencematte)->context, + "Failed to initialize vconv7 shader"); + GST_ELEMENT_ERROR (differencematte, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } + + if (!gst_gl_shader_compile_and_check (differencematte->shader[3], + texture_interp_fragment_source, GST_GL_SHADER_FRAGMENT_SOURCE)) { + gst_gl_context_set_error (GST_GL_FILTER (differencematte)->context, + "Failed to initialize interp shader"); + GST_ELEMENT_ERROR (differencematte, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return; + } +} + +/* free resources that need a gl context */ +static void +gst_gl_differencematte_reset_gl_resources (GstGLFilter * filter) +{ + GstGLDifferenceMatte *differencematte = GST_GL_DIFFERENCEMATTE (filter); + GstGLFuncs *gl = filter->context->gl_vtable; + gint i; + + gl->DeleteTextures (1, &differencematte->savedbgtexture); + gl->DeleteTextures (1, &differencematte->newbgtexture); + for (i = 0; i < 4; i++) { + if (differencematte->shader[i]) { + gst_object_unref (differencematte->shader[i]); + differencematte->shader[i] = NULL; + } + if (differencematte->midtexture[i]) { + gl->DeleteTextures (1, &differencematte->midtexture[i]); + differencematte->midtexture[i] = 0; + } + } + differencematte->location = NULL; + differencematte->pixbuf = NULL; + differencematte->savedbgtexture = 0; + differencematte->newbgtexture = 0; + differencematte->bg_has_changed = FALSE; +} + +static void +gst_gl_differencematte_class_init (GstGLDifferenceMatteClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + gobject_class->set_property = gst_gl_differencematte_set_property; + gobject_class->get_property = gst_gl_differencematte_get_property; + + GST_GL_FILTER_CLASS (klass)->filter_texture = + gst_gl_differencematte_filter_texture; + GST_GL_FILTER_CLASS (klass)->display_init_cb = + gst_gl_differencematte_init_gl_resources; + GST_GL_FILTER_CLASS (klass)->display_reset_cb = + gst_gl_differencematte_reset_gl_resources; + GST_GL_FILTER_CLASS (klass)->onStart = gst_gl_differencematte_init_resources; + GST_GL_FILTER_CLASS (klass)->onStop = gst_gl_differencematte_reset_resources; + + g_object_class_install_property (gobject_class, + PROP_LOCATION, + g_param_spec_string ("location", + "Background image location", + "Background image location", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_set_metadata (element_class, + "Gstreamer OpenGL DifferenceMatte", "Filter/Effect/Video", + "Saves a background frame and replace it with a pixbuf", + "Filippo Argiolas "); +} + +static void +gst_gl_differencematte_init (GstGLDifferenceMatte * differencematte) +{ + differencematte->shader[0] = NULL; + differencematte->shader[1] = NULL; + differencematte->shader[2] = NULL; + differencematte->shader[3] = NULL; + differencematte->location = NULL; + differencematte->pixbuf = NULL; + differencematte->savedbgtexture = 0; + differencematte->newbgtexture = 0; + differencematte->bg_has_changed = FALSE; + + fill_gaussian_kernel (differencematte->kernel, 7, 30.0); +} + +static void +gst_gl_differencematte_reset_resources (GstGLFilter * filter) +{ +// GstGLDifferenceMatte* differencematte = GST_GL_DIFFERENCEMATTE(filter); +} + +static void +gst_gl_differencematte_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGLDifferenceMatte *differencematte = GST_GL_DIFFERENCEMATTE (object); + + switch (prop_id) { + case PROP_LOCATION: + if (differencematte->location != NULL) + g_free (differencematte->location); + differencematte->bg_has_changed = TRUE; + differencematte->location = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_differencematte_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstGLDifferenceMatte *differencematte = GST_GL_DIFFERENCEMATTE (object); + + switch (prop_id) { + case PROP_LOCATION: + g_value_set_string (value, differencematte->location); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_differencematte_init_resources (GstGLFilter * filter) +{ +// GstGLDifferenceMatte *differencematte = GST_GL_DIFFERENCEMATTE (filter); +} + +static void +gst_gl_differencematte_save_texture (gint width, gint height, guint texture, + gpointer stuff) +{ + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFuncs *gl = filter->context->gl_vtable; + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +static void +init_pixbuf_texture (GstGLContext * context, gpointer data) +{ + GstGLDifferenceMatte *differencematte = GST_GL_DIFFERENCEMATTE (data); + GstGLFilter *filter = GST_GL_FILTER (data); + GstGLFuncs *gl = filter->context->gl_vtable; + + gl->DeleteTextures (1, &differencematte->newbgtexture); + gl->GenTextures (1, &differencematte->newbgtexture); + gl->BindTexture (GL_TEXTURE_2D, differencematte->newbgtexture); + gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, + (gint) differencematte->pbuf_width, (gint) differencematte->pbuf_height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, differencematte->pixbuf); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if (differencematte->savedbgtexture == 0) { + gl->GenTextures (1, &differencematte->savedbgtexture); + gl->BindTexture (GL_TEXTURE_2D, differencematte->savedbgtexture); + gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, + GST_VIDEO_INFO_WIDTH (&filter->out_info), + GST_VIDEO_INFO_HEIGHT (&filter->out_info), + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } +} + +static void +gst_gl_differencematte_diff (gint width, gint height, guint texture, + gpointer stuff) +{ + GstGLDifferenceMatte *differencematte = GST_GL_DIFFERENCEMATTE (stuff); + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFuncs *gl = filter->context->gl_vtable; + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (differencematte->shader[0]); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (differencematte->shader[0], "current", 0); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, differencematte->savedbgtexture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (differencematte->shader[0], "saved", 1); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +static void +gst_gl_differencematte_hblur (gint width, gint height, guint texture, + gpointer stuff) +{ + GstGLDifferenceMatte *differencematte = GST_GL_DIFFERENCEMATTE (stuff); + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFuncs *gl = filter->context->gl_vtable; + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (differencematte->shader[1]); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (differencematte->shader[1], "tex", 0); + + gst_gl_shader_set_uniform_1fv (differencematte->shader[1], "kernel", 7, + differencematte->kernel); + gst_gl_shader_set_uniform_1f (differencematte->shader[1], "width", width); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +static void +gst_gl_differencematte_vblur (gint width, gint height, guint texture, + gpointer stuff) +{ + GstGLDifferenceMatte *differencematte = GST_GL_DIFFERENCEMATTE (stuff); + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFuncs *gl = filter->context->gl_vtable; + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (differencematte->shader[2]); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (differencematte->shader[2], "tex", 0); + + gst_gl_shader_set_uniform_1fv (differencematte->shader[2], "kernel", 7, + differencematte->kernel); + gst_gl_shader_set_uniform_1f (differencematte->shader[2], "height", height); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +static void +gst_gl_differencematte_interp (gint width, gint height, guint texture, + gpointer stuff) +{ + GstGLDifferenceMatte *differencematte = GST_GL_DIFFERENCEMATTE (stuff); + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFuncs *gl = filter->context->gl_vtable; + + gl->MatrixMode (GL_PROJECTION); + glLoadIdentity (); + + gst_gl_shader_use (differencematte->shader[3]); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (differencematte->shader[3], "blend", 0); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, differencematte->newbgtexture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (differencematte->shader[3], "base", 1); + + gl->ActiveTexture (GL_TEXTURE2); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, differencematte->midtexture[2]); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (differencematte->shader[3], "alpha", 2); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +static void +gst_gl_differencematte_identity (gint width, gint height, guint texture, + gpointer stuff) +{ + GstGLDifferenceMatte *differencematte = GST_GL_DIFFERENCEMATTE (stuff); + GstGLFilter *filter = GST_GL_FILTER (differencematte); + GstGLFuncs *gl = filter->context->gl_vtable; + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + +static gboolean +gst_gl_differencematte_filter_texture (GstGLFilter * filter, guint in_tex, + guint out_tex) +{ + GstGLDifferenceMatte *differencematte = GST_GL_DIFFERENCEMATTE (filter); + + differencematte->intexture = in_tex; + + if (differencematte->bg_has_changed && (differencematte->location != NULL)) { + + if (!gst_gl_differencematte_loader (filter)) + differencematte->pixbuf = NULL; + + /* if loader failed then context is turned off */ + gst_gl_context_thread_add (filter->context, init_pixbuf_texture, + differencematte); + + /* save current frame, needed to calculate difference between + * this frame and next ones */ + gst_gl_filter_render_to_target (filter, TRUE, in_tex, + differencematte->savedbgtexture, + gst_gl_differencematte_save_texture, differencematte); + + if (differencematte->pixbuf) { + free (differencematte->pixbuf); + differencematte->pixbuf = NULL; + } + + differencematte->bg_has_changed = FALSE; + } + + if (differencematte->savedbgtexture != 0) { + gst_gl_filter_render_to_target (filter, TRUE, in_tex, + differencematte->midtexture[0], gst_gl_differencematte_diff, + differencematte); + gst_gl_filter_render_to_target (filter, FALSE, + differencematte->midtexture[0], differencematte->midtexture[1], + gst_gl_differencematte_hblur, differencematte); + gst_gl_filter_render_to_target (filter, FALSE, + differencematte->midtexture[1], differencematte->midtexture[2], + gst_gl_differencematte_vblur, differencematte); + gst_gl_filter_render_to_target (filter, TRUE, in_tex, out_tex, + gst_gl_differencematte_interp, differencematte); + } else { + gst_gl_filter_render_to_target (filter, TRUE, in_tex, out_tex, + gst_gl_differencematte_identity, differencematte); + } + + return TRUE; +} + +static void +user_warning_fn (png_structp png_ptr, png_const_charp warning_msg) +{ + g_warning ("%s\n", warning_msg); +} + +#define LOAD_ERROR(msg) { GST_WARNING ("unable to load %s: %s", differencematte->location, msg); return FALSE; } + +static gboolean +gst_gl_differencematte_loader (GstGLFilter * filter) +{ + GstGLDifferenceMatte *differencematte = GST_GL_DIFFERENCEMATTE (filter); + + png_structp png_ptr; + png_infop info_ptr; + guint sig_read = 0; + png_uint_32 width = 0; + png_uint_32 height = 0; + gint bit_depth = 0; + gint color_type = 0; + gint interlace_type = 0; + png_FILE_p fp = NULL; + guint y = 0; + guchar **rows = NULL; + gint filler; + + if (!filter->context) + return TRUE; + + if ((fp = fopen (differencematte->location, "rb")) == NULL) + LOAD_ERROR ("file not found"); + + png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (png_ptr == NULL) { + fclose (fp); + LOAD_ERROR ("failed to initialize the png_struct"); + } + + png_set_error_fn (png_ptr, NULL, NULL, user_warning_fn); + + info_ptr = png_create_info_struct (png_ptr); + if (info_ptr == NULL) { + fclose (fp); + png_destroy_read_struct (&png_ptr, png_infopp_NULL, png_infopp_NULL); + LOAD_ERROR ("failed to initialize the memory for image information"); + } + + png_init_io (png_ptr, fp); + + png_set_sig_bytes (png_ptr, sig_read); + + png_read_info (png_ptr, info_ptr); + + png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, + &interlace_type, int_p_NULL, int_p_NULL); + + if (color_type == PNG_COLOR_TYPE_RGB) { + filler = 0xff; + png_set_filler (png_ptr, filler, PNG_FILLER_AFTER); + color_type = PNG_COLOR_TYPE_RGB_ALPHA; + } + + if (color_type != PNG_COLOR_TYPE_RGB_ALPHA) { + fclose (fp); + png_destroy_read_struct (&png_ptr, png_infopp_NULL, png_infopp_NULL); + LOAD_ERROR ("color type is not rgb"); + } + + differencematte->pbuf_width = width; + differencematte->pbuf_height = height; + + differencematte->pixbuf = + (guchar *) malloc (sizeof (guchar) * width * height * 4); + + rows = (guchar **) malloc (sizeof (guchar *) * height); + + for (y = 0; y < height; ++y) + rows[y] = (guchar *) (differencematte->pixbuf + y * width * 4); + + png_read_image (png_ptr, rows); + + free (rows); + + png_read_end (png_ptr, info_ptr); + png_destroy_read_struct (&png_ptr, &info_ptr, png_infopp_NULL); + fclose (fp); + + return TRUE; +} diff --git a/ext/gl/gstgldifferencematte.h b/ext/gl/gstgldifferencematte.h new file mode 100644 index 0000000..a153c38 --- /dev/null +++ b/ext/gl/gstgldifferencematte.h @@ -0,0 +1,59 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_GL_DIFFERENCEMATTE_H_ +#define _GST_GL_DIFFERENCEMATTE_H_ + +#include + +#define GST_TYPE_GL_DIFFERENCEMATTE (gst_gl_differencematte_get_type()) +#define GST_GL_DIFFERENCEMATTE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_GL_DIFFERENCEMATTE,GstGLDifferenceMatte)) +#define GST_IS_GL_DIFFERENCEMATTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_GL_DIFFERENCEMATTE)) +#define GST_GL_DIFFERENCEMATTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) , GST_TYPE_GL_DIFFERENCEMATTE,GstGLDifferenceMatteClass)) +#define GST_IS_GL_DIFFERENCEMATTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) , GST_TYPE_GL_DIFFERENCEMATTE)) +#define GST_GL_DIFFERENCEMATTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) , GST_TYPE_GL_DIFFERENCEMATTE,GstGLDifferenceMatteClass)) + +typedef struct _GstGLDifferenceMatte GstGLDifferenceMatte; +typedef struct _GstGLDifferenceMatteClass GstGLDifferenceMatteClass; + +struct _GstGLDifferenceMatte +{ + GstGLFilter filter; + + GstGLShader *shader[4]; + + gchar *location; + gboolean bg_has_changed; + + guchar *pixbuf; + gint pbuf_width, pbuf_height; + GLuint savedbgtexture; + GLuint newbgtexture; + GLuint midtexture[4]; + GLuint intexture; + float kernel[7]; +}; + +struct _GstGLDifferenceMatteClass +{ + GstGLFilterClass filter_class; +}; + +#endif /* _GST_GL_DIFFERENCEMATTE_H_ */ diff --git a/ext/gl/gstgleffects.c b/ext/gl/gstgleffects.c new file mode 100644 index 0000000..4f7338a --- /dev/null +++ b/ext/gl/gstgleffects.c @@ -0,0 +1,398 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-gleffects. + * + * GL Shading Language effects. + * + * + * Examples + * |[ + * gst-launch videotestsrc ! glupload ! gleffects effect=5 ! glimagesink + * ]| + * FBO (Frame Buffer Object) and GLSL (OpenGL Shading Language) are required. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "gstgleffects.h" + +#define GST_TYPE_GL_EFFECTS (gst_gl_effects_get_type()) +#define GST_GL_EFFECTS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_GL_EFFECTS,GstGLEffects)) +#define GST_IS_GL_EFFECTS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_GL_EFFECTS)) +#define GST_GL_EFFECTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) , GST_TYPE_GL_EFFECTS,GstGLEffectsClass)) +#define GST_IS_GL_EFFECTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) , GST_TYPE_GL_EFFECTS)) +#define GST_GL_EFFECTS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) , GST_TYPE_GL_EFFECTS,GstGLEffectsClass)) + +#define GST_CAT_DEFAULT gst_gl_effects_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_effects_debug, "gleffects", 0, "gleffects element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLEffects, gst_gl_effects, GST_TYPE_GL_FILTER, + DEBUG_INIT); + +static void gst_gl_effects_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_effects_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void gst_gl_effects_init_resources (GstGLFilter * filter); +static void gst_gl_effects_reset_resources (GstGLFilter * filter); + +static gboolean gst_gl_effects_on_init_gl_context (GstGLFilter * filter); + +static void gst_gl_effects_ghash_func_clean (gpointer key, gpointer value, + gpointer data); + +static gboolean gst_gl_effects_filter_texture (GstGLFilter * filter, + guint in_tex, guint out_tex); + +/* dont' forget to edit the following when a new effect is added */ +typedef enum +{ + GST_GL_EFFECT_IDENTITY, + GST_GL_EFFECT_MIRROR, + GST_GL_EFFECT_SQUEEZE, + GST_GL_EFFECT_STRETCH, + GST_GL_EFFECT_TUNNEL, + GST_GL_EFFECT_FISHEYE, + GST_GL_EFFECT_TWIRL, + GST_GL_EFFECT_BULGE, + GST_GL_EFFECT_SQUARE, + GST_GL_EFFECT_HEAT, + GST_GL_EFFECT_SEPIA, + GST_GL_EFFECT_XPRO, + GST_GL_EFFECT_LUMA_XPRO, + GST_GL_EFFECT_XRAY, + GST_GL_EFFECT_SIN, + GST_GL_EFFECT_GLOW, + GST_GL_N_EFFECTS +} GstGLEffectsEffect; + +#define GST_TYPE_GL_EFFECTS_EFFECT (gst_gl_effects_effect_get_type ()) +static GType +gst_gl_effects_effect_get_type (void) +{ + static GType gl_effects_effect_type = 0; + static const GEnumValue effect_types[] = { + {GST_GL_EFFECT_IDENTITY, "Do nothing Effect", "identity"}, + {GST_GL_EFFECT_MIRROR, "Mirror Effect", "mirror"}, + {GST_GL_EFFECT_SQUEEZE, "Squeeze Effect", "squeeze"}, +#if GST_GL_HAVE_OPENGL + {GST_GL_EFFECT_STRETCH, "Stretch Effect", "stretch"}, + {GST_GL_EFFECT_FISHEYE, "FishEye Effect", "fisheye"}, + {GST_GL_EFFECT_TWIRL, "Twirl Effect", "twirl"}, + {GST_GL_EFFECT_BULGE, "Bulge Effect", "bulge"}, + {GST_GL_EFFECT_TUNNEL, "Light Tunnel Effect", "tunnel"}, + {GST_GL_EFFECT_SQUARE, "Square Effect", "square"}, + {GST_GL_EFFECT_HEAT, "Heat Signature Effect", "heat"}, + {GST_GL_EFFECT_SEPIA, "Sepia Toning Effect", "sepia"}, + {GST_GL_EFFECT_XPRO, "Cross Processing Effect", "xpro"}, + {GST_GL_EFFECT_LUMA_XPRO, "Luma Cross Processing Effect", "lumaxpro"}, + {GST_GL_EFFECT_XRAY, "Glowing negative effect", "xray"}, + {GST_GL_EFFECT_SIN, "All Grey but Red Effect", "sin"}, + {GST_GL_EFFECT_GLOW, "Glow Lighting Effect", "glow"}, +#endif + {0, NULL, NULL} + }; + + if (!gl_effects_effect_type) { + gl_effects_effect_type = + g_enum_register_static ("GstGLEffectsEffect", effect_types); + } + return gl_effects_effect_type; +} + +static void +gst_gl_effects_set_effect (GstGLEffects * effects, gint effect_type) +{ + + switch (effect_type) { + case GST_GL_EFFECT_IDENTITY: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_identity; + break; + case GST_GL_EFFECT_MIRROR: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_mirror; + break; + case GST_GL_EFFECT_SQUEEZE: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_squeeze; + break; +#if GST_GL_HAVE_OPENGL + case GST_GL_EFFECT_STRETCH: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_stretch; + break; + case GST_GL_EFFECT_TUNNEL: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_tunnel; + break; + case GST_GL_EFFECT_FISHEYE: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_fisheye; + break; + case GST_GL_EFFECT_TWIRL: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_twirl; + break; + case GST_GL_EFFECT_BULGE: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_bulge; + break; + case GST_GL_EFFECT_SQUARE: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_square; + break; + case GST_GL_EFFECT_HEAT: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_heat; + break; + case GST_GL_EFFECT_SEPIA: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_sepia; + break; + case GST_GL_EFFECT_XPRO: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_xpro; + break; + case GST_GL_EFFECT_LUMA_XPRO: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_luma_xpro; + break; + case GST_GL_EFFECT_XRAY: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_xray; + break; + case GST_GL_EFFECT_SIN: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_sin; + break; + case GST_GL_EFFECT_GLOW: + effects->effect = (GstGLEffectProcessFunc) gst_gl_effects_glow; + break; +#endif + default: + g_assert_not_reached (); + } + effects->current_effect = effect_type; +} + +/* init resources that need a gl context */ +static void +gst_gl_effects_init_gl_resources (GstGLFilter * filter) +{ + GstGLEffects *effects = GST_GL_EFFECTS (filter); + gint i; + + for (i = 0; i < NEEDED_TEXTURES; i++) { + glGenTextures (1, &effects->midtexture[i]); + glBindTexture (GL_TEXTURE_2D, effects->midtexture[i]); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, + GST_VIDEO_INFO_WIDTH (&filter->out_info), + GST_VIDEO_INFO_HEIGHT (&filter->out_info), + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } +} + +/* free resources that need a gl context */ +static void +gst_gl_effects_reset_gl_resources (GstGLFilter * filter) +{ + GstGLEffects *effects = GST_GL_EFFECTS (filter); + gint i; + + for (i = 0; i < NEEDED_TEXTURES; i++) { + glDeleteTextures (1, &effects->midtexture[i]); + effects->midtexture[i] = 0; + } + for (i = 0; i < GST_GL_EFFECTS_N_CURVES; i++) { + glDeleteTextures (1, &effects->curve[i]); + effects->curve[i] = 0; + } +} + +static void +gst_gl_effects_class_init (GstGLEffectsClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_gl_effects_set_property; + gobject_class->get_property = gst_gl_effects_get_property; + + GST_GL_FILTER_CLASS (klass)->filter_texture = gst_gl_effects_filter_texture; + GST_GL_FILTER_CLASS (klass)->display_init_cb = + gst_gl_effects_init_gl_resources; + GST_GL_FILTER_CLASS (klass)->display_reset_cb = + gst_gl_effects_reset_gl_resources; + GST_GL_FILTER_CLASS (klass)->onStart = gst_gl_effects_init_resources; + GST_GL_FILTER_CLASS (klass)->onStop = gst_gl_effects_reset_resources; + GST_GL_FILTER_CLASS (klass)->onInitFBO = gst_gl_effects_on_init_gl_context; + + g_object_class_install_property (gobject_class, + PROP_EFFECT, + g_param_spec_enum ("effect", + "Effect", + "Select which effect apply to GL video texture", + GST_TYPE_GL_EFFECTS_EFFECT, + GST_GL_EFFECT_IDENTITY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, + PROP_HSWAP, + g_param_spec_boolean ("hswap", + "Horizontal Swap", + "Switch video texture left to right, useful with webcams", + FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_set_metadata (element_class, + "Gstreamer OpenGL Effects", "Filter/Effect/Video", + "GL Shading Language effects", + "Filippo Argiolas "); +} + +static void +set_horizontal_swap (GstGLContext * context, gpointer data) +{ +#if GST_GL_HAVE_OPENGL + GstGLFuncs *gl = context->gl_vtable; + + if (gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL) { + const gfloat mirrormatrix[16] = { + -1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 + }; + + gl->MatrixMode (GL_MODELVIEW); + gl->LoadMatrixf (mirrormatrix); + } +#endif +} + +static void +gst_gl_effects_init (GstGLEffects * effects) +{ + effects->effect = gst_gl_effects_identity; + effects->horizontal_swap = FALSE; +} + +static void +gst_gl_effects_ghash_func_clean (gpointer key, gpointer value, gpointer data) +{ + GstGLShader *shader = (GstGLShader *) value; + GstGLFilter *filter = (GstGLFilter *) data; + + //blocking call, wait the opengl thread has destroyed the shader + gst_gl_context_del_shader (filter->context, shader); + + value = NULL; +} + +static void +gst_gl_effects_reset_resources (GstGLFilter * filter) +{ + GstGLEffects *effects = GST_GL_EFFECTS (filter); + + /* release shaders in the gl thread */ + g_hash_table_foreach (effects->shaderstable, gst_gl_effects_ghash_func_clean, + filter); + + /* clean the htable without calling values destructors + * because shaders have been released in the glthread + * through the foreach func */ + g_hash_table_unref (effects->shaderstable); + effects->shaderstable = NULL; +} + +static void +gst_gl_effects_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGLEffects *effects = GST_GL_EFFECTS (object); + + switch (prop_id) { + case PROP_EFFECT: + gst_gl_effects_set_effect (effects, g_value_get_enum (value)); + break; + case PROP_HSWAP: + effects->horizontal_swap = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_effects_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstGLEffects *effects = GST_GL_EFFECTS (object); + + switch (prop_id) { + case PROP_EFFECT: + g_value_set_enum (value, effects->current_effect); + break; + case PROP_HSWAP: + g_value_set_boolean (value, effects->horizontal_swap); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_effects_init_resources (GstGLFilter * filter) +{ + GstGLEffects *effects = GST_GL_EFFECTS (filter); + gint i; + + effects->shaderstable = g_hash_table_new (g_str_hash, g_str_equal); + + for (i = 0; i < NEEDED_TEXTURES; i++) { + effects->midtexture[i] = 0; + } + for (i = 0; i < GST_GL_EFFECTS_N_CURVES; i++) { + effects->curve[i] = 0; + } +} + +static gboolean +gst_gl_effects_on_init_gl_context (GstGLFilter * filter) +{ + return TRUE; +} + +static gboolean +gst_gl_effects_filter_texture (GstGLFilter * filter, guint in_tex, + guint out_tex) +{ + GstGLEffects *effects = GST_GL_EFFECTS (filter); + + effects->intexture = in_tex; + effects->outtexture = out_tex; + + if (effects->horizontal_swap == TRUE) + gst_gl_context_thread_add (filter->context, set_horizontal_swap, effects); + + effects->effect (effects); + + return TRUE; +} diff --git a/ext/gl/gstgleffects.h b/ext/gl/gstgleffects.h new file mode 100644 index 0000000..71bec25 --- /dev/null +++ b/ext/gl/gstgleffects.h @@ -0,0 +1,105 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_GL_EFFECTS_H__ +#define __GST_GL_EFFECTS_H__ + +#include +#include "effects/gstgleffectssources.h" + +G_BEGIN_DECLS + + +#define GST_TYPE_GL_EFFECTS (gst_gl_effects_get_type()) +#define GST_GL_EFFECTS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_GL_EFFECTS,GstGLEffects)) +#define GST_IS_GL_EFFECTS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_GL_EFFECTS)) +#define GST_GL_EFFECTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) , GST_TYPE_GL_EFFECTS,GstGLEffectsClass)) +#define GST_IS_GL_EFFECTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) , GST_TYPE_GL_EFFECTS)) +#define GST_GL_EFFECTS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) , GST_TYPE_GL_EFFECTS,GstGLEffectsClass)) + +typedef struct _GstGLEffects GstGLEffects; +typedef struct _GstGLEffectsClass GstGLEffectsClass; + +typedef void (* GstGLEffectProcessFunc) (GstGLEffects *effects); + +#define NEEDED_TEXTURES 5 + +enum { + GST_GL_EFFECTS_CURVE_HEAT, + GST_GL_EFFECTS_CURVE_SEPIA, + GST_GL_EFFECTS_CURVE_XPRO, + GST_GL_EFFECTS_CURVE_LUMA_XPRO, + GST_GL_EFFECTS_CURVE_XRAY, + GST_GL_EFFECTS_N_CURVES +}; + +struct _GstGLEffects +{ + GstGLFilter filter; + + GstGLEffectProcessFunc effect; + gint current_effect; + + GLuint intexture; + GLuint midtexture[NEEDED_TEXTURES]; + GLuint outtexture; + + GLuint curve[GST_GL_EFFECTS_N_CURVES]; + + GHashTable *shaderstable; + + gboolean horizontal_swap; /* switch left to right */ +}; + +struct _GstGLEffectsClass +{ + GstGLFilterClass filter_class; +}; + +enum +{ + PROP_0, + PROP_EFFECT, + PROP_HSWAP +}; + + +GType gst_gl_effects_get_type (void); + +void gst_gl_effects_identity (GstGLEffects *effects); +void gst_gl_effects_mirror (GstGLEffects *effects); +void gst_gl_effects_squeeze (GstGLEffects *effects); +void gst_gl_effects_stretch (GstGLEffects *effects); +void gst_gl_effects_tunnel (GstGLEffects *effects); +void gst_gl_effects_fisheye (GstGLEffects *effects); +void gst_gl_effects_twirl (GstGLEffects *effects); +void gst_gl_effects_bulge (GstGLEffects *effects); +void gst_gl_effects_square (GstGLEffects *effects); +void gst_gl_effects_heat (GstGLEffects *effects); +void gst_gl_effects_sepia (GstGLEffects *effects); +void gst_gl_effects_xpro (GstGLEffects *effects); +void gst_gl_effects_xray (GstGLEffects *effects); +void gst_gl_effects_luma_xpro (GstGLEffects *effects); +void gst_gl_effects_sin (GstGLEffects *effects); +void gst_gl_effects_glow (GstGLEffects *effects); + +G_END_DECLS + +#endif /*__GST_GL_EFFECTS_H__ */ diff --git a/ext/gl/gstglfilterapp.c b/ext/gl/gstglfilterapp.c new file mode 100644 index 0000000..ba60a46 --- /dev/null +++ b/ext/gl/gstglfilterapp.c @@ -0,0 +1,214 @@ +/* + * GStreamer + * Copyright (C) 2008 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-glfilterapp + * + * The resize and redraw callbacks can be set from a client code. + * + * + * CLient callbacks + * + * The graphic scene can be written from a client code through the + * two glfilterapp properties. + * + * + * + * Examples + * see gst-plugins-gl/tests/examples/generic/recordgraphic + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstglfilterapp.h" + +#define GST_CAT_DEFAULT gst_gl_filter_app_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +enum +{ + PROP_0, + PROP_CLIENT_RESHAPE_CALLBACK, + PROP_CLIENT_DRAW_CALLBACK, + PROP_CLIENT_DATA +}; + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_filter_app_debug, "glfilterapp", 0, "glfilterapp element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLFilterApp, gst_gl_filter_app, + GST_TYPE_GL_FILTER, DEBUG_INIT); + +static void gst_gl_filter_app_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_filter_app_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_gl_filter_app_set_caps (GstGLFilter * filter, + GstCaps * incaps, GstCaps * outcaps); +static gboolean gst_gl_filter_app_filter_texture (GstGLFilter * filter, + guint in_tex, guint out_tex); +static void gst_gl_filter_app_callback (gint width, gint height, guint texture, + gpointer stuff); + + +static void +gst_gl_filter_app_class_init (GstGLFilterAppClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_gl_filter_app_set_property; + gobject_class->get_property = gst_gl_filter_app_get_property; + + GST_GL_FILTER_CLASS (klass)->set_caps = gst_gl_filter_app_set_caps; + GST_GL_FILTER_CLASS (klass)->filter_texture = + gst_gl_filter_app_filter_texture; + + g_object_class_install_property (gobject_class, PROP_CLIENT_RESHAPE_CALLBACK, + g_param_spec_pointer ("client-reshape-callback", + "Client reshape callback", + "Define a custom reshape callback in a client code", + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_CLIENT_DRAW_CALLBACK, + g_param_spec_pointer ("client-draw-callback", "Client draw callback", + "Define a custom draw callback in a client code", + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_CLIENT_DATA, + g_param_spec_pointer ("client-data", "Client data", + "Pass data to the draw and reshape callbacks", + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_set_metadata (element_class, + "OpenGL application filter", "Filter/Effect", + "Use client callbacks to define the scene", + "Julien Isorce "); +} + +static void +gst_gl_filter_app_init (GstGLFilterApp * filter) +{ + filter->clientReshapeCallback = NULL; + filter->clientDrawCallback = NULL; + filter->client_data = NULL; +} + +static void +gst_gl_filter_app_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGLFilterApp *filter = GST_GL_FILTER_APP (object); + + switch (prop_id) { + case PROP_CLIENT_RESHAPE_CALLBACK: + { + filter->clientReshapeCallback = g_value_get_pointer (value); + break; + } + case PROP_CLIENT_DRAW_CALLBACK: + { + filter->clientDrawCallback = g_value_get_pointer (value); + break; + } + case PROP_CLIENT_DATA: + { + filter->client_data = g_value_get_pointer (value); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_filter_app_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + //GstGLFilterApp* filter = GST_GL_FILTER_APP (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_gl_filter_app_set_caps (GstGLFilter * filter, GstCaps * incaps, + GstCaps * outcaps) +{ + //GstGLFilterApp* app_filter = GST_GL_FILTER_APP(filter); + + return TRUE; +} + +static gboolean +gst_gl_filter_app_filter_texture (GstGLFilter * filter, guint in_tex, + guint out_tex) +{ + GstGLFilterApp *app_filter = GST_GL_FILTER_APP (filter); + + if (app_filter->clientDrawCallback) { + //blocking call, use a FBO + gst_gl_context_use_fbo (filter->context, + GST_VIDEO_INFO_WIDTH (&filter->out_info), + GST_VIDEO_INFO_HEIGHT (&filter->out_info), + filter->fbo, filter->depthbuffer, out_tex, + app_filter->clientDrawCallback, + GST_VIDEO_INFO_WIDTH (&filter->in_info), + GST_VIDEO_INFO_HEIGHT (&filter->in_info), + in_tex, 45, + (gfloat) GST_VIDEO_INFO_WIDTH (&filter->out_info) / + (gfloat) GST_VIDEO_INFO_HEIGHT (&filter->out_info), + 0.1, 100, GST_GL_DISPLAY_PROJECTION_PERSPECTIVE, + app_filter->client_data); + } + //default + else { + //blocking call, use a FBO + gst_gl_filter_render_to_target (filter, TRUE, in_tex, out_tex, + gst_gl_filter_app_callback, filter); + } + + return TRUE; +} + +//opengl scene, params: input texture (not the output filter->texture) +static void +gst_gl_filter_app_callback (gint width, gint height, guint texture, + gpointer stuff) +{ + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFuncs *gl = filter->context->gl_vtable; + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} diff --git a/ext/gl/gstglfilterapp.h b/ext/gl/gstglfilterapp.h new file mode 100644 index 0000000..f582ae3 --- /dev/null +++ b/ext/gl/gstglfilterapp.h @@ -0,0 +1,55 @@ +/* + * GStreamer + * Copyright (C) 2008 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_GL_FILTERAPP_H_ +#define _GST_GL_FILTERAPP_H_ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_GL_FILTER_APP (gst_gl_filter_app_get_type()) +#define GST_GL_FILTER_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_FILTER_APP,GstGLFilterApp)) +#define GST_IS_GL_FILTER_APP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_FILTER_APP)) +#define GST_GL_FILTER_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_FILTER_APP,GstGLFilterAppClass)) +#define GST_IS_GL_FILTER_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_FILTER_APP)) +#define GST_GL_FILTER_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_FILTER_APP,GstGLFilterAppClass)) +typedef struct _GstGLFilterApp GstGLFilterApp; +typedef struct _GstGLFilterAppClass GstGLFilterAppClass; + +struct _GstGLFilterApp +{ + GstGLFilter filter; + + CRCB clientReshapeCallback; + GLCB clientDrawCallback; + gpointer client_data; +}; + +struct _GstGLFilterAppClass +{ + GstGLFilterClass filter_class; +}; + +GType gst_gl_glfilterapp_get_type (void); + +G_END_DECLS + +#endif /* _GST_GLFILTERAPP_H_ */ diff --git a/ext/gl/gstglfilterblur.c b/ext/gl/gstglfilterblur.c new file mode 100644 index 0000000..fd275ec --- /dev/null +++ b/ext/gl/gstglfilterblur.c @@ -0,0 +1,257 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-glfilterblur + * + * Blur with 9x9 separable convolution. + * + * + * Examples + * |[ + * gst-launch videotestsrc ! glupload ! glfilterblur ! glimagesink + * ]| + * FBO (Frame Buffer Object) and GLSL (OpenGL Shading Language) are required. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstglfilterblur.h" +#include "effects/gstgleffectssources.h" + +#define GST_CAT_DEFAULT gst_gl_filterblur_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_filterblur_debug, "glfilterblur", 0, "glfilterblur element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLFilterBlur, gst_gl_filterblur, + GST_TYPE_GL_FILTER, DEBUG_INIT); + +static void gst_gl_filterblur_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_filterblur_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_gl_filter_filterblur_reset (GstGLFilter * filter); + +static gboolean gst_gl_filterblur_init_shader (GstGLFilter * filter); +static gboolean gst_gl_filterblur_filter_texture (GstGLFilter * filter, + guint in_tex, guint out_tex); +static void gst_gl_filterblur_hcallback (gint width, gint height, guint texture, + gpointer stuff); +static void gst_gl_filterblur_vcallback (gint width, gint height, guint texture, + gpointer stuff); + + +static void +gst_gl_filterblur_init_resources (GstGLFilter * filter) +{ + GstGLFilterBlur *filterblur = GST_GL_FILTERBLUR (filter); + GstGLFuncs *gl = filter->context->gl_vtable; + + gl->GenTextures (1, &filterblur->midtexture); + gl->BindTexture (GL_TEXTURE_2D, filterblur->midtexture); + gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, + GST_VIDEO_INFO_WIDTH (&filter->out_info), + GST_VIDEO_INFO_HEIGHT (&filter->out_info), + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +static void +gst_gl_filterblur_reset_resources (GstGLFilter * filter) +{ + GstGLFilterBlur *filterblur = GST_GL_FILTERBLUR (filter); + GstGLFuncs *gl = filter->context->gl_vtable; + + gl->DeleteTextures (1, &filterblur->midtexture); +} + +static void +gst_gl_filterblur_class_init (GstGLFilterBlurClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_gl_filterblur_set_property; + gobject_class->get_property = gst_gl_filterblur_get_property; + + gst_element_class_set_metadata (element_class, "Gstreamer OpenGL Blur", + "Filter/Effect/Video", "Blur with 9x9 separable convolution", + "Filippo Argiolas "); + + GST_GL_FILTER_CLASS (klass)->filter_texture = + gst_gl_filterblur_filter_texture; + GST_GL_FILTER_CLASS (klass)->display_init_cb = + gst_gl_filterblur_init_resources; + GST_GL_FILTER_CLASS (klass)->display_reset_cb = + gst_gl_filterblur_reset_resources; + GST_GL_FILTER_CLASS (klass)->onInitFBO = gst_gl_filterblur_init_shader; + GST_GL_FILTER_CLASS (klass)->onReset = gst_gl_filter_filterblur_reset; +} + +static void +gst_gl_filterblur_init (GstGLFilterBlur * filterblur) +{ + filterblur->shader0 = NULL; + filterblur->shader1 = NULL; + filterblur->midtexture = 0; + /* gaussian kernel (well, actually vector), size 9, standard + * deviation 3.0 */ + /* FIXME: eventually make this a runtime property */ + fill_gaussian_kernel (filterblur->gauss_kernel, 7, 3.0); +} + +static void +gst_gl_filter_filterblur_reset (GstGLFilter * filter) +{ + GstGLFilterBlur *filterblur = GST_GL_FILTERBLUR (filter); + + //blocking call, wait the opengl thread has destroyed the shader + if (filterblur->shader0) + gst_gl_context_del_shader (filter->context, filterblur->shader0); + filterblur->shader0 = NULL; + + //blocking call, wait the opengl thread has destroyed the shader + if (filterblur->shader1) + gst_gl_context_del_shader (filter->context, filterblur->shader1); + filterblur->shader1 = NULL; +} + +static void +gst_gl_filterblur_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + /* GstGLFilterBlur *filterblur = GST_GL_FILTERBLUR (object); */ + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_filterblur_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + /* GstGLFilterBlur *filterblur = GST_GL_FILTERBLUR (object); */ + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_gl_filterblur_init_shader (GstGLFilter * filter) +{ + GstGLFilterBlur *blur_filter = GST_GL_FILTERBLUR (filter); + + //blocking call, wait the opengl thread has compiled the shader + if (!gst_gl_context_gen_shader (filter->context, 0, hconv7_fragment_source, + &blur_filter->shader0)) + return FALSE; + + //blocking call, wait the opengl thread has compiled the shader + if (!gst_gl_context_gen_shader (filter->context, 0, vconv7_fragment_source, + &blur_filter->shader1)) + return FALSE; + + return TRUE; +} + +static gboolean +gst_gl_filterblur_filter_texture (GstGLFilter * filter, guint in_tex, + guint out_tex) +{ + GstGLFilterBlur *filterblur = GST_GL_FILTERBLUR (filter); + + gst_gl_filter_render_to_target (filter, TRUE, in_tex, + filterblur->midtexture, gst_gl_filterblur_hcallback, filterblur); + + gst_gl_filter_render_to_target (filter, FALSE, filterblur->midtexture, + out_tex, gst_gl_filterblur_vcallback, filterblur); + + return TRUE; +} + +static void +gst_gl_filterblur_hcallback (gint width, gint height, guint texture, + gpointer stuff) +{ + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFilterBlur *filterblur = GST_GL_FILTERBLUR (filter); + GstGLFuncs *gl = filter->context->gl_vtable; + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (filterblur->shader0); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (filterblur->shader0, "tex", 1); + gst_gl_shader_set_uniform_1fv (filterblur->shader0, "kernel", 7, + filterblur->gauss_kernel); + gst_gl_shader_set_uniform_1f (filterblur->shader0, "width", width); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} + + +static void +gst_gl_filterblur_vcallback (gint width, gint height, guint texture, + gpointer stuff) +{ + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFilterBlur *filterblur = GST_GL_FILTERBLUR (filter); + GstGLFuncs *gl = filter->context->gl_vtable; + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (filterblur->shader1); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (filterblur->shader1, "tex", 1); + gst_gl_shader_set_uniform_1fv (filterblur->shader1, "kernel", 7, + filterblur->gauss_kernel); + gst_gl_shader_set_uniform_1f (filterblur->shader1, "height", height); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} diff --git a/ext/gl/gstglfilterblur.h b/ext/gl/gstglfilterblur.h new file mode 100644 index 0000000..5561cb5 --- /dev/null +++ b/ext/gl/gstglfilterblur.h @@ -0,0 +1,53 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_GL_FILTERBLUR_H_ +#define _GST_GL_FILTERBLUR_H_ + +#include + +#define GST_TYPE_GL_FILTERBLUR (gst_gl_filterblur_get_type()) +#define GST_GL_FILTERBLUR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_FILTERBLUR,GstGLFilterBlur)) +#define GST_IS_GL_FILTERBLUR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_FILTERBLUR)) +#define GST_GL_FILTERBLUR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_FILTERBLUR,GstGLFilterBlurClass)) +#define GST_IS_GL_FILTERBLUR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_FILTERBLUR)) +#define GST_GL_FILTERBLUR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_FILTERBLUR,GstGLFilterBlurClass)) + +typedef struct _GstGLFilterBlur GstGLFilterBlur; +typedef struct _GstGLFilterBlurClass GstGLFilterBlurClass; + +struct _GstGLFilterBlur +{ + GstGLFilter filter; + GstGLShader *shader0; + GstGLShader *shader1; + + GLuint midtexture; + float gauss_kernel[7]; +}; + +struct _GstGLFilterBlurClass +{ + GstGLFilterClass filter_class; +}; + +GType gst_gl_glfilterblur_get_type (void); + +#endif /* _GST_GL_FILTERBLUR_H_ */ diff --git a/ext/gl/gstglfiltercube.c b/ext/gl/gstglfiltercube.c new file mode 100644 index 0000000..35bf677 --- /dev/null +++ b/ext/gl/gstglfiltercube.c @@ -0,0 +1,561 @@ +/* + * GStreamer + * Copyright (C) 2008 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-glfiltercube + * + * The resize and redraw callbacks can be set from a client code. + * + * + * Examples + * |[ + * gst-launch -v videotestsrc ! glupload ! glfiltercube ! glimagesink + * ]| A pipeline to mpa textures on the 6 cube faces.. + * FBO is required. + * |[ + * gst-launch -v videotestsrc ! glupload ! glfiltercube ! video/x-raw-gl, width=640, height=480 ! glimagesink + * ]| Resize scene after drawing the cube. + * The scene size is greater than the input video size. + |[ + * gst-launch -v videotestsrc ! glupload ! video/x-raw-gl, width=640, height=480 ! glfiltercube ! glimagesink + * ]| Resize scene before drawing the cube. + * The scene size is greater than the input video size. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "gstglfiltercube.h" + +#define GST_CAT_DEFAULT gst_gl_filter_cube_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +enum +{ + PROP_0, + PROP_RED, + PROP_GREEN, + PROP_BLUE, + PROP_FOVY, + PROP_ASPECT, + PROP_ZNEAR, + PROP_ZFAR +}; + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_filter_cube_debug, "glfiltercube", 0, "glfiltercube element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLFilterCube, gst_gl_filter_cube, + GST_TYPE_GL_FILTER, DEBUG_INIT); + +static void gst_gl_filter_cube_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_filter_cube_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_gl_filter_cube_set_caps (GstGLFilter * filter, + GstCaps * incaps, GstCaps * outcaps); +#if GST_GL_HAVE_GLES2 +static void gst_gl_filter_cube_reset (GstGLFilter * filter); +static gboolean gst_gl_filter_cube_init_shader (GstGLFilter * filter); +static void _callback_gles2 (gint width, gint height, guint texture, + gpointer stuff); +#endif +#if GST_GL_HAVE_OPENGL +static void _callback_opengl (gint width, gint height, guint texture, + gpointer stuff); +#endif +static gboolean gst_gl_filter_cube_filter_texture (GstGLFilter * filter, + guint in_tex, guint out_tex); + +#if GST_GL_HAVE_GLES2 +/* vertex source */ +static const gchar *cube_v_src = + "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" + "uniform mat4 u_matrix; \n" + "uniform float xrot_degree, yrot_degree, zrot_degree; \n" + "varying vec2 v_texCoord; \n" + "void main() \n" + "{ \n" + " float PI = 3.14159265; \n" + " float xrot = xrot_degree*2.0*PI/360.0; \n" + " float yrot = yrot_degree*2.0*PI/360.0; \n" + " float zrot = zrot_degree*2.0*PI/360.0; \n" + " mat4 matX = mat4 ( \n" + " 1.0, 0.0, 0.0, 0.0, \n" + " 0.0, cos(xrot), sin(xrot), 0.0, \n" + " 0.0, -sin(xrot), cos(xrot), 0.0, \n" + " 0.0, 0.0, 0.0, 1.0 ); \n" + " mat4 matY = mat4 ( \n" + " cos(yrot), 0.0, -sin(yrot), 0.0, \n" + " 0.0, 1.0, 0.0, 0.0, \n" + " sin(yrot), 0.0, cos(yrot), 0.0, \n" + " 0.0, 0.0, 0.0, 1.0 ); \n" + " mat4 matZ = mat4 ( \n" + " cos(zrot), sin(zrot), 0.0, 0.0, \n" + " -sin(zrot), cos(zrot), 0.0, 0.0, \n" + " 0.0, 0.0, 1.0, 0.0, \n" + " 0.0, 0.0, 0.0, 1.0 ); \n" + " gl_Position = u_matrix * matZ * matY * matX * a_position; \n" + " v_texCoord = a_texCoord; \n" + "} \n"; + +/* fragment source */ +static const gchar *cube_f_src = + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D s_texture; \n" + "void main() \n" + "{ \n" + " gl_FragColor = texture2D( s_texture, v_texCoord );\n" + "} \n"; +#endif + +static void +gst_gl_filter_cube_class_init (GstGLFilterCubeClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_gl_filter_cube_set_property; + gobject_class->get_property = gst_gl_filter_cube_get_property; + +#if GST_GL_HAVE_GLES2 + GST_GL_FILTER_CLASS (klass)->onInitFBO = gst_gl_filter_cube_init_shader; + GST_GL_FILTER_CLASS (klass)->onReset = gst_gl_filter_cube_reset; +#endif + GST_GL_FILTER_CLASS (klass)->set_caps = gst_gl_filter_cube_set_caps; + GST_GL_FILTER_CLASS (klass)->filter_texture = + gst_gl_filter_cube_filter_texture; + + g_object_class_install_property (gobject_class, PROP_RED, + g_param_spec_float ("red", "Red", "Background red color", + 0.0f, 1.0f, 0.0f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_GREEN, + g_param_spec_float ("green", "Green", "Background reen color", + 0.0f, 1.0f, 0.0f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_BLUE, + g_param_spec_float ("blue", "Blue", "Background blue color", + 0.0f, 1.0f, 0.0f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_FOVY, + g_param_spec_double ("fovy", "Fovy", "Field of view angle in degrees", + 0.0, 180.0, 45.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_ASPECT, + g_param_spec_double ("aspect", "Aspect", + "Field of view in the x direction", 0.0, 100, 0.0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_ZNEAR, + g_param_spec_double ("znear", "Znear", + "Specifies the distance from the viewer to the near clipping plane", + 0.0, 100.0, 0.1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_ZFAR, + g_param_spec_double ("zfar", "Zfar", + "Specifies the distance from the viewer to the far clipping plane", + 0.0, 1000.0, 100.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_set_metadata (element_class, "OpenGL cube filter", + "Filter/Effect/Video", "Map input texture on the 6 cube faces", + "Julien Isorce "); +} + +static void +gst_gl_filter_cube_init (GstGLFilterCube * filter) +{ + filter->shader = NULL; + filter->fovy = 45; + filter->aspect = 0; + filter->znear = 0.1; + filter->zfar = 100; +} + +static void +gst_gl_filter_cube_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGLFilterCube *filter = GST_GL_FILTER_CUBE (object); + + switch (prop_id) { + case PROP_RED: + filter->red = g_value_get_float (value); + break; + case PROP_GREEN: + filter->green = g_value_get_float (value); + break; + case PROP_BLUE: + filter->blue = g_value_get_float (value); + break; + case PROP_FOVY: + filter->fovy = g_value_get_double (value); + break; + case PROP_ASPECT: + filter->aspect = g_value_get_double (value); + break; + case PROP_ZNEAR: + filter->znear = g_value_get_double (value); + break; + case PROP_ZFAR: + filter->zfar = g_value_get_double (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_filter_cube_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstGLFilterCube *filter = GST_GL_FILTER_CUBE (object); + + switch (prop_id) { + case PROP_RED: + g_value_set_float (value, filter->red); + break; + case PROP_GREEN: + g_value_set_float (value, filter->green); + break; + case PROP_BLUE: + g_value_set_float (value, filter->blue); + break; + case PROP_FOVY: + g_value_set_double (value, filter->fovy); + break; + case PROP_ASPECT: + g_value_set_double (value, filter->aspect); + break; + case PROP_ZNEAR: + g_value_set_double (value, filter->znear); + break; + case PROP_ZFAR: + g_value_set_double (value, filter->zfar); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_gl_filter_cube_set_caps (GstGLFilter * filter, GstCaps * incaps, + GstCaps * outcaps) +{ + GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter); + + if (cube_filter->aspect == 0) + cube_filter->aspect = (gdouble) GST_VIDEO_INFO_WIDTH (&filter->out_info) / + (gdouble) GST_VIDEO_INFO_HEIGHT (&filter->out_info); + + return TRUE; +} + +#if GST_GL_HAVE_GLES2 +static void +gst_gl_filter_cube_reset (GstGLFilter * filter) +{ + GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter); + + /* blocking call, wait the opengl thread has destroyed the shader */ + if (cube_filter->shader) + gst_gl_context_del_shader (filter->context, cube_filter->shader); + cube_filter->shader = NULL; +} + +static gboolean +gst_gl_filter_cube_init_shader (GstGLFilter * filter) +{ + GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter); + + if (gst_gl_context_get_gl_api (filter->context) & GST_GL_API_GLES2) { + /* blocking call, wait the opengl thread has compiled the shader */ + return gst_gl_context_gen_shader (filter->context, cube_v_src, cube_f_src, + &cube_filter->shader); + } + return TRUE; +} +#endif + +static gboolean +gst_gl_filter_cube_filter_texture (GstGLFilter * filter, guint in_tex, + guint out_tex) +{ + GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter); + GLCB cb = NULL; + GstGLAPI api; + + api = gst_gl_context_get_gl_api (GST_GL_FILTER (cube_filter)->context); + +#if GST_GL_HAVE_OPENGL + if (api & GST_GL_API_OPENGL) + cb = _callback_opengl; +#endif +#if GST_GL_HAVE_GLES2 + if (api & GST_GL_API_GLES2) + cb = _callback_gles2; +#endif + + /* blocking call, use a FBO */ + gst_gl_context_use_fbo (filter->context, + GST_VIDEO_INFO_WIDTH (&filter->out_info), + GST_VIDEO_INFO_HEIGHT (&filter->out_info), + filter->fbo, filter->depthbuffer, out_tex, + cb, + GST_VIDEO_INFO_WIDTH (&filter->in_info), + GST_VIDEO_INFO_HEIGHT (&filter->in_info), + in_tex, cube_filter->fovy, cube_filter->aspect, + cube_filter->znear, cube_filter->zfar, + GST_GL_DISPLAY_PROJECTION_PERSPECTIVE, (gpointer) cube_filter); + + return TRUE; +} + +/* opengl scene, params: input texture (not the output filter->texture) */ +#if GST_GL_HAVE_OPENGL +static void +_callback_opengl (gint width, gint height, guint texture, gpointer stuff) +{ + GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (stuff); + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFuncs *gl = filter->context->gl_vtable; + + static GLfloat xrot = 0; + static GLfloat yrot = 0; + static GLfloat zrot = 0; + +/* *INDENT-OFF* */ + const GLfloat v_vertices[] = { + /*| Vertex | TexCoord |*/ + /* front face */ + 1.0, 1.0, -1.0, 0.0, 0.0, + 1.0, -1.0, -1.0, 1.0, 0.0, + -1.0, -1.0, -1.0, 1.0, 1.0, + -1.0, 1.0, -1.0, 0.0, 1.0, + /* back face */ + -1.0, 1.0, 1.0, 0.0, 0.0, + -1.0, -1.0, 1.0, 1.0, 0.0, + 1.0, -1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 0.0, 1.0, + /* right face */ + -1.0, 1.0, -1.0, 0.0, 0.0, + -1.0, -1.0, -1.0, 1.0, 0.0, + -1.0, -1.0, 1.0, 1.0, 1.0, + -1.0, 1.0, 1.0, 0.0, 1.0, + /* left face */ + 1.0, 1.0, 1.0, 0.0, 0.0, + 1.0, -1.0, 1.0, 1.0, 0.0, + 1.0, -1.0, -1.0, 1.0, 1.0, + 1.0, 1.0, -1.0, 0.0, 1.0, + /* top face */ + 1.0, 1.0, 1.0, 0.0, 0.0, + 1.0, 1.0, -1.0, 1.0, 0.0, + -1.0, 1.0, -1.0, 1.0, 1.0, + -1.0, 1.0, 1.0, 0.0, 1.0, + /* bottom face */ + 1.0, -1.0, 1.0, 0.0, 0.0, + 1.0, -1.0, -1.0, 1.0, 0.0, + -1.0, -1.0, -1.0, 1.0, 1.0, + -1.0, -1.0, 1.0, 0.0, 1.0, + }; +/* *INDENT-ON* */ + + GLushort indices[] = { + 0, 1, 2, + 0, 2, 3, + 4, 5, 6, + 4, 6, 7, + 8, 9, 10, + 8, 10, 11, + 12, 13, 14, + 12, 14, 15, + 16, 17, 18, + 16, 18, 19, + 20, 21, 22, + 20, 22, 23 + }; + + gl->Enable (GL_DEPTH_TEST); + + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gl->ClearColor (cube_filter->red, cube_filter->green, cube_filter->blue, 0.0); + gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + gl->MatrixMode (GL_PROJECTION); + gluLookAt (0.0, 0.0, -6.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); + gl->MatrixMode (GL_MODELVIEW); + gl->LoadIdentity (); + +// gl->Translatef (0.0f, 0.0f, -5.0f); + + gl->Rotatef (xrot, 1.0f, 0.0f, 0.0f); + gl->Rotatef (yrot, 0.0f, 1.0f, 0.0f); + gl->Rotatef (zrot, 0.0f, 0.0f, 1.0f); + + gl->ClientActiveTexture (GL_TEXTURE0); + gl->EnableClientState (GL_TEXTURE_COORD_ARRAY); + gl->EnableClientState (GL_VERTEX_ARRAY); + + gl->VertexPointer (3, GL_FLOAT, 5 * sizeof (float), v_vertices); + gl->TexCoordPointer (2, GL_FLOAT, 5 * sizeof (float), &v_vertices[3]); + + gl->DrawElements (GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, indices); + + gl->DisableClientState (GL_TEXTURE_COORD_ARRAY); + gl->DisableClientState (GL_VERTEX_ARRAY); + + gl->Disable (GL_DEPTH_TEST); + + xrot += 0.3f; + yrot += 0.2f; + zrot += 0.4f; +} +#endif + +#if GST_GL_HAVE_GLES2 +static void +_callback_gles2 (gint width, gint height, guint texture, gpointer stuff) +{ + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter); + GstGLFuncs *gl = filter->context->gl_vtable; + + static GLfloat xrot = 0; + static GLfloat yrot = 0; + static GLfloat zrot = 0; + +/* *INDENT-OFF* */ + const GLfloat v_vertices[] = { + /*| Vertex | TexCoord |*/ + /* front face */ + 1.0, 1.0, -1.0, 1.0, 0.0, + 1.0, -1.0, -1.0, 1.0, 1.0, + -1.0, -1.0, -1.0, 0.0, 1.0, + -1.0, 1.0, -1.0, 0.0, 0.0, + /* back face */ + 1.0, 1.0, 1.0, 1.0, 0.0, + -1.0, 1.0, 1.0, 0.0, 0.0, + -1.0, -1.0, 1.0, 0.0, 1.0, + 1.0, -1.0, 1.0, 1.0, 1.0, + /* right face */ + 1.0, 1.0, 1.0, 1.0, 0.0, + 1.0, -1.0, 1.0, 0.0, 0.0, + 1.0, -1.0, -1.0, 0.0, 1.0, + 1.0, 1.0, -1.0, 1.0, 1.0, + /* left face */ + -1.0, 1.0, 1.0, 1.0, 0.0, + -1.0, 1.0, -1.0, 1.0, 1.0, + -1.0, -1.0, -1.0, 0.0, 1.0, + -1.0, -1.0, 1.0, 0.0, 0.0, + /* top face */ + 1.0, -1.0, 1.0, 1.0, 0.0, + -1.0, -1.0, 1.0, 0.0, 0.0, + -1.0, -1.0, -1.0, 0.0, 1.0, + 1.0, -1.0, -1.0, 1.0, 1.0, + /* bottom face */ + 1.0, 1.0, 1.0, 1.0, 0.0, + 1.0, 1.0, -1.0, 1.0, 1.0, + -1.0, 1.0, -1.0, 0.0, 1.0, + -1.0, 1.0, 1.0, 0.0, 0.0 + }; +/* *INDENT-ON* */ + + GLushort indices[] = { + 0, 1, 2, + 0, 2, 3, + 4, 5, 6, + 4, 6, 7, + 8, 9, 10, + 8, 10, 11, + 12, 13, 14, + 12, 14, 15, + 16, 17, 18, + 16, 18, 19, + 20, 21, 22, + 20, 22, 23 + }; + + GLint attr_position_loc = 0; + GLint attr_texture_loc = 0; + + const GLfloat matrix[] = { + 0.5f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.5f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + gl->Enable (GL_DEPTH_TEST); + + gl->ClearColor (cube_filter->red, cube_filter->green, cube_filter->blue, 0.0); + gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + gst_gl_shader_use (cube_filter->shader); + + attr_position_loc = + gst_gl_shader_get_attribute_location (cube_filter->shader, "a_position"); + attr_texture_loc = + gst_gl_shader_get_attribute_location (cube_filter->shader, "a_texCoord"); + + /* Load the vertex position */ + gl->VertexAttribPointer (attr_position_loc, 3, GL_FLOAT, + GL_FALSE, 5 * sizeof (GLfloat), v_vertices); + + /* Load the texture coordinate */ + gl->VertexAttribPointer (attr_texture_loc, 2, GL_FLOAT, + GL_FALSE, 5 * sizeof (GLfloat), &v_vertices[3]); + + gl->EnableVertexAttribArray (attr_position_loc); + gl->EnableVertexAttribArray (attr_texture_loc); + + gl->ActiveTexture (GL_TEXTURE0); + gl->BindTexture (GL_TEXTURE_2D, texture); + gst_gl_shader_set_uniform_1i (cube_filter->shader, "s_texture", 0); + gst_gl_shader_set_uniform_1f (cube_filter->shader, "xrot_degree", xrot); + gst_gl_shader_set_uniform_1f (cube_filter->shader, "yrot_degree", yrot); + gst_gl_shader_set_uniform_1f (cube_filter->shader, "zrot_degree", zrot); + gst_gl_shader_set_uniform_matrix_4fv (cube_filter->shader, "u_matrix", 1, + GL_FALSE, matrix); + + gl->DrawElements (GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, indices); + + gl->DisableVertexAttribArray (attr_position_loc); + gl->DisableVertexAttribArray (attr_texture_loc); + + gl->Disable (GL_DEPTH_TEST); + + xrot += 0.3f; + yrot += 0.2f; + zrot += 0.4f; +} +#endif diff --git a/ext/gl/gstglfiltercube.h b/ext/gl/gstglfiltercube.h new file mode 100644 index 0000000..a72d09a --- /dev/null +++ b/ext/gl/gstglfiltercube.h @@ -0,0 +1,65 @@ +/* + * GStreamer + * Copyright (C) 2008 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_GL_FILTERCUBE_H_ +#define _GST_GL_FILTERCUBE_H_ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_GL_FILTER_CUBE (gst_gl_filter_cube_get_type()) +#define GST_GL_FILTER_CUBE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_FILTER_CUBE,GstGLFilterCube)) +#define GST_IS_GL_FILTER_CUBE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_FILTER_CUBE)) +#define GST_GL_FILTER_CUBE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_FILTER_CUBE,GstGLFilterCubeClass)) +#define GST_IS_GL_FILTER_CUBE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_FILTER_CUBE)) +#define GST_GL_FILTER_CUBE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_FILTER_CUBE,GstGLFilterCubeClass)) + +typedef struct _GstGLFilterCube GstGLFilterCube; +typedef struct _GstGLFilterCubeClass GstGLFilterCubeClass; + +struct _GstGLFilterCube +{ + GstGLFilter filter; + + GstGLShader *shader; + + /* background color */ + gfloat red; + gfloat green; + gfloat blue; + + /* perspective */ + gdouble fovy; + gdouble aspect; + gdouble znear; + gdouble zfar; +}; + +struct _GstGLFilterCubeClass +{ + GstGLFilterClass filter_class; +}; + +GType gst_gl_glfiltercube_get_type (void); + +G_END_DECLS + +#endif /* _GST_GLFILTERCUBE_H_ */ diff --git a/ext/gl/gstglfilterglass.c b/ext/gl/gstglfilterglass.c new file mode 100644 index 0000000..4b26552 --- /dev/null +++ b/ext/gl/gstglfilterglass.c @@ -0,0 +1,406 @@ +/* + * GStreamer + * Copyright (C) 2008 Julien Isorce + * Inspired from http://www.mdk.org.pl/2007/11/17/gl-colorspace-conversions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-glfilterglass + * + * Map textures on moving glass. + * + * + * Examples + * |[ + * gst-launch -v videotestsrc ! glupload ! glfilterglass ! glimagesink + * ]| A pipeline inspired from http://www.mdk.org.pl/2007/11/17/gl-colorspace-conversions + * FBO is required. + * |[ + * gst-launch -v videotestsrc ! glupload ! glfilterglass ! "video/x-raw-gl, width=640, height=480" ! glimagesink + * ]| The scene is greater than the input size. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "gstglfilterglass.h" + +#define GST_CAT_DEFAULT gst_gl_filter_glass_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +enum +{ + PROP_0 +}; + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_filter_glass_debug, "glfilterglass", 0, "glfilterglass element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLFilterGlass, gst_gl_filter_glass, + GST_TYPE_GL_FILTER, DEBUG_INIT); + +static void gst_gl_filter_glass_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_filter_glass_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void gst_gl_filter_glass_reset (GstGLFilter * filter); +static gboolean gst_gl_filter_glass_init_shader (GstGLFilter * filter); +static gboolean gst_gl_filter_glass_filter_texture (GstGLFilter * filter, + guint in_tex, guint out_tex); + +static void gst_gl_filter_glass_draw_background_gradient (); +static void gst_gl_filter_glass_draw_video_plane (GstGLFilter * filter, + gint width, gint height, guint texture, gfloat center_x, gfloat center_y, + gfloat start_alpha, gfloat stop_alpha, gboolean reversed, gfloat rotation); + +static void gst_gl_filter_glass_callback (gpointer stuff); + +/* *INDENT-OFF* */ +static const gchar *glass_fragment_source = + "uniform sampler2D tex;\n" + "varying float alpha;\n" + "void main () {\n" + " float p = 0.0525;\n" + " float L1 = p*1.0;\n" + " float L2 = 1.0 - L1;\n" + " float L3 = 1.0 - L1;\n" + " float w = 1.0;\n" + " float r = L1;\n" + " if (gl_TexCoord[0].x < L1 && gl_TexCoord[0].y < L1)\n" + " r = sqrt( (gl_TexCoord[0].x - L1) * (gl_TexCoord[0].x - L1) + (gl_TexCoord[0].y - L1) * (gl_TexCoord[0].y - L1) );\n" + " else if (gl_TexCoord[0].x > L2 && gl_TexCoord[0].y < L1)\n" + " r = sqrt( (gl_TexCoord[0].x - L2) * (gl_TexCoord[0].x - L2) + (gl_TexCoord[0].y - L1) * (gl_TexCoord[0].y - L1) );\n" + " else if (gl_TexCoord[0].x > L2 && gl_TexCoord[0].y > L3)\n" + " r = sqrt( (gl_TexCoord[0].x - L2) * (gl_TexCoord[0].x - L2) + (gl_TexCoord[0].y - L3) * (gl_TexCoord[0].y - L3) );\n" + " else if (gl_TexCoord[0].x < L1 && gl_TexCoord[0].y > L3)\n" + " r = sqrt( (gl_TexCoord[0].x - L1) * (gl_TexCoord[0].x - L1) + (gl_TexCoord[0].y - L3) * (gl_TexCoord[0].y - L3) );\n" + " if (r > L1)\n" + " w = 0.0;\n" + " vec4 color = texture2D (tex, gl_TexCoord[0].st);\n" + " gl_FragColor = vec4(color.rgb, alpha * w);\n" + "}\n"; + +static const gchar *glass_vertex_source = + "uniform float yrot;\n" + "uniform float aspect;\n" + "const float fovy = 80.0;\n" + "const float znear = 1.0;\n" + "const float zfar = 5000.0;\n" + "varying float alpha;\n" + "void main () {\n" + " float f = 1.0/(tan(radians(fovy/2.0)));\n" + " float rot = radians (yrot);\n" + " // replacement for gluPerspective\n" + " mat4 perspective = mat4 (\n" + " f/aspect, 0.0, 0.0, 0.0,\n" + " 0.0, f, 0.0, 0.0,\n" + " 0.0, 0.0, (znear+zfar)/(znear-zfar), 2.0*znear*zfar/(znear-zfar),\n" + " 0.0, 0.0, -1.0, 0.0 );\n" + " mat4 trans = mat4 (\n" + " 1.0, 0.0, 0.0, 0.0,\n" + " 0.0, 1.0, 0.0, 0.0,\n" + " 0.0, 0.0, 1.0, -3.0,\n" + " 0.0, 0.0, 0.0, 1.0 );\n" + " mat4 rotation = mat4 (\n" + " cos(rot), 0.0, sin(rot), 0.0,\n" + " 0.0, 1.0, 0.0, 0.0,\n" + " -sin(rot), 0.0, cos(rot), 0.0,\n" + " 0.0, 0.0, 0.0, 1.0 );\n" + " gl_Position = trans * perspective * rotation * gl_ModelViewProjectionMatrix * gl_Vertex;\n" + " gl_TexCoord[0] = gl_MultiTexCoord0;\n" + " alpha = gl_Color.a;\n" + "}\n"; + +static const gchar * passthrough_vertex = + "void main () {\n" + " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" + " gl_FrontColor = gl_Color;\n" + "}\n"; + +static const gchar * passthrough_fragment = + "void main () {\n" + " gl_FragColor = gl_Color;\n" + "}\n"; +/* *INDENT-ON* */ + +static void +gst_gl_filter_glass_class_init (GstGLFilterGlassClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_gl_filter_glass_set_property; + gobject_class->get_property = gst_gl_filter_glass_get_property; + + gst_element_class_set_metadata (element_class, "OpenGL glass filter", + "Filter/Effect/Video", "Glass Filter", + "Julien Isorce "); + + GST_GL_FILTER_CLASS (klass)->filter_texture = + gst_gl_filter_glass_filter_texture; + GST_GL_FILTER_CLASS (klass)->onInitFBO = gst_gl_filter_glass_init_shader; + GST_GL_FILTER_CLASS (klass)->onReset = gst_gl_filter_glass_reset; +} + +static void +gst_gl_filter_glass_init (GstGLFilterGlass * filter) +{ + filter->shader = NULL; + filter->timestamp = 0; +} + +static void +gst_gl_filter_glass_reset (GstGLFilter * filter) +{ + GstGLFilterGlass *glass_filter = GST_GL_FILTER_GLASS (filter); + + //blocking call, wait the opengl thread has destroyed the shader + if (glass_filter->shader) + gst_gl_context_del_shader (filter->context, glass_filter->shader); + glass_filter->shader = NULL; + if (glass_filter->passthrough_shader) + gst_gl_context_del_shader (filter->context, + glass_filter->passthrough_shader); + glass_filter->passthrough_shader = NULL; +} + +static void +gst_gl_filter_glass_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + //GstGLFilterGlass *filter = GST_GL_FILTER_GLASS (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_filter_glass_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + //GstGLFilterGlass *filter = GST_GL_FILTER_GLASS (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_gl_filter_glass_init_shader (GstGLFilter * filter) +{ + gboolean ret; + GstGLFilterGlass *glass_filter = GST_GL_FILTER_GLASS (filter); + + //blocking call, wait the opengl thread has compiled the shader + ret = + gst_gl_context_gen_shader (filter->context, glass_vertex_source, + glass_fragment_source, &glass_filter->shader); + if (ret) + ret = + gst_gl_context_gen_shader (filter->context, passthrough_vertex, + passthrough_fragment, &glass_filter->passthrough_shader); + + return ret; +} + +static gboolean +gst_gl_filter_glass_filter_texture (GstGLFilter * filter, guint in_tex, + guint out_tex) +{ + GstGLFilterGlass *glass_filter = GST_GL_FILTER_GLASS (filter); + glass_filter->in_tex = in_tex; + + //blocking call, use a FBO + gst_gl_context_use_fbo_v2 (filter->context, + GST_VIDEO_INFO_WIDTH (&filter->out_info), + GST_VIDEO_INFO_HEIGHT (&filter->out_info), + filter->fbo, filter->depthbuffer, out_tex, + gst_gl_filter_glass_callback, (gpointer) glass_filter); + + return TRUE; +} + +static gint64 +get_time (void) +{ + static GTimeVal val; + g_get_current_time (&val); + + return (val.tv_sec * G_USEC_PER_SEC) + val.tv_usec; +} + +static void +gst_gl_filter_glass_draw_background_gradient (GstGLFilterGlass * glass) +{ + GstGLFilter *filter = GST_GL_FILTER (glass); + GstGLFuncs *gl = filter->context->gl_vtable; + +/* *INDENT-OFF* */ + gfloat mesh[] = { + /* | Vertex | Color | */ + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 0.8f, 0.0f, 0.0f, 0.0f, 0.2f, 1.0f, + -1.0f, 0.8f, 0.0f, 0.0f, 0.0f, 0.2f, 1.0f, + -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.2f, 1.0f, + 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.2f, 1.0f, + }; +/* *INDENT-ON* */ + + GLushort indices[] = { + 0, 1, 2, + 0, 2, 3, + 2, 3, 4, + 2, 4, 5 + }; + + gl->ClientActiveTexture (GL_TEXTURE0); + gl->EnableClientState (GL_VERTEX_ARRAY); + gl->EnableClientState (GL_COLOR_ARRAY); + + gl->VertexPointer (3, GL_FLOAT, 7 * sizeof (gfloat), mesh); + gl->ColorPointer (4, GL_FLOAT, 7 * sizeof (gfloat), &mesh[3]); + + gl->DrawElements (GL_TRIANGLES, 12, GL_UNSIGNED_SHORT, indices); + + gl->DisableClientState (GL_VERTEX_ARRAY); + gl->DisableClientState (GL_COLOR_ARRAY); +} + +static void +gst_gl_filter_glass_draw_video_plane (GstGLFilter * filter, + gint width, gint height, guint texture, + gfloat center_x, gfloat center_y, + gfloat start_alpha, gfloat stop_alpha, gboolean reversed, gfloat rotation) +{ + GstGLFilterGlass *glass_filter = GST_GL_FILTER_GLASS (filter); + GstGLFuncs *gl = filter->context->gl_vtable; + + gfloat topy = reversed ? center_y - 1.0f : center_y + 1.0f; + gfloat bottomy = reversed ? center_y + 1.0f : center_y - 1.0f; + +/* *INDENT-OFF* */ + gfloat mesh[] = { + /*| Vertex |TexCoord0| Colour |*/ + center_x-1.6, topy, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, start_alpha, + center_x+1.6, topy, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, start_alpha, + center_x+1.6, bottomy, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, stop_alpha, + center_x-1.6, bottomy, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, stop_alpha, + }; +/* *INDENT-ON* */ + + GLushort indices[] = { + 0, 1, 2, + 0, 2, 3 + }; + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (glass_filter->shader, "tex", 0); + gst_gl_shader_set_uniform_1f (glass_filter->shader, "yrot", rotation); + gst_gl_shader_set_uniform_1f (glass_filter->shader, "aspect", + (gfloat) width / (gfloat) height); + + gl->ClientActiveTexture (GL_TEXTURE0); + gl->EnableClientState (GL_TEXTURE_COORD_ARRAY); + gl->EnableClientState (GL_VERTEX_ARRAY); + gl->EnableClientState (GL_COLOR_ARRAY); + + gl->VertexPointer (3, GL_FLOAT, 9 * sizeof (gfloat), mesh); + gl->TexCoordPointer (2, GL_FLOAT, 9 * sizeof (gfloat), &mesh[3]); + gl->ColorPointer (4, GL_FLOAT, 9 * sizeof (gfloat), &mesh[5]); + + gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); + + gl->DisableClientState (GL_TEXTURE_COORD_ARRAY); + gl->DisableClientState (GL_VERTEX_ARRAY); + gl->DisableClientState (GL_COLOR_ARRAY); +} + +//opengl scene, params: input texture (not the output filter->texture) +static void +gst_gl_filter_glass_callback (gpointer stuff) +{ + static gint64 start_time = 0; + gfloat rotation; + + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFilterGlass *glass_filter = GST_GL_FILTER_GLASS (stuff); + GstGLFuncs *gl = filter->context->gl_vtable; + + gint width = GST_VIDEO_INFO_WIDTH (&filter->out_info); + gint height = GST_VIDEO_INFO_HEIGHT (&filter->out_info); + guint texture = glass_filter->in_tex; + + if (start_time == 0) + start_time = get_time (); + else { + gint64 time_left = + (glass_filter->timestamp / 1000) - (get_time () - start_time); + time_left -= 1000000 / 25; + if (time_left > 2000) { + GST_LOG ("escape"); + return; + } + } + + gst_gl_shader_use (glass_filter->passthrough_shader); + + gst_gl_filter_glass_draw_background_gradient (glass_filter); + + //Rotation + if (start_time != 0) { + gint64 time_passed = get_time () - start_time; + rotation = sin (time_passed / 1200000.0) * 45.0f; + } else { + rotation = 0.0f; + } + + gl->Enable (GL_BLEND); + gl->BlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + gst_gl_shader_use (glass_filter->shader); + + //Reflection + gst_gl_filter_glass_draw_video_plane (filter, width, height, texture, + 0.0f, 2.0f, 0.3f, 0.0f, TRUE, rotation); + + //Main video + gst_gl_filter_glass_draw_video_plane (filter, width, height, texture, + 0.0f, 0.0f, 1.0f, 1.0f, FALSE, rotation); + + gst_gl_context_clear_shader (filter->context); + + gl->Disable (GL_TEXTURE_2D); + gl->Disable (GL_BLEND); +} diff --git a/ext/gl/gstglfilterglass.h b/ext/gl/gstglfilterglass.h new file mode 100644 index 0000000..0048851 --- /dev/null +++ b/ext/gl/gstglfilterglass.h @@ -0,0 +1,56 @@ +/* + * GStreamer + * Copyright (C) 2008 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_GL_FILTERGLASS_H_ +#define _GST_GL_FILTERGLASS_H_ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_GL_FILTER_GLASS (gst_gl_filter_glass_get_type()) +#define GST_GL_FILTER_GLASS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_FILTER_GLASS,GstGLFilterGlass)) +#define GST_IS_GL_FILTER_GLASS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_FILTER_GLASS)) +#define GST_GL_FILTER_GLASS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_FILTER_GLASS,GstGLFilterGlassClass)) +#define GST_IS_GL_FILTER_GLASS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_FILTER_GLASS)) +#define GST_GL_FILTER_GLASS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_FILTER_GLASS,GstGLFilterGlassClass)) + +typedef struct _GstGLFilterGlass GstGLFilterGlass; +typedef struct _GstGLFilterGlassClass GstGLFilterGlassClass; + +struct _GstGLFilterGlass +{ + GstGLFilter filter; + GstGLShader *passthrough_shader; + GstGLShader *shader; + gint64 timestamp; + guint in_tex; +}; + +struct _GstGLFilterGlassClass +{ + GstGLFilterClass filter_class; +}; + +GType gst_gl_glfilterglass_get_type (void); + +G_END_DECLS + +#endif /* _GST_GLFILTERGLASS_H_ */ diff --git a/ext/gl/gstglfilterlaplacian.c b/ext/gl/gstglfilterlaplacian.c new file mode 100644 index 0000000..6424a9c --- /dev/null +++ b/ext/gl/gstglfilterlaplacian.c @@ -0,0 +1,222 @@ +/* + * GStreamer + * Copyright (C) 2008-2010 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-glfilterlaplacian + * + * Laplacian Convolution Demo Filter. + * + * + * Examples + * |[ + * gst-launch videotestsrc ! glupload ! glfilterlaplacian ! glimagesink + * ]| + * FBO (Frame Buffer Object) and GLSL (OpenGL Shading Language) are required. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstglfilterlaplacian.h" + +#define GST_CAT_DEFAULT gst_gl_filter_laplacian_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +enum +{ + PROP_0 +}; + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_filter_laplacian_debug, "glfilterlaplacian", 0, "glfilterlaplacian element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLFilterLaplacian, gst_gl_filter_laplacian, + GST_TYPE_GL_FILTER, DEBUG_INIT); + +static void gst_gl_filter_laplacian_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_gl_filter_laplacian_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static void gst_gl_filter_laplacian_reset (GstGLFilter * filter); +static gboolean gst_gl_filter_laplacian_init_shader (GstGLFilter * filter); +static gboolean gst_gl_filter_laplacian_filter_texture (GstGLFilter * filter, + guint in_tex, guint out_tex); +static void gst_gl_filter_laplacian_callback (gint width, gint height, + guint texture, gpointer stuff); + +/* *INDENT-OFF* */ + +/* This filter is meant as a demo of gst-plugins-gl + glsl + capabilities. So I'm keeping this shader readable enough. If and + when this shader will be used in production be careful to hard code + kernel into the shader and remove unneeded zero multiplications in + the convolution */ +static const gchar *convolution_fragment_source = + "uniform sampler2D tex;" + "uniform float kernel[9];" + "uniform float width, height;" + "void main () {" + " float w = 1.0 / width;" + " float h = 1.0 / height;" + " vec2 texturecoord[9];" + " texturecoord[4] = gl_TexCoord[0].st;" /* 0 0 */ + " texturecoord[5] = texturecoord[4] + vec2(w, 0.0);" /* 1 0 */ + " texturecoord[2] = texturecoord[5] - vec2(0.0, h);" /* 1 -1 */ + " texturecoord[1] = texturecoord[2] - vec2(w, 0.0);" /* 0 -1 */ + " texturecoord[0] = texturecoord[1] - vec2(w, 0.0);" /* -1 -1 */ + " texturecoord[3] = texturecoord[0] + vec2(0.0, h);" /* -1 0 */ + " texturecoord[6] = texturecoord[3] + vec2(0.0, h);" /* -1 1 */ + " texturecoord[7] = texturecoord[6] + vec2(w, 0.0);" /* 0 1 */ + " texturecoord[8] = texturecoord[7] + vec2(w, 0.0);" /* 1 1 */ + " int i;" + " vec4 sum = vec4 (0.0);" + " for (i = 0; i < 9; i++) { " + " vec4 neighbor = texture2D(tex, texturecoord[i]);" + " sum += neighbor * kernel[i];" + " }" + " gl_FragColor = sum;" + "}"; +/* *INDENT-ON* */ + +static void +gst_gl_filter_laplacian_class_init (GstGLFilterLaplacianClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_gl_filter_laplacian_set_property; + gobject_class->get_property = gst_gl_filter_laplacian_get_property; + + gst_element_class_set_metadata (element_class, + "OpenGL laplacian filter", "Filter/Effect/Video", + "Laplacian Convolution Demo Filter", + "Filippo Argiolas "); + + GST_GL_FILTER_CLASS (klass)->filter_texture = + gst_gl_filter_laplacian_filter_texture; + GST_GL_FILTER_CLASS (klass)->onInitFBO = gst_gl_filter_laplacian_init_shader; + GST_GL_FILTER_CLASS (klass)->onReset = gst_gl_filter_laplacian_reset; +} + +static void +gst_gl_filter_laplacian_init (GstGLFilterLaplacian * filter) +{ + filter->shader = NULL; +} + +static void +gst_gl_filter_laplacian_reset (GstGLFilter * filter) +{ + GstGLFilterLaplacian *laplacian_filter = GST_GL_FILTER_LAPLACIAN (filter); + + //blocking call, wait the opengl thread has destroyed the shader + if (laplacian_filter->shader) + gst_gl_context_del_shader (filter->context, laplacian_filter->shader); + laplacian_filter->shader = NULL; +} + +static void +gst_gl_filter_laplacian_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + //GstGLFilterLaplacian *filter = GST_GL_FILTER_LAPLACIAN (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_filter_laplacian_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + //GstGLFilterLaplacian *filter = GST_GL_FILTER_LAPLACIAN (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_gl_filter_laplacian_init_shader (GstGLFilter * filter) +{ + GstGLFilterLaplacian *laplacian_filter = GST_GL_FILTER_LAPLACIAN (filter); + + //blocking call, wait the opengl thread has compiled the shader + return gst_gl_context_gen_shader (filter->context, 0, + convolution_fragment_source, &laplacian_filter->shader); +} + +static gboolean +gst_gl_filter_laplacian_filter_texture (GstGLFilter * filter, guint in_tex, + guint out_tex) +{ + gpointer laplacian_filter = GST_GL_FILTER_LAPLACIAN (filter); + + + //blocking call, use a FBO + gst_gl_filter_render_to_target (filter, TRUE, in_tex, out_tex, + gst_gl_filter_laplacian_callback, laplacian_filter); + + return TRUE; +} + +//opengl scene, params: input texture (not the output filter->texture) +static void +gst_gl_filter_laplacian_callback (gint width, gint height, guint texture, + gpointer stuff) +{ + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFilterLaplacian *laplacian_filter = GST_GL_FILTER_LAPLACIAN (filter); + GstGLFuncs *gl = filter->context->gl_vtable; + + gfloat kernel[9] = { 0.0, -1.0, 0.0, + -1.0, 4.0, -1.0, + 0.0, -1.0, 0.0 + }; + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (laplacian_filter->shader); + + gl->ActiveTexture (GL_TEXTURE0); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + + gst_gl_shader_set_uniform_1i (laplacian_filter->shader, "tex", 0); + gst_gl_shader_set_uniform_1fv (laplacian_filter->shader, "kernel", 9, kernel); + gst_gl_shader_set_uniform_1f (laplacian_filter->shader, "width", + (gfloat) width); + gst_gl_shader_set_uniform_1f (laplacian_filter->shader, "height", + (gfloat) height); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} diff --git a/ext/gl/gstglfilterlaplacian.h b/ext/gl/gstglfilterlaplacian.h new file mode 100644 index 0000000..acfa376 --- /dev/null +++ b/ext/gl/gstglfilterlaplacian.h @@ -0,0 +1,53 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_GL_FILTERLAPLACIAN_H_ +#define _GST_GL_FILTERLAPLACIAN_H_ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_GL_FILTER_LAPLACIAN (gst_gl_filter_laplacian_get_type()) +#define GST_GL_FILTER_LAPLACIAN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_FILTER_LAPLACIAN,GstGLFilterLaplacian)) +#define GST_IS_GL_FILTER_LAPLACIAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_FILTER_LAPLACIAN)) +#define GST_GL_FILTER_LAPLACIAN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_FILTER_LAPLACIAN,GstGLFilterLaplacianClass)) +#define GST_IS_GL_FILTER_LAPLACIAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_FILTER_LAPLACIAN)) +#define GST_GL_FILTER_LAPLACIAN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_FILTER_LAPLACIAN,GstGLFilterLaplacianClass)) + +typedef struct _GstGLFilterLaplacian GstGLFilterLaplacian; +typedef struct _GstGLFilterLaplacianClass GstGLFilterLaplacianClass; + +struct _GstGLFilterLaplacian +{ + GstGLFilter filter; + GstGLShader *shader; +}; + +struct _GstGLFilterLaplacianClass +{ + GstGLFilterClass filter_class; +}; + +GType gst_gl_glfilterlaplacian_get_type (void); + +G_END_DECLS + +#endif /* _GST_GLFILTERLAPLACIAN_H_ */ diff --git a/ext/gl/gstglfilterreflectedscreen.c b/ext/gl/gstglfilterreflectedscreen.c new file mode 100644 index 0000000..4934bca --- /dev/null +++ b/ext/gl/gstglfilterreflectedscreen.c @@ -0,0 +1,482 @@ +/* + * GStreamer + * Copyright (C) 2010 Pierre Pouzol + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-glfilterreflectedscreen + * + * Map Video Texture upon a screen, on a reflecting surface + * + * + * Examples + * |[ + * gst-launch videotestsrc ! glupload ! glfilterreflectedscreen ! glimagesink + * ]| + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "gstglfilterreflectedscreen.h" + +#define GST_CAT_DEFAULT gst_gl_filter_reflected_screen_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +enum +{ + PROP_0, + PROP_ACTIVE_GRAPHIC_MODE, + PROP_SEPARATED_SCREEN, + PROP_SHOW_FLOOR, + PROP_FOVY, + PROP_ASPECT, + PROP_ZNEAR, + PROP_ZFAR +}; + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_filter_reflected_screen_debug, "glfilterreflectedscreen", 0, "glfilterreflectedscreen element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLFilterReflectedScreen, + gst_gl_filter_reflected_screen, GST_TYPE_GL_FILTER, DEBUG_INIT); + +static void gst_gl_filter_reflected_screen_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_gl_filter_reflected_screen_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static gboolean gst_gl_filter_reflected_screen_filter_texture (GstGLFilter * + filter, guint in_tex, guint out_tex); + +static void gst_gl_filter_reflected_screen_draw_background (); +static void gst_gl_filter_reflected_screen_draw_floor (); +static void gst_gl_filter_reflected_screen_draw_screen (GstGLFilter * filter, + gint width, gint height, guint texture); +static void gst_gl_filter_reflected_screen_draw_separated_screen (GstGLFilter * + filter, gint width, gint height, guint texture, gfloat alphs, gfloat alphe); + +static void gst_gl_filter_reflected_screen_callback (gint width, gint height, + guint texture, gpointer stuff); + +static GLfloat LightPos[] = { 4.0f, -4.0f, 6.0f, 1.0f }; // Light Position +static GLfloat LightAmb[] = { 4.0f, 4.0f, 4.0f, 1.0f }; // Ambient Light +static GLfloat LightDif[] = { 1.0f, 1.0f, 1.0f, 1.0f }; // Diffuse Light + +static void +gst_gl_filter_reflected_screen_class_init (GstGLFilterReflectedScreenClass * + klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_gl_filter_reflected_screen_set_property; + gobject_class->get_property = gst_gl_filter_reflected_screen_get_property; + + GST_GL_FILTER_CLASS (klass)->filter_texture = + gst_gl_filter_reflected_screen_filter_texture; + + g_object_class_install_property (gobject_class, PROP_ACTIVE_GRAPHIC_MODE, + g_param_spec_boolean ("active-graphic-mode", + "Activate graphic mode", + "Allow user to activate stencil buffer and blending.", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_SEPARATED_SCREEN, + g_param_spec_boolean ("separated-screen", + "Create a separation space", + "Allow to insert a space between the two screen. Will cancel 'show floor' if active. Value are TRUE or FALSE(default)", + FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_SHOW_FLOOR, + g_param_spec_boolean ("show-floor", + "Show the support", + "Allow the user to show the supportive floor. Will cancel 'separated screen' if active. Value are TRUE(default) or FALSE", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_FOVY, + g_param_spec_double ("fovy", "Fovy", "Field of view angle in degrees", + 0.0, 180.0, 60, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_ASPECT, + g_param_spec_double ("aspect", "Aspect", + "Field of view in the x direction", 1.0, 100, 1.0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_ZNEAR, + g_param_spec_double ("znear", "Znear", + "Specifies the distance from the viewer to the near clipping plane", + 0.0000000001, 100.0, 0.1, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_ZFAR, + g_param_spec_double ("zfar", "Zfar", + "Specifies the distance from the viewer to the far clipping plane", + 0.0, 1000.0, 100.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_set_metadata (element_class, + "OpenGL Reflected Screen filter", "Filter/Effect/Video", + "Reflected Screen Filter", "Pierre POUZOL "); +} + +static void +gst_gl_filter_reflected_screen_init (GstGLFilterReflectedScreen * filter) +{ + filter->active_graphic_mode = TRUE; + filter->separated_screen = FALSE; + filter->show_floor = TRUE; + filter->fovy = 90; + filter->aspect = 1.0; + filter->znear = 0.1; + filter->zfar = 1000; +} + +static void +gst_gl_filter_reflected_screen_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGLFilterReflectedScreen *filter = GST_GL_FILTER_REFLECTED_SCREEN (object); + + switch (prop_id) { + case PROP_ACTIVE_GRAPHIC_MODE: + filter->active_graphic_mode = g_value_get_boolean (value); + break; + case PROP_SEPARATED_SCREEN: + filter->separated_screen = g_value_get_boolean (value); + break; + case PROP_SHOW_FLOOR: + filter->show_floor = g_value_get_boolean (value); + break; + case PROP_FOVY: + filter->fovy = g_value_get_double (value); + break; + case PROP_ASPECT: + filter->aspect = g_value_get_double (value); + break; + case PROP_ZNEAR: + filter->znear = g_value_get_double (value); + break; + case PROP_ZFAR: + filter->zfar = g_value_get_double (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_filter_reflected_screen_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstGLFilterReflectedScreen *filter = GST_GL_FILTER_REFLECTED_SCREEN (object); + + switch (prop_id) { + case PROP_ACTIVE_GRAPHIC_MODE: + g_value_set_boolean (value, filter->active_graphic_mode); + break; + case PROP_SEPARATED_SCREEN: + g_value_set_boolean (value, filter->separated_screen); + break; + case PROP_SHOW_FLOOR: + g_value_set_boolean (value, filter->show_floor); + break; + case PROP_FOVY: + g_value_set_double (value, filter->fovy); + break; + case PROP_ASPECT: + g_value_set_double (value, filter->aspect); + break; + case PROP_ZNEAR: + g_value_set_double (value, filter->znear); + break; + case PROP_ZFAR: + g_value_set_double (value, filter->zfar); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_gl_filter_reflected_screen_filter_texture (GstGLFilter * filter, + guint in_tex, guint out_tex) +{ + GstGLFilterReflectedScreen *reflected_screen_filter = + GST_GL_FILTER_REFLECTED_SCREEN (filter); + + //blocking call, use a FBO + gst_gl_context_use_fbo (filter->context, + GST_VIDEO_INFO_WIDTH (&filter->out_info), + GST_VIDEO_INFO_HEIGHT (&filter->out_info), + filter->fbo, filter->depthbuffer, out_tex, + gst_gl_filter_reflected_screen_callback, + GST_VIDEO_INFO_WIDTH (&filter->in_info), + GST_VIDEO_INFO_HEIGHT (&filter->in_info), in_tex, + reflected_screen_filter->fovy, reflected_screen_filter->aspect, + reflected_screen_filter->znear, reflected_screen_filter->zfar, + GST_GL_DISPLAY_PROJECTION_PERSPECTIVE, + (gpointer) reflected_screen_filter); + + return TRUE; +} + +static void +gst_gl_filter_reflected_screen_draw_separated_screen (GstGLFilter * filter, + gint width, gint height, guint texture, gfloat alphs, gfloat alphe) +{ + //enable ARB Rectangular texturing + //that's necessary to have the video displayed on our screen (with gstreamer) + glEnable (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, texture); + //configure parameters for the texturing + //the two first are used to specified how the texturing will be done if the screen is greater than the texture herself + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + //the next two specified how the texture will comport near the limits + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + //creating screen and setting the texture (depending on texture's height and width) + glBegin (GL_QUADS); + + // right Face + glColor4f (1.0f, 1.0f, 1.0f, alphs); + glTexCoord2f (0.5f, 1.0f); + glVertex3f (-0.75f, 0.0f, -1.0f); + glColor4f (1.0f, 1.0f, 1.0f, alphe); + glTexCoord2f (0.5f, 0.0f); + glVertex3f (-0.75f, 1.25f, -1.0f); + glTexCoord2f (1.0f, 0.0f); + glVertex3f (1.25f, 1.25f, -1.0f); + glColor4f (1.0f, 1.0f, 1.0f, alphs); + glTexCoord2f (1.0f, 1.0f); + glVertex3f (1.25f, 0.0f, -1.0f); + // Left Face + glColor4f (1.0f, 1.0f, 1.0f, alphs); + glTexCoord2f (0.5f, 1.0f); + glVertex3f (-1.0f, 0.0f, -0.75f); + glTexCoord2f (0.0f, 1.0f); + glVertex3f (-1.0f, 0.0f, 1.25f); + glColor4f (1.0f, 1.0f, 1.0f, alphe); + glTexCoord2f (0.0f, 0.0f); + glVertex3f (-1.0f, 1.25f, 1.25f); + glTexCoord2f (0.5f, 0.0f); + glVertex3f (-1.0f, 1.25f, -0.75f); + + glEnd (); + glDisable (GL_TEXTURE_2D); +} + +static void +gst_gl_filter_reflected_screen_draw_screen (GstGLFilter * filter, + gint width, gint height, guint texture) +{ + //enable ARB Rectangular texturing + //that's necessary to have the video displayed on our screen (with gstreamer) + glEnable (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, texture); + //configure parameters for the texturing + //the two first are used to specified how the texturing will be done if the screen is greater than the texture herself + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + //the next two specified how the texture will comport near the limits + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + //creating screen and setting the texture (depending on texture's height and width) + glBegin (GL_QUADS); + + glTexCoord2f (0.5f, 1.0f); + glVertex3f (-1.0f, 0.0f, -1.0f); + glTexCoord2f (0.5f, 0.0f); + glVertex3f (-1.0f, 1.0f, -1.0f); + glTexCoord2f (1.0f, 0.0f); + glVertex3f (1.0f, 1.0f, -1.0f); + glTexCoord2f (1.0f, 1.0f); + glVertex3f (1.0f, 0.0f, -1.0f); + // Left Face + glTexCoord2f (0.5f, 1.0f); + glVertex3f (-1.0f, 0.0f, -1.0f); + glTexCoord2f (0.0f, 1.0f); + glVertex3f (-1.0f, 0.0f, 1.0f); + glTexCoord2f (0.0f, 0.0f); + glVertex3f (-1.0f, 1.0f, 1.0f); + glTexCoord2f (0.5f, 0.0f); + glVertex3f (-1.0f, 1.0f, -1.0f); + + glEnd (); + + //disable this kind of texturing (useless for the gluDisk) + glDisable (GL_TEXTURE_2D); +} + +static void +gst_gl_filter_reflected_screen_draw_background () +{ + glBegin (GL_QUADS); + + // right Face + + glColor4f (0.0f, 0.0f, 0.0f, 1.0f); + glVertex3f (-10.0f, -10.0f, -1.0f); + + glColor4f (0.0f, 0.0f, 0.2f, 1.0f); + glVertex3f (-10.0f, 10.0f, -1.0f); + glVertex3f (10.0f, 10.0f, -1.0f); + glVertex3f (10.0f, -10.0f, -1.0f); + + glEnd (); +} + +static void +gst_gl_filter_reflected_screen_draw_floor () +{ + GLUquadricObj *q; + //create a quadric for the floor's drawing + q = gluNewQuadric (); + //configure this quadric's parameter (for lighting and texturing) + gluQuadricNormals (q, GL_SMOOTH); + gluQuadricTexture (q, GL_FALSE); + + //drawing the disk. The texture are mapped thanks to the parameter we gave to the GLUquadric q + gluDisk (q, 0.0, 2.2, 50, 1); +} + +//opengl scene, params: input texture (not the output filter->texture) +static void +gst_gl_filter_reflected_screen_callback (gint width, gint height, guint texture, + gpointer stuff) +{ + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFilterReflectedScreen *reflected_screen_filter = + GST_GL_FILTER_REFLECTED_SCREEN (stuff); + + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + //load identity befor tracing + glLoadIdentity (); + //camera translation + glTranslatef (0.0f, 0.1f, -1.3f); + //camera configuration + if (reflected_screen_filter->separated_screen) + gluLookAt (0.1, -0.25, 2.0, 0.025, 0.0, 0.0, 0.0, 1.0, 0.0); + else + gluLookAt (0.1, -0.35, 2.0, 0.025, 0.0, 0.0, 0.0, 1.0, 0.0); + + gst_gl_filter_reflected_screen_draw_background (); + + if (reflected_screen_filter->separated_screen) { + glEnable (GL_BLEND); + + glPushMatrix (); + glScalef (1.0f, -1.0f, 1.0f); + glTranslatef (0.0f, 0.0f, 1.2f); + glRotatef (-45.0f, 0.0, 1.0, 0.0); + gst_gl_filter_reflected_screen_draw_separated_screen (filter, width, height, + texture, 1.0f, 1.0f); + glPopMatrix (); + + if (reflected_screen_filter->active_graphic_mode) { + //configuration of the transparency function + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTranslatef (0.0f, 0.0f, 1.2f); + glRotatef (-45.0f, 0.0, 1.0, 0.0); + gst_gl_filter_reflected_screen_draw_separated_screen (filter, width, + height, texture, 0.5f, 0.0f); + glDisable (GL_BLEND); + } + } + if (reflected_screen_filter->show_floor) { + glLightfv (GL_LIGHT0, GL_AMBIENT, LightAmb); + glLightfv (GL_LIGHT0, GL_DIFFUSE, LightDif); + glLightfv (GL_LIGHT0, GL_POSITION, LightPos); + + //enable lighting + glEnable (GL_LIGHT0); + glEnable (GL_LIGHTING); + + if (reflected_screen_filter->active_graphic_mode) { + glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + //enable stencil buffer use + glEnable (GL_STENCIL_TEST); + //setting the stencil buffer. Each time a pixel will be drawn by now, this pixel value will be set to 1 + glStencilFunc (GL_ALWAYS, 1, 1); + glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE); + + //disable the zbuffer + glDisable (GL_DEPTH_TEST); + //make a rotation of 90 degree on x axis. By default, gluDisk draw a disk on z axis + glRotatef (-90.0f, 1.0, 0.0, 0.0); + //draw the floor. Each pixel representing this floor will now have a value of 1 on stencil buffer + gst_gl_filter_reflected_screen_draw_floor (); + //make an anti-rotation of 90 degree to draw the rest of the scene on the right angle + glRotatef (90.0f, 1.0, 0.0, 0.0); + //enable zbuffer again + glEnable (GL_DEPTH_TEST); + //enable the drawing to be shown + glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + //say that the next object have to be drawn ONLY where the stencil buffer's pixel's value is 1 + glStencilFunc (GL_EQUAL, 1, 1); + glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); + + //save the actual matrix + glPushMatrix (); + glLightfv (GL_LIGHT0, GL_POSITION, LightPos); + //translate the object on z axis + glTranslatef (0.0f, 0.0f, 1.4f); + //rotate it (because the drawing method place the user behind the left part of the screen) + glRotatef (-45.0f, 0.0, 1.0, 0.0); + //draw the reflexion + gst_gl_filter_reflected_screen_draw_screen (filter, width, height, + texture); + //return to the saved matrix position + glPopMatrix (); + //end of the stencil buffer uses + glDisable (GL_STENCIL_TEST); + + //enable the blending to mix the floor and reflexion color + glEnable (GL_BLEND); + glDisable (GL_LIGHTING); + //specified a white color (for the floor) with 20% transparency + glColor4f (1.0f, 1.0f, 1.0f, 0.8f); + //configuration of the transparency function + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + //draw the floor (which will appear this time) + glRotatef (-90.0f, 1.0, 0.0, 0.0); + gst_gl_filter_reflected_screen_draw_floor (); + glRotatef (90.0f, 1.0, 0.0, 0.0); + glDisable (GL_BLEND); + glEnable (GL_LIGHTING); + //draw the real object + //scale on y axis. The object must be drawn upside down (to suggest a reflexion) + glScalef (1.0f, -1.0f, 1.0f); + glTranslatef (0.0f, 0.0f, 1.4f); + glRotatef (-45.0f, 0.0, 1.0, 0.0); + gst_gl_filter_reflected_screen_draw_screen (filter, width, height, texture); + glDisable (GL_LIGHTING); + } +} diff --git a/ext/gl/gstglfilterreflectedscreen.h b/ext/gl/gstglfilterreflectedscreen.h new file mode 100644 index 0000000..7f6c5cf --- /dev/null +++ b/ext/gl/gstglfilterreflectedscreen.h @@ -0,0 +1,61 @@ +/* + * GStreamer + * Copyright (C) 2008 Pierre Pouzol + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_GL_FILTERREFLECTEDSCREEN_H_ +#define _GST_GL_FILTERREFLECTEDSCREEN_H_ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_GL_FILTER_REFLECTED_SCREEN (gst_gl_filter_reflected_screen_get_type()) +#define GST_GL_FILTER_REFLECTED_SCREEN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_FILTER_REFLECTED_SCREEN,GstGLFilterReflectedScreen)) +#define GST_IS_GL_FILTER_REFLECTED_SCREEN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_FILTER_REFLECTED_SCREEN)) +#define GST_GL_FILTER_REFLECTED_SCREEN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_FILTER_REFLECTED_SCREEN,GstGLFilterReflectedScreenClass)) +#define GST_IS_GL_FILTER_REFLECTED_SCREEN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_FILTER_REFLECTED_SCREEN)) +#define GST_GL_FILTER_REFLECTED_SCREEN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_FILTER_REFLECTED_SCREEN,GstGLFilterReflectedScreenClass)) + +typedef struct _GstGLFilterReflectedScreen GstGLFilterReflectedScreen; +typedef struct _GstGLFilterReflectedScreenClass GstGLFilterReflectedScreenClass; + +struct _GstGLFilterReflectedScreen +{ + GstGLFilter filter; + gdouble fovy; + gdouble aspect; + gdouble znear; + gdouble zfar; + + gboolean active_graphic_mode; + gboolean separated_screen; + gboolean show_floor; +}; + +struct _GstGLFilterReflectedScreenClass +{ + GstGLFilterClass filter_class; +}; + +GType gst_gl_glfilterreflectedscreen_get_type (void); + +G_END_DECLS + +#endif /* _GST_GLFILTERREFLECTEDSCREEN_H_ */ + diff --git a/ext/gl/gstglfiltershader.c b/ext/gl/gstglfiltershader.c new file mode 100644 index 0000000..6cf88d5 --- /dev/null +++ b/ext/gl/gstglfiltershader.c @@ -0,0 +1,382 @@ +/* + * glshader gstreamer plugin + * Copyrithg (C) 2008 Filippo Argiolas + * Copyright (C) 2009 Luc Deschenaux + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-glshader + * + * Filter loading OpenGL fragment shader from file + * + * + * Examples + * |[ + * gst-launch videotestsrc ! glupload ! glshader location=myshader.fs ! glimagesink + * ]| + * FBO (Frame Buffer Object) and GLSL (OpenGL Shading Language) are required. + * + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "gstglfiltershader.h" + +/* horizontal filter */ +static gchar *hfilter_fragment_source; +static gchar *hfilter_fragment_variables[2]; + +enum +{ + PROP_0, + PROP_LOCATION, + PROP_PRESET, + PROP_VARIABLES +}; + +#define GST_CAT_DEFAULT gst_gl_filtershader_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_filtershader_debug, "glshader", 0, "glshader element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLFilterShader, gst_gl_filtershader, + GST_TYPE_GL_FILTER, DEBUG_INIT); + +static void gst_gl_filtershader_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_filtershader_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_gl_filter_filtershader_reset (GstGLFilter * filter); + +static gboolean gst_gl_filtershader_load_shader (GstGLFilterShader * + filter_shader, char *filename, char **storage); +static gboolean gst_gl_filtershader_load_variables (GstGLFilterShader * + filter_shader, char *filename, char **storage); +static gboolean gst_gl_filtershader_init_shader (GstGLFilter * filter); +static gboolean gst_gl_filtershader_filter_texture (GstGLFilter * filter, + guint in_tex, guint out_tex); +static void gst_gl_filtershader_hcallback (gint width, gint height, + guint texture, gpointer stuff); + + +static void +gst_gl_filtershader_init_resources (GstGLFilter * filter) +{ + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, + GST_VIDEO_INFO_WIDTH (&filter->out_info), + GST_VIDEO_INFO_HEIGHT (&filter->out_info), + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +static void +gst_gl_filtershader_reset_resources (GstGLFilter * filter) +{ + //GstGLFilterShader *filtershader = GST_GL_FILTERSHADER (filter); +} + +static void +gst_gl_filtershader_class_init (GstGLFilterShaderClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_gl_filtershader_set_property; + gobject_class->get_property = gst_gl_filtershader_get_property; + + g_object_class_install_property (gobject_class, PROP_LOCATION, + g_param_spec_string ("location", "File Location", + "Location of the GLSL file to load", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_PRESET, + g_param_spec_string ("preset", "Preset File Location", + "Location of the shader uniform variables preset file", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_VARIABLES, + g_param_spec_string ("vars", "Uniform variables", + "Set the shader uniform variables", NULL, + G_PARAM_WRITABLE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_set_metadata (element_class, + "OpenGL fragment shader filter", "Filter/Effect", + "Load GLSL fragment shader from file", ""); + + GST_GL_FILTER_CLASS (klass)->filter_texture = + gst_gl_filtershader_filter_texture; + GST_GL_FILTER_CLASS (klass)->display_init_cb = + gst_gl_filtershader_init_resources; + GST_GL_FILTER_CLASS (klass)->display_reset_cb = + gst_gl_filtershader_reset_resources; + GST_GL_FILTER_CLASS (klass)->onInitFBO = gst_gl_filtershader_init_shader; + GST_GL_FILTER_CLASS (klass)->onReset = gst_gl_filter_filtershader_reset; +} + +static void +gst_gl_filtershader_init (GstGLFilterShader * filtershader) +{ + filtershader->shader0 = NULL; +} + +static void +gst_gl_filter_filtershader_reset (GstGLFilter * filter) +{ + GstGLFilterShader *filtershader = GST_GL_FILTERSHADER (filter); + + //blocking call, wait the opengl thread has destroyed the shader + if (filtershader->shader0) + gst_gl_context_del_shader (filter->context, filtershader->shader0); + filtershader->shader0 = NULL; +} + +static void +gst_gl_filtershader_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGLFilterShader *filtershader = GST_GL_FILTERSHADER (object); + + switch (prop_id) { + + case PROP_LOCATION: + + if (filtershader->filename) { + g_free (filtershader->filename); + } + if (filtershader->compiled) { + //gst_gl_context_del_shader (filtershader->filter.context, filtershader->shader0); + gst_gl_filter_filtershader_reset (&filtershader->filter); + filtershader->shader0 = 0; + } + filtershader->filename = g_strdup (g_value_get_string (value)); + filtershader->compiled = 0; + filtershader->texSet = 0; + + break; + + case PROP_PRESET: + + if (filtershader->presetfile) { + g_free (filtershader->presetfile); + } + + filtershader->presetfile = g_strdup (g_value_get_string (value)); + + if (hfilter_fragment_variables[0]) { + g_free (hfilter_fragment_variables[0]); + hfilter_fragment_variables[0] = 0; + } + + if (!filtershader->presetfile[0]) { + g_free (filtershader->presetfile); + filtershader->presetfile = 0; + } + + break; + + case PROP_VARIABLES: + + if (hfilter_fragment_variables[1]) { + g_free (hfilter_fragment_variables[1]); + } + + hfilter_fragment_variables[1] = g_strdup (g_value_get_string (value)); + + if (!hfilter_fragment_variables[1][0]) { + g_free (hfilter_fragment_variables[1]); + hfilter_fragment_variables[1] = 0; + } + + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_filtershader_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstGLFilterShader *filtershader = GST_GL_FILTERSHADER (object); + + switch (prop_id) { + case PROP_LOCATION: + g_value_set_string (value, filtershader->filename); + break; + + case PROP_PRESET: + g_value_set_string (value, filtershader->presetfile); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_gl_filtershader_load_shader (GstGLFilterShader * filter_shader, + char *filename, char **storage) +{ + GError *error = NULL; + gsize length; + + g_return_val_if_fail (storage != NULL, FALSE); + + if (!filename) { + GST_ELEMENT_ERROR (filter_shader, RESOURCE, NOT_FOUND, + ("A shader file is required"), (NULL)); + return FALSE; + } + + if (!g_file_get_contents (filename, storage, &length, &error)) { + GST_ELEMENT_ERROR (filter_shader, RESOURCE, NOT_FOUND, ("%s", + error->message), (NULL)); + g_error_free (error); + + return FALSE; + } + + return TRUE; +} + +static gboolean +gst_gl_filtershader_load_variables (GstGLFilterShader * filter_shader, + char *filename, char **storage) +{ + GError *error = NULL; + gsize length; + + if (storage[0]) { + g_free (storage[0]); + storage[0] = 0; + } + + if (!filename) + return TRUE; + + if (!g_file_get_contents (filename, storage, &length, &error)) { + GST_ELEMENT_ERROR (filter_shader, RESOURCE, NOT_FOUND, ("%s", + error->message), (NULL)); + g_error_free (error); + + return FALSE; + } + + return TRUE; +} + +static void +gst_gl_filtershader_variables_parse (GstGLShader * shader, gchar * variables) +{ + gst_gl_shadervariables_parse (shader, variables, 0); +} + +static gboolean +gst_gl_filtershader_init_shader (GstGLFilter * filter) +{ + + GstGLFilterShader *filtershader = GST_GL_FILTERSHADER (filter); + + if (!gst_gl_filtershader_load_shader (filtershader, filtershader->filename, + &hfilter_fragment_source)) + return FALSE; + + //blocking call, wait the opengl thread has compiled the shader + if (!gst_gl_context_gen_shader (filter->context, 0, hfilter_fragment_source, + &filtershader->shader0)) + return FALSE; + + + if (!gst_gl_filtershader_load_variables (filtershader, + filtershader->presetfile, &hfilter_fragment_variables[0])) + return FALSE; + + filtershader->compiled = 1; + + return TRUE; +} + +static gboolean +gst_gl_filtershader_filter_texture (GstGLFilter * filter, guint in_tex, + guint out_tex) +{ + GstGLFilterShader *filtershader = GST_GL_FILTERSHADER (filter); + + gst_gl_filter_render_to_target (filter, TRUE, in_tex, out_tex, + gst_gl_filtershader_hcallback, filtershader); + + return TRUE; +} + +static void +gst_gl_filtershader_hcallback (gint width, gint height, guint texture, + gpointer stuff) +{ + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFilterShader *filtershader = GST_GL_FILTERSHADER (filter); + GstGLFuncs *gl = filter->context->gl_vtable; + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gst_gl_shader_use (filtershader->shader0); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (filtershader->shader0, "tex", 1); + + if (hfilter_fragment_variables[0]) { + gst_gl_filtershader_variables_parse (filtershader->shader0, + hfilter_fragment_variables[0]); + g_free (hfilter_fragment_variables[0]); + hfilter_fragment_variables[0] = 0; + } + if (hfilter_fragment_variables[1]) { + gst_gl_filtershader_variables_parse (filtershader->shader0, + hfilter_fragment_variables[1]); + g_free (hfilter_fragment_variables[1]); + hfilter_fragment_variables[1] = 0; + } + + gst_gl_filter_draw_texture (filter, texture, width, height); + +} diff --git a/ext/gl/gstglfiltershader.h b/ext/gl/gstglfiltershader.h new file mode 100644 index 0000000..00fa587 --- /dev/null +++ b/ext/gl/gstglfiltershader.h @@ -0,0 +1,55 @@ +/* + * glshader gstreamer plugin + * Copyright (C) 2008 Filippo Argiolas + * Copyright (C) 2009 Luc Deschenaux + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_GL_FILTERSHADER_H_ +#define _GST_GL_FILTERSHADER_H_ + +#include + +#define GST_TYPE_GL_FILTERSHADER (gst_gl_filtershader_get_type()) +#define GST_GL_FILTERSHADER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_FILTERSHADER,GstGLFilterShader)) +#define GST_IS_GL_FILTERSHADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_FILTERSHADER)) +#define GST_GL_FILTERSHADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_FILTERSHADER,GstGLFilterShaderClass)) +#define GST_IS_GL_FILTERSHADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_FILTERSHADER)) +#define GST_GL_FILTERSHADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_FILTERSHADER,GstGLFilterShaderClass)) + +typedef struct _GstGLFilterShader GstGLFilterShader; +typedef struct _GstGLFilterShaderClass GstGLFilterShaderClass; + +struct _GstGLFilterShader +{ + GstGLFilter filter; + GstGLShader *shader0; + int compiled; + gchar *filename; + gchar *presetfile; + int texSet; + +}; + +struct _GstGLFilterShaderClass +{ + GstGLFilterClass filter_class; +}; + +GType gst_gl_glfiltershader_get_type (void); + +#endif /* _GST_GL_FILTERSHADER_H_ */ diff --git a/ext/gl/gstglfiltersobel.c b/ext/gl/gstglfiltersobel.c new file mode 100644 index 0000000..538f96e --- /dev/null +++ b/ext/gl/gstglfiltersobel.c @@ -0,0 +1,269 @@ +/* + * GStreamer + * Copyright (C) 2008-2010 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-glfiltersobel. + * + * Sobel Edge Detection. + * + * + * Examples + * |[ + * gst-launch videotestsrc ! glupload ! glfiltersobel ! glimagesink + * ]| + * FBO (Frame Buffer Object) and GLSL (OpenGL Shading Language) are required. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstglfiltersobel.h" +#include "effects/gstgleffectssources.h" + +enum +{ + PROP_0, + PROP_INVERT +}; + +#define GST_CAT_DEFAULT gst_gl_filtersobel_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_filtersobel_debug, "glfiltersobel", 0, "glfiltersobel element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLFilterSobel, gst_gl_filtersobel, + GST_TYPE_GL_FILTER, DEBUG_INIT); + +static void gst_gl_filtersobel_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_filtersobel_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_gl_filter_filtersobel_reset (GstGLFilter * filter); + +static gboolean gst_gl_filtersobel_init_shader (GstGLFilter * filter); +static gboolean gst_gl_filtersobel_filter_texture (GstGLFilter * filter, + guint in_tex, guint out_tex); + +static void gst_gl_filtersobel_length (gint width, gint height, guint texture, + gpointer stuff); + +static void +gst_gl_filtersobel_init_resources (GstGLFilter * filter) +{ + GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter); + GstGLFuncs *gl = filter->context->gl_vtable; + int i; + + for (i = 0; i < 2; i++) { + gl->GenTextures (1, &filtersobel->midtexture[i]); + gl->BindTexture (GL_TEXTURE_2D, filtersobel->midtexture[i]); + gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, + GST_VIDEO_INFO_WIDTH (&filter->out_info), + GST_VIDEO_INFO_HEIGHT (&filter->out_info), + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } +} + +static void +gst_gl_filtersobel_reset_resources (GstGLFilter * filter) +{ + GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter); + GstGLFuncs *gl = filter->context->gl_vtable; + int i; + + for (i = 0; i < 2; i++) { + gl->DeleteTextures (1, &filtersobel->midtexture[i]); + } +} + +static void +gst_gl_filtersobel_class_init (GstGLFilterSobelClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_gl_filtersobel_set_property; + gobject_class->get_property = gst_gl_filtersobel_get_property; + + GST_GL_FILTER_CLASS (klass)->filter_texture = + gst_gl_filtersobel_filter_texture; + GST_GL_FILTER_CLASS (klass)->display_init_cb = + gst_gl_filtersobel_init_resources; + GST_GL_FILTER_CLASS (klass)->display_reset_cb = + gst_gl_filtersobel_reset_resources; + GST_GL_FILTER_CLASS (klass)->onInitFBO = gst_gl_filtersobel_init_shader; + GST_GL_FILTER_CLASS (klass)->onReset = gst_gl_filter_filtersobel_reset; + + g_object_class_install_property (gobject_class, + PROP_INVERT, + g_param_spec_boolean ("invert", + "Invert the colors", + "Invert colors to get dark edges on bright background", + FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_set_metadata (element_class, + "Gstreamer OpenGL Sobel", "Filter/Effect/Video", "Sobel edge detection", + "Filippo Argiolas "); +} + +static void +gst_gl_filtersobel_init (GstGLFilterSobel * filtersobel) +{ + int i; + filtersobel->hconv = NULL; + filtersobel->vconv = NULL; + filtersobel->invert = FALSE; + for (i = 0; i < 2; i++) { + filtersobel->midtexture[i] = 0; + } +} + +static void +gst_gl_filter_filtersobel_reset (GstGLFilter * filter) +{ + GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter); + + //blocking call, wait the opengl thread has destroyed the shader + if (filtersobel->desat) + gst_gl_context_del_shader (filter->context, filtersobel->desat); + filtersobel->desat = NULL; + + if (filtersobel->hconv) + gst_gl_context_del_shader (filter->context, filtersobel->hconv); + filtersobel->hconv = NULL; + + if (filtersobel->vconv) + gst_gl_context_del_shader (filter->context, filtersobel->vconv); + filtersobel->vconv = NULL; + + if (filtersobel->len) + gst_gl_context_del_shader (filter->context, filtersobel->len); + filtersobel->len = NULL; +} + +static void +gst_gl_filtersobel_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (object); + + switch (prop_id) { + case PROP_INVERT: + filtersobel->invert = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_filtersobel_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (object); + + switch (prop_id) { + case PROP_INVERT: + g_value_set_boolean (value, filtersobel->invert); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_gl_filtersobel_init_shader (GstGLFilter * filter) +{ + GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter); + gboolean ret = TRUE; + + //blocking call, wait the opengl thread has compiled the shader + ret = + gst_gl_context_gen_shader (filter->context, 0, desaturate_fragment_source, + &filtersobel->desat); + ret &= + gst_gl_context_gen_shader (filter->context, 0, + sep_sobel_hconv3_fragment_source, &filtersobel->hconv); + ret &= + gst_gl_context_gen_shader (filter->context, 0, + sep_sobel_vconv3_fragment_source, &filtersobel->vconv); + ret &= + gst_gl_context_gen_shader (filter->context, 0, + sep_sobel_length_fragment_source, &filtersobel->len); + + return ret; +} + +static gboolean +gst_gl_filtersobel_filter_texture (GstGLFilter * filter, guint in_tex, + guint out_tex) +{ + GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter); + + gst_gl_filter_render_to_target_with_shader (filter, TRUE, in_tex, + filtersobel->midtexture[0], filtersobel->desat); + gst_gl_filter_render_to_target_with_shader (filter, FALSE, + filtersobel->midtexture[0], filtersobel->midtexture[1], + filtersobel->hconv); + gst_gl_filter_render_to_target_with_shader (filter, FALSE, + filtersobel->midtexture[1], filtersobel->midtexture[0], + filtersobel->vconv); + gst_gl_filter_render_to_target (filter, FALSE, filtersobel->midtexture[0], + out_tex, gst_gl_filtersobel_length, filtersobel); + + return TRUE; +} + +static void +gst_gl_filtersobel_length (gint width, gint height, guint texture, + gpointer stuff) +{ + GstGLFilter *filter = GST_GL_FILTER (stuff); + GstGLFuncs *gl = filter->context->gl_vtable; + GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + + gst_gl_shader_use (filtersobel->len); + + gl->ActiveTexture (GL_TEXTURE1); + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, texture); + gl->Disable (GL_TEXTURE_2D); + + gst_gl_shader_set_uniform_1i (filtersobel->len, "tex", 1); + gst_gl_shader_set_uniform_1i (filtersobel->len, "invert", + filtersobel->invert); + + gst_gl_filter_draw_texture (filter, texture, width, height); +} diff --git a/ext/gl/gstglfiltersobel.h b/ext/gl/gstglfiltersobel.h new file mode 100644 index 0000000..1a73891 --- /dev/null +++ b/ext/gl/gstglfiltersobel.h @@ -0,0 +1,56 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_GL_FILTERSOBEL_H_ +#define _GST_GL_FILTERSOBEL_H_ + +#include + +#define GST_TYPE_GL_FILTERSOBEL (gst_gl_filtersobel_get_type()) +#define GST_GL_FILTERSOBEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_FILTERSOBEL,GstGLFilterSobel)) +#define GST_IS_GL_FILTERSOBEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_FILTERSOBEL)) +#define GST_GL_FILTERSOBEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_FILTERSOBEL,GstGLFilterSobelClass)) +#define GST_IS_GL_FILTERSOBEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_FILTERSOBEL)) +#define GST_GL_FILTERSOBEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_FILTERSOBEL,GstGLFilterSobelClass)) + +typedef struct _GstGLFilterSobel GstGLFilterSobel; +typedef struct _GstGLFilterSobelClass GstGLFilterSobelClass; + +struct _GstGLFilterSobel +{ + GstGLFilter filter; + GstGLShader *hconv; + GstGLShader *vconv; + GstGLShader *len; + GstGLShader *desat; + + GLuint midtexture[5]; + + gboolean invert; +}; + +struct _GstGLFilterSobelClass +{ + GstGLFilterClass filter_class; +}; + +GType gst_gl_glfiltersobel_get_type (void); + +#endif /* _GST_GL_FILTERSOBEL_H_ */ diff --git a/ext/gl/gstglimagesink.c b/ext/gl/gstglimagesink.c new file mode 100644 index 0000000..f49422c --- /dev/null +++ b/ext/gl/gstglimagesink.c @@ -0,0 +1,1206 @@ +/* + * GStreamer + * Copyright (C) 2003 Julien Moutte + * Copyright (C) 2005,2006,2007 David A. Schleef + * Copyright (C) 2008 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-glimagesink + * + * glimagesink renders video frames to a drawable on a local or remote + * display using OpenGL. This element can receive a Window ID from the + * application through the VideoOverlay interface and will then render video + * frames in this drawable. + * If no Window ID was provided by the application, the element will + * create its own internal window and render into it. + * + * + * Scaling + * + * Depends on the driver, OpenGL handles hardware accelerated + * scaling of video frames. This means that the element will just accept + * incoming video frames no matter their geometry and will then put them to the + * drawable scaling them on the fly. Using the #GstGLImageSink:force-aspect-ratio + * property it is possible to enforce scaling with a constant aspect ratio, + * which means drawing black borders around the video frame. + * + * + * + * Events + * + * Through the gl thread, glimagesink handle some events coming from the drawable + * to manage its appearance even when the data is not flowing (GST_STATE_PAUSED). + * That means that even when the element is paused, it will receive expose events + * from the drawable and draw the latest frame with correct borders/aspect-ratio. + * + * + * + * Examples + * |[ + * gst-launch -v videotestsrc ! "video/x-raw-rgb" ! glimagesink + * ]| A pipeline to test hardware scaling. + * No special opengl extension is used in this pipeline, that's why it should work + * with OpenGL >= 1.1. That's the case if you are using the MESA3D driver v1.3. + * |[ + * gst-launch -v videotestsrc ! "video/x-raw-yuv, format=(fourcc)I420" ! glimagesink + * ]| A pipeline to test hardware scaling and hardware colorspace conversion. + * When your driver supports GLSL (OpenGL Shading Language needs OpenGL >= 2.1), + * the 4 following format YUY2, UYVY, I420, YV12 and AYUV are converted to RGB32 + * through some fragment shaders and using one framebuffer (FBO extension OpenGL >= 1.4). + * If your driver does not support GLSL but supports MESA_YCbCr extension then + * the you can use YUY2 and UYVY. In this case the colorspace conversion is automatically + * made when loading the texture and therefore no framebuffer is used. + * |[ + * gst-launch -v gltestsrc ! glimagesink + * ]| A pipeline 100% OpenGL. + * No special opengl extension is used in this pipeline, that's why it should work + * with OpenGL >= 1.1. That's the case if you are using the MESA3D driver v1.3. + * |[ + * gst-plugins-gl/tests/examples/generic/cube + * ]| The graphic FPS scene can be greater than the input video FPS. + * The graphic scene can be written from a client code through the + * two glfilterapp properties. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstglimagesink.h" + +GST_DEBUG_CATEGORY (gst_debug_glimage_sink); +#define GST_CAT_DEFAULT gst_debug_glimage_sink + +#define GST_GLIMAGE_SINK_GET_LOCK(glsink) \ + (GST_GLIMAGE_SINK(glsink)->drawing_lock) +#define GST_GLIMAGE_SINK_LOCK(glsink) \ + (g_mutex_lock(&GST_GLIMAGE_SINK_GET_LOCK (glsink))) +#define GST_GLIMAGE_SINK_UNLOCK(glsink) \ + (g_mutex_unlock(&GST_GLIMAGE_SINK_GET_LOCK (glsink))) + +#define USING_OPENGL(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL) +#define USING_OPENGL3(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL3) +#define USING_GLES(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES) +#define USING_GLES2(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES2) +#define USING_GLES3(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES3) + +#if GST_GL_HAVE_GLES2 +static void gst_glimage_sink_thread_init_redisplay (GstGLImageSink * gl_sink); +#endif +static void gst_glimage_sink_on_close (GstGLImageSink * gl_sink); +static void gst_glimage_sink_on_resize (const GstGLImageSink * gl_sink, + gint width, gint height); +static void gst_glimage_sink_on_draw (const GstGLImageSink * gl_sink); +static gboolean gst_glimage_sink_redisplay (GstGLImageSink * gl_sink); + +static void gst_glimage_sink_finalize (GObject * object); +static void gst_glimage_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * param_spec); +static void gst_glimage_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * param_spec); + +static gboolean gst_glimage_sink_stop (GstBaseSink * bsink); + +static gboolean gst_glimage_sink_query (GstBaseSink * bsink, GstQuery * query); +static void gst_glimage_sink_set_context (GstElement * element, + GstContext * context); + +static GstStateChangeReturn +gst_glimage_sink_change_state (GstElement * element, GstStateChange transition); + +static void gst_glimage_sink_get_times (GstBaseSink * bsink, GstBuffer * buf, + GstClockTime * start, GstClockTime * end); +static gboolean gst_glimage_sink_set_caps (GstBaseSink * bsink, GstCaps * caps); +static GstFlowReturn gst_glimage_sink_render (GstBaseSink * bsink, + GstBuffer * buf); +static gboolean gst_glimage_sink_propose_allocation (GstBaseSink * bsink, + GstQuery * query); + +static void gst_glimage_sink_video_overlay_init (GstVideoOverlayInterface * + iface); +static void gst_glimage_sink_set_window_handle (GstVideoOverlay * overlay, + guintptr id); +static void gst_glimage_sink_expose (GstVideoOverlay * overlay); + + +#if GST_GL_HAVE_GLES2 +/* *INDENT-OFF* */ +static const gchar *redisplay_vertex_shader_str_gles2 = + "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" + "varying vec2 v_texCoord; \n" + "void main() \n" + "{ \n" + " gl_Position = a_position; \n" + " v_texCoord = a_texCoord; \n" + "} \n"; + +static const gchar *redisplay_fragment_shader_str_gles2 = + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D s_texture; \n" + "void main() \n" + "{ \n" + " gl_FragColor = texture2D( s_texture, v_texCoord );\n" + "} \n"; +/* *INDENT-ON* */ +#endif + +static GstStaticPadTemplate gst_glimage_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_GL_UPLOAD_FORMATS) "; " + GST_VIDEO_CAPS_MAKE_WITH_FEATURES + (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, + GST_GL_UPLOAD_FORMATS)) + ); + +enum +{ + ARG_0, + ARG_DISPLAY, + PROP_CLIENT_RESHAPE_CALLBACK, + PROP_CLIENT_DRAW_CALLBACK, + PROP_CLIENT_DATA, + PROP_FORCE_ASPECT_RATIO, + PROP_PIXEL_ASPECT_RATIO, + PROP_OTHER_CONTEXT +}; + +#define gst_glimage_sink_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstGLImageSink, gst_glimage_sink, + GST_TYPE_VIDEO_SINK, G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY, + gst_glimage_sink_video_overlay_init); + GST_DEBUG_CATEGORY_INIT (gst_debug_glimage_sink, "glimagesink", 0, + "OpenGL Video Sink")); + +static void +gst_glimage_sink_class_init (GstGLImageSinkClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_glimage_sink_set_property; + gobject_class->get_property = gst_glimage_sink_get_property; + + g_object_class_install_property (gobject_class, ARG_DISPLAY, + g_param_spec_string ("display", "Display", "Display name", + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_CLIENT_RESHAPE_CALLBACK, + g_param_spec_pointer ("client-reshape-callback", + "Client reshape callback", + "Define a custom reshape callback in a client code", + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_CLIENT_DRAW_CALLBACK, + g_param_spec_pointer ("client-draw-callback", "Client draw callback", + "Define a custom draw callback in a client code", + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_CLIENT_DATA, + g_param_spec_pointer ("client-data", "Client data", + "Pass data to the draw and reshape callbacks", + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO, + g_param_spec_boolean ("force-aspect-ratio", + "Force aspect ratio", + "When enabled, scaling will respect original aspect ratio", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO, + gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio", + "The pixel aspect ratio of the device", 0, 1, G_MAXINT, 1, 0, 1, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_set_metadata (element_class, "OpenGL video sink", + "Sink/Video", "A videosink based on OpenGL", + "Julien Isorce "); + + g_object_class_install_property (gobject_class, PROP_OTHER_CONTEXT, + g_param_spec_object ("other-context", + "External OpenGL context", + "Give an external OpenGL context with which to share textures", + GST_GL_TYPE_CONTEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_glimage_sink_template)); + + gobject_class->finalize = gst_glimage_sink_finalize; + + gstelement_class->change_state = gst_glimage_sink_change_state; + gstelement_class->set_context = gst_glimage_sink_set_context; + gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_glimage_sink_query); + gstbasesink_class->set_caps = gst_glimage_sink_set_caps; + gstbasesink_class->get_times = gst_glimage_sink_get_times; + gstbasesink_class->preroll = gst_glimage_sink_render; + gstbasesink_class->render = gst_glimage_sink_render; + gstbasesink_class->propose_allocation = gst_glimage_sink_propose_allocation; + gstbasesink_class->stop = gst_glimage_sink_stop; +} + +static void +gst_glimage_sink_init (GstGLImageSink * glimage_sink) +{ + glimage_sink->display_name = NULL; + glimage_sink->window_id = 0; + glimage_sink->new_window_id = 0; + glimage_sink->display = NULL; + glimage_sink->clientReshapeCallback = NULL; + glimage_sink->clientDrawCallback = NULL; + glimage_sink->client_data = NULL; + glimage_sink->keep_aspect_ratio = FALSE; + glimage_sink->par_n = 0; + glimage_sink->par_d = 1; + glimage_sink->pool = NULL; + glimage_sink->redisplay_texture = 0; + + g_mutex_init (&glimage_sink->drawing_lock); +} + +static void +gst_glimage_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGLImageSink *glimage_sink; + + g_return_if_fail (GST_IS_GLIMAGE_SINK (object)); + + glimage_sink = GST_GLIMAGE_SINK (object); + + switch (prop_id) { + case ARG_DISPLAY: + { + g_free (glimage_sink->display_name); + glimage_sink->display_name = g_strdup (g_value_get_string (value)); + break; + } + case PROP_CLIENT_RESHAPE_CALLBACK: + { + glimage_sink->clientReshapeCallback = g_value_get_pointer (value); + break; + } + case PROP_CLIENT_DRAW_CALLBACK: + { + glimage_sink->clientDrawCallback = g_value_get_pointer (value); + break; + } + case PROP_CLIENT_DATA: + { + glimage_sink->client_data = g_value_get_pointer (value); + break; + } + case PROP_FORCE_ASPECT_RATIO: + { + glimage_sink->keep_aspect_ratio = g_value_get_boolean (value); + break; + } + case PROP_PIXEL_ASPECT_RATIO: + { + glimage_sink->par_n = gst_value_get_fraction_numerator (value); + glimage_sink->par_d = gst_value_get_fraction_denominator (value); + break; + } + case PROP_OTHER_CONTEXT: + { + if (glimage_sink->other_context) + gst_object_unref (glimage_sink->other_context); + glimage_sink->other_context = g_value_dup_object (value); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_glimage_sink_finalize (GObject * object) +{ + GstGLImageSink *glimage_sink; + + g_return_if_fail (GST_IS_GLIMAGE_SINK (object)); + + glimage_sink = GST_GLIMAGE_SINK (object); + + g_mutex_clear (&glimage_sink->drawing_lock); + + g_free (glimage_sink->display_name); + + GST_DEBUG ("finalized"); +} + +static void +gst_glimage_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstGLImageSink *glimage_sink; + + g_return_if_fail (GST_IS_GLIMAGE_SINK (object)); + + glimage_sink = GST_GLIMAGE_SINK (object); + + switch (prop_id) { + case ARG_DISPLAY: + g_value_set_string (value, glimage_sink->display_name); + break; + case PROP_FORCE_ASPECT_RATIO: + g_value_set_boolean (value, glimage_sink->keep_aspect_ratio); + break; + case PROP_PIXEL_ASPECT_RATIO: + gst_value_set_fraction (value, glimage_sink->par_n, glimage_sink->par_d); + break; + case PROP_OTHER_CONTEXT: + g_value_set_object (value, glimage_sink->other_context); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +_ensure_gl_setup (GstGLImageSink * gl_sink) +{ + GError *error = NULL; + + if (!gst_gl_ensure_display (gl_sink, &gl_sink->display)) + return FALSE; + + if (!gl_sink->context) { + gl_sink->context = gst_gl_context_new (gl_sink->display); + if (!gst_gl_context_create (gl_sink->context, gl_sink->other_context, + &error)) + goto context_error; + } + + if (!gl_sink->upload) { + gl_sink->upload = gst_gl_upload_new (gl_sink->context); + if (!gst_gl_upload_init_format (gl_sink->upload, gl_sink->info, + gl_sink->info)) + goto upload_error; + } + + return TRUE; + +upload_error: + { + GST_ELEMENT_ERROR (gl_sink, RESOURCE, NOT_FOUND, ("Failed to init upload"), + (NULL)); + return FALSE; + } +context_error: + { + GST_ELEMENT_ERROR (gl_sink, RESOURCE, NOT_FOUND, ("%s", error->message), + (NULL)); + return FALSE; + } +} + +static gboolean +gst_glimage_sink_query (GstBaseSink * bsink, GstQuery * query) +{ + GstGLImageSink *glimage_sink = GST_GLIMAGE_SINK (bsink); + gboolean res = FALSE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONTEXT: + { + return gst_gl_handle_context_query ((GstElement *) glimage_sink, query, + &glimage_sink->display); + break; + } + default: + res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query); + break; + } + + return res; +} + +static void +gst_glimage_sink_cleanup_glthread (GstGLImageSink * gl_sink) +{ +#if GST_GL_HAVE_GLES2 + if (gl_sink->redisplay_shader) { + gst_object_unref (gl_sink->redisplay_shader); + gl_sink->redisplay_shader = NULL; + } +#endif +} + +/* + * GstElement methods + */ + +static gboolean +gst_glimage_sink_stop (GstBaseSink * bsink) +{ + GstGLImageSink *glimage_sink = GST_GLIMAGE_SINK (bsink); + + if (glimage_sink->pool) { + gst_object_unref (glimage_sink->pool); + glimage_sink->pool = NULL; + } + + return TRUE; +} + +static void +gst_glimage_sink_set_context (GstElement * element, GstContext * context) +{ + GstGLImageSink *gl_sink = GST_GLIMAGE_SINK (element); + + gst_gl_handle_set_context (element, context, &gl_sink->display); +} + +static GstStateChangeReturn +gst_glimage_sink_change_state (GstElement * element, GstStateChange transition) +{ + GstGLImageSink *glimage_sink; + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + GST_DEBUG ("change state"); + + glimage_sink = GST_GLIMAGE_SINK (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + g_atomic_int_set (&glimage_sink->to_quit, 0); + if (!glimage_sink->display) { + GstGLWindow *window; + GError *error = NULL; + + if (!gst_gl_ensure_display (glimage_sink, &glimage_sink->display)) + return GST_STATE_CHANGE_FAILURE; + + glimage_sink->context = gst_gl_context_new (glimage_sink->display); + window = gst_gl_context_get_window (glimage_sink->context); + + if (!glimage_sink->window_id && !glimage_sink->new_window_id) + gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY + (glimage_sink)); + + if (glimage_sink->window_id != glimage_sink->new_window_id) { + glimage_sink->window_id = glimage_sink->new_window_id; + gst_gl_window_set_window_handle (window, glimage_sink->window_id); + } + + if (!gst_gl_context_create (glimage_sink->context, + glimage_sink->other_context, &error)) { + GST_ELEMENT_ERROR (glimage_sink, RESOURCE, NOT_FOUND, ("%s", + error->message), (NULL)); + + if (glimage_sink->display) { + gst_object_unref (glimage_sink->display); + glimage_sink->display = NULL; + } + gst_object_unref (glimage_sink->context); + gst_object_unref (window); + + return GST_STATE_CHANGE_FAILURE; + } + + /* setup callbacks */ + gst_gl_window_set_resize_callback (window, + GST_GL_WINDOW_RESIZE_CB (gst_glimage_sink_on_resize), + gst_object_ref (glimage_sink), (GDestroyNotify) gst_object_unref); + gst_gl_window_set_draw_callback (window, + GST_GL_WINDOW_CB (gst_glimage_sink_on_draw), + gst_object_ref (glimage_sink), (GDestroyNotify) gst_object_unref); + gst_gl_window_set_close_callback (window, + GST_GL_WINDOW_CB (gst_glimage_sink_on_close), + gst_object_ref (glimage_sink), (GDestroyNotify) gst_object_unref); + + gst_object_unref (window); + } + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + { + /* mark the redisplay_texture as unavailable (=0) + * to avoid drawing + */ + GST_GLIMAGE_SINK_LOCK (glimage_sink); + glimage_sink->redisplay_texture = 0; + GST_GLIMAGE_SINK_UNLOCK (glimage_sink); + + if (glimage_sink->upload) { + gst_object_unref (glimage_sink->upload); + glimage_sink->upload = NULL; + } + + glimage_sink->window_id = 0; + //but do not reset glimage_sink->new_window_id + + if (glimage_sink->pool) { + gst_buffer_pool_set_active (glimage_sink->pool, FALSE); + gst_object_unref (glimage_sink->pool); + glimage_sink->pool = NULL; + } + + GST_VIDEO_SINK_WIDTH (glimage_sink) = 1; + GST_VIDEO_SINK_HEIGHT (glimage_sink) = 1; + if (glimage_sink->context) { + GstGLWindow *window = gst_gl_context_get_window (glimage_sink->context); + + gst_gl_window_send_message (window, + GST_GL_WINDOW_CB (gst_glimage_sink_cleanup_glthread), glimage_sink); + + gst_gl_window_set_resize_callback (window, NULL, NULL, NULL); + gst_gl_window_set_draw_callback (window, NULL, NULL, NULL); + gst_gl_window_set_close_callback (window, NULL, NULL, NULL); + + gst_object_unref (window); + gst_object_unref (glimage_sink->context); + glimage_sink->context = NULL; + gst_object_unref (glimage_sink->display); + glimage_sink->display = NULL; + } + break; + } + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return ret; +} + +static void +gst_glimage_sink_get_times (GstBaseSink * bsink, GstBuffer * buf, + GstClockTime * start, GstClockTime * end) +{ + GstGLImageSink *glimagesink; + + glimagesink = GST_GLIMAGE_SINK (bsink); + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + *start = GST_BUFFER_TIMESTAMP (buf); + if (GST_BUFFER_DURATION_IS_VALID (buf)) + *end = *start + GST_BUFFER_DURATION (buf); + else { + if (GST_VIDEO_INFO_FPS_N (&glimagesink->info) > 0) { + *end = *start + + gst_util_uint64_scale_int (GST_SECOND, + GST_VIDEO_INFO_FPS_D (&glimagesink->info), + GST_VIDEO_INFO_FPS_N (&glimagesink->info)); + } + } + } +} + +static gboolean +gst_glimage_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) +{ + GstGLImageSink *glimage_sink; + gint width; + gint height; + gboolean ok; + gint par_n, par_d; + gint display_par_n, display_par_d; + guint display_ratio_num, display_ratio_den; + GstVideoInfo vinfo; + GstStructure *structure; + GstBufferPool *newpool, *oldpool; + + GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps); + + glimage_sink = GST_GLIMAGE_SINK (bsink); + + ok = gst_video_info_from_caps (&vinfo, caps); + if (!ok) + return FALSE; + + width = GST_VIDEO_INFO_WIDTH (&vinfo); + height = GST_VIDEO_INFO_HEIGHT (&vinfo); + + if (glimage_sink->tex_id) + gst_gl_context_del_texture (glimage_sink->context, &glimage_sink->tex_id); + //FIXME: this texture seems to be never deleted when going to STATE_NULL + gst_gl_context_gen_texture (glimage_sink->context, &glimage_sink->tex_id, + GST_VIDEO_INFO_FORMAT (&vinfo), width, height); + + par_n = GST_VIDEO_INFO_PAR_N (&vinfo); + par_d = GST_VIDEO_INFO_PAR_D (&vinfo); + + if (!par_n) + par_n = 1; + + /* get display's PAR */ + if (glimage_sink->par_n != 0 && glimage_sink->par_d != 0) { + display_par_n = glimage_sink->par_n; + display_par_d = glimage_sink->par_d; + } else { + display_par_n = 1; + display_par_d = 1; + } + + ok = gst_video_calculate_display_ratio (&display_ratio_num, + &display_ratio_den, width, height, par_n, par_d, display_par_n, + display_par_d); + + if (!ok) + return FALSE; + + GST_TRACE ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n, + display_par_d); + + if (height % display_ratio_den == 0) { + GST_DEBUG ("keeping video height"); + GST_VIDEO_SINK_WIDTH (glimage_sink) = (guint) + gst_util_uint64_scale_int (height, display_ratio_num, + display_ratio_den); + GST_VIDEO_SINK_HEIGHT (glimage_sink) = height; + } else if (width % display_ratio_num == 0) { + GST_DEBUG ("keeping video width"); + GST_VIDEO_SINK_WIDTH (glimage_sink) = width; + GST_VIDEO_SINK_HEIGHT (glimage_sink) = (guint) + gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num); + } else { + GST_DEBUG ("approximating while keeping video height"); + GST_VIDEO_SINK_WIDTH (glimage_sink) = (guint) + gst_util_uint64_scale_int (height, display_ratio_num, + display_ratio_den); + GST_VIDEO_SINK_HEIGHT (glimage_sink) = height; + } + GST_DEBUG ("scaling to %dx%d", GST_VIDEO_SINK_WIDTH (glimage_sink), + GST_VIDEO_SINK_HEIGHT (glimage_sink)); + + glimage_sink->info = vinfo; + + newpool = gst_gl_buffer_pool_new (glimage_sink->context); + structure = gst_buffer_pool_get_config (newpool); + gst_buffer_pool_config_set_params (structure, caps, vinfo.size, 2, 0); + gst_buffer_pool_set_config (newpool, structure); + + oldpool = glimage_sink->pool; + /* we don't activate the pool yet, this will be done by downstream after it + * has configured the pool. If downstream does not want our pool we will + * activate it when we render into it */ + glimage_sink->pool = newpool; + + /* unref the old sink */ + if (oldpool) { + /* we don't deactivate, some elements might still be using it, it will + * be deactivated when the last ref is gone */ + gst_object_unref (oldpool); + } + + return TRUE; +} + +static GstFlowReturn +gst_glimage_sink_render (GstBaseSink * bsink, GstBuffer * buf) +{ + GstGLImageSink *glimage_sink; + guint tex_id; + + GST_TRACE ("rendering buffer:%p", buf); + + glimage_sink = GST_GLIMAGE_SINK (bsink); + + if (GST_VIDEO_SINK_WIDTH (glimage_sink) < 1 || + GST_VIDEO_SINK_HEIGHT (glimage_sink) < 1) { + return GST_FLOW_NOT_NEGOTIATED; + } + + if (!_ensure_gl_setup (glimage_sink)) + return GST_FLOW_NOT_NEGOTIATED; + + if (!gst_gl_upload_perform_with_buffer (glimage_sink->upload, buf, &tex_id)) + goto upload_failed; + + if (glimage_sink->window_id != glimage_sink->new_window_id) { + GstGLWindow *window = gst_gl_context_get_window (glimage_sink->context); + + glimage_sink->window_id = glimage_sink->new_window_id; + gst_gl_window_set_window_handle (window, glimage_sink->window_id); + + gst_object_unref (window); + } + + GST_TRACE ("redisplay texture:%u of size:%ux%u, window size:%ux%u", tex_id, + GST_VIDEO_INFO_WIDTH (&glimage_sink->info), + GST_VIDEO_INFO_HEIGHT (&glimage_sink->info), + GST_VIDEO_SINK_WIDTH (glimage_sink), + GST_VIDEO_SINK_HEIGHT (glimage_sink)); + + /* Avoid to release the texture while drawing */ + GST_GLIMAGE_SINK_LOCK (glimage_sink); + glimage_sink->redisplay_texture = tex_id; + GST_GLIMAGE_SINK_UNLOCK (glimage_sink); + + /* Ask the underlying window to redraw its content */ + if (!gst_glimage_sink_redisplay (glimage_sink)) + goto redisplay_failed; + + GST_TRACE ("post redisplay"); + + if (g_atomic_int_get (&glimage_sink->to_quit) != 0) { + GST_ELEMENT_ERROR (glimage_sink, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + gst_gl_upload_release_buffer (glimage_sink->upload); + return GST_FLOW_ERROR; + } + + gst_gl_upload_release_buffer (glimage_sink->upload); + return GST_FLOW_OK; + +/* ERRORS */ +redisplay_failed: + { + gst_gl_upload_release_buffer (glimage_sink->upload); + GST_ELEMENT_ERROR (glimage_sink, RESOURCE, NOT_FOUND, + ("%s", gst_gl_context_get_error ()), (NULL)); + return GST_FLOW_ERROR; + } + +upload_failed: + { + GST_ELEMENT_ERROR (glimage_sink, RESOURCE, NOT_FOUND, + ("%s", "Failed to upload format"), (NULL)); + return FALSE; + } +} + + +static void +gst_glimage_sink_video_overlay_init (GstVideoOverlayInterface * iface) +{ + iface->set_window_handle = gst_glimage_sink_set_window_handle; + iface->expose = gst_glimage_sink_expose; +} + + +static void +gst_glimage_sink_set_window_handle (GstVideoOverlay * overlay, guintptr id) +{ + GstGLImageSink *glimage_sink = GST_GLIMAGE_SINK (overlay); + guintptr window_id = (guintptr) id; + + g_return_if_fail (GST_IS_GLIMAGE_SINK (overlay)); + + GST_DEBUG ("set_xwindow_id %" G_GUINT64_FORMAT, (guint64) window_id); + + glimage_sink->new_window_id = window_id; +} + + +static void +gst_glimage_sink_expose (GstVideoOverlay * overlay) +{ + GstGLImageSink *glimage_sink = GST_GLIMAGE_SINK (overlay); + + /* redisplay opengl scene */ + if (glimage_sink->display && glimage_sink->window_id) { + + if (glimage_sink->window_id != glimage_sink->new_window_id) { + GstGLWindow *window = gst_gl_context_get_window (glimage_sink->context); + + glimage_sink->window_id = glimage_sink->new_window_id; + gst_gl_window_set_window_handle (window, glimage_sink->window_id); + + gst_object_unref (window); + } + + gst_glimage_sink_redisplay (glimage_sink); + } +} + +static gboolean +gst_glimage_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) +{ + GstGLImageSink *glimage_sink = GST_GLIMAGE_SINK (bsink); + GstBufferPool *pool; + GstStructure *config; + GstCaps *caps; + guint size; + gboolean need_pool; + GstStructure *gl_context; + gchar *platform, *gl_apis; + gpointer handle; + + if (!_ensure_gl_setup (glimage_sink)) + return FALSE; + + gst_query_parse_allocation (query, &caps, &need_pool); + + if (caps == NULL) + goto no_caps; + + if ((pool = glimage_sink->pool)) + gst_object_ref (pool); + + if (pool != NULL) { + GstCaps *pcaps; + + /* we had a pool, check caps */ + GST_DEBUG_OBJECT (glimage_sink, "check existing pool caps"); + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL); + + if (!gst_caps_is_equal (caps, pcaps)) { + GST_DEBUG_OBJECT (glimage_sink, "pool has different caps"); + /* different caps, we can't use this pool */ + gst_object_unref (pool); + pool = NULL; + } + gst_structure_free (config); + } + + if (pool == NULL && need_pool) { + GstVideoInfo info; + + if (!gst_video_info_from_caps (&info, caps)) + goto invalid_caps; + + GST_DEBUG_OBJECT (glimage_sink, "create new pool"); + pool = gst_gl_buffer_pool_new (glimage_sink->context); + + /* the normal size of a frame */ + size = info.size; + + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_set_params (config, caps, size, 0, 0); + if (!gst_buffer_pool_set_config (pool, config)) + goto config_failed; + } + /* we need at least 2 buffer because we hold on to the last one */ + gst_query_add_allocation_pool (query, pool, size, 2, 0); + + /* we also support various metadata */ + gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0); + + gst_object_unref (pool); + + gl_apis = + gst_gl_api_to_string (gst_gl_context_get_gl_api (glimage_sink->context)); + platform = + gst_gl_platform_to_string (gst_gl_context_get_gl_platform + (glimage_sink->context)); + handle = (gpointer) gst_gl_context_get_gl_context (glimage_sink->context); + + gl_context = + gst_structure_new ("GstVideoGLTextureUploadMeta", "gst.gl.GstGLContext", + GST_GL_TYPE_CONTEXT, glimage_sink->context, "gst.gl.context.handle", + G_TYPE_POINTER, handle, "gst.gl.context.type", G_TYPE_STRING, platform, + "gst.gl.context.apis", G_TYPE_STRING, gl_apis, NULL); + gst_query_add_allocation_meta (query, + GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, gl_context); + + g_free (gl_apis); + g_free (platform); + gst_structure_free (gl_context); + + return TRUE; + + /* ERRORS */ +no_caps: + { + GST_DEBUG_OBJECT (bsink, "no caps specified"); + return FALSE; + } +invalid_caps: + { + GST_DEBUG_OBJECT (bsink, "invalid caps specified"); + return FALSE; + } +config_failed: + { + GST_DEBUG_OBJECT (bsink, "failed setting config"); + return FALSE; + } +} + +#if GST_GL_HAVE_GLES2 +/* Called in the gl thread */ +static void +gst_glimage_sink_thread_init_redisplay (GstGLImageSink * gl_sink) +{ + GError *error = NULL; + gl_sink->redisplay_shader = gst_gl_shader_new (gl_sink->context); + + gst_gl_shader_set_vertex_source (gl_sink->redisplay_shader, + redisplay_vertex_shader_str_gles2); + gst_gl_shader_set_fragment_source (gl_sink->redisplay_shader, + redisplay_fragment_shader_str_gles2); + + gst_gl_shader_compile (gl_sink->redisplay_shader, &error); + if (error) { + gst_gl_context_set_error (gl_sink->context, "%s", error->message); + g_error_free (error); + error = NULL; + gst_gl_context_clear_shader (gl_sink->context); + } else { + gl_sink->redisplay_attr_position_loc = + gst_gl_shader_get_attribute_location (gl_sink->redisplay_shader, + "a_position"); + gl_sink->redisplay_attr_texture_loc = + gst_gl_shader_get_attribute_location (gl_sink->redisplay_shader, + "a_texCoord"); + } +} +#endif + +static void +gst_glimage_sink_on_resize (const GstGLImageSink * gl_sink, gint width, + gint height) +{ + /* Here gl_sink members (ex:gl_sink->info) have a life time of set_caps. + * It means that they cannot not change between two set_caps + */ + const GstGLFuncs *gl = gl_sink->context->gl_vtable; + + GST_TRACE ("GL Window resized to %ux%u", width, height); + + /* check if a client reshape callback is registered */ + if (gl_sink->clientReshapeCallback) + gl_sink->clientReshapeCallback (width, height, gl_sink->client_data); + + /* default reshape */ + else { + if (gl_sink->keep_aspect_ratio) { + GstVideoRectangle src, dst, result; + + src.x = 0; + src.y = 0; + src.w = GST_VIDEO_INFO_WIDTH (&gl_sink->info); + src.h = GST_VIDEO_INFO_HEIGHT (&gl_sink->info); + + dst.x = 0; + dst.y = 0; + dst.w = width; + dst.h = height; + + gst_video_sink_center_rect (src, dst, &result, TRUE); + gl->Viewport (result.x, result.y, result.w, result.h); + } else { + gl->Viewport (0, 0, width, height); + } +#if GST_GL_HAVE_OPENGL + if (USING_OPENGL (gl_sink->context)) { + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + gluOrtho2D (0, width, 0, height); + gl->MatrixMode (GL_MODELVIEW); + } +#endif + } +} + + +static void +gst_glimage_sink_on_draw (const GstGLImageSink * gl_sink) +{ + /* Here gl_sink members (ex:gl_sink->info) have a life time of set_caps. + * It means that they cannot not change between two set_caps as well as + * for the redisplay_texture size. + * Whereas redisplay_texture id changes every sink_render + */ + + const GstGLFuncs *gl = NULL; + GstGLWindow *window = NULL; + + g_return_if_fail (GST_IS_GLIMAGE_SINK (gl_sink)); + + gl = gl_sink->context->gl_vtable; + + GST_GLIMAGE_SINK_LOCK (gl_sink); + + /* check if texture is ready for being drawn */ + if (!gl_sink->redisplay_texture) { + GST_GLIMAGE_SINK_UNLOCK (gl_sink); + return; + } + + window = gst_gl_context_get_window (gl_sink->context); + window->is_drawing = TRUE; + + /* opengl scene */ + GST_TRACE ("redrawing texture:%u", gl_sink->redisplay_texture); + + /* make sure that the environnement is clean */ + gst_gl_context_clear_shader (gl_sink->context); + +#if GST_GL_HAVE_OPENGL + if (USING_OPENGL (gl_sink->context)) + gl->Disable (GL_TEXTURE_2D); +#endif + + gl->BindTexture (GL_TEXTURE_2D, 0); + + /* check if a client draw callback is registered */ + if (gl_sink->clientDrawCallback) { + + gboolean doRedisplay = + gl_sink->clientDrawCallback (gl_sink->redisplay_texture, + GST_VIDEO_INFO_WIDTH (&gl_sink->info), + GST_VIDEO_INFO_HEIGHT (&gl_sink->info), + gl_sink->client_data); + + if (doRedisplay) + gst_gl_window_draw_unlocked (window, + GST_VIDEO_INFO_WIDTH (&gl_sink->info), + GST_VIDEO_INFO_HEIGHT (&gl_sink->info)); + } + /* default opengl scene */ + else { +#if GST_GL_HAVE_OPENGL + if (USING_OPENGL (gl_sink->context)) { + GLfloat verts[8] = { 1.0f, 1.0f, + -1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, -1.0f + }; + GLfloat texcoords[8] = { 1.0f, 0.0f, + 0.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 1.0f + }; + gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, gl_sink->redisplay_texture); + + gl->EnableClientState (GL_VERTEX_ARRAY); + gl->EnableClientState (GL_TEXTURE_COORD_ARRAY); + gl->VertexPointer (2, GL_FLOAT, 0, &verts); + gl->TexCoordPointer (2, GL_FLOAT, 0, &texcoords); + + gl->DrawArrays (GL_TRIANGLE_FAN, 0, 4); + + gl->DisableClientState (GL_VERTEX_ARRAY); + gl->DisableClientState (GL_TEXTURE_COORD_ARRAY); + + gl->Disable (GL_TEXTURE_2D); + } +#endif +#if GST_GL_HAVE_GLES2 + if (USING_GLES2 (gl_sink->context)) { + const GLfloat vVertices[] = { 1.0f, 1.0f, 0.0f, + 1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + 0.0f, 0.0f, + -1.0f, -1.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, -1.0f, 0.0f, + 1.0f, 1.0f + }; + + GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; + + gl->Clear (GL_COLOR_BUFFER_BIT); + + gst_gl_shader_use (gl_sink->redisplay_shader); + + /* Load the vertex position */ + gl->VertexAttribPointer (gl_sink->redisplay_attr_position_loc, 3, + GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices); + + /* Load the texture coordinate */ + gl->VertexAttribPointer (gl_sink->redisplay_attr_texture_loc, 2, + GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]); + + gl->EnableVertexAttribArray (gl_sink->redisplay_attr_position_loc); + gl->EnableVertexAttribArray (gl_sink->redisplay_attr_texture_loc); + + gl->ActiveTexture (GL_TEXTURE0); + gl->BindTexture (GL_TEXTURE_2D, gl_sink->redisplay_texture); + gst_gl_shader_set_uniform_1i (gl_sink->redisplay_shader, "s_texture", 0); + + gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); + } +#endif + } /* end default opengl scene */ + + window->is_drawing = FALSE; + gst_object_unref (window); + + GST_GLIMAGE_SINK_UNLOCK (gl_sink); +} + +static void +gst_glimage_sink_on_close (GstGLImageSink * gl_sink) +{ + gst_gl_context_set_error (gl_sink->context, "Output window was closed"); + + g_atomic_int_set (&gl_sink->to_quit, 1); +} + +static gboolean +gst_glimage_sink_redisplay (GstGLImageSink * gl_sink) +{ + GstGLWindow *window; + gboolean alive; + + window = gst_gl_context_get_window (gl_sink->context); + + if (window && gst_gl_window_is_running (window)) { + +#if GST_GL_HAVE_GLES2 + if (USING_GLES2 (gl_sink->context)) { + if (!gl_sink->redisplay_shader) { + gst_gl_window_send_message (window, + GST_GL_WINDOW_CB (gst_glimage_sink_thread_init_redisplay), gl_sink); + } + } +#endif + + /* Drawing is asynchrone: gst_gl_window_draw is not blocking + * It means that it does not wait for stuff being executed in other threads + */ + gst_gl_window_draw (window, GST_VIDEO_SINK_WIDTH (gl_sink), + GST_VIDEO_SINK_HEIGHT (gl_sink)); + } + alive = gst_gl_window_is_running (window); + gst_object_unref (window); + + return alive; +} diff --git a/ext/gl/gstglimagesink.h b/ext/gl/gstglimagesink.h new file mode 100644 index 0000000..ea158fe --- /dev/null +++ b/ext/gl/gstglimagesink.h @@ -0,0 +1,102 @@ +/* + * GStreamer + * Copyright (C) 2003 Julien Moutte + * Copyright (C) 2005,2006,2007 David A. Schleef + * Copyright (C) 2008 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GLIMAGESINK_H_ +#define _GLIMAGESINK_H_ + +#include +#include +#include + +#include + +G_BEGIN_DECLS + +GST_DEBUG_CATEGORY_EXTERN (gst_debug_glimage_sink); + +#define GST_TYPE_GLIMAGE_SINK \ + (gst_glimage_sink_get_type()) +#define GST_GLIMAGE_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GLIMAGE_SINK,GstGLImageSink)) +#define GST_GLIMAGE_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GLIMAGE_SINK,GstGLImageSinkClass)) +#define GST_IS_GLIMAGE_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GLIMAGE_SINK)) +#define GST_IS_GLIMAGE_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GLIMAGE_SINK)) + +typedef struct _GstGLImageSink GstGLImageSink; +typedef struct _GstGLImageSinkClass GstGLImageSinkClass; + +struct _GstGLImageSink +{ + GstVideoSink video_sink; + + //properties + gchar *display_name; + + guintptr window_id; + guintptr new_window_id; + + //caps + GstVideoInfo info; + + GstGLDisplay *display; + GstGLContext *context; + GstGLContext *other_context; + + GstGLUpload *upload; + GLuint tex_id; + + CRCB clientReshapeCallback; + CDCB clientDrawCallback; + gpointer client_data; + + volatile gint to_quit; + gboolean keep_aspect_ratio; + gint par_n, par_d; + + GstBufferPool *pool; + + /* avoid replacing the stored_buffer while drawing */ + GMutex drawing_lock; + GLuint redisplay_texture; + +#if GST_GL_HAVE_GLES2 + GstGLShader *redisplay_shader; + GLint redisplay_attr_position_loc; + GLint redisplay_attr_texture_loc; +#endif + +}; + +struct _GstGLImageSinkClass +{ + GstVideoSinkClass video_sink_class; +}; + +GType gst_glimage_sink_get_type(void); + +G_END_DECLS + +#endif + diff --git a/ext/gl/gstglmosaic.c b/ext/gl/gstglmosaic.c new file mode 100644 index 0000000..040b88c --- /dev/null +++ b/ext/gl/gstglmosaic.c @@ -0,0 +1,365 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-glmosaic + * + * glmixer sub element. N gl sink pads to 1 source pad. + * N + 1 OpenGL contexts shared together. + * N <= 6 because the rendering is more a like a cube than a mosaic + * Each opengl input stream is rendered on a cube face + * + * + * Examples + * |[ + * gst-launch-0.10 videotestsrc ! "video/x-raw-yuv, format=(fourcc)YUY2" ! glupload ! queue ! glmosaic name=m ! glimagesink videotestsrc pattern=12 ! "video/x-raw-yuv, format=(fourcc)I420, framerate=(fraction)5/1, width=100, height=200" ! glupload ! queue ! m. videotestsrc ! "video/x-raw-rgb, framerate=(fraction)15/1, width=1500, height=1500" ! glupload ! gleffects effect=3 ! queue ! m. videotestsrc ! glupload ! gleffects effect=2 ! queue ! m. videotestsrc ! glupload ! glfiltercube ! queue ! m. videotestsrc ! glupload ! gleffects effect=6 ! queue ! m. + * ]| + * FBO (Frame Buffer Object) is required. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstglmosaic.h" + +#define GST_CAT_DEFAULT gst_gl_mosaic_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +enum +{ + PROP_0, +}; + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_mosaic_debug, "glmosaic", 0, "glmosaic element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLMosaic, gst_gl_mosaic, GST_TYPE_GL_MIXER, + DEBUG_INIT); + +static void gst_gl_mosaic_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_mosaic_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void gst_gl_mosaic_reset (GstGLMixer * mixer); +static gboolean gst_gl_mosaic_init_shader (GstGLMixer * mixer, + GstCaps * outcaps); + +static gboolean gst_gl_mosaic_process_textures (GstGLMixer * mixer, + GPtrArray * frames, guint out_tex); +static void gst_gl_mosaic_callback (gpointer stuff); + +//vertex source +static const gchar *mosaic_v_src = + "uniform mat4 u_matrix; \n" + "uniform float xrot_degree, yrot_degree, zrot_degree; \n" + "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" + "varying vec2 v_texCoord; \n" + "void main() \n" + "{ \n" + " float PI = 3.14159265; \n" + " float xrot = xrot_degree*2.0*PI/360.0; \n" + " float yrot = yrot_degree*2.0*PI/360.0; \n" + " float zrot = zrot_degree*2.0*PI/360.0; \n" + " mat4 matX = mat4 ( \n" + " 1.0, 0.0, 0.0, 0.0, \n" + " 0.0, cos(xrot), sin(xrot), 0.0, \n" + " 0.0, -sin(xrot), cos(xrot), 0.0, \n" + " 0.0, 0.0, 0.0, 1.0 ); \n" + " mat4 matY = mat4 ( \n" + " cos(yrot), 0.0, -sin(yrot), 0.0, \n" + " 0.0, 1.0, 0.0, 0.0, \n" + " sin(yrot), 0.0, cos(yrot), 0.0, \n" + " 0.0, 0.0, 0.0, 1.0 ); \n" + " mat4 matZ = mat4 ( \n" + " cos(zrot), sin(zrot), 0.0, 0.0, \n" + " -sin(zrot), cos(zrot), 0.0, 0.0, \n" + " 0.0, 0.0, 1.0, 0.0, \n" + " 0.0, 0.0, 0.0, 1.0 ); \n" + " gl_Position = u_matrix * matZ * matY * matX * a_position; \n" + " v_texCoord = a_texCoord; \n" + "} \n"; + +//fragment source +static const gchar *mosaic_f_src = + "uniform sampler2D s_texture; \n" + "varying vec2 v_texCoord; \n" + "void main() \n" + "{ \n" + //" gl_FragColor = vec4( 1.0, 0.5, 1.0, 1.0 );\n" + " gl_FragColor = texture2D( s_texture, v_texCoord );\n" + "} \n"; + +static void +gst_gl_mosaic_class_init (GstGLMosaicClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_gl_mosaic_set_property; + gobject_class->get_property = gst_gl_mosaic_get_property; + + gst_element_class_set_metadata (element_class, "OpenGL mosaic", + "Filter/Effect/Video", "OpenGL mosaic", + "Julien Isorce "); + + GST_GL_MIXER_CLASS (klass)->set_caps = gst_gl_mosaic_init_shader; + GST_GL_MIXER_CLASS (klass)->reset = gst_gl_mosaic_reset; + GST_GL_MIXER_CLASS (klass)->process_textures = gst_gl_mosaic_process_textures; +} + +static void +gst_gl_mosaic_init (GstGLMosaic * mosaic) +{ + mosaic->shader = NULL; + mosaic->input_frames = NULL; +} + +static void +gst_gl_mosaic_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + //GstGLMosaic *mixer = GST_GL_MOSAIC (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_mosaic_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + //GstGLMosaic* mixer = GST_GL_MOSAIC (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_mosaic_reset (GstGLMixer * mixer) +{ + GstGLMosaic *mosaic = GST_GL_MOSAIC (mixer); + + mosaic->input_frames = NULL; + + //blocking call, wait the opengl thread has destroyed the shader + if (mosaic->shader) + gst_gl_context_del_shader (mixer->context, mosaic->shader); + mosaic->shader = NULL; +} + +static gboolean +gst_gl_mosaic_init_shader (GstGLMixer * mixer, GstCaps * outcaps) +{ + GstGLMosaic *mosaic = GST_GL_MOSAIC (mixer); + + //blocking call, wait the opengl thread has compiled the shader + return gst_gl_context_gen_shader (mixer->context, mosaic_v_src, mosaic_f_src, + &mosaic->shader); +} + +static gboolean +gst_gl_mosaic_process_textures (GstGLMixer * mix, GPtrArray * frames, + guint out_tex) +{ + GstGLMosaic *mosaic = GST_GL_MOSAIC (mix); + + mosaic->input_frames = frames; + + //blocking call, use a FBO + gst_gl_context_use_fbo_v2 (mix->context, + GST_VIDEO_INFO_WIDTH (&mix->out_info), + GST_VIDEO_INFO_HEIGHT (&mix->out_info), mix->fbo, mix->depthbuffer, + out_tex, gst_gl_mosaic_callback, (gpointer) mosaic); + + return TRUE; +} + +/* opengl scene, params: input texture (not the output mixer->texture) */ +static void +gst_gl_mosaic_callback (gpointer stuff) +{ + GstGLMosaic *mosaic = GST_GL_MOSAIC (stuff); + GstGLMixer *mixer = GST_GL_MIXER (mosaic); + GstGLFuncs *gl = mixer->context->gl_vtable; + + static GLfloat xrot = 0; + static GLfloat yrot = 0; + static GLfloat zrot = 0; + + GLint attr_position_loc = 0; + GLint attr_texture_loc = 0; + + const GLfloat matrix[] = { + 0.5f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.5f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + const GLushort indices[] = { + 0, 1, 2, + 0, 2, 3 + }; + + guint count = 0; + + gst_gl_context_clear_shader (mixer->context); + gl->BindTexture (GL_TEXTURE_2D, 0); + gl->Disable (GL_TEXTURE_2D); + + gl->Enable (GL_DEPTH_TEST); + + gl->ClearColor (0.0, 0.0, 0.0, 0.0); + gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + gst_gl_shader_use (mosaic->shader); + + attr_position_loc = + gst_gl_shader_get_attribute_location (mosaic->shader, "a_position"); + attr_texture_loc = + gst_gl_shader_get_attribute_location (mosaic->shader, "a_texCoord"); + + while (count < mosaic->input_frames->len && count < 6) { + GstGLMixerFrameData *frame; + GLfloat *v_vertices; + guint in_tex; + guint width, height; + + frame = g_ptr_array_index (mosaic->input_frames, count); + in_tex = frame->texture; + width = GST_VIDEO_INFO_WIDTH (&frame->pad->in_info); + height = GST_VIDEO_INFO_HEIGHT (&frame->pad->in_info); + + if (!frame || !in_tex || width <= 0 || height <= 0) { + GST_DEBUG ("skipping texture:%u frame:%p width:%u height %u", + in_tex, frame, width, height); + count++; + continue; + } + + GST_TRACE ("processing texture:%u dimensions:%ux%u", in_tex, width, height); + + /* *INDENT-OFF* */ + v_vertices = (GLfloat[]) { + /* front face */ + 1.0f, 1.0f, -1.0f, + 1.0f, 0.0f, + 1.0f, -1.0f, -1.0f, + 1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, + 0.0f, 1.0f, + -1.0f, 1.0f, -1.0f, + 0.0f, 0.0f, + /* right face */ + 1.0f, 1.0f, 1.0f, + 1.0f, 0.0f, + 1.0f, -1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, -1.0f, -1.0f, + 0.0f, 1.0f, + 1.0f, 1.0f, -1.0f, + 1.0f, 1.0f, + /* left face */ + -1.0f, 1.0f, 1.0f, + 1.0f, 0.0f, + -1.0f, 1.0f, -1.0f, + 1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, + 0.0f, 1.0f, + -1.0f, -1.0f, 1.0f, + 0.0f, 0.0f, + /* top face */ + 1.0f, -1.0f, 1.0f, + 1.0f, 0.0f, + -1.0f, -1.0f, 1.0f, + 0.0f, 0.0f, + -1.0f, -1.0f, -1.0f, + 0.0f, 1.0f, + 1.0f, -1.0f, -1.0f, + 1.0f, 1.0f, + /* bottom face */ + 1.0f, 1.0f, 1.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, -1.0f, + 1.0f, 1.0f, + -1.0f, 1.0f, -1.0f, + 0.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + 0.0f, 0.0f, + /* back face */ + 1.0f, 1.0f, 1.0f, + 1.0f, 0.0f, + -1.0f, 1.0f, 1.0f, + 0.0f, 0.0f, + -1.0f, -1.0f, 1.0f, + 0.0f, 1.0f, + 1.0f, -1.0f, 1.0f, + 1.0f, 1.0f + }; + /* *INDENT-ON* */ + + gl->VertexAttribPointer (attr_position_loc, 3, GL_FLOAT, + GL_FALSE, 5 * sizeof (GLfloat), &v_vertices[5 * 4 * count]); + + gl->VertexAttribPointer (attr_texture_loc, 2, GL_FLOAT, + GL_FALSE, 5 * sizeof (GLfloat), &v_vertices[5 * 4 * count + 3]); + + gl->EnableVertexAttribArray (attr_position_loc); + gl->EnableVertexAttribArray (attr_texture_loc); + + gl->ActiveTexture (GL_TEXTURE0); + gl->BindTexture (GL_TEXTURE_2D, in_tex); + gst_gl_shader_set_uniform_1i (mosaic->shader, "s_texture", 0); + gst_gl_shader_set_uniform_1f (mosaic->shader, "xrot_degree", xrot); + gst_gl_shader_set_uniform_1f (mosaic->shader, "yrot_degree", yrot); + gst_gl_shader_set_uniform_1f (mosaic->shader, "zrot_degree", zrot); + gst_gl_shader_set_uniform_matrix_4fv (mosaic->shader, "u_matrix", 1, + GL_FALSE, matrix); + + gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); + + ++count; + } + + gl->DisableVertexAttribArray (attr_position_loc); + gl->DisableVertexAttribArray (attr_texture_loc); + + gl->BindTexture (GL_TEXTURE_2D, 0); + + gl->Disable (GL_DEPTH_TEST); + + gst_gl_context_clear_shader (mixer->context); + + xrot += 0.6f; + yrot += 0.4f; + zrot += 0.8f; +} diff --git a/ext/gl/gstglmosaic.h b/ext/gl/gstglmosaic.h new file mode 100644 index 0000000..1da9dbe --- /dev/null +++ b/ext/gl/gstglmosaic.h @@ -0,0 +1,55 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_GL_MOSAIC_H_ +#define _GST_GL_MOSAIC_H_ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_GL_MOSAIC (gst_gl_mosaic_get_type()) +#define GST_GL_MOSAIC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_MOSAIC,GstGLMosaic)) +#define GST_IS_GL_MOSAIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_MOSAIC)) +#define GST_GL_MOSAIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_MOSAIC,GstGLMosaicClass)) +#define GST_IS_GL_MOSAIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_MOSAIC)) +#define GST_GL_MOSAIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_MOSAIC,GstGLMosaicClass)) + +typedef struct _GstGLMosaic GstGLMosaic; +typedef struct _GstGLMosaicClass GstGLMosaicClass; + +struct _GstGLMosaic +{ + GstGLMixer mixer; + + GstGLShader *shader; + GPtrArray *input_frames; +}; + +struct _GstGLMosaicClass +{ + GstGLMixerClass mixer_class; +}; + +GType gst_gl_mosaic_get_type (void); + +G_END_DECLS + +#endif /* _GST_GLFILTERCUBE_H_ */ diff --git a/ext/gl/gstgloverlay.c b/ext/gl/gstgloverlay.c new file mode 100644 index 0000000..ba108a9 --- /dev/null +++ b/ext/gl/gstgloverlay.c @@ -0,0 +1,809 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-gloverlay + * + * Overlay GL video texture with a PNG image + * + * + * Examples + * |[ + * gst-launch videotestsrc ! "video/x-raw-rgb" ! glupload ! gloverlay location=imagefile ! glimagesink + * ]| + * FBO (Frame Buffer Object) is required. + * + */ + +/* FIXME: Redo this */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstgloverlay.h" +#include "effects/gstgleffectssources.h" + +#include +#include +#include +#include + +#if PNG_LIBPNG_VER >= 10400 +#define int_p_NULL NULL +#define png_infopp_NULL NULL +#endif + +#define GST_CAT_DEFAULT gst_gl_overlay_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_overlay_debug, "gloverlay", 0, "gloverlay element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLOverlay, gst_gl_overlay, GST_TYPE_GL_FILTER, + DEBUG_INIT); + +static gboolean gst_gl_overlay_set_caps (GstGLFilter * filter, + GstCaps * incaps, GstCaps * outcaps); + +static void gst_gl_overlay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_overlay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void gst_gl_overlay_init_resources (GstGLFilter * filter); +static void gst_gl_overlay_reset_resources (GstGLFilter * filter); + +static gboolean gst_gl_overlay_filter_texture (GstGLFilter * filter, + guint in_tex, guint out_tex); + +static gint gst_gl_overlay_load_png (GstGLFilter * filter); +static gint gst_gl_overlay_load_jpeg (GstGLFilter * filter); + +enum +{ + PROP_0, + PROP_LOCATION, + PROP_XPOS_PNG, + PROP_YPOS_PNG, + PROP_SIZE_PNG, + PROP_XPOS_VIDEO, + PROP_YPOS_VIDEO, + PROP_SIZE_VIDEO, + PROP_VIDEOTOP, + PROP_ROTATE_PNG, + PROP_ROTATE_VIDEO, + PROP_ANGLE_PNG, + PROP_ANGLE_VIDEO, + PROP_RATIO_VIDEO +}; + + +/* init resources that need a gl context */ +static void +gst_gl_overlay_init_gl_resources (GstGLFilter * filter) +{ +// GstGLOverlay *overlay = GST_GL_OVERLAY (filter); +} + +/* free resources that need a gl context */ +static void +gst_gl_overlay_reset_gl_resources (GstGLFilter * filter) +{ + GstGLOverlay *overlay = GST_GL_OVERLAY (filter); + const GstGLFuncs *gl = filter->context->gl_vtable; + + gl->DeleteTextures (1, &overlay->pbuftexture); +} + +static void +gst_gl_overlay_class_init (GstGLOverlayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_gl_overlay_set_property; + gobject_class->get_property = gst_gl_overlay_get_property; + + GST_GL_FILTER_CLASS (klass)->set_caps = gst_gl_overlay_set_caps; + GST_GL_FILTER_CLASS (klass)->filter_texture = gst_gl_overlay_filter_texture; + GST_GL_FILTER_CLASS (klass)->display_init_cb = + gst_gl_overlay_init_gl_resources; + GST_GL_FILTER_CLASS (klass)->display_reset_cb = + gst_gl_overlay_reset_gl_resources; + GST_GL_FILTER_CLASS (klass)->onStart = gst_gl_overlay_init_resources; + GST_GL_FILTER_CLASS (klass)->onStop = gst_gl_overlay_reset_resources; + + g_object_class_install_property (gobject_class, + PROP_LOCATION, + g_param_spec_string ("location", + "Location of the image", + "Location of the image", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_XPOS_PNG, + g_param_spec_int ("xpos-png", + "X position of overlay image in percents", + "X position of overlay image in percents", + 0, 100, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_YPOS_PNG, + g_param_spec_int ("ypos-png", + "Y position of overlay image in percents", + "Y position of overlay image in percents", + 0, 100, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_SIZE_PNG, + g_param_spec_int ("proportion-png", + "Relative size of overlay image, in percents", + "Relative size of iverlay image, in percents", + 0, 100, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_XPOS_VIDEO, + g_param_spec_int ("xpos-video", + "X position of overlay video in percents", + "X position of overlay video in percents", + 0, 100, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_YPOS_VIDEO, + g_param_spec_int ("ypos-video", + "Y position of overlay video in percents", + "Y position of overlay video in percents", + 0, 100, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_SIZE_VIDEO, + g_param_spec_int ("proportion-video", + "Relative size of overlay video, in percents", + "Relative size of iverlay video, in percents", + 0, 100, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_VIDEOTOP, + g_param_spec_boolean ("video-top", + "Video-top", "Video is over png image", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_ROTATE_PNG, + g_param_spec_int ("rotate-png", + "choose rotation axis for the moment only Y axis is implemented", + "choose rotation axis for the moment only Y axis is implemented", + 0, 3, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_ROTATE_VIDEO, + g_param_spec_int ("rotate-video", + "choose rotation axis for the moment only Y axis is implemented", + "choose rotation axis for the moment only Y axis is implemented", + 0, 3, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_ANGLE_PNG, + g_param_spec_int ("angle-png", + "choose angle in axis to choosen between -90 and 90", + "choose angle in axis to choosen between -90 and 90", + -90, 90, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_ANGLE_VIDEO, + g_param_spec_int ("angle-video", + "choose angle in axis to choosen between -90 and 90", + "choose angle in axis to choosen between -90 and 90", + -90, 90, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_RATIO_VIDEO, + g_param_spec_int ("ratio-video", + "choose ratio video between 0 and 3\n \t\t\t0 : Default ratio\n\t\t\t1 : 4 / 3\n\t\t\t2 : 16 / 9\n\t\t\t3 : 16 / 10", + "choose ratio video between 0 and 3\n \t\t\t0 : Default ratio\n\t\t\t1 : 4 / 3\n\t\t\t2 : 16 / 9\n\t\t\t3 : 16 / 10", + 0, 3, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_set_metadata (element_class, + "Gstreamer OpenGL Overlay", "Filter/Effect/Video", + "Overlay GL video texture with a PNG image", + "Filippo Argiolas "); + + /* + g_object_class_install_property (gobject_class, + PROP_STRETCH, + g_param_spec_boolean ("stretch", + "Stretch the image to texture size", + "Stretch the image to fit video texture size", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + */ +} + +static void +gst_gl_overlay_calc_ratio_video (GstGLOverlay * o, gfloat * video_ratio_w, + gfloat * video_ratio_h) +{ + if (o->ratio_video == 0) { + o->ratio_texture = (gfloat) o->ratio_window; + *video_ratio_w = (gfloat) o->width_window; + *video_ratio_h = (gfloat) o->height_window; + } else if (o->ratio_video == 1) { + o->ratio_texture = (gfloat) 1.33; + *video_ratio_w = 4.0; + *video_ratio_h = 3.0; + } else if (o->ratio_video == 2) { + o->ratio_texture = (gfloat) 1.77; + *video_ratio_w = 16.0; + *video_ratio_h = 9.0; + } else { + o->ratio_texture = (gfloat) 1.6; + *video_ratio_w = 16.0; + *video_ratio_h = 10.0; + } +} + +static void +gst_gl_overlay_init_texture (GstGLOverlay * o, GLuint tex, int flag) +{ + GstGLFilter *filter = GST_GL_FILTER (o); + const GstGLFuncs *gl = filter->context->gl_vtable; + + if (flag == 0 && o->type_file == 2) { + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, tex); + } else { + gl->Enable (GL_TEXTURE_2D); + gl->BindTexture (GL_TEXTURE_2D, tex); + } +} + +static void +gst_gl_overlay_draw (GstGLOverlay * o, int flag) +{ + GstGLFilter *filter = GST_GL_FILTER (o); + const GstGLFuncs *gl = filter->context->gl_vtable; + + float y = 0.0f; + float width = 0.0f; + float height = 0.0f; + +/* *INDENT-OFF* */ + float v_vertices[] = { + /*| Vertex | TexCoord |*/ + -o->ratio_x + o->posx, y, 0.0f, 0.0f, 0.0f, + o->ratio_x + o->posx, y, 0.0f, width, 0.0f, + o->ratio_x + o->posx, y, 0.0f, width, height, + -o->ratio_x + o->posx, y, 0.0f, 0.0, height, + }; + + GLushort indices[] = { + 0, 1, 2, + 0, 2, 3, + }; +/* *INDENT-ON* */ + + if (flag == 1) { + width = 1.0f; + height = 1.0f; + } else if (flag == 0 && o->type_file == 1) { + width = (gfloat) o->width; + height = (gfloat) o->height; + } else if (flag == 0 && o->type_file == 2) { + width = 1.0f; + height = 1.0f; + } + + v_vertices[8] = width; + v_vertices[13] = width; + v_vertices[14] = height; + v_vertices[19] = height; + + y = (o->type_file == 2 && flag == 0 ? o->ratio_y : -o->ratio_y) + o->posy; + v_vertices[1] = y; + v_vertices[6] = y; + y = (o->type_file == 2 && flag == 0 ? -o->ratio_y : o->ratio_y) + o->posy; + v_vertices[11] = y; + v_vertices[16] = y; + + gst_gl_context_clear_shader (filter->context); + + gl->ClientActiveTexture (GL_TEXTURE0); + gl->EnableClientState (GL_TEXTURE_COORD_ARRAY); + gl->EnableClientState (GL_VERTEX_ARRAY); + + gl->VertexPointer (3, GL_FLOAT, 5 * sizeof (float), v_vertices); + gl->TexCoordPointer (2, GL_FLOAT, 5 * sizeof (float), &v_vertices[3]); + + gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); + + gl->DisableClientState (GL_TEXTURE_COORD_ARRAY); + gl->DisableClientState (GL_VERTEX_ARRAY); +} + +static void +gst_gl_overlay_calc_proportion (GstGLOverlay * o, int flag, float size_texture, + float width, float height) +{ + if ((1.59f < o->ratio_window && o->ratio_window < 1.61f + && 1.77f < o->ratio_texture && o->ratio_texture < 1.78f) + || (1.3f < o->ratio_window && o->ratio_window < 1.34f + && ((1.7f < o->ratio_texture && o->ratio_texture < 1.78f) + || (1.59f < o->ratio_texture && o->ratio_texture < 1.61f)))) { + o->ratio_x = o->ratio_window * (gfloat) size_texture / 100.0f; + o->ratio_y = + (o->ratio_window / width) * height * (gfloat) size_texture / 100.0f; + } else { + o->ratio_x = o->ratio_texture * (gfloat) size_texture / 100.0f; + o->ratio_y = 1.0f * size_texture / 100.0f; + } + o->posx = + ((o->ratio_window - o->ratio_x) * ((flag == + 1 ? o->pos_x_video : o->pos_x_png) - 50.0f) / 50.0f); + o->posy = + (1.0f - o->ratio_y) * (((flag == + 1 ? o->pos_y_video : o->pos_y_png) - 50.0f) / 50.0f); +} + +static void +gst_gl_overlay_load_texture (GstGLOverlay * o, GLuint tex, int flag) +{ + GstGLFilter *filter = GST_GL_FILTER (o); + const GstGLFuncs *gl = filter->context->gl_vtable; + + gfloat video_ratio_w; + gfloat video_ratio_h; + + o->ratio_window = (gfloat) o->width_window / (gfloat) o->height_window; + + gl->MatrixMode (GL_MODELVIEW); + gl->ActiveTexture (GL_TEXTURE0); + + gst_gl_overlay_init_texture (o, tex, flag); + + gl->BlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl->Enable (GL_BLEND); + gl->Translatef (0.0f, 0.0f, -1.43f); + + if (flag == 1) { + if (o->rotate_video) + gl->Rotatef (o->angle_video, 0, 1, 0); + gst_gl_overlay_calc_ratio_video (o, &video_ratio_w, &video_ratio_h); + gst_gl_overlay_calc_proportion (o, flag, o->size_video, video_ratio_w, + video_ratio_h); + } else { + o->ratio_texture = (gfloat) o->width / (gfloat) o->height; + if (o->rotate_png == 2) + gl->Rotatef (o->angle_png, 0, 1, 0); + gst_gl_overlay_calc_proportion (o, flag, o->size_png, (gfloat) o->width, + (gfloat) o->height); + } + + gst_gl_overlay_draw (o, flag); + if (flag == 1) + gl->Disable (GL_TEXTURE_2D); +} + +static void +gst_gl_overlay_init (GstGLOverlay * overlay) +{ + overlay->location = NULL; + overlay->pixbuf = NULL; + overlay->pbuftexture = 0; + overlay->pbuftexture = 0; + overlay->width = 0; + overlay->height = 0; + overlay->pos_x_png = 0; + overlay->pos_y_png = 0; + overlay->size_png = 100; + overlay->pos_x_video = 0; + overlay->pos_y_video = 0; + overlay->size_video = 100; + overlay->video_top = FALSE; + overlay->rotate_png = 0; + overlay->rotate_video = 0; + overlay->angle_png = 0; + overlay->angle_video = 0; + overlay->ratio_video = 0; + // overlay->stretch = TRUE; + overlay->pbuf_has_changed = FALSE; +} + +static void +gst_gl_overlay_reset_resources (GstGLFilter * filter) +{ + // GstGLOverlay* overlay = GST_GL_OVERLAY(filter); +} + +static void +gst_gl_overlay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGLOverlay *overlay = GST_GL_OVERLAY (object); + + switch (prop_id) { + case PROP_LOCATION: + if (overlay->location != NULL) + g_free (overlay->location); + overlay->pbuf_has_changed = TRUE; + overlay->location = g_value_dup_string (value); + break; + case PROP_XPOS_PNG: + overlay->pos_x_png = g_value_get_int (value); + break; + case PROP_YPOS_PNG: + overlay->pos_y_png = g_value_get_int (value); + break; + case PROP_SIZE_PNG: + overlay->size_png = g_value_get_int (value); + break; + case PROP_XPOS_VIDEO: + overlay->pos_x_video = g_value_get_int (value); + break; + case PROP_YPOS_VIDEO: + overlay->pos_y_video = g_value_get_int (value); + break; + case PROP_SIZE_VIDEO: + overlay->size_video = g_value_get_int (value); + break; + case PROP_VIDEOTOP: + overlay->video_top = g_value_get_boolean (value); + break; + case PROP_ROTATE_PNG: + overlay->rotate_png = g_value_get_int (value); + break; + case PROP_ROTATE_VIDEO: + overlay->rotate_video = g_value_get_int (value); + break; + case PROP_ANGLE_PNG: + overlay->angle_png = g_value_get_int (value); + break; + case PROP_ANGLE_VIDEO: + overlay->angle_video = g_value_get_int (value); + break; + case PROP_RATIO_VIDEO: + overlay->ratio_video = (gfloat) g_value_get_int (value); + break; + /* case PROP_STRETCH: + overlay->stretch = g_value_get_boolean (value); + break; + */ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_overlay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstGLOverlay *overlay = GST_GL_OVERLAY (object); + + switch (prop_id) { + case PROP_LOCATION: + g_value_set_string (value, overlay->location); + break; + case PROP_XPOS_PNG: + g_value_set_int (value, overlay->pos_x_png); + break; + case PROP_YPOS_PNG: + g_value_set_int (value, overlay->pos_y_png); + break; + case PROP_SIZE_PNG: + g_value_set_int (value, overlay->size_png); + break; + case PROP_XPOS_VIDEO: + g_value_set_int (value, overlay->pos_x_video); + break; + case PROP_YPOS_VIDEO: + g_value_set_int (value, overlay->pos_y_video); + break; + case PROP_SIZE_VIDEO: + g_value_set_int (value, overlay->size_video); + break; + case PROP_VIDEOTOP: + g_value_set_boolean (value, overlay->video_top); + break; + case PROP_ROTATE_PNG: + g_value_set_int (value, overlay->rotate_png); + break; + case PROP_ROTATE_VIDEO: + g_value_set_int (value, overlay->rotate_video); + break; + case PROP_ANGLE_PNG: + g_value_set_int (value, overlay->angle_png); + break; + case PROP_ANGLE_VIDEO: + g_value_set_int (value, overlay->angle_video); + break; + case PROP_RATIO_VIDEO: + g_value_set_int (value, (gint) overlay->ratio_video); + break; + /* case PROP_STRETCH: + g_value_set_boolean (value, overlay->stretch); + break; + */ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_gl_overlay_set_caps (GstGLFilter * filter, GstCaps * incaps, + GstCaps * outcaps) +{ + GstGLOverlay *overlay = GST_GL_OVERLAY (filter); + GstStructure *s = gst_caps_get_structure (incaps, 0); + gint width = 0; + gint height = 0; + + gst_structure_get_int (s, "width", &width); + gst_structure_get_int (s, "height", &height); + + overlay->width_window = (gfloat) width; + overlay->height_window = (gfloat) height; + + return TRUE; +} + +static void +gst_gl_overlay_init_resources (GstGLFilter * filter) +{ +// GstGLOverlay *overlay = GST_GL_OVERLAY (filter); +} + +static void +gst_gl_overlay_callback (gint width, gint height, guint texture, gpointer stuff) +{ + GstGLOverlay *overlay = GST_GL_OVERLAY (stuff); + GstGLFilter *filter = GST_GL_FILTER (overlay); + const GstGLFuncs *gl = filter->context->gl_vtable; + + gl->MatrixMode (GL_PROJECTION); + gl->LoadIdentity (); + gluPerspective (70.0f, + (GLfloat) overlay->width_window / (GLfloat) overlay->height_window, 1.0f, + 1000.0f); + gl->Enable (GL_DEPTH_TEST); + gluLookAt (0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); + if (!overlay->video_top) { + if (overlay->pbuftexture != 0) + gst_gl_overlay_load_texture (overlay, overlay->pbuftexture, 0); + // if (overlay->stretch) { + // width = (gfloat) overlay->width; + // height = (gfloat) overlay->height; + // } + gl->LoadIdentity (); + gst_gl_overlay_load_texture (overlay, texture, 1); + } else { + gst_gl_overlay_load_texture (overlay, texture, 1); + if (overlay->pbuftexture == 0) + return; + // if (overlay->stretch) { + // width = (gfloat) overlay->width; + // height = (gfloat) overlay->height; + // } + gl->LoadIdentity (); + gst_gl_overlay_load_texture (overlay, overlay->pbuftexture, 0); + } +} + +static void +init_pixbuf_texture (GstGLContext * context, gpointer data) +{ + GstGLOverlay *overlay = GST_GL_OVERLAY (data); + GstGLFilter *filter = GST_GL_FILTER (overlay); + const GstGLFuncs *gl = filter->context->gl_vtable; + + if (overlay->pixbuf) { + gl->DeleteTextures (1, &overlay->pbuftexture); + gl->GenTextures (1, &overlay->pbuftexture); + if (overlay->type_file == 1) { + gl->BindTexture (GL_TEXTURE_2D, overlay->pbuftexture); + gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, + (gint) overlay->width, (gint) overlay->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, overlay->pixbuf); + } else if (overlay->type_file == 2) { + gl->BindTexture (GL_TEXTURE_2D, overlay->pbuftexture); + gl->TexImage2D (GL_TEXTURE_2D, 0, overlay->internalFormat, + overlay->width, overlay->height, 0, overlay->format, + GL_UNSIGNED_BYTE, overlay->pixbuf); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + } +} + +static gboolean +gst_gl_overlay_filter_texture (GstGLFilter * filter, guint in_tex, + guint out_tex) +{ + GstGLOverlay *overlay = GST_GL_OVERLAY (filter); + + if (overlay->pbuf_has_changed && (overlay->location != NULL)) { + if ((overlay->type_file = gst_gl_overlay_load_png (filter)) == 0) + if ((overlay->type_file = gst_gl_overlay_load_jpeg (filter)) == 0) + overlay->pixbuf = NULL; + /* if loader failed then context is turned off */ + gst_gl_context_thread_add (filter->context, init_pixbuf_texture, overlay); + if (overlay->pixbuf) { + free (overlay->pixbuf); + overlay->pixbuf = NULL; + } + + overlay->pbuf_has_changed = FALSE; + } + + gst_gl_filter_render_to_target (filter, TRUE, in_tex, out_tex, + gst_gl_overlay_callback, overlay); + + return TRUE; +} + +static void +user_warning_fn (png_structp png_ptr, png_const_charp warning_msg) +{ + g_warning ("%s\n", warning_msg); +} + +#define LOAD_ERROR(msg) { GST_WARNING ("unable to load %s: %s", overlay->location, msg); return FALSE; } + +static gint +gst_gl_overlay_load_jpeg (GstGLFilter * filter) +{ + GstGLOverlay *overlay = GST_GL_OVERLAY (filter); + FILE *fp = NULL; + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + JSAMPROW j; + int i; + + fp = fopen (overlay->location, "rb"); + if (!fp) { + g_error ("error: couldn't open file!\n"); + return 0; + } + jpeg_create_decompress (&cinfo); + cinfo.err = jpeg_std_error (&jerr); + jpeg_stdio_src (&cinfo, fp); + jpeg_read_header (&cinfo, TRUE); + jpeg_start_decompress (&cinfo); + overlay->width = cinfo.image_width; + overlay->height = cinfo.image_height; + overlay->internalFormat = cinfo.num_components; + if (cinfo.num_components == 1) + overlay->format = GL_LUMINANCE; + else + overlay->format = GL_RGB; + overlay->pixbuf = (GLubyte *) malloc (sizeof (GLubyte) * overlay->width + * overlay->height * overlay->internalFormat); + for (i = 0; i < overlay->height; ++i) { + j = (overlay->pixbuf + + (((int) overlay->height - (i + + 1)) * (int) overlay->width * overlay->internalFormat)); + jpeg_read_scanlines (&cinfo, &j, 1); + } + jpeg_finish_decompress (&cinfo); + jpeg_destroy_decompress (&cinfo); + fclose (fp); + return 2; +} + +static gint +gst_gl_overlay_load_png (GstGLFilter * filter) +{ + GstGLOverlay *overlay = GST_GL_OVERLAY (filter); + + png_structp png_ptr; + png_infop info_ptr; + png_uint_32 width = 0; + png_uint_32 height = 0; + gint bit_depth = 0; + gint color_type = 0; + gint interlace_type = 0; + png_FILE_p fp = NULL; + guint y = 0; + guchar **rows = NULL; + gint filler; + png_byte magic[8]; + gint n_read; + + if (!filter->context) + return 1; + + if ((fp = fopen (overlay->location, "rb")) == NULL) + LOAD_ERROR ("file not found"); + + /* Read magic number */ + n_read = fread (magic, 1, sizeof (magic), fp); + if (n_read != sizeof (magic)) { + fclose (fp); + LOAD_ERROR ("can't read PNG magic number"); + } + + /* Check for valid magic number */ + if (png_sig_cmp (magic, 0, sizeof (magic))) { + fclose (fp); + LOAD_ERROR ("not a valid PNG image"); + } + + png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (png_ptr == NULL) { + fclose (fp); + LOAD_ERROR ("failed to initialize the png_struct"); + } + + png_set_error_fn (png_ptr, NULL, NULL, user_warning_fn); + + info_ptr = png_create_info_struct (png_ptr); + if (info_ptr == NULL) { + fclose (fp); + png_destroy_read_struct (&png_ptr, png_infopp_NULL, png_infopp_NULL); + LOAD_ERROR ("failed to initialize the memory for image information"); + } + + png_init_io (png_ptr, fp); + + png_set_sig_bytes (png_ptr, sizeof (magic)); + + png_read_info (png_ptr, info_ptr); + + png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, + &interlace_type, int_p_NULL, int_p_NULL); + + if (color_type == PNG_COLOR_TYPE_RGB) { + filler = 0xff; + png_set_filler (png_ptr, filler, PNG_FILLER_AFTER); + color_type = PNG_COLOR_TYPE_RGB_ALPHA; + } + + if (color_type != PNG_COLOR_TYPE_RGB_ALPHA) { + fclose (fp); + png_destroy_read_struct (&png_ptr, png_infopp_NULL, png_infopp_NULL); + LOAD_ERROR ("color type is not rgb"); + } + + overlay->width = width; + overlay->height = height; + + overlay->pixbuf = (guchar *) malloc (sizeof (guchar) * width * height * 4); + + rows = (guchar **) malloc (sizeof (guchar *) * height); + + for (y = 0; y < height; ++y) + rows[y] = (guchar *) (overlay->pixbuf + y * width * 4); + + png_read_image (png_ptr, rows); + + free (rows); + + png_read_end (png_ptr, info_ptr); + png_destroy_read_struct (&png_ptr, &info_ptr, png_infopp_NULL); + fclose (fp); + + return 1; +} diff --git a/ext/gl/gstgloverlay.h b/ext/gl/gstgloverlay.h new file mode 100644 index 0000000..6497ec3 --- /dev/null +++ b/ext/gl/gstgloverlay.h @@ -0,0 +1,80 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_GL_OVERLAY_H_ +#define _GST_GL_OVERLAY_H_ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_GL_OVERLAY (gst_gl_overlay_get_type()) +#define GST_GL_OVERLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_GL_OVERLAY,GstGLOverlay)) +#define GST_IS_GL_OVERLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_GL_OVERLAY)) +#define GST_GL_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) , GST_TYPE_GL_OVERLAY,GstGLOverlayClass)) +#define GST_IS_GL_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) , GST_TYPE_GL_OVERLAY)) +#define GST_GL_OVERLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) , GST_TYPE_GL_OVERLAY,GstGLOverlayClass)) + +typedef struct _GstGLOverlay GstGLOverlay; +typedef struct _GstGLOverlayClass GstGLOverlayClass; + +struct _GstGLOverlay +{ + GstGLFilter filter; + gchar *location; + gboolean pbuf_has_changed; + gint8 pos_x_png; + gint8 pos_y_png; + guint8 size_png; + gint8 pos_x_video; + gint8 pos_y_video; + guint8 size_video; + gboolean video_top; + guint8 rotate_png; + guint8 rotate_video; + gint8 angle_png; + gint8 angle_video; + guchar *pixbuf; + gint width, height; + GLuint pbuftexture; + GLint internalFormat; + GLenum format; + gint type_file; // 0 = No; 1 = PNG and 2 = JPEG + gfloat width_window; + gfloat height_window; + gfloat posx; + gfloat posy; + gfloat ratio_window; + gfloat ratio_texture; + gfloat ratio_x; + gfloat ratio_y; + gfloat ratio_video; + +/* gboolean stretch; */ +}; + +struct _GstGLOverlayClass +{ + GstGLFilterClass filter_class; +}; + +G_END_DECLS + +#endif /* _GST_GL_OVERLAY_H_ */ diff --git a/ext/gl/gstgltestsrc.c b/ext/gl/gstgltestsrc.c new file mode 100644 index 0000000..b788dcd --- /dev/null +++ b/ext/gl/gstgltestsrc.c @@ -0,0 +1,708 @@ +/* + * GStreamer + * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) 2002,2007 David A. Schleef + * Copyright (C) 2008 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-gltestsrc + * + * + * + * The gltestsrc element is used to produce test video texture. + * The video test produced can be controlled with the "pattern" + * property. + * + * Example launch line + * + * + * gst-launch -v gltestsrc pattern=smpte ! glimagesink + * + * Shows original SMPTE color bars in a window. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstgltestsrc.h" +#include "gltestsrc.h" +#include + +#define USE_PEER_BUFFERALLOC + +GST_DEBUG_CATEGORY_STATIC (gl_test_src_debug); +#define GST_CAT_DEFAULT gl_test_src_debug + +enum +{ + PROP_0, + PROP_PATTERN, + PROP_TIMESTAMP_OFFSET, + PROP_IS_LIVE + /* FILL ME */ +}; + +#define gst_gl_test_src_parent_class parent_class +G_DEFINE_TYPE (GstGLTestSrc, gst_gl_test_src, GST_TYPE_PUSH_SRC); + +static void gst_gl_test_src_set_pattern (GstGLTestSrc * gltestsrc, + int pattern_type); +static void gst_gl_test_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_test_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_gl_test_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps); +static GstCaps *gst_gl_test_src_fixate (GstBaseSrc * bsrc, GstCaps * caps); + +static gboolean gst_gl_test_src_is_seekable (GstBaseSrc * psrc); +static gboolean gst_gl_test_src_do_seek (GstBaseSrc * bsrc, + GstSegment * segment); +static gboolean gst_gl_test_src_query (GstBaseSrc * bsrc, GstQuery * query); +static void gst_gl_test_src_set_context (GstElement * element, + GstContext * context); + +static void gst_gl_test_src_get_times (GstBaseSrc * basesrc, + GstBuffer * buffer, GstClockTime * start, GstClockTime * end); +static GstFlowReturn gst_gl_test_src_fill (GstPushSrc * psrc, + GstBuffer * buffer); +static gboolean gst_gl_test_src_start (GstBaseSrc * basesrc); +static gboolean gst_gl_test_src_stop (GstBaseSrc * basesrc); +static gboolean gst_gl_test_src_decide_allocation (GstBaseSrc * basesrc, + GstQuery * query); + +static void gst_gl_test_src_callback (gpointer stuff); + +#define GST_TYPE_GL_TEST_SRC_PATTERN (gst_gl_test_src_pattern_get_type ()) +static GType +gst_gl_test_src_pattern_get_type (void) +{ + static GType gl_test_src_pattern_type = 0; + static const GEnumValue pattern_types[] = { + {GST_GL_TEST_SRC_SMPTE, "SMPTE 100% color bars", "smpte"}, + {GST_GL_TEST_SRC_SNOW, "Random (television snow)", "snow"}, + {GST_GL_TEST_SRC_BLACK, "100% Black", "black"}, + {GST_GL_TEST_SRC_WHITE, "100% White", "white"}, + {GST_GL_TEST_SRC_RED, "Red", "red"}, + {GST_GL_TEST_SRC_GREEN, "Green", "green"}, + {GST_GL_TEST_SRC_BLUE, "Blue", "blue"}, + {GST_GL_TEST_SRC_CHECKERS1, "Checkers 1px", "checkers-1"}, + {GST_GL_TEST_SRC_CHECKERS2, "Checkers 2px", "checkers-2"}, + {GST_GL_TEST_SRC_CHECKERS4, "Checkers 4px", "checkers-4"}, + {GST_GL_TEST_SRC_CHECKERS8, "Checkers 8px", "checkers-8"}, + {GST_GL_TEST_SRC_CIRCULAR, "Circular", "circular"}, + {GST_GL_TEST_SRC_BLINK, "Blink", "blink"}, + {0, NULL, NULL} + }; + + if (!gl_test_src_pattern_type) { + gl_test_src_pattern_type = + g_enum_register_static ("GstGLTestSrcPattern", pattern_types); + } + return gl_test_src_pattern_type; +} + +static void +gst_gl_test_src_class_init (GstGLTestSrcClass * klass) +{ + GObjectClass *gobject_class; + GstBaseSrcClass *gstbasesrc_class; + GstPushSrcClass *gstpushsrc_class; + GstElementClass *element_class; + + GST_DEBUG_CATEGORY_INIT (gl_test_src_debug, "gltestsrc", 0, + "Video Test Source"); + + gobject_class = (GObjectClass *) klass; + gstbasesrc_class = (GstBaseSrcClass *) klass; + gstpushsrc_class = (GstPushSrcClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_gl_test_src_set_property; + gobject_class->get_property = gst_gl_test_src_get_property; + + g_object_class_install_property (gobject_class, PROP_PATTERN, + g_param_spec_enum ("pattern", "Pattern", + "Type of test pattern to generate", GST_TYPE_GL_TEST_SRC_PATTERN, + GST_GL_TEST_SRC_SMPTE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, + PROP_TIMESTAMP_OFFSET, g_param_spec_int64 ("timestamp-offset", + "Timestamp offset", + "An offset added to timestamps set on buffers (in ns)", G_MININT64, + G_MAXINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_IS_LIVE, + g_param_spec_boolean ("is-live", "Is Live", + "Whether to act as a live source", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_set_metadata (element_class, "Video test source", + "Source/Video", "Creates a test video stream", + "David A. Schleef "); + + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + gst_caps_from_string (GST_GL_DOWNLOAD_VIDEO_CAPS))); + + element_class->set_context = gst_gl_test_src_set_context; + + gstbasesrc_class->set_caps = gst_gl_test_src_setcaps; + gstbasesrc_class->is_seekable = gst_gl_test_src_is_seekable; + gstbasesrc_class->do_seek = gst_gl_test_src_do_seek; + gstbasesrc_class->query = gst_gl_test_src_query; + gstbasesrc_class->get_times = gst_gl_test_src_get_times; + gstbasesrc_class->start = gst_gl_test_src_start; + gstbasesrc_class->stop = gst_gl_test_src_stop; + gstbasesrc_class->fixate = gst_gl_test_src_fixate; + gstbasesrc_class->decide_allocation = gst_gl_test_src_decide_allocation; + + gstpushsrc_class->fill = gst_gl_test_src_fill; +} + +static void +gst_gl_test_src_init (GstGLTestSrc * src) +{ + gst_gl_test_src_set_pattern (src, GST_GL_TEST_SRC_SMPTE); + + src->timestamp_offset = 0; + + /* we operate in time */ + gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME); + gst_base_src_set_live (GST_BASE_SRC (src), FALSE); +} + +static GstCaps * +gst_gl_test_src_fixate (GstBaseSrc * bsrc, GstCaps * caps) +{ + GstStructure *structure; + + GST_DEBUG ("fixate"); + + caps = gst_caps_make_writable (caps); + + structure = gst_caps_get_structure (caps, 0); + + gst_structure_fixate_field_nearest_int (structure, "width", 320); + gst_structure_fixate_field_nearest_int (structure, "height", 240); + gst_structure_fixate_field_nearest_fraction (structure, "framerate", 30, 1); + + caps = GST_BASE_SRC_CLASS (parent_class)->fixate (bsrc, caps); + + return caps; +} + +static void +gst_gl_test_src_set_pattern (GstGLTestSrc * gltestsrc, gint pattern_type) +{ + gltestsrc->pattern_type = pattern_type; + + GST_DEBUG_OBJECT (gltestsrc, "setting pattern to %d", pattern_type); + + switch (pattern_type) { + case GST_GL_TEST_SRC_SMPTE: + gltestsrc->make_image = gst_gl_test_src_smpte; + break; + case GST_GL_TEST_SRC_SNOW: + gltestsrc->make_image = gst_gl_test_src_snow; + break; + case GST_GL_TEST_SRC_BLACK: + gltestsrc->make_image = gst_gl_test_src_black; + break; + case GST_GL_TEST_SRC_WHITE: + gltestsrc->make_image = gst_gl_test_src_white; + break; + case GST_GL_TEST_SRC_RED: + gltestsrc->make_image = gst_gl_test_src_red; + break; + case GST_GL_TEST_SRC_GREEN: + gltestsrc->make_image = gst_gl_test_src_green; + break; + case GST_GL_TEST_SRC_BLUE: + gltestsrc->make_image = gst_gl_test_src_blue; + break; + case GST_GL_TEST_SRC_CHECKERS1: + gltestsrc->make_image = gst_gl_test_src_checkers1; + break; + case GST_GL_TEST_SRC_CHECKERS2: + gltestsrc->make_image = gst_gl_test_src_checkers2; + break; + case GST_GL_TEST_SRC_CHECKERS4: + gltestsrc->make_image = gst_gl_test_src_checkers4; + break; + case GST_GL_TEST_SRC_CHECKERS8: + gltestsrc->make_image = gst_gl_test_src_checkers8; + break; + case GST_GL_TEST_SRC_CIRCULAR: + gltestsrc->make_image = gst_gl_test_src_circular; + break; + case GST_GL_TEST_SRC_BLINK: + gltestsrc->make_image = gst_gl_test_src_black; + break; + default: + g_assert_not_reached (); + } +} + +static void +gst_gl_test_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGLTestSrc *src = GST_GL_TEST_SRC (object); + + switch (prop_id) { + case PROP_PATTERN: + gst_gl_test_src_set_pattern (src, g_value_get_enum (value)); + break; + case PROP_TIMESTAMP_OFFSET: + src->timestamp_offset = g_value_get_int64 (value); + break; + case PROP_IS_LIVE: + gst_base_src_set_live (GST_BASE_SRC (src), g_value_get_boolean (value)); + break; + default: + break; + } +} + +static void +gst_gl_test_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstGLTestSrc *src = GST_GL_TEST_SRC (object); + + switch (prop_id) { + case PROP_PATTERN: + g_value_set_enum (value, src->pattern_type); + break; + case PROP_TIMESTAMP_OFFSET: + g_value_set_int64 (value, src->timestamp_offset); + break; + case PROP_IS_LIVE: + g_value_set_boolean (value, gst_base_src_is_live (GST_BASE_SRC (src))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_gl_test_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps) +{ + GstGLTestSrc *gltestsrc = GST_GL_TEST_SRC (bsrc); + + GST_DEBUG ("setcaps"); + + if (!gst_video_info_from_caps (&gltestsrc->out_info, caps)) + goto wrong_caps; + + gltestsrc->negotiated = TRUE; + + return TRUE; + +/* ERRORS */ +wrong_caps: + { + GST_WARNING ("wrong caps"); + return FALSE; + } +} + +static void +gst_gl_test_src_set_context (GstElement * element, GstContext * context) +{ + GstGLTestSrc *src = GST_GL_TEST_SRC (element); + + gst_gl_handle_set_context (element, context, &src->display); +} + +static gboolean +gst_gl_test_src_query (GstBaseSrc * bsrc, GstQuery * query) +{ + gboolean res = FALSE; + GstGLTestSrc *src; + + src = GST_GL_TEST_SRC (bsrc); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONTEXT: + { + res = gst_gl_handle_context_query ((GstElement *) src, query, + &src->display); + break; + } + case GST_QUERY_CONVERT: + { + GstFormat src_fmt, dest_fmt; + gint64 src_val, dest_val; + + gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); + res = + gst_video_info_convert (&src->out_info, src_fmt, src_val, dest_fmt, + &dest_val); + gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); + break; + } + default: + res = GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query); + } + + return res; +} + +static void +gst_gl_test_src_get_times (GstBaseSrc * basesrc, GstBuffer * buffer, + GstClockTime * start, GstClockTime * end) +{ + /* for live sources, sync on the timestamp of the buffer */ + if (gst_base_src_is_live (basesrc)) { + GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer); + + if (GST_CLOCK_TIME_IS_VALID (timestamp)) { + /* get duration to calculate end time */ + GstClockTime duration = GST_BUFFER_DURATION (buffer); + + if (GST_CLOCK_TIME_IS_VALID (duration)) + *end = timestamp + duration; + *start = timestamp; + } + } else { + *start = -1; + *end = -1; + } +} + +static gboolean +gst_gl_test_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment) +{ + GstClockTime time; + GstGLTestSrc *src; + + src = GST_GL_TEST_SRC (bsrc); + + segment->time = segment->start; + time = segment->position; + + /* now move to the time indicated */ + if (src->out_info.fps_n) { + src->n_frames = gst_util_uint64_scale (time, + src->out_info.fps_n, src->out_info.fps_d * GST_SECOND); + } else + src->n_frames = 0; + + if (src->out_info.fps_n) { + src->running_time = gst_util_uint64_scale (src->n_frames, + src->out_info.fps_d * GST_SECOND, src->out_info.fps_n); + } else { + /* FIXME : Not sure what to set here */ + src->running_time = 0; + } + + g_return_val_if_fail (src->running_time <= time, FALSE); + + return TRUE; +} + +static gboolean +gst_gl_test_src_is_seekable (GstBaseSrc * psrc) +{ + /* we're seekable... */ + return TRUE; +} + +static GstFlowReturn +gst_gl_test_src_fill (GstPushSrc * psrc, GstBuffer * buffer) +{ + GstGLTestSrc *src; + GstClockTime next_time; + gint width, height; + GstVideoFrame out_frame; + gboolean out_gl_wrapped = FALSE; + guint out_tex; + + src = GST_GL_TEST_SRC (psrc); + + if (G_UNLIKELY (!src->negotiated)) + goto not_negotiated; + + width = GST_VIDEO_INFO_WIDTH (&src->out_info); + height = GST_VIDEO_INFO_HEIGHT (&src->out_info); + + /* 0 framerate and we are at the second frame, eos */ + if (G_UNLIKELY (GST_VIDEO_INFO_FPS_N (&src->out_info) == 0 + && src->n_frames == 1)) + goto eos; + + if (src->pattern_type == GST_GL_TEST_SRC_BLINK) { + if (src->n_frames & 0x1) + src->make_image = gst_gl_test_src_white; + else + src->make_image = gst_gl_test_src_black; + } + + if (!gst_video_frame_map (&out_frame, &src->out_info, buffer, + GST_MAP_WRITE | GST_MAP_GL)) { + return GST_FLOW_NOT_NEGOTIATED; + } + + if (gst_is_gl_memory (out_frame.map[0].memory)) { + out_tex = *(guint *) out_frame.data[0]; + } else { + GST_INFO ("Output Buffer does not contain correct meta, " + "attempting to wrap for download"); + + if (!src->out_tex_id) { + gst_gl_context_gen_texture (src->context, &src->out_tex_id, + GST_VIDEO_FORMAT_RGBA, GST_VIDEO_FRAME_WIDTH (&out_frame), + GST_VIDEO_FRAME_HEIGHT (&out_frame)); + } + out_tex = src->out_tex_id; + + if (!src->download) { + src->download = gst_gl_download_new (src->context); + + if (!gst_gl_download_init_format (src->download, + GST_VIDEO_FRAME_FORMAT (&out_frame), + GST_VIDEO_FRAME_WIDTH (&out_frame), + GST_VIDEO_FRAME_HEIGHT (&out_frame))) { + GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, + ("%s", "Failed to init download format"), (NULL)); + return FALSE; + } + } + + out_gl_wrapped = TRUE; + } + + gst_buffer_replace (&src->buffer, buffer); + + //blocking call, generate a FBO + if (!gst_gl_context_use_fbo_v2 (src->context, width, height, src->fbo, + src->depthbuffer, out_tex, gst_gl_test_src_callback, + (gpointer) src)) { + goto not_negotiated; + } + + if (out_gl_wrapped) { + if (!gst_gl_download_perform_with_data (src->download, out_tex, + out_frame.data)) { + GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, ("%s", + "Failed to init upload format"), (NULL)); + return FALSE; + } + } + gst_video_frame_unmap (&out_frame); + + GST_BUFFER_TIMESTAMP (buffer) = src->timestamp_offset + src->running_time; + GST_BUFFER_OFFSET (buffer) = src->n_frames; + src->n_frames++; + GST_BUFFER_OFFSET_END (buffer) = src->n_frames; + if (src->out_info.fps_n) { + next_time = gst_util_uint64_scale_int (src->n_frames * GST_SECOND, + src->out_info.fps_d, src->out_info.fps_n); + GST_BUFFER_DURATION (buffer) = next_time - src->running_time; + } else { + next_time = src->timestamp_offset; + /* NONE means forever */ + GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE; + } + + src->running_time = next_time; + + return GST_FLOW_OK; + +not_negotiated: + { + GST_ELEMENT_ERROR (src, CORE, NEGOTIATION, (NULL), + (_("format wasn't negotiated before get function"))); + return GST_FLOW_NOT_NEGOTIATED; + } +eos: + { + GST_DEBUG_OBJECT (src, "eos: 0 framerate, frame %d", (gint) src->n_frames); + return GST_FLOW_EOS; + } +} + +static gboolean +gst_gl_test_src_start (GstBaseSrc * basesrc) +{ + GstGLTestSrc *src = GST_GL_TEST_SRC (basesrc); + + if (!gst_gl_ensure_display (src, &src->display)) + return FALSE; + + src->running_time = 0; + src->n_frames = 0; + src->negotiated = FALSE; + + return TRUE; +} + +static gboolean +gst_gl_test_src_stop (GstBaseSrc * basesrc) +{ + GstGLTestSrc *src = GST_GL_TEST_SRC (basesrc); + + if (src->context) { + if (src->out_tex_id) { + gst_gl_context_del_texture (src->context, &src->out_tex_id); + } + + if (src->download) { + gst_object_unref (src->download); + src->download = NULL; + } + //blocking call, delete the FBO + gst_gl_context_del_fbo (src->context, src->fbo, src->depthbuffer); + gst_object_unref (src->context); + src->context = NULL; + } + + if (src->display) { + gst_object_unref (src->display); + src->display = NULL; + } + + return TRUE; +} + +static gboolean +gst_gl_test_src_decide_allocation (GstBaseSrc * basesrc, GstQuery * query) +{ + GstGLTestSrc *src = GST_GL_TEST_SRC (basesrc); + GstBufferPool *pool = NULL; + GstStructure *config; + GstCaps *caps; + guint min, max, size; + gboolean update_pool; + GError *error = NULL; + guint idx; + guint out_width, out_height; + GstGLContext *other_context = NULL; + + gst_query_parse_allocation (query, &caps, NULL); + + if (gst_query_get_n_allocation_pools (query) > 0) { + gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); + + update_pool = TRUE; + } else { + GstVideoInfo vinfo; + + gst_video_info_init (&vinfo); + gst_video_info_from_caps (&vinfo, caps); + size = vinfo.size; + min = max = 0; + update_pool = FALSE; + } + + if (!gst_gl_ensure_display (src, &src->display)) + return FALSE; + + if (gst_query_find_allocation_meta (query, + GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, &idx)) { + GstGLContext *context; + const GstStructure *upload_meta_params; + gpointer handle; + gchar *type; + gchar *apis; + + gst_query_parse_nth_allocation_meta (query, idx, &upload_meta_params); + if (gst_structure_get (upload_meta_params, "gst.gl.GstGLContext", + GST_GL_TYPE_CONTEXT, &context, NULL) && context) { + GstGLContext *old = src->context; + + src->context = context; + if (old) + gst_object_unref (old); + } else if (gst_structure_get (upload_meta_params, "gst.gl.context.handle", + G_TYPE_POINTER, &handle, "gst.gl.context.type", G_TYPE_STRING, + &type, "gst.gl.context.apis", G_TYPE_STRING, &apis, NULL) + && handle) { + GstGLPlatform platform = GST_GL_PLATFORM_NONE; + GstGLAPI gl_apis; + + GST_DEBUG ("got GL context handle 0x%p with type %s and apis %s", handle, + type, apis); + + if (g_strcmp0 (type, "glx") == 0) + platform = GST_GL_PLATFORM_GLX; + + gl_apis = gst_gl_api_from_string (apis); + + if (gl_apis && platform) + other_context = + gst_gl_context_new_wrapped (src->display, (guintptr) handle, + platform, gl_apis); + } + } + + if (!src->context) { + src->context = gst_gl_context_new (src->display); + if (!gst_gl_context_create (src->context, other_context, &error)) + goto context_error; + } + + out_width = GST_VIDEO_INFO_WIDTH (&src->out_info); + out_height = GST_VIDEO_INFO_HEIGHT (&src->out_info); + + if (!gst_gl_context_gen_fbo (src->context, out_width, out_height, + &src->fbo, &src->depthbuffer)) + goto context_error; + + if (!pool) + pool = gst_gl_buffer_pool_new (src->context); + + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_set_params (config, caps, size, min, max); + gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); + gst_buffer_pool_set_config (pool, config); + + if (update_pool) + gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max); + else + gst_query_add_allocation_pool (query, pool, size, min, max); + + gst_object_unref (pool); + + return TRUE; + +context_error: + { + GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, ("%s", error->message), + (NULL)); + return FALSE; + } +} + +//opengl scene +static void +gst_gl_test_src_callback (gpointer stuff) +{ + GstGLTestSrc *src = GST_GL_TEST_SRC (stuff); + + src->make_image (src, src->buffer, GST_VIDEO_INFO_WIDTH (&src->out_info), + GST_VIDEO_INFO_HEIGHT (&src->out_info)); + + gst_buffer_unref (src->buffer); + src->buffer = NULL; +} diff --git a/ext/gl/gstgltestsrc.h b/ext/gl/gstgltestsrc.h new file mode 100644 index 0000000..48e4d86 --- /dev/null +++ b/ext/gl/gstgltestsrc.h @@ -0,0 +1,125 @@ +/* + * GStreamer + * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) 2002,2007 David A. Schleef + * Copyright (C) 2008 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_GL_TEST_SRC_H__ +#define __GST_GL_TEST_SRC_H__ + +#include +#include + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_GL_TEST_SRC \ + (gst_gl_test_src_get_type()) +#define GST_GL_TEST_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_TEST_SRC,GstGLTestSrc)) +#define GST_GL_TEST_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GL_TEST_SRC,GstGLTestSrcClass)) +#define GST_IS_GL_TEST_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_TEST_SRC)) +#define GST_IS_GL_TEST_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GL_TEST_SRC)) + +/** + * GstGLTestSrcPattern: + * @GST_GL_TEST_SRC_SMPTE: A standard SMPTE test pattern + * @GST_GL_TEST_SRC_SNOW: Random noise + * @GST_GL_TEST_SRC_BLACK: A black image + * @GST_GL_TEST_SRC_WHITE: A white image + * @GST_GL_TEST_SRC_RED: A red image + * @GST_GL_TEST_SRC_GREEN: A green image + * @GST_GL_TEST_SRC_BLUE: A blue image + * @GST_GL_TEST_SRC_CHECKERS1: Checkers pattern (1px) + * @GST_GL_TEST_SRC_CHECKERS2: Checkers pattern (2px) + * @GST_GL_TEST_SRC_CHECKERS4: Checkers pattern (4px) + * @GST_GL_TEST_SRC_CHECKERS8: Checkers pattern (8px) + * @GST_GL_TEST_SRC_CIRCULAR: Circular pattern + * @GST_GL_TEST_SRC_BLINK: Alternate between black and white + * + * The test pattern to produce. + */ +typedef enum { + GST_GL_TEST_SRC_SMPTE, + GST_GL_TEST_SRC_SNOW, + GST_GL_TEST_SRC_BLACK, + GST_GL_TEST_SRC_WHITE, + GST_GL_TEST_SRC_RED, + GST_GL_TEST_SRC_GREEN, + GST_GL_TEST_SRC_BLUE, + GST_GL_TEST_SRC_CHECKERS1, + GST_GL_TEST_SRC_CHECKERS2, + GST_GL_TEST_SRC_CHECKERS4, + GST_GL_TEST_SRC_CHECKERS8, + GST_GL_TEST_SRC_CIRCULAR, + GST_GL_TEST_SRC_BLINK +} GstGLTestSrcPattern; + +typedef struct _GstGLTestSrc GstGLTestSrc; +typedef struct _GstGLTestSrcClass GstGLTestSrcClass; + +/** + * GstGLTestSrc: + * + * Opaque data structure. + */ +struct _GstGLTestSrc { + GstPushSrc element; + + /*< private >*/ + + /* type of output */ + GstGLTestSrcPattern pattern_type; + + /* video state */ + char *format_name; + GstVideoInfo out_info; + + GLuint fbo; + GLuint depthbuffer; + + GstBuffer* buffer; + GstBufferPool *pool; + + guint out_tex_id; + GstGLDownload *download; + + GstGLDisplay *display; + GstGLContext *context; + gint64 timestamp_offset; /* base offset */ + GstClockTime running_time; /* total running time */ + gint64 n_frames; /* total frames sent */ + gboolean negotiated; + + void (*make_image) (GstGLTestSrc* v, GstBuffer* buffer, gint w, gint h); +}; + +struct _GstGLTestSrcClass { + GstPushSrcClass parent_class; +}; + +GType gst_gl_test_src_get_type (void); + +G_END_DECLS + +#endif /* __GST_GL_TEST_SRC_H__ */ diff --git a/ext/gl/gstglvideomixer.c b/ext/gl/gstglvideomixer.c new file mode 100644 index 0000000..d72f83b --- /dev/null +++ b/ext/gl/gstglvideomixer.c @@ -0,0 +1,294 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-glvideomixer + * + * glmixer sub element. N gl sink pads to 1 source pad. + * N + 1 OpenGL contexts shared together. + * N <= 6 because the rendering is more a like a cube than a video_mixer + * Each opengl input stream is rendered on a cube face + * + * + * Examples + * |[ + * gst-launch-0.10 videotestsrc ! "video/x-raw-yuv, format=(fourcc)YUY2" ! glupload ! queue ! glvideomixer name=m ! glimagesink videotestsrc pattern=12 ! "video/x-raw-yuv, format=(fourcc)I420, framerate=(fraction)5/1, width=100, height=200" ! glupload ! queue ! m. videotestsrc ! "video/x-raw-rgb, framerate=(fraction)15/1, width=1500, height=1500" ! glupload ! gleffects effect=3 ! queue ! m. videotestsrc ! glupload ! gleffects effect=2 ! queue ! m. videotestsrc ! glupload ! glfiltercube ! queue ! m. videotestsrc ! glupload ! gleffects effect=6 ! queue ! m. + * ]| + * FBO (Frame Buffer Object) is required. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstglvideomixer.h" + +#define GST_CAT_DEFAULT gst_gl_video_mixer_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +enum +{ + PROP_0, +}; + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_video_mixer_debug, "glvideomixer", 0, "glvideomixer element"); + +G_DEFINE_TYPE_WITH_CODE (GstGLVideoMixer, gst_gl_video_mixer, GST_TYPE_GL_MIXER, + DEBUG_INIT); + +static void gst_gl_video_mixer_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_video_mixer_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void gst_gl_video_mixer_reset (GstGLMixer * mixer); +static gboolean gst_gl_video_mixer_init_shader (GstGLMixer * mixer, + GstCaps * outcaps); + +static gboolean gst_gl_video_mixer_process_textures (GstGLMixer * mixer, + GPtrArray * in_frames, guint out_tex); +static void gst_gl_video_mixer_callback (gpointer stuff); + +/* vertex source */ +static const gchar *video_mixer_v_src = + "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" + "uniform float x_scale; \n" + "uniform float y_scale; \n" + "varying vec2 v_texCoord; \n" + "void main() \n" + "{ \n" + " gl_Position = a_position * vec4(x_scale, y_scale, 1.0, 1.0);\n" + " v_texCoord = a_texCoord; \n" "}"; + +/* fragment source */ +static const gchar *video_mixer_f_src = + "uniform sampler2D texture; \n" + "varying vec2 v_texCoord; \n" + "void main() \n" + "{ \n" + " vec4 rgba = texture2D( texture, v_texCoord );\n" + " gl_FragColor = vec4(rgba.rgb, 1.0);\n" + "} \n"; + +static void +gst_gl_video_mixer_class_init (GstGLVideoMixerClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_gl_video_mixer_set_property; + gobject_class->get_property = gst_gl_video_mixer_get_property; + + gst_element_class_set_metadata (element_class, "OpenGL video_mixer", + "Filter/Effect/Video", "OpenGL video_mixer", + "Julien Isorce "); + + GST_GL_MIXER_CLASS (klass)->set_caps = gst_gl_video_mixer_init_shader; + GST_GL_MIXER_CLASS (klass)->reset = gst_gl_video_mixer_reset; + GST_GL_MIXER_CLASS (klass)->process_textures = + gst_gl_video_mixer_process_textures; +} + +static void +gst_gl_video_mixer_init (GstGLVideoMixer * video_mixer) +{ + video_mixer->shader = NULL; + video_mixer->input_frames = NULL; +} + +static void +gst_gl_video_mixer_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_video_mixer_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_video_mixer_reset (GstGLMixer * mixer) +{ + GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (mixer); + + video_mixer->input_frames = NULL; + + if (video_mixer->shader) + gst_gl_context_del_shader (mixer->context, video_mixer->shader); + video_mixer->shader = NULL; +} + +static gboolean +gst_gl_video_mixer_init_shader (GstGLMixer * mixer, GstCaps * outcaps) +{ + GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (mixer); + + return gst_gl_context_gen_shader (mixer->context, video_mixer_v_src, + video_mixer_f_src, &video_mixer->shader); +} + +static gboolean +gst_gl_video_mixer_process_textures (GstGLMixer * mix, GPtrArray * frames, + guint out_tex) +{ + GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (mix); + + video_mixer->input_frames = frames; + + gst_gl_context_use_fbo_v2 (mix->context, + GST_VIDEO_INFO_WIDTH (&mix->out_info), + GST_VIDEO_INFO_HEIGHT (&mix->out_info), mix->fbo, mix->depthbuffer, + out_tex, gst_gl_video_mixer_callback, (gpointer) video_mixer); + + return TRUE; +} + +/* opengl scene, params: input texture (not the output mixer->texture) */ +static void +gst_gl_video_mixer_callback (gpointer stuff) +{ + GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (stuff); + GstGLMixer *mixer = GST_GL_MIXER (video_mixer); + GstGLFuncs *gl = mixer->context->gl_vtable; + + GLint attr_position_loc = 0; + GLint attr_texture_loc = 0; + guint out_width, out_height; + + const GLushort indices[] = { + 0, 1, 2, + 0, 2, 3 + }; + + guint count = 0; + + out_width = GST_VIDEO_INFO_WIDTH (&mixer->out_info); + out_height = GST_VIDEO_INFO_HEIGHT (&mixer->out_info); + + gst_gl_context_clear_shader (mixer->context); + gl->BindTexture (GL_TEXTURE_2D, 0); + gl->Disable (GL_TEXTURE_2D); + + gl->Disable (GL_DEPTH_TEST); + gl->Disable (GL_CULL_FACE); + + gl->ClearColor (0.0, 0.0, 0.0, 0.0); + gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + gst_gl_shader_use (video_mixer->shader); + + attr_position_loc = + gst_gl_shader_get_attribute_location (video_mixer->shader, "a_position"); + attr_texture_loc = + gst_gl_shader_get_attribute_location (video_mixer->shader, "a_texCoord"); + + gl->Enable (GL_BLEND); + + while (count < video_mixer->input_frames->len) { + GstGLMixerFrameData *frame; + GLfloat *v_vertices; + guint in_tex; + guint in_width, in_height; + gfloat w, h; + + frame = g_ptr_array_index (video_mixer->input_frames, count); + in_tex = frame->texture; + in_width = GST_VIDEO_INFO_WIDTH (&frame->pad->in_info); + in_height = GST_VIDEO_INFO_HEIGHT (&frame->pad->in_info); + + if (!frame || !in_tex || in_width <= 0 || in_height <= 0) { + GST_DEBUG ("skipping texture:%u frame:%p width:%u height %u", + in_tex, frame, in_width, in_height); + count++; + continue; + } + + GST_TRACE ("processing texture:%u dimensions:%ux%u", in_tex, in_width, + in_height); + + w = ((gfloat) in_width / (gfloat) out_width); + h = ((gfloat) in_height / (gfloat) out_height); + GST_TRACE ("processing texture:%u dimensions:%ux%u, %fx%f", in_tex, + in_width, in_height, w, h); + + /* *INDENT-OFF* */ + v_vertices = (GLfloat[]) { + /* front face */ + -1.0, -1.0, -1.0f, + 0.0f, 0.0f, + 1.0, -1.0, -1.0f, + 1.0f, 0.0f, + 1.0, 1.0, -1.0f, + 1.0f, 1.0f, + -1.0, 1.0, -1.0f, + 0.0f, 1.0f, + }; + /* *INDENT-ON* */ + + gl->VertexAttribPointer (attr_position_loc, 3, GL_FLOAT, + GL_FALSE, 5 * sizeof (GLfloat), &v_vertices[0]); + + gl->VertexAttribPointer (attr_texture_loc, 2, GL_FLOAT, + GL_FALSE, 5 * sizeof (GLfloat), &v_vertices[3]); + + gl->EnableVertexAttribArray (attr_position_loc); + gl->EnableVertexAttribArray (attr_texture_loc); + + gl->BlendFunc (GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR); + gl->BlendEquation (GL_FUNC_ADD); + + gl->ActiveTexture (GL_TEXTURE0); + gl->BindTexture (GL_TEXTURE_2D, in_tex); + gst_gl_shader_set_uniform_1i (video_mixer->shader, "texture", 0); + gst_gl_shader_set_uniform_1f (video_mixer->shader, "x_scale", w); + gst_gl_shader_set_uniform_1f (video_mixer->shader, "y_scale", h); + + gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); + + ++count; + } + + gl->DisableVertexAttribArray (attr_position_loc); + gl->DisableVertexAttribArray (attr_texture_loc); + + gl->BindTexture (GL_TEXTURE_2D, 0); + + gl->Disable (GL_BLEND); + + gst_gl_context_clear_shader (mixer->context); +} diff --git a/ext/gl/gstglvideomixer.h b/ext/gl/gstglvideomixer.h new file mode 100644 index 0000000..716c60a --- /dev/null +++ b/ext/gl/gstglvideomixer.h @@ -0,0 +1,55 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_GL_VIDEO_MIXER_H_ +#define _GST_GL_VIDEO_MIXER_H_ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_GL_VIDEO_MIXER (gst_gl_video_mixer_get_type()) +#define GST_GL_VIDEO_MIXER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_VIDEO_MIXER,GstGLVideoMixer)) +#define GST_IS_GL_VIDEO_MIXER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_VIDEO_MIXER)) +#define GST_GL_VIDEO_MIXER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_VIDEO_MIXER,GstGLVideoMixerClass)) +#define GST_IS_GL_VIDEO_MIXER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_VIDEO_MIXER)) +#define GST_GL_VIDEO_MIXER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_VIDEO_MIXER,GstGLVideoMixerClass)) + +typedef struct _GstGLVideoMixer GstGLVideoMixer; +typedef struct _GstGLVideoMixerClass GstGLVideoMixerClass; + +struct _GstGLVideoMixer +{ + GstGLMixer mixer; + + GstGLShader *shader; + GPtrArray *input_frames; +}; + +struct _GstGLVideoMixerClass +{ + GstGLMixerClass mixer_class; +}; + +GType gst_gl_video_mixer_get_type (void); + +G_END_DECLS + +#endif /* _GST_GLFILTERCUBE_H_ */ diff --git a/ext/gl/gstopengl.c b/ext/gl/gstopengl.c new file mode 100644 index 0000000..b439859 --- /dev/null +++ b/ext/gl/gstopengl.c @@ -0,0 +1,212 @@ +/* + * GStreamer + * Copyright (C) 2003 Julien Moutte + * Copyright (C) 2005,2006,2007 David A. Schleef + * Copyright (C) 2008 Julien Isorce + * Copyright (C) 2008 Filippo Argiolas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:plugin-opengl + * + * Cross-platform OpenGL plugin. + * + * Debugging + * + * + * Examples + * |[ + * gst-launch-0.10 --gst-debug=gldisplay:3 videotestsrc ! glimagesink + * ]| A debugging pipeline. + |[ + * GST_GL_SHADER_DEBUG=1 gst-launch-0.10 videotestsrc ! glimagesink + * ]| A debugging pipelines related to shaders. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef USE_EGL_RPI +#include +#endif + +#include + +#include "gstglimagesink.h" + +#include "gstglfiltercube.h" +#include "gstgleffects.h" +#include "gstglcolorscale.h" + +GType gst_gl_filter_cube_get_type (void); +GType gst_gl_effects_get_type (void); + +#if GST_GL_HAVE_OPENGL +#include "gstgltestsrc.h" +#include "gstglfilterlaplacian.h" +#include "gstglfilterglass.h" +#include "gstglfilterapp.h" +#include "gstglfilterreflectedscreen.h" +#include "gstglfiltershader.h" +#include "gstgldeinterlace.h" +#include "gstglmosaic.h" +#include "gstglvideomixer.h" + +GType gst_gl_deinterlace_get_type (void); +GType gst_gl_filter_app_get_type (void); +GType gst_gl_filter_reflected_screen_get_type (void); +GType gst_gl_filterblur_get_type (void); +GType gst_gl_filtershader_get_type (void); +GType gst_gl_filtersobel_get_type (void); +GType gst_gl_filter_laplacian_get_type (void); +GType gst_gl_filter_glass_get_type (void); +GType gst_gl_mosaic_get_type (void); + +#if HAVE_PNG +#include "gstgldifferencematte.h" +#include "gstglbumper.h" + +GType gst_gl_differencematte_get_type (void); +GType gst_gl_bumper_get_type (void); + +#if HAVE_JPEG +#include "gstgloverlay.h" + +GType gst_gl_overlay_get_type (void); + +#endif /* HAVE_JPEG */ +#endif /* HAVE_PNG */ +#endif /* GST_GL_HAVE_OPENGL */ + +#define GST_CAT_DEFAULT gst_gl_gstgl_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +/* Register filters that make up the gstgl plugin */ +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (gst_gl_gstgl_debug, "gstopengl", 0, "gstopengl"); + +#ifdef USE_EGL_RPI + GST_DEBUG ("Initialize BCM host"); + bcm_host_init (); +#endif + + if (!gst_element_register (plugin, "glimagesink", + GST_RANK_MARGINAL, GST_TYPE_GLIMAGE_SINK)) { + return FALSE; + } + + if (!gst_element_register (plugin, "glfiltercube", + GST_RANK_NONE, GST_TYPE_GL_FILTER_CUBE)) { + return FALSE; + } + + if (!gst_element_register (plugin, "gleffects", + GST_RANK_NONE, gst_gl_effects_get_type ())) { + return FALSE; + } + + if (!gst_element_register (plugin, "glcolorscale", + GST_RANK_NONE, GST_TYPE_GL_COLORSCALE)) { + return FALSE; + } +#if GST_GL_HAVE_OPENGL + if (!gst_element_register (plugin, "gltestsrc", + GST_RANK_NONE, GST_TYPE_GL_TEST_SRC)) { + return FALSE; + } + + if (!gst_element_register (plugin, "glfilterblur", + GST_RANK_NONE, gst_gl_filterblur_get_type ())) { + return FALSE; + } + + if (!gst_element_register (plugin, "glshader", + GST_RANK_NONE, gst_gl_filtershader_get_type ())) { + return FALSE; + } + + if (!gst_element_register (plugin, "glfiltersobel", + GST_RANK_NONE, gst_gl_filtersobel_get_type ())) { + return FALSE; + } + + if (!gst_element_register (plugin, "glfilterlaplacian", + GST_RANK_NONE, GST_TYPE_GL_FILTER_LAPLACIAN)) { + return FALSE; + } + + if (!gst_element_register (plugin, "glfilterglass", + GST_RANK_NONE, GST_TYPE_GL_FILTER_GLASS)) { + return FALSE; + } + + if (!gst_element_register (plugin, "glfilterapp", + GST_RANK_NONE, GST_TYPE_GL_FILTER_APP)) { + return FALSE; + } + + if (!gst_element_register (plugin, "glfilterreflectedscreen", + GST_RANK_NONE, GST_TYPE_GL_FILTER_REFLECTED_SCREEN)) { + return FALSE; + } + + if (!gst_element_register (plugin, "gldeinterlace", + GST_RANK_NONE, GST_TYPE_GL_DEINTERLACE)) { + return FALSE; + } + + if (!gst_element_register (plugin, "glmosaic", + GST_RANK_NONE, GST_TYPE_GL_MOSAIC)) { + return FALSE; + } + + if (!gst_element_register (plugin, "glvideomixer", + GST_RANK_NONE, GST_TYPE_GL_VIDEO_MIXER)) { + return FALSE; + } +#if HAVE_PNG + if (!gst_element_register (plugin, "gldifferencematte", + GST_RANK_NONE, gst_gl_differencematte_get_type ())) { + return FALSE; + } + + if (!gst_element_register (plugin, "glbumper", + GST_RANK_NONE, gst_gl_bumper_get_type ())) { + return FALSE; + } +#if HAVE_JPEG + if (!gst_element_register (plugin, "gloverlay", + GST_RANK_NONE, gst_gl_overlay_get_type ())) { + return FALSE; + } +#endif /* HAVE_JPEG */ +#endif /* HAVE_PNG */ +#endif /* GST_GL_HAVE_OPENGL */ + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + opengl, + "OpenGL plugin", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) -- 2.7.4