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