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 retrieve 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