1 /*
2  * GStreamer
3  * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include <gst/gst.h>
22 #include <gst/gl/gl.h>
23 #include <gst/gl/gstglfuncs.h>
24 #include <gtk/gtk.h>
25 #if GST_GL_HAVE_WINDOW_X11
26 #include <X11/Xlib.h>
27 #endif
28 
29 #ifndef GL_GEOMETRY_SHADER
30 #define GL_GEOMETRY_SHADER 0x8DD9
31 #endif
32 
33 static GMainLoop *loop;
34 
35 static const gchar *vert = "#version 330\n\
36 in vec4 a_position;\n\
37 in vec2 a_texcoord;\n\
38 out vec2 v_texcoord;\n\
39 uniform float time;\n\
40 uniform float width;\n\
41 uniform float height;\n\
42 void main()\n\
43 {\n\
44   gl_Position = a_position;\n\
45   v_texcoord = a_texcoord;\n\
46 }\n";
47 
48 static const gchar *geom = "#version 330\n\
49 \n\
50 layout(triangles) in;\n\
51 layout(triangle_strip, max_vertices = 3) out;\n\
52 in vec2 v_texcoord[];\n\
53 out vec2 g_texcoord;\n\
54 \n\
55 void main() {\n\
56   for(int i = 0; i < 3; i++) {\n\
57     gl_Position = gl_in[i].gl_Position;\n\
58     g_texcoord = v_texcoord[i];\n\
59     EmitVertex();\n\
60   }\n\
61   EndPrimitive();\n\
62 }\n";
63 
64 static const gchar *frag = "#version 330\n\
65 in vec2 g_texcoord;\n\
66 uniform sampler2D tex;\n\
67 uniform float time;\n\
68 uniform float width;\n\
69 uniform float height;\n\
70 void main()\n\
71 {\n\
72   gl_FragColor = texture2D(tex, g_texcoord);\n\
73 }\n";
74 
75 #define MAX_SHADER_STAGES 8
76 struct shader_state;
77 
78 struct text_view_state
79 {
80   struct shader_state *state;
81 
82   GLenum type;
83   gchar *str;
84 };
85 
86 struct shader_state
87 {
88   GstGLContext *context;
89   GstElement *shader;
90   gboolean shader_linked;
91   GtkWidget *label;
92   struct text_view_state text_states[MAX_SHADER_STAGES];
93   gint n_stages;
94 };
95 
96 static gboolean
bus_call(GstBus * bus,GstMessage * msg,gpointer data)97 bus_call (GstBus * bus, GstMessage * msg, gpointer data)
98 {
99   switch (GST_MESSAGE_TYPE (msg)) {
100     case GST_MESSAGE_EOS:
101       g_print ("End of stream\n");
102       g_main_loop_quit (loop);
103       break;
104     case GST_MESSAGE_ERROR:{
105       gchar *debug;
106       GError *error;
107 
108       gst_message_parse_error (msg, &error, &debug);
109       g_free (debug);
110 
111       g_printerr ("Error: %s\n", error->message);
112       g_error_free (error);
113 
114       g_main_loop_quit (loop);
115       break;
116     }
117     default:
118       break;
119   }
120 
121   return TRUE;
122 }
123 
124 static gchar *
_find_source_for_shader_type(struct shader_state * state,GLenum type)125 _find_source_for_shader_type (struct shader_state *state, GLenum type)
126 {
127   int i = 0;
128 
129   for (i = 0; i < state->n_stages; i++) {
130     if (state->text_states[i].type == type)
131       return state->text_states[i].str;
132   }
133 
134   return NULL;
135 }
136 
137 static gboolean
_add_stage_to_shader(GstGLShader * shader,struct shader_state * state,GLenum type,const gchar * default_src)138 _add_stage_to_shader (GstGLShader * shader, struct shader_state *state,
139     GLenum type, const gchar * default_src)
140 {
141   GError *error = NULL;
142   GstGLSLVersion version;
143   GstGLSLProfile profile;
144   GstGLSLStage *stage;
145   const gchar *src;
146 
147   src = _find_source_for_shader_type (state, type);
148   if (!src)
149     src = default_src;
150   if (!src)
151     /* FIXME: assume this stage is not needed */
152     return TRUE;
153 
154   if (!gst_glsl_string_get_version_profile (src, &version, &profile)) {
155     g_print ("Warning: failed to retreive GLSL version and profile for "
156         "shader type 0x%x\nsrc:\n%s\n", type, src);
157   }
158 
159   if (!(stage = gst_glsl_stage_new_with_string (shader->context, type,
160               version, profile, src))) {
161     g_print ("Error: Failed to create GLSL Stage from src:\n%s\n", src);
162     return FALSE;
163   }
164 
165   if (!gst_gl_shader_compile_attach_stage (shader, stage, &error)) {
166     /* ignore failed shader compilations */
167     g_print ("%s", error->message);
168     return FALSE;
169   }
170 
171   return TRUE;
172 }
173 
174 static GstGLShader *
_new_shader(GstGLContext * context,struct shader_state * state)175 _new_shader (GstGLContext * context, struct shader_state *state)
176 {
177   GstGLShader *shader = gst_gl_shader_new (context);
178   GError *error = NULL;
179 
180   if (!_add_stage_to_shader (shader, state, GL_VERTEX_SHADER, vert)) {
181     gst_object_unref (shader);
182     return NULL;
183   }
184   if (!_add_stage_to_shader (shader, state, GL_GEOMETRY_SHADER, geom)) {
185     gst_object_unref (shader);
186     return NULL;
187   }
188   if (!_add_stage_to_shader (shader, state, GL_FRAGMENT_SHADER, frag)) {
189     gst_object_unref (shader);
190     return NULL;
191   }
192 
193   if (!gst_gl_shader_link (shader, &error)) {
194     /* ignore failed shader compilations */
195     g_print ("%s", error->message);
196     gst_object_unref (shader);
197     return NULL;
198   }
199 
200   return shader;
201 }
202 
203 static gboolean
_set_compilation_state(struct shader_state * state)204 _set_compilation_state (struct shader_state *state)
205 {
206   gtk_label_set_text (GTK_LABEL (state->label),
207       state->shader_linked ? "Success" : "Failure");
208 
209   return G_SOURCE_REMOVE;
210 }
211 
212 static GstGLShader *
_create_shader(GstElement * element,struct shader_state * state)213 _create_shader (GstElement * element, struct shader_state *state)
214 {
215   GstGLContext *context;
216   GstGLShader *shader, *new_shader;
217 
218   g_object_get (G_OBJECT (element), "context", &context, "shader", &shader,
219       NULL);
220 
221   new_shader = _new_shader (context, state);
222   if (!shader && !new_shader)
223     g_warning ("Failed to create a shader!");
224   state->shader_linked = new_shader != NULL;
225 
226   if (shader)
227     gst_object_unref (shader);
228   gst_object_unref (context);
229 
230   g_main_context_invoke (NULL, (GSourceFunc) _set_compilation_state, state);
231 
232   return new_shader;
233 }
234 
235 static void
_on_text_changed(GtkTextBuffer * text,struct text_view_state * state)236 _on_text_changed (GtkTextBuffer * text, struct text_view_state *state)
237 {
238   GtkTextIter start, end;
239 
240   gtk_text_buffer_get_bounds (text, &start, &end);
241   g_free (state->str);
242   state->str = gtk_text_buffer_get_text (text, &start, &end, FALSE);
243   g_object_set (state->state->shader, "update-shader", TRUE, NULL);
244 }
245 
246 static GtkWidget *
_new_source_view(struct shader_state * state,GLenum type,const gchar * templ)247 _new_source_view (struct shader_state *state, GLenum type, const gchar * templ)
248 {
249   static int i = 0;
250   GtkWidget *scroll, *text_view;
251   GtkTextBuffer *text;
252 
253   g_return_val_if_fail (i < MAX_SHADER_STAGES, NULL);
254 
255   state->text_states[i].state = state;
256   state->text_states[i].type = type;
257   state->text_states[i].str = g_strdup (templ);
258 
259   scroll = gtk_scrolled_window_new (NULL, NULL);
260   gtk_widget_set_size_request (scroll, 20, 20);
261   text_view = gtk_text_view_new ();
262   gtk_container_add (GTK_CONTAINER (scroll), text_view);
263   text = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
264   if (state->text_states[i].str)
265     gtk_text_buffer_set_text (text, state->text_states[i].str, -1);
266   g_signal_connect (text, "changed", G_CALLBACK (_on_text_changed),
267       &state->text_states[i]);
268   state->n_stages++;
269   i++;
270 
271   return scroll;
272 }
273 
274 int
main(int argc,char * argv[])275 main (int argc, char *argv[])
276 {
277   GstElement *pipeline, *src, *upload, *shader, *sink;
278   GtkWidget *window, *paned, *video, *right_box, *book;
279   struct shader_state state = { 0, };
280   GstBus *bus;
281 
282 #if GST_GL_HAVE_WINDOW_X11
283   XInitThreads ();
284 #endif
285 
286   gst_init (&argc, &argv);
287   gtk_init (&argc, &argv);
288 
289   loop = g_main_loop_new (NULL, FALSE);
290 
291   pipeline = gst_pipeline_new (NULL);
292   src = gst_element_factory_make ("videotestsrc", NULL);
293   upload = gst_element_factory_make ("glupload", NULL);
294   shader = gst_element_factory_make ("glshader", NULL);
295   sink = gst_element_factory_make ("gtkglsink", NULL);
296   g_object_get (sink, "widget", &video, NULL);
297 
298   g_assert (src && shader && sink);
299   gst_bin_add_many (GST_BIN (pipeline), src, upload, shader, sink, NULL);
300   g_assert (gst_element_link_many (src, upload, shader, sink, NULL));
301 
302   bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
303   gst_bus_add_watch (bus, bus_call, loop);
304   gst_object_unref (bus);
305 
306   state.shader = gst_object_ref (shader);
307   g_signal_connect (shader, "create-shader", G_CALLBACK (_create_shader),
308       &state);
309 
310   book = gtk_notebook_new ();
311   /* text view inside a scroll view */
312   gtk_notebook_append_page (GTK_NOTEBOOK (book), _new_source_view (&state,
313           GL_VERTEX_SHADER, vert), gtk_label_new ("Vertex"));
314   gtk_notebook_append_page (GTK_NOTEBOOK (book), _new_source_view (&state,
315           GL_GEOMETRY_SHADER, geom), gtk_label_new ("Geometry"));
316   gtk_notebook_append_page (GTK_NOTEBOOK (book), _new_source_view (&state,
317           GL_FRAGMENT_SHADER, frag), gtk_label_new ("Fragment"));
318   /* status label */
319   state.label = gtk_label_new ("Success");
320 
321   /* right side source code editor */
322   right_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
323   gtk_box_pack_start (GTK_BOX (right_box), book, TRUE, TRUE, 0);
324   gtk_box_pack_start (GTK_BOX (right_box), state.label, FALSE, TRUE, 0);
325 
326   paned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
327   gtk_paned_pack1 (GTK_PANED (paned), video, TRUE, FALSE);
328   gtk_widget_set_size_request (video, 20, 20);
329   gtk_paned_pack2 (GTK_PANED (paned), right_box, TRUE, FALSE);
330 
331   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
332   gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
333   gtk_container_add (GTK_CONTAINER (window), paned);
334 
335   gtk_widget_show_all (window);
336 
337   gst_element_set_state (pipeline, GST_STATE_PLAYING);
338 
339   g_main_loop_run (loop);
340 
341   gst_element_set_state (pipeline, GST_STATE_NULL);
342 
343   /*shader strings leaked here */
344   /*g_free (state.str); */
345   gst_object_unref (state.shader);
346 
347   gst_object_unref (pipeline);
348 
349   return 0;
350 }
351