• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2009 Julien Isorce <julien.isorce@gmail.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #ifdef WIN32
25 #include <windows.h>
26 #endif
27 
28 #include <GL/gl.h>
29 #include <SDL2/SDL.h>
30 #include <SDL2/SDL_opengl.h>
31 
32 #ifndef WIN32
33 #include <GL/glx.h>
34 #include <SDL2/SDL_syswm.h>
35 #include <gst/gl/x11/gstgldisplay_x11.h>
36 #endif
37 
38 #include <gst/gst.h>
39 #include <gst/gl/gl.h>
40 #include <gst/gl/gstglfuncs.h>
41 
42 static GstStaticCaps render_caps =
43     GST_STATIC_CAPS
44     ("video/x-raw(memory:GLMemory),format=RGBA,width=320,height=240,framerate=(fraction)30/1,texture-target=2D");
45 static GstVideoInfo render_video_info;
46 static GstPipeline *pipeline = NULL;
47 
48 static GstGLContext *sdl_context;
49 static GstGLContext *gst_context;
50 static GstGLDisplay *sdl_gl_display;
51 
52 static GstGLShader *texture_shader;
53 static GLuint texture_vao;
54 static GLuint texture_vbo;
55 static GLint texture_vertex_attr;
56 static GLint texture_texcoord_attr;
57 static GstGLShader *triangle_shader;
58 static GLuint triangle_vao;
59 static GLuint triangle_vbo;
60 static GLint triangle_vertex_attr;
61 static GLint triangle_color_attr;
62 static GLuint index_buffer;
63 
64 /* OpenGL shaders */
65 static const gchar *triangle_vert = "attribute vec4 a_position;\n\
66 attribute vec4 a_color;\n\
67 uniform float yrot;\n\
68 varying vec4 v_color;\n\
69 void main()\n\
70 {\n\
71    mat4 rotate_y = mat4 (\n\
72       cos(yrot),        0.0, -sin(yrot),    0.0,\n\
73             0.0,        1.0,        0.0,    0.0,\n\
74       sin(yrot),        0.0,  cos(yrot),    0.0,\n\
75             0.0,        0.0,       0.0,     1.0 );\n\
76    mat4 translate_x = mat4 (\n\
77             1.0,        0.0,        0.0,    0.0,\n\
78             0.0,        1.0,        0.0,    0.0,\n\
79             0.0,        0.0,        1.0,    0.0,\n\
80            -0.4,        0.0,        0.0,    1.0 );\n\
81    gl_Position = translate_x * rotate_y * a_position;\n\
82    v_color = a_color;\n\
83 }";
84 
85 static const gchar *triangle_frag = "#ifdef GL_ES\n\
86 precision mediump float;\n\
87 #endif\n\
88 varying vec4 v_color;\n\
89 void main()\n\
90 {\n\
91   gl_FragColor = v_color;\n\
92 }";
93 
94 static const gchar *texture_vert = "attribute vec4 a_position;\n\
95 attribute vec2 a_texcoord;\n\
96 uniform float xrot;\n\
97 varying vec2 v_texcoord;\n\
98 void main()\n\
99 {\n\
100    mat4 rotate_x = mat4 (\n\
101             1.0,        0.0,        0.0, 0.0,\n\
102             0.0,  cos(xrot),  sin(xrot), 0.0,\n\
103             0.0, -sin(xrot),  cos(xrot), 0.0,\n\
104             0.0,        0.0,        0.0, 1.0 );\n\
105    gl_Position = rotate_x * a_position;\n\
106    v_texcoord = a_texcoord;\n\
107 }";
108 
109 static const gchar *texture_frag = "#ifdef GL_ES\n\
110 precision mediump float;\n\
111 #endif\n\
112 varying vec2 v_texcoord;\n\
113 uniform sampler2D tex;\n\
114 void main()\n\
115 {\n\
116   gl_FragColor = texture2D(tex, v_texcoord);\n\
117 }";
118 
119 /* *INDENT-OFF* */
120 static const float texture_vertices[] = {
121 /*  X      Y      Z      S      T */
122    0.1f,  0.4f,  0.0f,  0.0f,  0.0f,
123    0.9f,  0.4f,  0.0f,  1.0f,  0.0f,
124    0.9f, -0.4f,  0.0f,  1.0f,  1.0f,
125    0.1f, -0.4f,  0.0f,  0.0f,  1.0f,
126 };
127 
128 static const float triangle_vertices[] = {
129 /*  X      Y      Z      R      G      B      A */
130    0.0f,  0.4f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f,
131    0.4f, -0.4f,  0.0f,  0.0f,  1.0f,  0.0f,  1.0f,
132   -0.4f, -0.4f,  0.0f,  0.0f,  0.0f,  1.0f,  1.0f
133 };
134 
135 static const GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
136 /* *INDENT-ON* */
137 
138 static guint32 sdl_message_event = -1;
139 static SDL_Window *sdl_window;
140 static SDL_GLContext sdl_gl_context;
141 
142 static GAsyncQueue *queue_input_buf;
143 static GAsyncQueue *queue_output_buf;
144 
145 /* rotation angle for the triangle. */
146 static float rtri = 0.0f;
147 
148 /* rotation angle for the quadrilateral. */
149 static float rquad = 0.0f;
150 
151 /* A general OpenGL initialization function.  Sets all of the initial parameters. */
152 static gboolean
InitGL(int width,int height)153 InitGL (int width, int height)  // We call this right after our OpenGL window is created.
154 {
155   const GstGLFuncs *gl = sdl_context->gl_vtable;
156   GError *error = NULL;
157   gboolean ret = TRUE;
158 
159   gl->Viewport (0, 0, width, height);
160   gl->ClearColor (0.0f, 0.0f, 0.0f, 0.0f);      // This Will Clear The Background Color To Black
161   gl->ClearDepth (1.0);         // Enables Clearing Of The Depth Buffer
162   gl->DepthFunc (GL_LESS);      // The Type Of Depth Test To Do
163   gl->Enable (GL_DEPTH_TEST);   // Enables Depth Testing
164 
165   /* setup the index buffer shared between the texture and triangle draw code */
166   gl->GenBuffers (1, &index_buffer);
167   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, index_buffer);
168   gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indices), indices,
169       GL_STATIC_DRAW);
170 
171   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
172 
173   /* setup texture shader */
174   texture_shader = gst_gl_shader_new_link_with_stages (sdl_context, &error,
175       gst_glsl_stage_new_with_string (sdl_context, GL_VERTEX_SHADER,
176           GST_GLSL_VERSION_NONE,
177           GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, texture_vert),
178       gst_glsl_stage_new_with_string (sdl_context, GL_FRAGMENT_SHADER,
179           GST_GLSL_VERSION_NONE,
180           GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, texture_frag),
181       NULL);
182   if (!texture_shader) {
183     g_warning ("Failed to compile and link shader: %s", error->message);
184     g_clear_error (&error);
185     ret = FALSE;
186     goto out;
187   }
188 
189   /* setup buffers for drawing the texture */
190   gl->GenVertexArrays (1, &texture_vao);
191   gl->BindVertexArray (texture_vao);
192 
193   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, index_buffer);
194 
195   gl->GenBuffers (1, &texture_vbo);
196   gl->BindBuffer (GL_ARRAY_BUFFER, texture_vbo);
197   gl->BufferData (GL_ARRAY_BUFFER, sizeof (texture_vertices), texture_vertices,
198       GL_STATIC_DRAW);
199 
200   texture_vertex_attr = gst_gl_shader_get_attribute_location (texture_shader,
201       "a_position");
202   gl->VertexAttribPointer (texture_vertex_attr, 3, GL_FLOAT, GL_FALSE,
203       5 * sizeof (float), (void *) 0);
204 
205   texture_texcoord_attr = gst_gl_shader_get_attribute_location (texture_shader,
206       "a_texcoord");
207   gl->VertexAttribPointer (texture_texcoord_attr, 2, GL_FLOAT, GL_FALSE,
208       5 * sizeof (float), (void *) (3 * sizeof (float)));
209 
210   gl->EnableVertexAttribArray (texture_vertex_attr);
211   gl->EnableVertexAttribArray (texture_texcoord_attr);
212 
213   gl->BindVertexArray (0);
214 
215   /* setup triangle shader */
216   triangle_shader = gst_gl_shader_new_link_with_stages (sdl_context, &error,
217       gst_glsl_stage_new_with_string (sdl_context, GL_VERTEX_SHADER,
218           GST_GLSL_VERSION_NONE,
219           GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, triangle_vert),
220       gst_glsl_stage_new_with_string (sdl_context, GL_FRAGMENT_SHADER,
221           GST_GLSL_VERSION_NONE,
222           GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, triangle_frag),
223       NULL);
224   if (!triangle_shader) {
225     g_warning ("Failed to compile and link shader: %s", error->message);
226     gst_clear_object (&texture_shader);
227     g_clear_error (&error);
228     ret = FALSE;
229     goto out;
230   }
231 
232   /* setup buffers for drawing the triangle */
233   gl->GenVertexArrays (1, &triangle_vao);
234   gl->BindVertexArray (triangle_vao);
235 
236   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, index_buffer);
237 
238   gl->GenBuffers (1, &triangle_vbo);
239   gl->BindBuffer (GL_ARRAY_BUFFER, triangle_vbo);
240   gl->BufferData (GL_ARRAY_BUFFER, sizeof (triangle_vertices),
241       triangle_vertices, GL_STATIC_DRAW);
242 
243   /* reuse the index buffer */
244 
245   triangle_vertex_attr = gst_gl_shader_get_attribute_location (triangle_shader,
246       "a_position");
247   gl->VertexAttribPointer (triangle_vertex_attr, 3, GL_FLOAT, GL_FALSE,
248       7 * sizeof (float), (void *) 0);
249 
250   triangle_color_attr = gst_gl_shader_get_attribute_location (triangle_shader,
251       "a_color");
252   gl->VertexAttribPointer (triangle_color_attr, 4, GL_FLOAT, GL_FALSE,
253       7 * sizeof (float), (void *) (3 * sizeof (float)));
254 
255   gl->EnableVertexAttribArray (triangle_vertex_attr);
256   gl->EnableVertexAttribArray (triangle_color_attr);
257 
258   gl->BindVertexArray (0);
259 
260 out:
261   return ret;
262 }
263 
264 static void
DeinitGL(void)265 DeinitGL (void)
266 {
267   const GstGLFuncs *gl = sdl_context->gl_vtable;
268 
269   gst_gl_context_activate (sdl_context, TRUE);
270 
271   gst_clear_object (&texture_shader);
272   gst_clear_object (&triangle_shader);
273 
274   gst_gl_context_activate (sdl_context, FALSE);
275 
276   gl->DeleteBuffers (1, &triangle_vbo);
277   gl->DeleteBuffers (1, &texture_vbo);
278   gl->DeleteBuffers (1, &index_buffer);
279 
280   gl->DeleteVertexArrays (1, &triangle_vao);
281   gl->DeleteVertexArrays (1, &texture_vao);
282 }
283 
284 /* The main drawing function. */
285 static void
DrawGLScene(GstVideoFrame * vframe)286 DrawGLScene (GstVideoFrame * vframe)
287 {
288   const GstGLFuncs *gl = sdl_context->gl_vtable;
289   guint texture;
290 
291   texture = *(guint *) vframe->data[0];
292 
293   gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);        // Clear The Screen And The Depth Buffer
294 
295   /* draw the triangle */
296   gl->BindVertexArray (triangle_vao);
297 
298   gst_gl_shader_use (triangle_shader);
299   gst_gl_shader_set_uniform_1f (triangle_shader, "yrot", rtri);
300 
301   gl->DrawElements (GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0);
302 
303   /* draw the textured quad */
304   gl->BindVertexArray (texture_vao);
305   gl->ActiveTexture (GL_TEXTURE0);
306   gl->BindTexture (GL_TEXTURE_2D, texture);
307 
308   gst_gl_shader_use (texture_shader);
309   gst_gl_shader_set_uniform_1i (texture_shader, "tex", 0);
310   gst_gl_shader_set_uniform_1f (texture_shader, "xrot", rquad);
311 
312   gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
313 
314   /* reset GL state we have changed to the default */
315   gl->BindTexture (GL_TEXTURE_2D, 0);
316   gl->BindVertexArray (0);
317   gst_gl_context_clear_shader (sdl_context);
318 
319   rtri += 1.0f * G_PI / 360.0;  // Increase The Rotation Variable For The Triangle
320   rquad -= 1.0f * G_PI / 360.0; // Decrease The Rotation Variable For The Quad
321 
322   // swap buffers to display, since we're double buffered.
323   SDL_GL_SwapWindow (sdl_window);
324 }
325 
326 /* appsink new-sample callback */
327 static GstFlowReturn
on_new_sample(GstElement * appsink,gpointer data)328 on_new_sample (GstElement * appsink, gpointer data)
329 {
330   GstSample *sample = NULL;
331   GstBuffer *buf;
332   GstVideoFrame *vframe;
333   GstGLSyncMeta *sync_meta;
334 
335   g_signal_emit_by_name (appsink, "pull-sample", &sample, NULL);
336   if (!sample)
337     return GST_FLOW_FLUSHING;
338 
339   buf = gst_buffer_ref (gst_sample_get_buffer (sample));
340   gst_sample_unref (sample);
341 
342   if (!gst_context) {
343     GstMemory *mem = gst_buffer_peek_memory (buf, 0);
344     gst_context = gst_object_ref (((GstGLBaseMemory *) mem)->context);
345   }
346 
347   sync_meta = gst_buffer_get_gl_sync_meta (buf);
348   if (!sync_meta) {
349     buf = gst_buffer_make_writable (buf);
350     sync_meta = gst_buffer_add_gl_sync_meta (gst_context, buf);
351   }
352   gst_gl_sync_meta_set_sync_point (sync_meta, gst_context);
353 
354   vframe = g_new0 (GstVideoFrame, 1);
355   if (!gst_video_frame_map (vframe, &render_video_info, buf,
356           GST_MAP_READ | GST_MAP_GL)) {
357     g_warning ("Failed to map the video buffer");
358     gst_buffer_unref (buf);
359     return GST_FLOW_ERROR;
360   }
361   // Another reference is owned by the video frame now and we can
362   // get rid of our own
363   gst_buffer_unref (buf);
364   g_async_queue_push (queue_input_buf, vframe);
365 
366   /* pop then unref buffer we have finished to use in sdl */
367   if (g_async_queue_length (queue_output_buf) > 3) {
368     vframe = (GstVideoFrame *) g_async_queue_pop (queue_output_buf);
369     gst_video_frame_unmap (vframe);
370     g_free (vframe);
371   }
372 
373   return GST_FLOW_OK;
374 }
375 
376 static void
sync_bus_call(GstBus * bus,GstMessage * msg,gpointer data)377 sync_bus_call (GstBus * bus, GstMessage * msg, gpointer data)
378 {
379   switch (GST_MESSAGE_TYPE (msg)) {
380     case GST_MESSAGE_NEED_CONTEXT:
381     {
382       const gchar *context_type;
383 
384       gst_message_parse_context_type (msg, &context_type);
385       g_print ("got need context %s\n", context_type);
386 
387       if (g_strcmp0 (context_type, GST_GL_DISPLAY_CONTEXT_TYPE) == 0) {
388         GstContext *display_context =
389             gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
390         gst_context_set_gl_display (display_context, sdl_gl_display);
391         gst_element_set_context (GST_ELEMENT (msg->src), display_context);
392         gst_context_unref (display_context);
393       } else if (g_strcmp0 (context_type, "gst.gl.app_context") == 0) {
394         GstContext *app_context = gst_context_new ("gst.gl.app_context", TRUE);
395         GstStructure *s = gst_context_writable_structure (app_context);
396         gst_structure_set (s, "context", GST_TYPE_GL_CONTEXT, sdl_context,
397             NULL);
398         gst_element_set_context (GST_ELEMENT (msg->src), app_context);
399         gst_context_unref (app_context);
400       }
401       break;
402     }
403     default:
404     {
405       SDL_Event event = { 0, };
406 
407       event.type = sdl_message_event;
408       SDL_PushEvent (&event);
409 
410       break;
411     }
412   }
413 }
414 
415 static void
sdl_event_loop(GstBus * bus)416 sdl_event_loop (GstBus * bus)
417 {
418   GstVideoFrame *vframe = NULL;
419   gboolean quit = FALSE;
420 
421   SDL_GL_MakeCurrent (sdl_window, sdl_gl_context);
422   SDL_GL_SetSwapInterval (1);
423 
424   if (!InitGL (640, 480))
425     return;
426 
427   while (!quit) {
428     SDL_Event event;
429 
430     while (SDL_PollEvent (&event)) {
431       if (event.type == SDL_QUIT) {
432         quit = TRUE;
433       }
434       if (event.type == SDL_KEYDOWN) {
435         if (event.key.keysym.sym == SDLK_ESCAPE) {
436           quit = TRUE;
437         }
438       }
439 
440       if (event.type == sdl_message_event) {
441         GstMessage *msg;
442 
443         while ((msg = gst_bus_pop (bus))) {
444           switch (GST_MESSAGE_TYPE (msg)) {
445 
446             case GST_MESSAGE_EOS:
447               g_print ("End-of-stream\n");
448               g_print
449                   ("For more information, try to run: GST_DEBUG=gl*:3 ./sdlshare\n");
450               quit = TRUE;
451               break;
452 
453             case GST_MESSAGE_ERROR:
454             {
455               gchar *debug = NULL;
456               GError *err = NULL;
457 
458               gst_message_parse_error (msg, &err, &debug);
459 
460               g_print ("Error: %s\n", err->message);
461               g_error_free (err);
462 
463               if (debug) {
464                 g_print ("Debug deails: %s\n", debug);
465                 g_free (debug);
466               }
467 
468               quit = TRUE;
469               break;
470             }
471 
472             default:
473               break;
474           }
475 
476           gst_message_unref (msg);
477         }
478       }
479     }
480 
481     if (g_async_queue_length (queue_input_buf) > 3) {
482       GstGLSyncMeta *sync_meta;
483 
484       if (vframe) {
485         g_async_queue_push (queue_output_buf, vframe);
486         vframe = NULL;
487       }
488 
489       while (g_async_queue_length (queue_input_buf) > 3) {
490         if (vframe) {
491           gst_video_frame_unmap (vframe);
492           g_free (vframe);
493         }
494         vframe = (GstVideoFrame *) g_async_queue_pop (queue_input_buf);
495       }
496 
497       sync_meta = gst_buffer_get_gl_sync_meta (vframe->buffer);
498       if (sync_meta)
499         gst_gl_sync_meta_wait (sync_meta, sdl_context);
500     }
501 
502     if (vframe)
503       DrawGLScene (vframe);
504   }
505 
506   SDL_GL_MakeCurrent (sdl_window, NULL);
507 
508   if (vframe)
509     g_async_queue_push (queue_output_buf, vframe);
510 
511   DeinitGL ();
512 }
513 
514 int
main(int argc,char ** argv)515 main (int argc, char **argv)
516 {
517 
518 #ifdef WIN32
519   HGLRC gl_context = 0;
520   HDC sdl_dc = 0;
521 #else
522   SDL_SysWMinfo info;
523   Display *sdl_display = NULL;
524   GLXContext gl_context = NULL;
525 #endif
526 
527   GstBus *bus = NULL;
528   GstCaps *caps;
529   GstElement *appsink;
530   GstGLPlatform gl_platform;
531   GError *err = NULL;
532   GstGLAPI gl_api;
533 
534   /* Initialize SDL for video output */
535   if (SDL_Init (SDL_INIT_VIDEO) < 0) {
536     fprintf (stderr, "Unable to initialize SDL: %s\n", SDL_GetError ());
537     return -1;
538   }
539 
540   gst_init (&argc, &argv);
541 
542   SDL_GL_SetAttribute (SDL_GL_CONTEXT_PROFILE_MASK,
543       SDL_GL_CONTEXT_PROFILE_CORE);
544   SDL_GL_SetAttribute (SDL_GL_CONTEXT_MAJOR_VERSION, 3);
545   SDL_GL_SetAttribute (SDL_GL_CONTEXT_MINOR_VERSION, 2);
546 
547   sdl_message_event = SDL_RegisterEvents (1);
548   g_assert (sdl_message_event != -1);
549 
550   /* Create a 640x480 OpenGL window */
551   sdl_window =
552       SDL_CreateWindow ("SDL and gst-plugins-gl", SDL_WINDOWPOS_UNDEFINED,
553       SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_OPENGL);
554   if (sdl_window == NULL) {
555     fprintf (stderr, "Unable to create OpenGL screen: %s\n", SDL_GetError ());
556     SDL_Quit ();
557     return -1;
558   }
559 
560   /* Create GL context and a wrapped GStreamer context around it */
561   sdl_gl_context = SDL_GL_CreateContext (sdl_window);
562 
563   SDL_GL_MakeCurrent (sdl_window, sdl_gl_context);
564 
565 #ifdef WIN32
566   gl_context = wglGetCurrentContext ();
567   sdl_dc = wglGetCurrentDC ();
568   gl_platform = GST_GL_PLATFORM_WGL;
569   sdl_gl_display = gst_gl_display_new ();
570 #else
571   SDL_VERSION (&info.version);
572   SDL_GetWindowWMInfo (sdl_window, &info);
573   sdl_display = info.info.x11.display;
574   gl_context = glXGetCurrentContext ();
575   gl_platform = GST_GL_PLATFORM_GLX;
576   sdl_gl_display =
577       (GstGLDisplay *) gst_gl_display_x11_new_with_display (sdl_display);
578 #endif
579 
580   gl_api = gst_gl_context_get_current_gl_api (gl_platform, NULL, NULL);
581 
582   sdl_context =
583       gst_gl_context_new_wrapped (sdl_gl_display, (guintptr) gl_context,
584       gl_platform, gl_api);
585 
586   gst_gl_context_activate (sdl_context, TRUE);
587 
588   if (!gst_gl_context_fill_info (sdl_context, &err)) {
589     fprintf (stderr, "Failed to fill in wrapped GStreamer context: %s\n",
590         err->message);
591     g_clear_error (&err);
592     SDL_Quit ();
593     return -1;
594   }
595   SDL_GL_MakeCurrent (sdl_window, NULL);
596 
597   pipeline =
598       GST_PIPELINE (gst_parse_launch
599       ("videotestsrc ! glupload name=upload ! gleffects effect=5 ! "
600           "appsink name=sink", NULL));
601 
602   bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
603   gst_bus_enable_sync_message_emission (bus);
604   g_signal_connect (bus, "sync-message", G_CALLBACK (sync_bus_call), NULL);
605 
606   queue_input_buf = g_async_queue_new ();
607   queue_output_buf = g_async_queue_new ();
608 
609   appsink = gst_bin_get_by_name (GST_BIN (pipeline), "sink");
610 
611   caps = gst_static_caps_get (&render_caps);
612   if (!gst_video_info_from_caps (&render_video_info, caps))
613     g_assert_not_reached ();
614 
615   g_object_set (appsink, "emit-signals", TRUE, "sync", TRUE, "caps", caps,
616       NULL);
617   g_signal_connect (appsink, "new-sample", G_CALLBACK (on_new_sample), NULL);
618   gst_object_unref (appsink);
619   gst_caps_unref (caps);
620 
621   gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
622 
623   sdl_event_loop (bus);
624 
625   gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
626   gst_object_unref (pipeline);
627 
628   gst_object_unref (bus);
629 
630   gst_gl_context_activate (sdl_context, FALSE);
631   gst_object_unref (sdl_context);
632   gst_object_unref (sdl_gl_display);
633   if (gst_context)
634     gst_object_unref (gst_context);
635 
636   /* make sure there is no pending gst gl buffer in the communication queues
637    * between sdl and gst-gl
638    */
639   while (g_async_queue_length (queue_input_buf) > 0) {
640     GstVideoFrame *vframe =
641         (GstVideoFrame *) g_async_queue_pop (queue_input_buf);
642     gst_video_frame_unmap (vframe);
643     g_free (vframe);
644   }
645 
646   while (g_async_queue_length (queue_output_buf) > 0) {
647     GstVideoFrame *vframe =
648         (GstVideoFrame *) g_async_queue_pop (queue_output_buf);
649     gst_video_frame_unmap (vframe);
650     g_free (vframe);
651   }
652 
653   SDL_GL_DeleteContext (gl_context);
654 
655   SDL_DestroyWindow (sdl_window);
656 
657   SDL_Quit ();
658 
659   return 0;
660 }
661