Merge branch 'move_subdir_rtsp-server' into tizen_gst_1.19.2_mono
[platform/upstream/gstreamer.git] / subprojects / gst-omx / examples / egl / testegl.c
1 /*
2 Copyright (c) 2012, Broadcom Europe Ltd
3 Copyright (c) 2012, OtherCrashOverride
4 Copyright (C) 2013, Fluendo S.A.
5    @author: Josep Torra <josep@fluendo.com>
6 Copyright (C) 2013, Video Experts Group LLC.
7    @author: Ilya Smelykh <ilya@videoexpertsgroup.com>
8 Copyright (C) 2014 Julien Isorce <julien.isorce@collabora.co.uk>
9 All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions are met:
13     * Redistributions of source code must retain the above copyright
14       notice, this list of conditions and the following disclaimer.
15     * Redistributions in binary form must reproduce the above copyright
16       notice, this list of conditions and the following disclaimer in the
17       documentation and/or other materials provided with the distribution.
18     * Neither the name of the copyright holder nor the
19       names of its contributors may be used to endorse or promote products
20       derived from this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
26 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /* A rotating cube rendered with OpenGL|ES and video played using GStreamer on
35  * the cube faces.
36  */
37
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41
42 #if defined (USE_OMX_TARGET_RPI) && defined (__GNUC__)
43 #ifndef __VCCOREVER__
44 #define __VCCOREVER__ 0x04000000
45 #endif
46 #pragma GCC diagnostic push
47 #pragma GCC diagnostic ignored "-Wredundant-decls"
48 #pragma GCC optimize ("gnu89-inline")
49 #endif
50
51 #include <EGL/egl.h>
52 #include <GLES2/gl2.h>
53
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <math.h>
58 #include <assert.h>
59 #include <unistd.h>
60
61 #include <gst/gst.h>
62
63 #define GST_USE_UNSTABLE_API
64 #include <gst/gl/gl.h>
65 #include <gst/gl/egl/gstgldisplay_egl.h>
66
67 #if defined (USE_OMX_TARGET_RPI)
68 #include <bcm_host.h>
69 #include <EGL/eglext.h>
70 #elif defined(HAVE_X11)
71 #include <X11/Xlib.h>
72 #include <X11/Xutil.h>
73 #endif
74
75 #if defined (USE_OMX_TARGET_RPI) && defined (__GNUC__)
76 #pragma GCC reset_options
77 #pragma GCC diagnostic pop
78 #endif
79
80 #include "cube_texture_and_coords.h"
81
82 #ifndef M_PI
83 #define M_PI 3.141592654
84 #endif
85
86 #define SYNC_BUFFERS TRUE
87
88 #define TRACE_VC_MEMORY_ENABLED 0
89
90 #if defined (USE_OMX_TARGET_RPI) && TRACE_VC_MEMORY_ENABLED
91 #define TRACE_VC_MEMORY(str)                 \
92   fprintf (stderr, "\n\n" str "\n");         \
93   system ("vcdbg reloc >&2")
94
95 #define TRACE_VC_MEMORY_DEFINE_ID(id)        \
96   static int id = 0
97
98 #define TRACE_VC_MEMORY_RESET_ID(id)         \
99   G_STMT_START {                             \
100     id = 0;                                  \
101   } G_STMT_END
102
103 #define TRACE_VC_MEMORY_ONCE_FOR_ID(str,id)  \
104   G_STMT_START {                             \
105     if (id == 0) {                           \
106       fprintf (stderr, "\n\n" str "\n");     \
107       system ("vcdbg reloc >&2");            \
108       id = 1;                                \
109     }                                        \
110   } G_STMT_END
111
112 #define TRACE_VC_MEMORY_ONCE(str,id)         \
113   G_STMT_START {                             \
114     static int id = 0;                       \
115     if (id == 0) {                           \
116       fprintf (stderr, "\n\n" str "\n");     \
117       system ("vcdbg reloc >&2");            \
118       id = 1;                                \
119     }                                        \
120   } G_STMT_END
121
122 #else
123 #define TRACE_VC_MEMORY(str) while(0)
124 #define TRACE_VC_MEMORY_DEFINE_ID(id)
125 #define TRACE_VC_MEMORY_RESET_ID(id) while(0)
126 #define TRACE_VC_MEMORY_ONCE_FOR_ID(str,id) while(0)
127 #define TRACE_VC_MEMORY_ONCE(str,id) while(0)
128 #endif
129
130 /* some helpers that we should provide in libgstgl */
131
132 typedef struct
133 {
134   GLfloat m[4][4];
135 } GstGLMatrix;
136
137 static void
138 gst_gl_matrix_load_identity (GstGLMatrix * matrix)
139 {
140   memset (matrix, 0x0, sizeof (GstGLMatrix));
141   matrix->m[0][0] = 1.0f;
142   matrix->m[1][1] = 1.0f;
143   matrix->m[2][2] = 1.0f;
144   matrix->m[3][3] = 1.0f;
145 }
146
147 static void
148 gst_gl_matrix_multiply (GstGLMatrix * matrix, GstGLMatrix * srcA,
149     GstGLMatrix * srcB)
150 {
151   GstGLMatrix tmp;
152   int i;
153
154   for (i = 0; i < 4; i++) {
155     tmp.m[i][0] = (srcA->m[i][0] * srcB->m[0][0]) +
156         (srcA->m[i][1] * srcB->m[1][0]) +
157         (srcA->m[i][2] * srcB->m[2][0]) + (srcA->m[i][3] * srcB->m[3][0]);
158
159     tmp.m[i][1] = (srcA->m[i][0] * srcB->m[0][1]) +
160         (srcA->m[i][1] * srcB->m[1][1]) +
161         (srcA->m[i][2] * srcB->m[2][1]) + (srcA->m[i][3] * srcB->m[3][1]);
162
163     tmp.m[i][2] = (srcA->m[i][0] * srcB->m[0][2]) +
164         (srcA->m[i][1] * srcB->m[1][2]) +
165         (srcA->m[i][2] * srcB->m[2][2]) + (srcA->m[i][3] * srcB->m[3][2]);
166
167     tmp.m[i][3] = (srcA->m[i][0] * srcB->m[0][3]) +
168         (srcA->m[i][1] * srcB->m[1][3]) +
169         (srcA->m[i][2] * srcB->m[2][3]) + (srcA->m[i][3] * srcB->m[3][3]);
170   }
171
172   memcpy (matrix, &tmp, sizeof (GstGLMatrix));
173 }
174
175 static void
176 gst_gl_matrix_translate (GstGLMatrix * matrix, GLfloat tx, GLfloat ty,
177     GLfloat tz)
178 {
179   matrix->m[3][0] +=
180       (matrix->m[0][0] * tx + matrix->m[1][0] * ty + matrix->m[2][0] * tz);
181   matrix->m[3][1] +=
182       (matrix->m[0][1] * tx + matrix->m[1][1] * ty + matrix->m[2][1] * tz);
183   matrix->m[3][2] +=
184       (matrix->m[0][2] * tx + matrix->m[1][2] * ty + matrix->m[2][2] * tz);
185   matrix->m[3][3] +=
186       (matrix->m[0][3] * tx + matrix->m[1][3] * ty + matrix->m[2][3] * tz);
187 }
188
189 static void
190 gst_gl_matrix_frustum (GstGLMatrix * matrix, GLfloat left, GLfloat right,
191     GLfloat bottom, GLfloat top, GLfloat nearZ, GLfloat farZ)
192 {
193   GLfloat deltaX = right - left;
194   GLfloat deltaY = top - bottom;
195   GLfloat deltaZ = farZ - nearZ;
196   GstGLMatrix frust;
197
198   if ((nearZ <= 0.0f) || (farZ <= 0.0f) ||
199       (deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f))
200     return;
201
202   frust.m[0][0] = 2.0f * nearZ / deltaX;
203   frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;
204
205   frust.m[1][1] = 2.0f * nearZ / deltaY;
206   frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;
207
208   frust.m[2][0] = (right + left) / deltaX;
209   frust.m[2][1] = (top + bottom) / deltaY;
210   frust.m[2][2] = -(nearZ + farZ) / deltaZ;
211   frust.m[2][3] = -1.0f;
212
213   frust.m[3][2] = -2.0f * nearZ * farZ / deltaZ;
214   frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f;
215
216   gst_gl_matrix_multiply (matrix, &frust, matrix);
217 }
218
219 static void
220 gst_gl_matrix_perspective (GstGLMatrix * matrix, GLfloat fovy, GLfloat aspect,
221     GLfloat nearZ, GLfloat farZ)
222 {
223   GLfloat frustumW, frustumH;
224
225   frustumH = tanf (fovy / 360.0f * M_PI) * nearZ;
226   frustumW = frustumH * aspect;
227
228   gst_gl_matrix_frustum (matrix, -frustumW, frustumW, -frustumH, frustumH,
229       nearZ, farZ);
230 }
231
232 /* *INDENT-OFF* */
233
234 /* vertex source */
235 static const gchar *cube_v_src =
236     "attribute vec4 a_position;                          \n"
237     "attribute vec2 a_texCoord;                          \n"
238     "uniform float u_rotx;                               \n"
239     "uniform float u_roty;                               \n"
240     "uniform float u_rotz;                               \n"
241     "uniform mat4 u_modelview;                           \n"
242     "uniform mat4 u_projection;                          \n"
243     "varying vec2 v_texCoord;                            \n"
244     "void main()                                         \n"
245     "{                                                   \n"
246     "   float PI = 3.14159265;                           \n"
247     "   float xrot = u_rotx*2.0*PI/360.0;                \n"
248     "   float yrot = u_roty*2.0*PI/360.0;                \n"
249     "   float zrot = u_rotz*2.0*PI/360.0;                \n"
250     "   mat4 matX = mat4 (                               \n"
251     "            1.0,        0.0,        0.0, 0.0,       \n"
252     "            0.0,  cos(xrot),  sin(xrot), 0.0,       \n"
253     "            0.0, -sin(xrot),  cos(xrot), 0.0,       \n"
254     "            0.0,        0.0,        0.0, 1.0 );     \n"
255     "   mat4 matY = mat4 (                               \n"
256     "      cos(yrot),        0.0, -sin(yrot), 0.0,       \n"
257     "            0.0,        1.0,        0.0, 0.0,       \n"
258     "      sin(yrot),        0.0,  cos(yrot), 0.0,       \n"
259     "            0.0,        0.0,       0.0,  1.0 );     \n"
260     "   mat4 matZ = mat4 (                               \n"
261     "      cos(zrot),  sin(zrot),        0.0, 0.0,       \n"
262     "     -sin(zrot),  cos(zrot),        0.0, 0.0,       \n"
263     "            0.0,        0.0,        1.0, 0.0,       \n"
264     "            0.0,        0.0,        0.0, 1.0 );     \n"
265     "   gl_Position = u_projection * u_modelview * matZ * matY * matX * a_position;\n"
266     "   v_texCoord = a_texCoord;                         \n"
267     "}                                                   \n";
268
269 /* fragment source */
270 static const gchar *cube_f_src =
271     "precision mediump float;                            \n"
272     "varying vec2 v_texCoord;                            \n"
273     "uniform sampler2D s_texture;                        \n"
274     "void main()                                         \n"
275     "{                                                   \n"
276     "  gl_FragColor = texture2D (s_texture, v_texCoord); \n"
277     "}                                                   \n";
278 /* *INDENT-ON* */
279
280 typedef struct
281 {
282 #if defined (USE_OMX_TARGET_RPI)
283   DISPMANX_DISPLAY_HANDLE_T dispman_display;
284   DISPMANX_ELEMENT_HANDLE_T dispman_element;
285 #endif
286
287   uint32_t screen_width;
288   uint32_t screen_height;
289   gboolean animate;
290
291   GstCaps *caps;
292
293   /* OpenGL|ES objects */
294   EGLDisplay display;
295   EGLSurface surface;
296   EGLContext context;
297   GLuint tex;
298
299   GLint vshader;
300   GLint fshader;
301   GLint program;
302
303   GLint u_modelviewmatrix;
304   GLint u_projectionmatrix;
305   GLint s_texture;
306   GLint u_rotx;
307   GLint u_roty;
308   GLint u_rotz;
309
310   GstGLMatrix modelview;
311   GstGLMatrix projection;
312   GLfloat fov;
313   GLfloat aspect;
314
315   /* model rotation vector and direction */
316   GLfloat rot_angle_x_inc;
317   GLfloat rot_angle_y_inc;
318   GLfloat rot_angle_z_inc;
319
320   /* current model rotation angles */
321   GLfloat rot_angle_x;
322   GLfloat rot_angle_y;
323   GLfloat rot_angle_z;
324
325   /* current distance from camera */
326   GLfloat distance;
327   GLfloat distance_inc;
328
329   /* GStreamer related resources */
330   GstElement *pipeline;
331   GstElement *vsink;
332   GstGLDisplayEGL *gst_display;
333   GstGLContext *gl_context;
334   gboolean can_avoid_upload;
335
336   /* Interthread comunication */
337   GAsyncQueue *queue;
338   GMutex queue_lock;
339   GCond cond;
340   gboolean flushing;
341   GstMiniObject *popped_obj;
342   GstBuffer *current_buffer;
343
344   /* GLib mainloop */
345   GMainLoop *main_loop;
346   GstBuffer *last_buffer;
347
348   /* Rendering thread state */
349   gboolean running;
350
351   /* number of rendered and dropped frames */
352   guint64 rendered;
353   guint64 dropped;
354
355 #if !defined (USE_OMX_TARGET_RPI) && defined(HAVE_X11)
356   Display *xdisplay;
357   Window xwindow;
358 #endif
359 } APP_STATE_T;
360
361 static void init_ogl (APP_STATE_T * state);
362 static void init_model_proj (APP_STATE_T * state);
363 static void reset_model (APP_STATE_T * state);
364 static GLfloat inc_and_wrap_angle (GLfloat angle, GLfloat angle_inc);
365 static void redraw_scene (APP_STATE_T * state);
366 static void update_model (APP_STATE_T * state);
367 static void init_textures (APP_STATE_T * state, GstBuffer * buffer);
368 static APP_STATE_T _state, *state = &_state;
369 static gboolean queue_object (APP_STATE_T * state, GstMiniObject * obj,
370     gboolean synchronous);
371
372 TRACE_VC_MEMORY_DEFINE_ID (gid0);
373 TRACE_VC_MEMORY_DEFINE_ID (gid1);
374 TRACE_VC_MEMORY_DEFINE_ID (gid2);
375
376 typedef enum
377 {
378   GST_PLAY_FLAG_VIDEO = (1 << 0),
379   GST_PLAY_FLAG_AUDIO = (1 << 1),
380   GST_PLAY_FLAG_TEXT = (1 << 2),
381   GST_PLAY_FLAG_VIS = (1 << 3),
382   GST_PLAY_FLAG_SOFT_VOLUME = (1 << 4),
383   GST_PLAY_FLAG_NATIVE_AUDIO = (1 << 5),
384   GST_PLAY_FLAG_NATIVE_VIDEO = (1 << 6),
385   GST_PLAY_FLAG_DOWNLOAD = (1 << 7),
386   GST_PLAY_FLAG_BUFFERING = (1 << 8),
387   GST_PLAY_FLAG_DEINTERLACE = (1 << 9),
388   GST_PLAY_FLAG_SOFT_COLORBALANCE = (1 << 10)
389 } GstPlayFlags;
390
391 /***********************************************************
392  * Name: init_ogl
393  *
394  * Arguments:
395  *       APP_STATE_T *state - holds OGLES model info
396  *
397  * Description: Sets the display, OpenGL|ES context and screen stuff
398  *
399  * Returns: void
400  *
401  ***********************************************************/
402 static void
403 init_ogl (APP_STATE_T * state)
404 {
405 #if defined (USE_OMX_TARGET_RPI)
406   int32_t success = 0;
407 #else
408   gint screen_num = 0;
409   gulong black_pixel = 0;
410 #endif
411   EGLBoolean result;
412   EGLint num_config;
413   EGLNativeWindowType window_handle = (EGLNativeWindowType) 0;
414
415 #if defined (USE_OMX_TARGET_RPI)
416   static EGL_DISPMANX_WINDOW_T nativewindow;
417
418   DISPMANX_UPDATE_HANDLE_T dispman_update;
419   VC_RECT_T dst_rect;
420   VC_RECT_T src_rect;
421
422   VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0 };
423 #endif
424
425   static const EGLint attribute_list[] = {
426     EGL_DEPTH_SIZE, 16,
427     EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
428     EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
429     EGL_NONE
430   };
431
432   static const EGLint context_attributes[] = {
433     EGL_CONTEXT_CLIENT_VERSION, 2,
434     EGL_NONE
435   };
436
437   EGLConfig config;
438
439   /* get an EGL display connection */
440   state->display = eglGetDisplay (EGL_DEFAULT_DISPLAY);
441   assert (state->display != EGL_NO_DISPLAY);
442
443   /* initialize the EGL display connection */
444   result = eglInitialize (state->display, NULL, NULL);
445   assert (EGL_FALSE != result);
446
447 #if defined (USE_OMX_TARGET_RPI)
448   /* get an appropriate EGL frame buffer configuration
449    * this uses a BRCM extension that gets the closest match, rather
450    * than standard which returns anything that matches. */
451   result =
452       eglSaneChooseConfigBRCM (state->display, attribute_list, &config, 1,
453       &num_config);
454   assert (EGL_FALSE != result);
455 #else
456   result =
457       eglChooseConfig (state->display, attribute_list, &config, 1, &num_config);
458 #endif
459
460   /* create an EGL rendering context */
461   state->context =
462       eglCreateContext (state->display, config, EGL_NO_CONTEXT,
463       context_attributes);
464   assert (state->context != EGL_NO_CONTEXT);
465
466 #if defined (USE_OMX_TARGET_RPI)
467   /* create an EGL window surface */
468   success = graphics_get_display_size (0 /* LCD */ , &state->screen_width,
469       &state->screen_height);
470   assert (success >= 0);
471
472   dst_rect.x = 0;
473   dst_rect.y = 0;
474   dst_rect.width = state->screen_width;
475   dst_rect.height = state->screen_height;
476
477   src_rect.x = 0;
478   src_rect.y = 0;
479   src_rect.width = state->screen_width << 16;
480   src_rect.height = state->screen_height << 16;
481
482   state->dispman_display = vc_dispmanx_display_open (0 /* LCD */ );
483   dispman_update = vc_dispmanx_update_start (0);
484
485   state->dispman_element =
486       vc_dispmanx_element_add (dispman_update, state->dispman_display,
487       0 /*layer */ , &dst_rect, 0 /*src */ ,
488       &src_rect, DISPMANX_PROTECTION_NONE, &alpha, 0 /*clamp */ ,
489       0 /*transform */ );
490
491   nativewindow.element = state->dispman_element;
492   nativewindow.width = state->screen_width;
493   nativewindow.height = state->screen_height;
494   vc_dispmanx_update_submit_sync (dispman_update);
495
496   window_handle = &nativewindow;
497 #elif defined(HAVE_X11)
498   state->screen_width = 1280;
499   state->screen_height = 720;
500   state->xdisplay = XOpenDisplay (NULL);
501   screen_num = DefaultScreen (state->xdisplay);
502   black_pixel = XBlackPixel (state->xdisplay, screen_num);
503   state->xwindow = XCreateSimpleWindow (state->xdisplay,
504       DefaultRootWindow (state->xdisplay), 0, 0, state->screen_width,
505       state->screen_height, 0, 0, black_pixel);
506   XSetWindowBackgroundPixmap (state->xdisplay, state->xwindow, None);
507   XMapRaised (state->xdisplay, state->xwindow);
508   XSync (state->xdisplay, FALSE);
509   window_handle = state->xwindow;
510 #endif
511
512   state->surface =
513       eglCreateWindowSurface (state->display, config, window_handle, NULL);
514   assert (state->surface != EGL_NO_SURFACE);
515
516   /* connect the context to the surface */
517   result =
518       eglMakeCurrent (state->display, state->surface, state->surface,
519       state->context);
520   assert (EGL_FALSE != result);
521
522   state->gst_display = gst_gl_display_egl_new_with_egl_display (state->display);
523   state->gl_context =
524       gst_gl_context_new_wrapped (GST_GL_DISPLAY (state->gst_display),
525       (guintptr) state->context, GST_GL_PLATFORM_EGL, GST_GL_API_GLES2);
526 }
527
528 /***********************************************************
529  * Name: init_model_proj
530  *
531  * Arguments:
532  *       APP_STATE_T *state - holds OGLES model info
533  *
534  * Description: Sets the OpenGL|ES model to default values
535  *
536  * Returns: void
537  *
538  ***********************************************************/
539 static void
540 init_model_proj (APP_STATE_T * state)
541 {
542   GLint ret = 0;
543
544   state->vshader = glCreateShader (GL_VERTEX_SHADER);
545
546   glShaderSource (state->vshader, 1, &cube_v_src, NULL);
547   glCompileShader (state->vshader);
548   assert (glGetError () == GL_NO_ERROR);
549
550   state->fshader = glCreateShader (GL_FRAGMENT_SHADER);
551
552   glShaderSource (state->fshader, 1, &cube_f_src, NULL);
553   glCompileShader (state->fshader);
554   assert (glGetError () == GL_NO_ERROR);
555
556   state->program = glCreateProgram ();
557
558   glAttachShader (state->program, state->vshader);
559   glAttachShader (state->program, state->fshader);
560
561   glBindAttribLocation (state->program, 0, "a_position");
562   glBindAttribLocation (state->program, 1, "a_texCoord");
563
564   glLinkProgram (state->program);
565
566   glGetProgramiv (state->program, GL_LINK_STATUS, &ret);
567   assert (ret == GL_TRUE);
568
569   glUseProgram (state->program);
570
571   state->u_rotx = glGetUniformLocation (state->program, "u_rotx");
572   state->u_roty = glGetUniformLocation (state->program, "u_roty");
573   state->u_rotz = glGetUniformLocation (state->program, "u_rotz");
574
575   state->u_modelviewmatrix =
576       glGetUniformLocation (state->program, "u_modelview");
577
578   state->u_projectionmatrix =
579       glGetUniformLocation (state->program, "u_projection");
580
581   state->s_texture = glGetUniformLocation (state->program, "s_texture");
582
583   glViewport (0, 0, (GLsizei) state->screen_width,
584       (GLsizei) state->screen_height);
585
586   state->fov = 45.0f;
587   state->distance = 5.0f;
588   state->aspect =
589       (GLfloat) state->screen_width / (GLfloat) state->screen_height;
590
591   gst_gl_matrix_load_identity (&state->projection);
592   gst_gl_matrix_perspective (&state->projection, state->fov, state->aspect,
593       1.0f, 100.0f);
594
595   gst_gl_matrix_load_identity (&state->modelview);
596   gst_gl_matrix_translate (&state->modelview, 0.0f, 0.0f, -state->distance);
597
598   reset_model (state);
599 }
600
601 /***********************************************************
602  * Name: reset_model
603  *
604  * Arguments:
605  *       APP_STATE_T *state - holds OGLES model info
606  *
607  * Description: Resets the Model projection and rotation direction
608  *
609  * Returns: void
610  *
611  ***********************************************************/
612 static void
613 reset_model (APP_STATE_T * state)
614 {
615   /* reset model rotation */
616   state->rot_angle_x = 45.f;
617   state->rot_angle_y = 30.f;
618   state->rot_angle_z = 0.f;
619   state->rot_angle_x_inc = 0.5f;
620   state->rot_angle_y_inc = 0.5f;
621   state->rot_angle_z_inc = 0.f;
622 }
623
624 /***********************************************************
625  * Name: update_model
626  *
627  * Arguments:
628  *       APP_STATE_T *state - holds OGLES model info
629  *
630  * Description: Updates model projection to current position/rotation
631  *
632  * Returns: void
633  *
634  ***********************************************************/
635 static void
636 update_model (APP_STATE_T * state)
637 {
638   if (state->animate) {
639     /* update position */
640     state->rot_angle_x =
641         inc_and_wrap_angle (state->rot_angle_x, state->rot_angle_x_inc);
642     state->rot_angle_y =
643         inc_and_wrap_angle (state->rot_angle_y, state->rot_angle_y_inc);
644     state->rot_angle_z =
645         inc_and_wrap_angle (state->rot_angle_z, state->rot_angle_z_inc);
646   }
647 }
648
649 /***********************************************************
650  * Name: inc_and_wrap_angle
651  *
652  * Arguments:
653  *       GLfloat angle     current angle
654  *       GLfloat angle_inc angle increment
655  *
656  * Description:   Increments or decrements angle by angle_inc degrees
657  *                Wraps to 0 at 360 deg.
658  *
659  * Returns: new value of angle
660  *
661  ***********************************************************/
662 static GLfloat
663 inc_and_wrap_angle (GLfloat angle, GLfloat angle_inc)
664 {
665   angle += angle_inc;
666
667   if (angle >= 360.0)
668     angle -= 360.f;
669   else if (angle <= 0)
670     angle += 360.f;
671
672   return angle;
673 }
674
675 /***********************************************************
676  * Name: redraw_scene
677  *
678  * Arguments:
679  *       APP_STATE_T *state - holds OGLES model info
680  *
681  * Description:   Draws the model and calls eglSwapBuffers
682  *                to render to screen
683  *
684  * Returns: void
685  *
686  ***********************************************************/
687 static void
688 redraw_scene (APP_STATE_T * state)
689 {
690   glBindFramebuffer (GL_FRAMEBUFFER, 0);
691
692   glEnable (GL_CULL_FACE);
693   glEnable (GL_DEPTH_TEST);
694
695   /* Set background color and clear buffers */
696   glClearColor (0.15f, 0.25f, 0.35f, 1.0f);
697   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
698
699   glUseProgram (state->program);
700
701   glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, quadx);
702   glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
703
704   glEnableVertexAttribArray (0);
705   glEnableVertexAttribArray (1);
706
707   glActiveTexture (GL_TEXTURE0);
708   glBindTexture (GL_TEXTURE_2D, state->tex);
709   glUniform1i (state->s_texture, 0);
710
711   glUniform1f (state->u_rotx, state->rot_angle_x);
712   glUniform1f (state->u_roty, state->rot_angle_y);
713   glUniform1f (state->u_rotz, state->rot_angle_z);
714
715   glUniformMatrix4fv (state->u_modelviewmatrix, 1, GL_FALSE,
716       &state->modelview.m[0][0]);
717
718   glUniformMatrix4fv (state->u_projectionmatrix, 1, GL_FALSE,
719       &state->projection.m[0][0]);
720
721   /* draw first 4 vertices */
722   glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
723   glDrawArrays (GL_TRIANGLE_STRIP, 4, 4);
724   glDrawArrays (GL_TRIANGLE_STRIP, 8, 4);
725   glDrawArrays (GL_TRIANGLE_STRIP, 12, 4);
726   glDrawArrays (GL_TRIANGLE_STRIP, 16, 4);
727   glDrawArrays (GL_TRIANGLE_STRIP, 20, 4);
728
729   if (!eglSwapBuffers (state->display, state->surface)) {
730     g_main_loop_quit (state->main_loop);
731     return;
732   }
733
734   glDisable (GL_DEPTH_TEST);
735   glDisable (GL_CULL_FACE);
736 }
737
738 /***********************************************************
739  * Name: init_textures
740  *
741  * Arguments:
742  *       APP_STATE_T *state - holds OGLES model info
743  *
744  * Description:   Initialise OGL|ES texture surfaces to use image
745  *                buffers
746  *
747  * Returns: void
748  *
749  ***********************************************************/
750 static void
751 init_textures (APP_STATE_T * state, GstBuffer * buffer)
752 {
753   GstCapsFeatures *feature = gst_caps_get_features (state->caps, 0);
754
755   if (gst_caps_features_contains (feature, "memory:GLMemory")) {
756     g_print ("Prepare texture for GLMemory\n");
757     state->can_avoid_upload = TRUE;
758     state->tex = 0;
759   } else if (gst_caps_features_contains (feature,
760           "meta:GstVideoGLTextureUploadMeta")) {
761     GstVideoMeta *meta = NULL;
762     guint internal_format =
763         gst_gl_sized_gl_format_from_gl_format_type (state->gl_context,
764         GL_RGBA, GL_UNSIGNED_BYTE);
765
766     g_print ("Prepare texture for GstVideoGLTextureUploadMeta\n");
767     meta = gst_buffer_get_video_meta (buffer);
768     state->can_avoid_upload = FALSE;
769     glGenTextures (1, &state->tex);
770     glBindTexture (GL_TEXTURE_2D, state->tex);
771     glTexImage2D (GL_TEXTURE_2D, 0, internal_format, meta->width, meta->height,
772         0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
773   } else {
774     g_assert_not_reached ();
775   }
776
777 #if 0
778   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
779   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
780 #else
781   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
782   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
783 #endif
784
785   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
786   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
787
788   assert (glGetError () == GL_NO_ERROR);
789 }
790
791 static void
792 render_scene (APP_STATE_T * state)
793 {
794   update_model (state);
795   redraw_scene (state);
796   TRACE_VC_MEMORY_ONCE_FOR_ID ("after render_scene", gid2);
797 }
798
799 static void
800 update_image (APP_STATE_T * state, GstBuffer * buffer)
801 {
802   GstVideoGLTextureUploadMeta *meta = NULL;
803
804   if (state->current_buffer) {
805     gst_buffer_unref (state->current_buffer);
806   } else {
807     /* Setup the model world */
808     init_model_proj (state);
809     TRACE_VC_MEMORY ("after init_model_proj");
810
811     /* initialize the OGLES texture(s) */
812     init_textures (state, buffer);
813     TRACE_VC_MEMORY ("after init_textures");
814   }
815   state->current_buffer = gst_buffer_ref (buffer);
816
817   TRACE_VC_MEMORY_ONCE_FOR_ID ("before GstVideoGLTextureUploadMeta", gid0);
818
819   if (state->can_avoid_upload) {
820     GstMemory *mem = gst_buffer_peek_memory (state->current_buffer, 0);
821     g_assert (gst_is_gl_memory (mem));
822     state->tex = ((GstGLMemory *) mem)->tex_id;
823   } else if ((meta = gst_buffer_get_video_gl_texture_upload_meta (buffer))) {
824     if (meta->n_textures == 1) {
825       guint ids[4] = { state->tex, 0, 0, 0 };
826       if (!gst_video_gl_texture_upload_meta_upload (meta, ids)) {
827         GST_WARNING ("failed to upload to texture");
828       }
829     }
830   }
831
832   TRACE_VC_MEMORY_ONCE_FOR_ID ("after GstVideoGLTextureUploadMeta", gid1);
833 }
834
835 static void
836 init_intercom (APP_STATE_T * state)
837 {
838   state->queue =
839       g_async_queue_new_full ((GDestroyNotify) gst_mini_object_unref);
840   g_mutex_init (&state->queue_lock);
841   g_cond_init (&state->cond);
842 }
843
844 static void
845 terminate_intercom (APP_STATE_T * state)
846 {
847   /* Release intercom */
848   if (state->queue) {
849     g_async_queue_unref (state->queue);
850   }
851
852   g_mutex_clear (&state->queue_lock);
853   g_cond_clear (&state->cond);
854 }
855
856 static void
857 flush_internal (APP_STATE_T * state)
858 {
859   if (state->current_buffer) {
860     gst_buffer_unref (state->current_buffer);
861   }
862   state->current_buffer = NULL;
863 }
864
865 static void
866 flush_start (APP_STATE_T * state)
867 {
868   GstMiniObject *object = NULL;
869
870   g_mutex_lock (&state->queue_lock);
871   state->flushing = TRUE;
872   g_cond_broadcast (&state->cond);
873   g_mutex_unlock (&state->queue_lock);
874
875   while ((object = g_async_queue_try_pop (state->queue))) {
876     gst_mini_object_unref (object);
877   }
878   g_mutex_lock (&state->queue_lock);
879   flush_internal (state);
880   state->popped_obj = NULL;
881   g_mutex_unlock (&state->queue_lock);
882 }
883
884 static void
885 flush_stop (APP_STATE_T * state)
886 {
887   GstMiniObject *object = NULL;
888
889   g_mutex_lock (&state->queue_lock);
890   while ((object = GST_MINI_OBJECT_CAST (g_async_queue_try_pop (state->queue)))) {
891     gst_mini_object_unref (object);
892   }
893   flush_internal (state);
894   state->popped_obj = NULL;
895   state->flushing = FALSE;
896   g_mutex_unlock (&state->queue_lock);
897 }
898
899 static void
900 pipeline_pause (APP_STATE_T * state)
901 {
902   gst_element_set_state (state->pipeline, GST_STATE_PAUSED);
903 }
904
905 static void
906 pipeline_play (APP_STATE_T * state)
907 {
908   gst_element_set_state (state->pipeline, GST_STATE_PLAYING);
909 }
910
911 static gint64
912 pipeline_get_position (APP_STATE_T * state)
913 {
914   gint64 position = -1;
915
916   if (state->pipeline) {
917     gst_element_query_position (state->vsink, GST_FORMAT_TIME, &position);
918   }
919
920   return position;
921 }
922
923 static gint64
924 pipeline_get_duration (APP_STATE_T * state)
925 {
926   gint64 duration = -1;
927
928   if (state->pipeline) {
929     gst_element_query_duration (state->pipeline, GST_FORMAT_TIME, &duration);
930   }
931
932   return duration;
933 }
934
935 static void
936 pipeline_seek (APP_STATE_T * state, gint64 position)
937 {
938   if (state->pipeline) {
939     GstEvent *event;
940     event = gst_event_new_seek (1.0,
941         GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
942         GST_SEEK_TYPE_SET, position, GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE);
943     if (!gst_element_send_event (state->vsink, event)) {
944       g_print ("seek failed\n");
945     }
946   }
947 }
948
949 static gboolean
950 handle_queued_objects (APP_STATE_T * state)
951 {
952   GstMiniObject *object = NULL;
953
954   g_mutex_lock (&state->queue_lock);
955   if (state->flushing) {
956     g_cond_broadcast (&state->cond);
957     goto beach;
958   } else if (g_async_queue_length (state->queue) == 0) {
959     goto beach;
960   }
961
962   if ((object = g_async_queue_try_pop (state->queue))) {
963     if (GST_IS_BUFFER (object)) {
964       GstBuffer *buffer = GST_BUFFER_CAST (object);
965       update_image (state, buffer);
966       render_scene (state);
967       gst_buffer_unref (buffer);
968       if (!SYNC_BUFFERS) {
969         object = NULL;
970       }
971     } else if (GST_IS_EVENT (object)) {
972       GstEvent *event = GST_EVENT_CAST (object);
973       g_print ("\nevent %p %s\n", event,
974           gst_event_type_get_name (GST_EVENT_TYPE (event)));
975
976       switch (GST_EVENT_TYPE (event)) {
977         case GST_EVENT_EOS:
978           flush_internal (state);
979           break;
980         default:
981           break;
982       }
983       gst_event_unref (event);
984       object = NULL;
985     }
986   }
987
988   if (object) {
989     state->popped_obj = object;
990     g_cond_broadcast (&state->cond);
991   }
992
993 beach:
994   g_mutex_unlock (&state->queue_lock);
995
996   return FALSE;
997 }
998
999 static gboolean
1000 queue_object (APP_STATE_T * state, GstMiniObject * obj, gboolean synchronous)
1001 {
1002   gboolean res = TRUE;
1003
1004   g_mutex_lock (&state->queue_lock);
1005   if (state->flushing) {
1006     gst_mini_object_unref (obj);
1007     res = FALSE;
1008     goto beach;
1009   }
1010
1011   g_async_queue_push (state->queue, obj);
1012
1013   if (synchronous) {
1014     /* Waiting for object to be handled */
1015     do {
1016       g_cond_wait (&state->cond, &state->queue_lock);
1017     } while (!state->flushing && state->popped_obj != obj);
1018   }
1019
1020 beach:
1021   g_mutex_unlock (&state->queue_lock);
1022   return res;
1023 }
1024
1025 static void
1026 preroll_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad,
1027     gpointer user_data)
1028 {
1029   APP_STATE_T *state = (APP_STATE_T *) user_data;
1030   queue_object (state, GST_MINI_OBJECT_CAST (gst_buffer_ref (buffer)), FALSE);
1031 }
1032
1033 static void
1034 buffers_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad,
1035     gpointer user_data)
1036 {
1037   APP_STATE_T *state = (APP_STATE_T *) user_data;
1038   queue_object (state, GST_MINI_OBJECT_CAST (gst_buffer_ref (buffer)),
1039       SYNC_BUFFERS);
1040 }
1041
1042 static GstPadProbeReturn
1043 events_cb (GstPad * pad, GstPadProbeInfo * probe_info, gpointer user_data)
1044 {
1045   APP_STATE_T *state = (APP_STATE_T *) user_data;
1046   GstEvent *event = GST_PAD_PROBE_INFO_EVENT (probe_info);
1047
1048   switch (GST_EVENT_TYPE (event)) {
1049     case GST_EVENT_CAPS:
1050     {
1051       if (state->caps) {
1052         gst_caps_unref (state->caps);
1053         state->caps = NULL;
1054       }
1055       gst_event_parse_caps (event, &state->caps);
1056       if (state->caps)
1057         gst_caps_ref (state->caps);
1058       break;
1059     }
1060     case GST_EVENT_FLUSH_START:
1061       flush_start (state);
1062       break;
1063     case GST_EVENT_FLUSH_STOP:
1064       flush_stop (state);
1065       break;
1066     case GST_EVENT_EOS:
1067       queue_object (state, GST_MINI_OBJECT_CAST (gst_event_ref (event)), FALSE);
1068       break;
1069     default:
1070       break;
1071   }
1072
1073   return GST_PAD_PROBE_OK;
1074 }
1075
1076 static GstPadProbeReturn
1077 query_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
1078 {
1079   APP_STATE_T *state = (APP_STATE_T *) user_data;
1080   GstQuery *query = GST_PAD_PROBE_INFO_QUERY (info);
1081
1082   switch (GST_QUERY_TYPE (query)) {
1083     case GST_QUERY_CONTEXT:
1084     {
1085       if (gst_gl_handle_context_query (state->pipeline, query,
1086               (GstGLDisplay *) state->gst_display, NULL,
1087               (GstGLContext *) state->gl_context))
1088         return GST_PAD_PROBE_HANDLED;
1089       break;
1090     }
1091     case GST_QUERY_DRAIN:
1092     {
1093       flush_internal (state);
1094       break;
1095     }
1096     default:
1097       break;
1098   }
1099
1100   return GST_PAD_PROBE_OK;
1101 }
1102
1103 static gboolean
1104 init_playbin_player (APP_STATE_T * state, const gchar * uri)
1105 {
1106   GstPad *pad = NULL;
1107   GstPad *ghostpad = NULL;
1108   GstElement *vbin = gst_bin_new ("vbin");
1109
1110   /* insert a gl filter so that the GstGLBufferPool
1111    * is managed automatically */
1112   GstElement *glfilter = gst_element_factory_make ("glupload", "glfilter");
1113   GstElement *capsfilter = gst_element_factory_make ("capsfilter", NULL);
1114   GstElement *vsink = gst_element_factory_make ("fakesink", "vsink");
1115
1116   g_object_set (capsfilter, "caps",
1117       gst_caps_from_string ("video/x-raw(memory:GLMemory), format=RGBA"), NULL);
1118   g_object_set (vsink, "sync", TRUE, "silent", TRUE, "qos", TRUE,
1119       "enable-last-sample", FALSE, "max-lateness", 20 * GST_MSECOND,
1120       "signal-handoffs", TRUE, NULL);
1121
1122   g_signal_connect (vsink, "preroll-handoff", G_CALLBACK (preroll_cb), state);
1123   g_signal_connect (vsink, "handoff", G_CALLBACK (buffers_cb), state);
1124
1125   gst_bin_add_many (GST_BIN (vbin), glfilter, capsfilter, vsink, NULL);
1126
1127   pad = gst_element_get_static_pad (glfilter, "sink");
1128   ghostpad = gst_ghost_pad_new ("sink", pad);
1129   gst_object_unref (pad);
1130   gst_element_add_pad (vbin, ghostpad);
1131
1132   pad = gst_element_get_static_pad (vsink, "sink");
1133   gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, events_cb, state,
1134       NULL);
1135   gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM, query_cb, state,
1136       NULL);
1137   gst_object_unref (pad);
1138
1139   gst_element_link (glfilter, capsfilter);
1140   gst_element_link (capsfilter, vsink);
1141
1142   /* Instantiate and configure playbin */
1143   state->pipeline = gst_element_factory_make ("playbin", "player");
1144   g_object_set (state->pipeline, "uri", uri,
1145       "video-sink", vbin, "flags",
1146       GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_AUDIO, NULL);
1147
1148   state->vsink = gst_object_ref (vsink);
1149   return TRUE;
1150 }
1151
1152 static gboolean
1153 init_parse_launch_player (APP_STATE_T * state, const gchar * spipeline)
1154 {
1155   GstElement *vsink;
1156   GError *error = NULL;
1157
1158   /* ex:
1159
1160      ./testegl "filesrc location=big_buck_bunny_720p_h264.mov ! qtdemux ! \
1161      h264parse !  omxh264dec ! glcolorscale ! fakesink name=vsink"
1162
1163      ./testegl "filesrc location=big_buck_bunny_720p_h264.mov ! qtdemux ! \
1164      h264parse ! omxh264dec ! glcolorscale ! \
1165      video/x-raw(memory:GLMemory) ! fakesink name=vsink"
1166
1167      ./testegl "filesrc location=big_buck_bunny_720p_h264.mov ! qtdemux ! \
1168      h264parse ! omxh264dec ! glcolorscale ! \
1169      video/x-raw(meta:GstVideoGLTextureUploadMeta) ! \
1170      fakesink name=vsink"
1171
1172    */
1173
1174   /* pipeline 1 and 2 are the same and the most efficient as glcolorscale
1175    * will enter in passthrough mode and testegl will just bind the eglimage
1176    * to a gl texture without any copy. */
1177
1178   state->pipeline = gst_parse_launch (spipeline, &error);
1179
1180   if (!state->pipeline) {
1181     g_printerr ("Unable to instatiate pipeline '%s': %s\n",
1182         spipeline, error->message);
1183     return FALSE;
1184   }
1185
1186   vsink = gst_bin_get_by_name (GST_BIN (state->pipeline), "vsink");
1187
1188   if (!vsink) {
1189     g_printerr ("Unable to find a fakesink named 'vsink'");
1190     return FALSE;
1191   }
1192
1193   g_object_set (vsink, "sync", TRUE, "silent", TRUE, "qos", TRUE,
1194       "enable-last-sample", FALSE,
1195       "max-lateness", 20 * GST_MSECOND, "signal-handoffs", TRUE, NULL);
1196
1197   g_signal_connect (vsink, "preroll-handoff", G_CALLBACK (preroll_cb), state);
1198   g_signal_connect (vsink, "handoff", G_CALLBACK (buffers_cb), state);
1199
1200   gst_pad_add_probe (gst_element_get_static_pad (vsink, "sink"),
1201       GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, events_cb, state, NULL);
1202   gst_pad_add_probe (gst_element_get_static_pad (vsink, "sink"),
1203       GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM, query_cb, state, NULL);
1204
1205   state->vsink = gst_object_ref (vsink);
1206   return TRUE;
1207 }
1208
1209 //------------------------------------------------------------------------------
1210
1211 static void
1212 report_position_duration (APP_STATE_T * state)
1213 {
1214   gint64 position, duration;
1215
1216   duration = pipeline_get_duration (state);
1217   position = pipeline_get_position (state);
1218
1219   if (position != -1) {
1220     g_print ("\n position / duration: %" GST_TIME_FORMAT,
1221         GST_TIME_ARGS (position));
1222   } else {
1223     g_print ("\n position / duration: unknown");
1224   }
1225
1226   if (duration != -1) {
1227     g_print (" / %" GST_TIME_FORMAT, GST_TIME_ARGS (duration));
1228   } else {
1229     g_print (" / unknown");
1230   }
1231   g_print ("\n");
1232 }
1233
1234 static void
1235 seek_forward (APP_STATE_T * state)
1236 {
1237   gint64 position, duration;
1238
1239   duration = pipeline_get_duration (state);
1240   position = pipeline_get_position (state);
1241
1242   if (position != -1) {
1243     position += 30 * GST_SECOND;
1244     if (duration != -1) {
1245       position = MIN (position, duration);
1246     }
1247     pipeline_seek (state, position);
1248   }
1249 }
1250
1251 static void
1252 seek_backward (APP_STATE_T * state)
1253 {
1254   gint64 position;
1255
1256   position = pipeline_get_position (state);
1257
1258   if (position != -1) {
1259     position -= 30 * GST_SECOND;
1260     position = MAX (position, 0);
1261     pipeline_seek (state, position);
1262   }
1263 }
1264
1265 #define SKIP(t) \
1266   while (*t) { \
1267     if ((*t == ' ') || (*t == '\n') || (*t == '\t') || (*t == '\r')) \
1268       t++; \
1269     else \
1270       break; \
1271   }
1272
1273 /* Process keyboard input */
1274 static gboolean
1275 handle_keyboard (GIOChannel * source, GIOCondition cond, APP_STATE_T * state)
1276 {
1277   gchar *str = NULL;
1278   char op;
1279
1280   if (g_io_channel_read_line (source, &str, NULL, NULL,
1281           NULL) == G_IO_STATUS_NORMAL) {
1282
1283     gchar *cmd = str;
1284     SKIP (cmd)
1285         op = *cmd;
1286     cmd++;
1287     switch (op) {
1288       case 'a':
1289         if (state->animate) {
1290           state->animate = FALSE;
1291         } else {
1292           state->animate = TRUE;
1293         }
1294         break;
1295       case 'p':
1296         pipeline_pause (state);
1297         break;
1298       case 'r':
1299         pipeline_play (state);
1300         break;
1301       case 'l':
1302         report_position_duration (state);
1303         break;
1304       case 'f':
1305         seek_forward (state);
1306         break;
1307       case 'b':
1308         seek_backward (state);
1309         break;
1310       case 'q':
1311         flush_start (state);
1312         gst_element_set_state (state->pipeline, GST_STATE_READY);
1313         break;
1314     }
1315   }
1316   g_free (str);
1317   return TRUE;
1318 }
1319
1320 static GstBusSyncReply
1321 bus_sync_handler (GstBus * bus, GstMessage * message, GstPipeline * data)
1322 {
1323   return GST_BUS_PASS;
1324 }
1325
1326 /* on error print the error and quit the application */
1327 static void
1328 error_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
1329 {
1330   GError *err;
1331   gchar *debug_info;
1332
1333   gst_message_parse_error (msg, &err, &debug_info);
1334   g_printerr ("Error received from element %s: %s\n",
1335       GST_OBJECT_NAME (msg->src), err->message);
1336   g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
1337   g_clear_error (&err);
1338   g_free (debug_info);
1339   flush_start (state);
1340   gst_element_set_state (state->pipeline, GST_STATE_READY);
1341 }
1342
1343 /* buffering */
1344 static void
1345 buffering_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
1346 {
1347   gint percent;
1348
1349   gst_message_parse_buffering (msg, &percent);
1350   g_print ("Buffering %3d%%\r", percent);
1351   if (percent < 100)
1352     pipeline_pause (state);
1353   else {
1354     g_print ("\n");
1355     pipeline_play (state);
1356   }
1357 }
1358
1359 /* on EOS just quit the application */
1360 static void
1361 eos_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
1362 {
1363   if (GST_MESSAGE_SRC (msg) == GST_OBJECT (state->pipeline)) {
1364     g_print ("End-Of-Stream reached.\n");
1365     gst_element_set_state (state->pipeline, GST_STATE_READY);
1366   }
1367 }
1368
1369 static void
1370 state_changed_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
1371 {
1372   GstState old_state, new_state, pending_state;
1373   if (GST_MESSAGE_SRC (msg) == GST_OBJECT (state->pipeline)) {
1374     gst_message_parse_state_changed (msg, &old_state, &new_state,
1375         &pending_state);
1376     g_print ("State changed to %s\n", gst_element_state_get_name (new_state));
1377     if (old_state == GST_STATE_PAUSED && new_state == GST_STATE_READY) {
1378       g_main_loop_quit (state->main_loop);
1379     }
1380   }
1381 }
1382
1383 static void
1384 qos_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
1385 {
1386   GstFormat fmt = GST_FORMAT_BUFFERS;
1387   gchar *name = gst_element_get_name (GST_MESSAGE_SRC (msg));
1388   gst_message_parse_qos_stats (msg, &fmt, &state->rendered, &state->dropped);
1389   g_print ("%s rendered: %" G_GUINT64_FORMAT " dropped: %" G_GUINT64_FORMAT
1390       " %s\n",
1391       name, state->rendered, state->dropped,
1392       (fmt == GST_FORMAT_BUFFERS ? "frames" : "samples"));
1393   g_free (name);
1394 }
1395
1396 //==============================================================================
1397
1398 static void
1399 close_ogl (void)
1400 {
1401 #if defined (USE_OMX_TARGET_RPI)
1402   DISPMANX_UPDATE_HANDLE_T dispman_update;
1403 #endif
1404
1405   if (state->fshader) {
1406     glDeleteShader (state->fshader);
1407     glDetachShader (state->program, state->fshader);
1408   }
1409
1410   if (state->vshader) {
1411     glDeleteShader (state->vshader);
1412     glDetachShader (state->program, state->vshader);
1413   }
1414
1415   if (state->program)
1416     glDeleteProgram (state->program);
1417
1418   if (state->tex)
1419     glDeleteTextures (1, &state->tex);
1420
1421   /* clear screen */
1422   glClear (GL_COLOR_BUFFER_BIT);
1423   eglSwapBuffers (state->display, state->surface);
1424
1425   /* Release OpenGL resources */
1426   eglMakeCurrent (state->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
1427       EGL_NO_CONTEXT);
1428   eglDestroySurface (state->display, state->surface);
1429   eglDestroyContext (state->display, state->context);
1430   gst_object_unref (state->gl_context);
1431   gst_object_unref (state->gst_display);
1432
1433 #if defined (USE_OMX_TARGET_RPI)
1434   dispman_update = vc_dispmanx_update_start (0);
1435   vc_dispmanx_element_remove (dispman_update, state->dispman_element);
1436   vc_dispmanx_update_submit_sync (dispman_update);
1437   vc_dispmanx_display_close (state->dispman_display);
1438 #elif defined(HAVE_X11)
1439   XSync (state->xdisplay, FALSE);
1440   XUnmapWindow (state->xdisplay, state->xwindow);
1441   XDestroyWindow (state->xdisplay, state->xwindow);
1442   XSync (state->xdisplay, FALSE);
1443   XCloseDisplay (state->xdisplay);
1444 #endif
1445 }
1446
1447 //==============================================================================
1448
1449 static void
1450 open_ogl (void)
1451 {
1452   TRACE_VC_MEMORY ("state 0");
1453
1454 #if defined (USE_OMX_TARGET_RPI)
1455   bcm_host_init ();
1456   TRACE_VC_MEMORY ("after bcm_host_init");
1457 #endif
1458
1459   /* Create surface and gl context */
1460   init_ogl (state);
1461   TRACE_VC_MEMORY ("after init_ogl");
1462 }
1463
1464 static gpointer
1465 render_func (gpointer data)
1466 {
1467   open_ogl ();
1468   state->running = TRUE;
1469
1470   do {
1471     handle_queued_objects (state);
1472     g_usleep (0);
1473   } while (state->running == TRUE);
1474
1475   close_ogl ();
1476   return NULL;
1477 }
1478
1479 int
1480 main (int argc, char **argv)
1481 {
1482   GstBus *bus;
1483   GOptionContext *ctx;
1484   GIOChannel *io_stdin;
1485   GError *err = NULL;
1486   gboolean res;
1487   GOptionEntry options[] = {
1488     {NULL}
1489   };
1490   GThread *rthread;
1491
1492   /* Clear application state */
1493   memset (state, 0, sizeof (*state));
1494   state->animate = TRUE;
1495   state->current_buffer = NULL;
1496   state->caps = NULL;
1497
1498   ctx = g_option_context_new ("[ADDITIONAL ARGUMENTS]");
1499   g_option_context_add_main_entries (ctx, options, NULL);
1500   g_option_context_add_group (ctx, gst_init_get_option_group ());
1501   if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
1502     g_print ("Error initializing: %s\n", GST_STR_NULL (err->message));
1503     g_option_context_free (ctx);
1504     g_clear_error (&err);
1505     exit (1);
1506   }
1507   g_option_context_free (ctx);
1508
1509   if (argc != 2) {
1510     g_print ("Usage: %s <URI> or <PIPELINE-DESCRIPTION>\n", argv[0]);
1511     exit (1);
1512   }
1513
1514   /* Initialize GStreamer */
1515   gst_init (&argc, &argv);
1516
1517   /* initialize inter thread comunnication */
1518   init_intercom (state);
1519
1520   TRACE_VC_MEMORY ("state 0");
1521
1522   if (!(rthread = g_thread_new ("render", (GThreadFunc) render_func, NULL))) {
1523     g_print ("Render thread create failed\n");
1524     exit (1);
1525   }
1526
1527   /* Initialize player */
1528   if (gst_uri_is_valid (argv[1])) {
1529     res = init_playbin_player (state, argv[1]);
1530   } else {
1531     res = init_parse_launch_player (state, argv[1]);
1532   }
1533
1534   if (!res)
1535     goto done;
1536
1537   /* Create a GLib Main Loop */
1538   state->main_loop = g_main_loop_new (NULL, FALSE);
1539
1540   /* Add a keyboard watch so we get notified of keystrokes */
1541   io_stdin = g_io_channel_unix_new (fileno (stdin));
1542   g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc) handle_keyboard, state);
1543   g_io_channel_unref (io_stdin);
1544
1545   /* *INDENT-OFF* */
1546   g_print ("Available commands: \n"
1547       "  a - Toggle animation \n"
1548       "  p - Pause playback \n"
1549       "  r - Resume playback \n"
1550       "  l - Query position/duration\n"
1551       "  f - Seek 30 seconds forward \n"
1552       "  b - Seek 30 seconds backward \n"
1553       "  q - Quit \n");
1554   /* *INDENT-ON* */
1555
1556   /* Connect the bus handlers */
1557   bus = gst_element_get_bus (state->pipeline);
1558
1559   gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bus_sync_handler, state,
1560       NULL);
1561
1562   gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
1563   gst_bus_enable_sync_message_emission (bus);
1564
1565   g_signal_connect (G_OBJECT (bus), "message::error", (GCallback) error_cb,
1566       state);
1567   g_signal_connect (G_OBJECT (bus), "message::buffering",
1568       (GCallback) buffering_cb, state);
1569   g_signal_connect (G_OBJECT (bus), "message::eos", (GCallback) eos_cb, state);
1570   g_signal_connect (G_OBJECT (bus), "message::qos", (GCallback) qos_cb, state);
1571   g_signal_connect (G_OBJECT (bus), "message::state-changed",
1572       (GCallback) state_changed_cb, state);
1573   gst_object_unref (bus);
1574
1575   /* Make player start playing */
1576   gst_element_set_state (state->pipeline, GST_STATE_PLAYING);
1577
1578   /* Start the mainloop */
1579   g_main_loop_run (state->main_loop);
1580
1581 done:
1582   /* Release pipeline */
1583   if (state->pipeline) {
1584     gst_element_set_state (state->pipeline, GST_STATE_NULL);
1585     if (state->vsink) {
1586       gst_object_unref (state->vsink);
1587       state->vsink = NULL;
1588     }
1589
1590     gst_object_unref (state->pipeline);
1591   }
1592
1593   /* Unref the mainloop */
1594   if (state->main_loop) {
1595     g_main_loop_unref (state->main_loop);
1596   }
1597
1598   /* Stop rendering thread */
1599   state->running = FALSE;
1600   g_thread_join (rthread);
1601
1602   if (state->caps) {
1603     gst_caps_unref (state->caps);
1604     state->caps = NULL;
1605   }
1606
1607   terminate_intercom (state);
1608
1609   TRACE_VC_MEMORY ("at exit");
1610   return 0;
1611 }