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>
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.
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.
34 /* A rotating cube rendered with OpenGL|ES and video played using GStreamer on
51 #if defined (USE_OMX_TARGET_RPI) && defined (__GNUC__)
53 #define __VCCOREVER__ 0x04000000
55 #pragma GCC diagnostic push
56 #pragma GCC diagnostic ignored "-Wredundant-decls"
57 #pragma GCC optimize ("gnu89-inline")
60 #define GST_USE_UNSTABLE_API
61 #include <gst/gl/gl.h>
62 #include <gst/gl/egl/gstgldisplay_egl.h>
64 #if defined (USE_OMX_TARGET_RPI)
68 #if defined (USE_OMX_TARGET_RPI) && defined (__GNUC__)
69 #pragma GCC reset_options
70 #pragma GCC diagnostic pop
73 #include "cube_texture_and_coords.h"
76 #define M_PI 3.141592654
79 #define SYNC_BUFFERS TRUE
81 #define TRACE_VC_MEMORY_ENABLED 0
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")
88 #define TRACE_VC_MEMORY_DEFINE_ID(id) \
91 #define TRACE_VC_MEMORY_RESET_ID(id) \
96 #define TRACE_VC_MEMORY_ONCE_FOR_ID(str,id) \
99 fprintf (stderr, "\n\n" str "\n"); \
100 system ("vcdbg reloc >&2"); \
105 #define TRACE_VC_MEMORY_ONCE(str,id) \
109 fprintf (stderr, "\n\n" str "\n"); \
110 system ("vcdbg reloc >&2"); \
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)
123 /* some helpers that we should provide in libgstgl */
131 gst_gl_matrix_load_identity (GstGLMatrix * matrix)
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;
141 gst_gl_matrix_multiply (GstGLMatrix * matrix, GstGLMatrix * srcA,
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]);
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]);
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]);
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]);
165 memcpy (matrix, &tmp, sizeof (GstGLMatrix));
169 gst_gl_matrix_translate (GstGLMatrix * matrix, GLfloat tx, GLfloat ty,
173 (matrix->m[0][0] * tx + matrix->m[1][0] * ty + matrix->m[2][0] * tz);
175 (matrix->m[0][1] * tx + matrix->m[1][1] * ty + matrix->m[2][1] * tz);
177 (matrix->m[0][2] * tx + matrix->m[1][2] * ty + matrix->m[2][2] * tz);
179 (matrix->m[0][3] * tx + matrix->m[1][3] * ty + matrix->m[2][3] * tz);
183 gst_gl_matrix_frustum (GstGLMatrix * matrix, GLfloat left, GLfloat right,
184 GLfloat bottom, GLfloat top, GLfloat nearZ, GLfloat farZ)
186 GLfloat deltaX = right - left;
187 GLfloat deltaY = top - bottom;
188 GLfloat deltaZ = farZ - nearZ;
191 if ((nearZ <= 0.0f) || (farZ <= 0.0f) ||
192 (deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f))
195 frust.m[0][0] = 2.0f * nearZ / deltaX;
196 frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;
198 frust.m[1][1] = 2.0f * nearZ / deltaY;
199 frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;
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;
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;
209 gst_gl_matrix_multiply (matrix, &frust, matrix);
213 gst_gl_matrix_perspective (GstGLMatrix * matrix, GLfloat fovy, GLfloat aspect,
214 GLfloat nearZ, GLfloat farZ)
216 GLfloat frustumW, frustumH;
218 frustumH = tanf (fovy / 360.0f * M_PI) * nearZ;
219 frustumW = frustumH * aspect;
221 gst_gl_matrix_frustum (matrix, -frustumW, frustumW, -frustumH, frustumH,
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"
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"
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"
269 " gl_FragColor = texture2D (s_texture, v_texCoord); \n"
275 #if defined (USE_OMX_TARGET_RPI)
276 DISPMANX_DISPLAY_HANDLE_T dispman_display;
277 DISPMANX_ELEMENT_HANDLE_T dispman_element;
280 uint32_t screen_width;
281 uint32_t screen_height;
286 /* OpenGL|ES objects */
296 GLint u_modelviewmatrix;
297 GLint u_projectionmatrix;
303 GstGLMatrix modelview;
304 GstGLMatrix projection;
308 /* model rotation vector and direction */
309 GLfloat rot_angle_x_inc;
310 GLfloat rot_angle_y_inc;
311 GLfloat rot_angle_z_inc;
313 /* current model rotation angles */
318 /* current distance from camera */
320 GLfloat distance_inc;
322 /* GStreamer related resources */
323 GstElement *pipeline;
325 GstGLDisplayEGL *gst_display;
326 gboolean can_avoid_upload;
328 /* Interthread comunication */
333 GstMiniObject *popped_obj;
334 GstBuffer *current_buffer;
337 GMainLoop *main_loop;
338 GstBuffer *last_buffer;
340 /* Rendering thread state */
343 /* number of rendered and dropped frames */
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);
359 TRACE_VC_MEMORY_DEFINE_ID (gid0);
360 TRACE_VC_MEMORY_DEFINE_ID (gid1);
361 TRACE_VC_MEMORY_DEFINE_ID (gid2);
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)
378 /***********************************************************
382 * APP_STATE_T *state - holds OGLES model info
384 * Description: Sets the display, OpenGL|ES context and screen stuff
388 ***********************************************************/
390 init_ogl (APP_STATE_T * state)
395 EGLNativeWindowType window_handle = (EGLNativeWindowType) 0;
397 #if defined (USE_OMX_TARGET_RPI)
398 static EGL_DISPMANX_WINDOW_T nativewindow;
400 DISPMANX_UPDATE_HANDLE_T dispman_update;
404 VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0 };
407 static const EGLint attribute_list[] = {
409 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
410 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
414 static const EGLint context_attributes[] = {
415 EGL_CONTEXT_CLIENT_VERSION, 2,
421 /* get an EGL display connection */
422 state->display = eglGetDisplay (EGL_DEFAULT_DISPLAY);
423 assert (state->display != EGL_NO_DISPLAY);
425 /* initialize the EGL display connection */
426 result = eglInitialize (state->display, NULL, NULL);
427 assert (EGL_FALSE != result);
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. */
434 eglSaneChooseConfigBRCM (state->display, attribute_list, &config, 1,
436 assert (EGL_FALSE != result);
439 eglChooseConfig (state->display, attribute_list, &config, 1, &num_config);
442 /* create an EGL rendering context */
444 eglCreateContext (state->display, config, EGL_NO_CONTEXT,
446 assert (state->context != EGL_NO_CONTEXT);
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);
456 dst_rect.width = state->screen_width;
457 dst_rect.height = state->screen_height;
461 src_rect.width = state->screen_width << 16;
462 src_rect.height = state->screen_height << 16;
464 state->dispman_display = vc_dispmanx_display_open (0 /* LCD */ );
465 dispman_update = vc_dispmanx_update_start (0);
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 */ ,
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);
478 window_handle = &nativewindow;
482 eglCreateWindowSurface (state->display, config, window_handle, NULL);
483 assert (state->surface != EGL_NO_SURFACE);
485 /* connect the context to the surface */
487 eglMakeCurrent (state->display, state->surface, state->surface,
489 assert (EGL_FALSE != result);
492 /***********************************************************
493 * Name: init_model_proj
496 * APP_STATE_T *state - holds OGLES model info
498 * Description: Sets the OpenGL|ES model to default values
502 ***********************************************************/
504 init_model_proj (APP_STATE_T * state)
508 state->vshader = glCreateShader (GL_VERTEX_SHADER);
510 glShaderSource (state->vshader, 1, &cube_v_src, NULL);
511 glCompileShader (state->vshader);
512 assert (glGetError () == GL_NO_ERROR);
514 state->fshader = glCreateShader (GL_FRAGMENT_SHADER);
516 glShaderSource (state->fshader, 1, &cube_f_src, NULL);
517 glCompileShader (state->fshader);
518 assert (glGetError () == GL_NO_ERROR);
520 state->program = glCreateProgram ();
522 glAttachShader (state->program, state->vshader);
523 glAttachShader (state->program, state->fshader);
525 glBindAttribLocation (state->program, 0, "a_position");
526 glBindAttribLocation (state->program, 1, "a_texCoord");
528 glLinkProgram (state->program);
530 glGetProgramiv (state->program, GL_LINK_STATUS, &ret);
531 assert (ret == GL_TRUE);
533 glUseProgram (state->program);
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");
539 state->u_modelviewmatrix =
540 glGetUniformLocation (state->program, "u_modelview");
542 state->u_projectionmatrix =
543 glGetUniformLocation (state->program, "u_projection");
545 state->s_texture = glGetUniformLocation (state->program, "s_texture");
547 glViewport (0, 0, (GLsizei) state->screen_width,
548 (GLsizei) state->screen_height);
551 state->distance = 5.0f;
553 (GLfloat) state->screen_width / (GLfloat) state->screen_height;
555 gst_gl_matrix_load_identity (&state->projection);
556 gst_gl_matrix_perspective (&state->projection, state->fov, state->aspect,
559 gst_gl_matrix_load_identity (&state->modelview);
560 gst_gl_matrix_translate (&state->modelview, 0.0f, 0.0f, -state->distance);
565 /***********************************************************
569 * APP_STATE_T *state - holds OGLES model info
571 * Description: Resets the Model projection and rotation direction
575 ***********************************************************/
577 reset_model (APP_STATE_T * state)
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;
588 /***********************************************************
592 * APP_STATE_T *state - holds OGLES model info
594 * Description: Updates model projection to current position/rotation
598 ***********************************************************/
600 update_model (APP_STATE_T * state)
602 if (state->animate) {
603 /* update position */
605 inc_and_wrap_angle (state->rot_angle_x, state->rot_angle_x_inc);
607 inc_and_wrap_angle (state->rot_angle_y, state->rot_angle_y_inc);
609 inc_and_wrap_angle (state->rot_angle_z, state->rot_angle_z_inc);
613 /***********************************************************
614 * Name: inc_and_wrap_angle
617 * GLfloat angle current angle
618 * GLfloat angle_inc angle increment
620 * Description: Increments or decrements angle by angle_inc degrees
621 * Wraps to 0 at 360 deg.
623 * Returns: new value of angle
625 ***********************************************************/
627 inc_and_wrap_angle (GLfloat angle, GLfloat angle_inc)
639 /***********************************************************
643 * APP_STATE_T *state - holds OGLES model info
645 * Description: Draws the model and calls eglSwapBuffers
646 * to render to screen
650 ***********************************************************/
652 redraw_scene (APP_STATE_T * state)
654 glBindFramebuffer (GL_FRAMEBUFFER, 0);
656 glEnable (GL_CULL_FACE);
657 glEnable (GL_DEPTH_TEST);
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);
663 glUseProgram (state->program);
665 glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, quadx);
666 glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
668 glEnableVertexAttribArray (0);
669 glEnableVertexAttribArray (1);
671 glActiveTexture (GL_TEXTURE0);
672 glBindTexture (GL_TEXTURE_2D, state->tex);
673 glUniform1i (state->s_texture, 0);
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);
679 glUniformMatrix4fv (state->u_modelviewmatrix, 1, GL_FALSE,
680 &state->modelview.m[0][0]);
682 glUniformMatrix4fv (state->u_projectionmatrix, 1, GL_FALSE,
683 &state->projection.m[0][0]);
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);
693 eglSwapBuffers (state->display, state->surface);
695 glDisable (GL_DEPTH_TEST);
696 glDisable (GL_CULL_FACE);
699 /***********************************************************
700 * Name: init_textures
703 * APP_STATE_T *state - holds OGLES model info
705 * Description: Initialise OGL|ES texture surfaces to use image
710 ***********************************************************/
712 init_textures (APP_STATE_T * state, GstBuffer * buffer)
714 GstCapsFeatures *feature = gst_caps_get_features (state->caps, 0);
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;
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);
737 g_assert_not_reached ();
741 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
742 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
744 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
745 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
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);
751 assert (glGetError () == GL_NO_ERROR);
755 render_scene (APP_STATE_T * state)
757 update_model (state);
758 redraw_scene (state);
759 TRACE_VC_MEMORY_ONCE_FOR_ID ("after render_scene", gid2);
763 update_image (APP_STATE_T * state, GstBuffer * buffer)
765 GstVideoGLTextureUploadMeta *meta = NULL;
767 if (state->current_buffer) {
768 gst_buffer_unref (state->current_buffer);
770 /* Setup the model world */
771 init_model_proj (state);
772 TRACE_VC_MEMORY ("after init_model_proj");
774 /* initialize the OGLES texture(s) */
775 init_textures (state, buffer);
776 TRACE_VC_MEMORY ("after init_textures");
778 state->current_buffer = gst_buffer_ref (buffer);
780 TRACE_VC_MEMORY_ONCE_FOR_ID ("before GstVideoGLTextureUploadMeta", gid0);
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");
795 TRACE_VC_MEMORY_ONCE_FOR_ID ("after GstVideoGLTextureUploadMeta", gid1);
799 init_intercom (APP_STATE_T * state)
802 g_async_queue_new_full ((GDestroyNotify) gst_mini_object_unref);
803 g_mutex_init (&state->queue_lock);
804 g_cond_init (&state->cond);
808 terminate_intercom (APP_STATE_T * state)
810 /* Release intercom */
812 g_async_queue_unref (state->queue);
815 g_mutex_clear (&state->queue_lock);
816 g_cond_clear (&state->cond);
820 flush_internal (APP_STATE_T * state)
822 if (state->current_buffer) {
823 gst_buffer_unref (state->current_buffer);
825 state->current_buffer = NULL;
829 flush_start (APP_STATE_T * state)
831 GstMiniObject *object = NULL;
833 g_mutex_lock (&state->queue_lock);
834 state->flushing = TRUE;
835 g_cond_broadcast (&state->cond);
836 g_mutex_unlock (&state->queue_lock);
838 while ((object = g_async_queue_try_pop (state->queue))) {
839 gst_mini_object_unref (object);
841 g_mutex_lock (&state->queue_lock);
842 flush_internal (state);
843 state->popped_obj = NULL;
844 g_mutex_unlock (&state->queue_lock);
848 flush_stop (APP_STATE_T * state)
850 GstMiniObject *object = NULL;
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);
856 flush_internal (state);
857 state->popped_obj = NULL;
858 state->flushing = FALSE;
859 g_mutex_unlock (&state->queue_lock);
863 pipeline_pause (APP_STATE_T * state)
865 gst_element_set_state (state->pipeline, GST_STATE_PAUSED);
869 pipeline_play (APP_STATE_T * state)
871 gst_element_set_state (state->pipeline, GST_STATE_PLAYING);
875 pipeline_get_position (APP_STATE_T * state)
877 gint64 position = -1;
879 if (state->pipeline) {
880 gst_element_query_position (state->vsink, GST_FORMAT_TIME, &position);
887 pipeline_get_duration (APP_STATE_T * state)
889 gint64 duration = -1;
891 if (state->pipeline) {
892 gst_element_query_duration (state->pipeline, GST_FORMAT_TIME, &duration);
899 pipeline_seek (APP_STATE_T * state, gint64 position)
901 if (state->pipeline) {
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");
913 handle_queued_objects (APP_STATE_T * state)
915 GstMiniObject *object = NULL;
917 g_mutex_lock (&state->queue_lock);
918 if (state->flushing) {
919 g_cond_broadcast (&state->cond);
921 } else if (g_async_queue_length (state->queue) == 0) {
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);
934 } else if (GST_IS_QUERY (object)) {
935 GstQuery *query = GST_QUERY_CAST (object);
936 GstStructure *s = (GstStructure *) gst_query_get_structure (query);
938 if (gst_structure_has_name (s, "not-used")) {
939 g_assert_not_reached ();
941 g_assert_not_reached ();
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)));
948 switch (GST_EVENT_TYPE (event)) {
950 flush_internal (state);
955 gst_event_unref (event);
961 state->popped_obj = object;
962 g_cond_broadcast (&state->cond);
966 g_mutex_unlock (&state->queue_lock);
972 queue_object (APP_STATE_T * state, GstMiniObject * obj, gboolean synchronous)
976 g_mutex_lock (&state->queue_lock);
977 if (state->flushing) {
978 gst_mini_object_unref (obj);
983 g_async_queue_push (state->queue, obj);
986 /* Waiting for object to be handled */
988 g_cond_wait (&state->cond, &state->queue_lock);
989 } while (!state->flushing && state->popped_obj != obj);
993 g_mutex_unlock (&state->queue_lock);
998 preroll_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad,
1001 APP_STATE_T *state = (APP_STATE_T *) user_data;
1002 queue_object (state, GST_MINI_OBJECT_CAST (gst_buffer_ref (buffer)), FALSE);
1006 buffers_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad,
1009 APP_STATE_T *state = (APP_STATE_T *) user_data;
1010 queue_object (state, GST_MINI_OBJECT_CAST (gst_buffer_ref (buffer)),
1014 static GstPadProbeReturn
1015 events_cb (GstPad * pad, GstPadProbeInfo * probe_info, gpointer user_data)
1017 APP_STATE_T *state = (APP_STATE_T *) user_data;
1018 GstEvent *event = GST_PAD_PROBE_INFO_EVENT (probe_info);
1020 switch (GST_EVENT_TYPE (event)) {
1021 case GST_EVENT_CAPS:
1024 gst_caps_unref (state->caps);
1027 gst_event_parse_caps (event, &state->caps);
1029 gst_caps_ref (state->caps);
1032 case GST_EVENT_FLUSH_START:
1033 flush_start (state);
1035 case GST_EVENT_FLUSH_STOP:
1039 queue_object (state, GST_MINI_OBJECT_CAST (gst_event_ref (event)), FALSE);
1045 return GST_PAD_PROBE_OK;
1048 static GstPadProbeReturn
1049 query_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
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;
1057 switch (GST_QUERY_TYPE (query)) {
1058 case GST_QUERY_ALLOCATION:
1060 platform = gst_gl_platform_to_string (GST_GL_PLATFORM_EGL);
1061 gl_apis = gst_gl_api_to_string (GST_GL_API_GLES2);
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);
1075 GST_DEBUG ("done alocation");
1076 return GST_PAD_PROBE_OK;
1079 case GST_QUERY_CONTEXT:
1081 return gst_gl_handle_context_query (state->pipeline, query,
1082 (GstGLDisplay **) & state->gst_display);
1085 case GST_QUERY_DRAIN:
1087 flush_internal (state);
1094 return GST_PAD_PROBE_OK;
1098 init_playbin_player (APP_STATE_T * state, const gchar * uri)
1101 GstPad *ghostpad = NULL;
1102 GstElement *vbin = gst_bin_new ("vbin");
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);
1112 g_signal_connect (vsink, "preroll-handoff", G_CALLBACK (preroll_cb), state);
1113 g_signal_connect (vsink, "handoff", G_CALLBACK (buffers_cb), state);
1115 gst_bin_add_many (GST_BIN (vbin), glfilter, vsink, NULL);
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);
1122 pad = gst_element_get_static_pad (vsink, "sink");
1123 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, events_cb, state,
1125 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM, query_cb, state,
1127 gst_object_unref (pad);
1129 gst_element_link (glfilter, vsink);
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);
1137 state->vsink = gst_object_ref (vsink);
1142 init_parse_launch_player (APP_STATE_T * state, const gchar * spipeline)
1145 GError *error = NULL;
1149 ./testegl "filesrc location=big_buck_bunny_720p_h264.mov ! qtdemux ! \
1150 h264parse ! omxh264dec ! glcolorscale ! fakesink name=vsink"
1152 ./testegl "filesrc location=big_buck_bunny_720p_h264.mov ! qtdemux ! \
1153 h264parse ! omxh264dec ! glcolorscale ! \
1154 video/x-raw(memory:EGLImage) ! fakesink name=vsink"
1156 ./testegl "filesrc location=big_buck_bunny_720p_h264.mov ! qtdemux ! \
1157 h264parse ! omxh264dec ! glcolorscale ! \
1158 video/x-raw(memory:GLMemory) ! fakesink name=vsink"
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"
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. */
1171 state->pipeline = gst_parse_launch (spipeline, &error);
1173 if (!state->pipeline) {
1174 g_printerr ("Unable to instatiate pipeline '%s': %s\n",
1175 spipeline, error->message);
1179 vsink = gst_bin_get_by_name (GST_BIN (state->pipeline), "vsink");
1182 g_printerr ("Unable to find a fakesink named 'vsink'");
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);
1190 g_signal_connect (vsink, "preroll-handoff", G_CALLBACK (preroll_cb), state);
1191 g_signal_connect (vsink, "handoff", G_CALLBACK (buffers_cb), state);
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);
1198 state->vsink = gst_object_ref (vsink);
1202 //------------------------------------------------------------------------------
1205 report_position_duration (APP_STATE_T * state)
1207 gint64 position, duration;
1209 duration = pipeline_get_duration (state);
1210 position = pipeline_get_position (state);
1212 if (position != -1) {
1213 g_print ("\n position / duration: %" GST_TIME_FORMAT,
1214 GST_TIME_ARGS (position));
1216 g_print ("\n position / duration: unknown");
1219 if (duration != -1) {
1220 g_print (" / %" GST_TIME_FORMAT, GST_TIME_ARGS (duration));
1222 g_print (" / unknown");
1228 seek_forward (APP_STATE_T * state)
1230 gint64 position, duration;
1232 duration = pipeline_get_duration (state);
1233 position = pipeline_get_position (state);
1235 if (position != -1) {
1236 position += 30 * GST_SECOND;
1237 if (duration != -1) {
1238 position = MIN (position, duration);
1240 pipeline_seek (state, position);
1245 seek_backward (APP_STATE_T * state)
1249 position = pipeline_get_position (state);
1251 if (position != -1) {
1252 position -= 30 * GST_SECOND;
1253 position = MAX (position, 0);
1254 pipeline_seek (state, position);
1260 if ((*t == ' ') || (*t == '\n') || (*t == '\t') || (*t == '\r')) \
1266 /* Process keyboard input */
1268 handle_keyboard (GIOChannel * source, GIOCondition cond, APP_STATE_T * state)
1273 if (g_io_channel_read_line (source, &str, NULL, NULL,
1274 NULL) == G_IO_STATUS_NORMAL) {
1282 if (state->animate) {
1283 state->animate = FALSE;
1285 state->animate = TRUE;
1289 pipeline_pause (state);
1292 pipeline_play (state);
1295 report_position_duration (state);
1298 seek_forward (state);
1301 seek_backward (state);
1304 flush_start (state);
1305 gst_element_set_state (state->pipeline, GST_STATE_READY);
1313 static GstBusSyncReply
1314 bus_sync_handler (GstBus * bus, GstMessage * message, GstPipeline * data)
1316 return GST_BUS_PASS;
1319 /* on error print the error and quit the application */
1321 error_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
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);
1338 buffering_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
1342 gst_message_parse_buffering (msg, &percent);
1343 g_print ("Buffering %3d%%\r", percent);
1345 pipeline_pause (state);
1348 pipeline_play (state);
1352 /* on EOS just quit the application */
1354 eos_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
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);
1363 state_changed_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
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,
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);
1377 qos_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
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
1384 name, state->rendered, state->dropped,
1385 (fmt == GST_FORMAT_BUFFERS ? "frames" : "samples"));
1389 //==============================================================================
1394 #if defined (USE_OMX_TARGET_RPI)
1395 DISPMANX_UPDATE_HANDLE_T dispman_update;
1398 if (state->fshader) {
1399 glDeleteShader (state->fshader);
1400 glDetachShader (state->program, state->fshader);
1403 if (state->vshader) {
1404 glDeleteShader (state->vshader);
1405 glDetachShader (state->program, state->vshader);
1409 glDeleteProgram (state->program);
1412 glDeleteTextures (1, &state->tex);
1415 glClear (GL_COLOR_BUFFER_BIT);
1416 eglSwapBuffers (state->display, state->surface);
1418 /* Release OpenGL resources */
1419 eglMakeCurrent (state->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
1421 eglDestroySurface (state->display, state->surface);
1422 eglDestroyContext (state->display, state->context);
1423 gst_object_unref (state->gst_display);
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);
1433 //==============================================================================
1438 TRACE_VC_MEMORY ("state 0");
1440 #if defined (USE_OMX_TARGET_RPI)
1442 TRACE_VC_MEMORY ("after bcm_host_init");
1445 /* Create surface and gl context */
1447 TRACE_VC_MEMORY ("after init_ogl");
1449 /* Wrap the EGLDisplay to GstGLDisplayEGL */
1450 state->gst_display = gst_gl_display_egl_new_with_egl_display (state->display);
1454 render_func (gpointer data)
1457 state->running = TRUE;
1460 handle_queued_objects (state);
1462 } while (state->running == TRUE);
1469 main (int argc, char **argv)
1472 GOptionContext *ctx;
1473 GIOChannel *io_stdin;
1476 GOptionEntry options[] = {
1481 /* Clear application state */
1482 memset (state, 0, sizeof (*state));
1483 state->animate = TRUE;
1484 state->current_buffer = NULL;
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);
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);
1502 g_option_context_free (ctx);
1505 g_print ("Usage: %s <URI> or <PIPELINE-DESCRIPTION>\n", argv[0]);
1509 /* Initialize GStreamer */
1510 gst_init (&argc, &argv);
1512 /* initialize inter thread comunnication */
1513 init_intercom (state);
1515 TRACE_VC_MEMORY ("state 0");
1517 if (!(rthread = g_thread_new ("render", (GThreadFunc) render_func, NULL))) {
1518 g_print ("Render thread create failed\n");
1522 /* Initialize player */
1523 if (gst_uri_is_valid (argv[1])) {
1524 res = init_playbin_player (state, argv[1]);
1526 res = init_parse_launch_player (state, argv[1]);
1532 /* Create a GLib Main Loop and set it to run */
1533 state->main_loop = g_main_loop_new (NULL, FALSE);
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);
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"
1551 /* Connect the bus handlers */
1552 bus = gst_element_get_bus (state->pipeline);
1554 gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bus_sync_handler, state,
1557 gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
1558 gst_bus_enable_sync_message_emission (bus);
1560 g_signal_connect (G_OBJECT (bus), "message::error", (GCallback) error_cb,
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);
1570 /* Make player start playing */
1571 gst_element_set_state (state->pipeline, GST_STATE_PLAYING);
1573 /* Start the mainloop */
1574 state->main_loop = g_main_loop_new (NULL, FALSE);
1575 g_main_loop_run (state->main_loop);
1578 /* Release pipeline */
1579 if (state->pipeline) {
1580 gst_element_set_state (state->pipeline, GST_STATE_NULL);
1582 gst_object_unref (state->vsink);
1583 state->vsink = NULL;
1586 gst_object_unref (state->pipeline);
1589 /* Unref the mainloop */
1590 if (state->main_loop) {
1591 g_main_loop_unref (state->main_loop);
1594 /* Stop rendering thread */
1595 state->running = FALSE;
1596 g_thread_join (rthread);
1599 gst_caps_unref (state->caps);
1603 terminate_intercom (state);
1605 TRACE_VC_MEMORY ("at exit");