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