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
42 #if defined (USE_OMX_TARGET_RPI) && defined (__GNUC__)
44 #define __VCCOREVER__ 0x04000000
46 #pragma GCC diagnostic push
47 #pragma GCC diagnostic ignored "-Wredundant-decls"
48 #pragma GCC optimize ("gnu89-inline")
52 #include <GLES2/gl2.h>
63 #define GST_USE_UNSTABLE_API
64 #include <gst/gl/gl.h>
65 #include <gst/gl/egl/gstgldisplay_egl.h>
67 #if defined (USE_OMX_TARGET_RPI)
69 #include <EGL/eglext.h>
70 #elif defined(HAVE_X11)
72 #include <X11/Xutil.h>
75 #if defined (USE_OMX_TARGET_RPI) && defined (__GNUC__)
76 #pragma GCC reset_options
77 #pragma GCC diagnostic pop
80 #include "cube_texture_and_coords.h"
83 #define M_PI 3.141592654
86 #define SYNC_BUFFERS TRUE
88 #define TRACE_VC_MEMORY_ENABLED 0
90 #if defined (USE_OMX_TARGET_RPI) && TRACE_VC_MEMORY_ENABLED
91 #define TRACE_VC_MEMORY(str) \
92 fprintf (stderr, "\n\n" str "\n"); \
93 system ("vcdbg reloc >&2")
95 #define TRACE_VC_MEMORY_DEFINE_ID(id) \
98 #define TRACE_VC_MEMORY_RESET_ID(id) \
103 #define TRACE_VC_MEMORY_ONCE_FOR_ID(str,id) \
106 fprintf (stderr, "\n\n" str "\n"); \
107 system ("vcdbg reloc >&2"); \
112 #define TRACE_VC_MEMORY_ONCE(str,id) \
116 fprintf (stderr, "\n\n" str "\n"); \
117 system ("vcdbg reloc >&2"); \
123 #define TRACE_VC_MEMORY(str) while(0)
124 #define TRACE_VC_MEMORY_DEFINE_ID(id)
125 #define TRACE_VC_MEMORY_RESET_ID(id) while(0)
126 #define TRACE_VC_MEMORY_ONCE_FOR_ID(str,id) while(0)
127 #define TRACE_VC_MEMORY_ONCE(str,id) while(0)
130 /* some helpers that we should provide in libgstgl */
138 gst_gl_matrix_load_identity (GstGLMatrix * matrix)
140 memset (matrix, 0x0, sizeof (GstGLMatrix));
141 matrix->m[0][0] = 1.0f;
142 matrix->m[1][1] = 1.0f;
143 matrix->m[2][2] = 1.0f;
144 matrix->m[3][3] = 1.0f;
148 gst_gl_matrix_multiply (GstGLMatrix * matrix, GstGLMatrix * srcA,
154 for (i = 0; i < 4; i++) {
155 tmp.m[i][0] = (srcA->m[i][0] * srcB->m[0][0]) +
156 (srcA->m[i][1] * srcB->m[1][0]) +
157 (srcA->m[i][2] * srcB->m[2][0]) + (srcA->m[i][3] * srcB->m[3][0]);
159 tmp.m[i][1] = (srcA->m[i][0] * srcB->m[0][1]) +
160 (srcA->m[i][1] * srcB->m[1][1]) +
161 (srcA->m[i][2] * srcB->m[2][1]) + (srcA->m[i][3] * srcB->m[3][1]);
163 tmp.m[i][2] = (srcA->m[i][0] * srcB->m[0][2]) +
164 (srcA->m[i][1] * srcB->m[1][2]) +
165 (srcA->m[i][2] * srcB->m[2][2]) + (srcA->m[i][3] * srcB->m[3][2]);
167 tmp.m[i][3] = (srcA->m[i][0] * srcB->m[0][3]) +
168 (srcA->m[i][1] * srcB->m[1][3]) +
169 (srcA->m[i][2] * srcB->m[2][3]) + (srcA->m[i][3] * srcB->m[3][3]);
172 memcpy (matrix, &tmp, sizeof (GstGLMatrix));
176 gst_gl_matrix_translate (GstGLMatrix * matrix, GLfloat tx, GLfloat ty,
180 (matrix->m[0][0] * tx + matrix->m[1][0] * ty + matrix->m[2][0] * tz);
182 (matrix->m[0][1] * tx + matrix->m[1][1] * ty + matrix->m[2][1] * tz);
184 (matrix->m[0][2] * tx + matrix->m[1][2] * ty + matrix->m[2][2] * tz);
186 (matrix->m[0][3] * tx + matrix->m[1][3] * ty + matrix->m[2][3] * tz);
190 gst_gl_matrix_frustum (GstGLMatrix * matrix, GLfloat left, GLfloat right,
191 GLfloat bottom, GLfloat top, GLfloat nearZ, GLfloat farZ)
193 GLfloat deltaX = right - left;
194 GLfloat deltaY = top - bottom;
195 GLfloat deltaZ = farZ - nearZ;
198 if ((nearZ <= 0.0f) || (farZ <= 0.0f) ||
199 (deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f))
202 frust.m[0][0] = 2.0f * nearZ / deltaX;
203 frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;
205 frust.m[1][1] = 2.0f * nearZ / deltaY;
206 frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;
208 frust.m[2][0] = (right + left) / deltaX;
209 frust.m[2][1] = (top + bottom) / deltaY;
210 frust.m[2][2] = -(nearZ + farZ) / deltaZ;
211 frust.m[2][3] = -1.0f;
213 frust.m[3][2] = -2.0f * nearZ * farZ / deltaZ;
214 frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f;
216 gst_gl_matrix_multiply (matrix, &frust, matrix);
220 gst_gl_matrix_perspective (GstGLMatrix * matrix, GLfloat fovy, GLfloat aspect,
221 GLfloat nearZ, GLfloat farZ)
223 GLfloat frustumW, frustumH;
225 frustumH = tanf (fovy / 360.0f * M_PI) * nearZ;
226 frustumW = frustumH * aspect;
228 gst_gl_matrix_frustum (matrix, -frustumW, frustumW, -frustumH, frustumH,
235 static const gchar *cube_v_src =
236 "attribute vec4 a_position; \n"
237 "attribute vec2 a_texCoord; \n"
238 "uniform float u_rotx; \n"
239 "uniform float u_roty; \n"
240 "uniform float u_rotz; \n"
241 "uniform mat4 u_modelview; \n"
242 "uniform mat4 u_projection; \n"
243 "varying vec2 v_texCoord; \n"
246 " float PI = 3.14159265; \n"
247 " float xrot = u_rotx*2.0*PI/360.0; \n"
248 " float yrot = u_roty*2.0*PI/360.0; \n"
249 " float zrot = u_rotz*2.0*PI/360.0; \n"
250 " mat4 matX = mat4 ( \n"
251 " 1.0, 0.0, 0.0, 0.0, \n"
252 " 0.0, cos(xrot), sin(xrot), 0.0, \n"
253 " 0.0, -sin(xrot), cos(xrot), 0.0, \n"
254 " 0.0, 0.0, 0.0, 1.0 ); \n"
255 " mat4 matY = mat4 ( \n"
256 " cos(yrot), 0.0, -sin(yrot), 0.0, \n"
257 " 0.0, 1.0, 0.0, 0.0, \n"
258 " sin(yrot), 0.0, cos(yrot), 0.0, \n"
259 " 0.0, 0.0, 0.0, 1.0 ); \n"
260 " mat4 matZ = mat4 ( \n"
261 " cos(zrot), sin(zrot), 0.0, 0.0, \n"
262 " -sin(zrot), cos(zrot), 0.0, 0.0, \n"
263 " 0.0, 0.0, 1.0, 0.0, \n"
264 " 0.0, 0.0, 0.0, 1.0 ); \n"
265 " gl_Position = u_projection * u_modelview * matZ * matY * matX * a_position;\n"
266 " v_texCoord = a_texCoord; \n"
269 /* fragment source */
270 static const gchar *cube_f_src =
271 "precision mediump float; \n"
272 "varying vec2 v_texCoord; \n"
273 "uniform sampler2D s_texture; \n"
276 " gl_FragColor = texture2D (s_texture, v_texCoord); \n"
282 #if defined (USE_OMX_TARGET_RPI)
283 DISPMANX_DISPLAY_HANDLE_T dispman_display;
284 DISPMANX_ELEMENT_HANDLE_T dispman_element;
287 uint32_t screen_width;
288 uint32_t screen_height;
293 /* OpenGL|ES objects */
303 GLint u_modelviewmatrix;
304 GLint u_projectionmatrix;
310 GstGLMatrix modelview;
311 GstGLMatrix projection;
315 /* model rotation vector and direction */
316 GLfloat rot_angle_x_inc;
317 GLfloat rot_angle_y_inc;
318 GLfloat rot_angle_z_inc;
320 /* current model rotation angles */
325 /* current distance from camera */
327 GLfloat distance_inc;
329 /* GStreamer related resources */
330 GstElement *pipeline;
332 GstGLDisplayEGL *gst_display;
333 GstGLContext *gl_context;
334 gboolean can_avoid_upload;
336 /* Interthread comunication */
341 GstMiniObject *popped_obj;
342 GstBuffer *current_buffer;
345 GMainLoop *main_loop;
346 GstBuffer *last_buffer;
348 /* Rendering thread state */
351 /* number of rendered and dropped frames */
355 #if !defined (USE_OMX_TARGET_RPI) && defined(HAVE_X11)
361 static void init_ogl (APP_STATE_T * state);
362 static void init_model_proj (APP_STATE_T * state);
363 static void reset_model (APP_STATE_T * state);
364 static GLfloat inc_and_wrap_angle (GLfloat angle, GLfloat angle_inc);
365 static void redraw_scene (APP_STATE_T * state);
366 static void update_model (APP_STATE_T * state);
367 static void init_textures (APP_STATE_T * state, GstBuffer * buffer);
368 static APP_STATE_T _state, *state = &_state;
369 static gboolean queue_object (APP_STATE_T * state, GstMiniObject * obj,
370 gboolean synchronous);
372 TRACE_VC_MEMORY_DEFINE_ID (gid0);
373 TRACE_VC_MEMORY_DEFINE_ID (gid1);
374 TRACE_VC_MEMORY_DEFINE_ID (gid2);
378 GST_PLAY_FLAG_VIDEO = (1 << 0),
379 GST_PLAY_FLAG_AUDIO = (1 << 1),
380 GST_PLAY_FLAG_TEXT = (1 << 2),
381 GST_PLAY_FLAG_VIS = (1 << 3),
382 GST_PLAY_FLAG_SOFT_VOLUME = (1 << 4),
383 GST_PLAY_FLAG_NATIVE_AUDIO = (1 << 5),
384 GST_PLAY_FLAG_NATIVE_VIDEO = (1 << 6),
385 GST_PLAY_FLAG_DOWNLOAD = (1 << 7),
386 GST_PLAY_FLAG_BUFFERING = (1 << 8),
387 GST_PLAY_FLAG_DEINTERLACE = (1 << 9),
388 GST_PLAY_FLAG_SOFT_COLORBALANCE = (1 << 10)
391 /***********************************************************
395 * APP_STATE_T *state - holds OGLES model info
397 * Description: Sets the display, OpenGL|ES context and screen stuff
401 ***********************************************************/
403 init_ogl (APP_STATE_T * state)
405 #if defined (USE_OMX_TARGET_RPI)
409 gulong black_pixel = 0;
413 EGLNativeWindowType window_handle = (EGLNativeWindowType) 0;
415 #if defined (USE_OMX_TARGET_RPI)
416 static EGL_DISPMANX_WINDOW_T nativewindow;
418 DISPMANX_UPDATE_HANDLE_T dispman_update;
422 VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0 };
425 static const EGLint attribute_list[] = {
427 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
428 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
432 static const EGLint context_attributes[] = {
433 EGL_CONTEXT_CLIENT_VERSION, 2,
439 /* get an EGL display connection */
440 state->display = eglGetDisplay (EGL_DEFAULT_DISPLAY);
441 assert (state->display != EGL_NO_DISPLAY);
443 /* initialize the EGL display connection */
444 result = eglInitialize (state->display, NULL, NULL);
445 assert (EGL_FALSE != result);
447 #if defined (USE_OMX_TARGET_RPI)
448 /* get an appropriate EGL frame buffer configuration
449 * this uses a BRCM extension that gets the closest match, rather
450 * than standard which returns anything that matches. */
452 eglSaneChooseConfigBRCM (state->display, attribute_list, &config, 1,
454 assert (EGL_FALSE != result);
457 eglChooseConfig (state->display, attribute_list, &config, 1, &num_config);
460 /* create an EGL rendering context */
462 eglCreateContext (state->display, config, EGL_NO_CONTEXT,
464 assert (state->context != EGL_NO_CONTEXT);
466 #if defined (USE_OMX_TARGET_RPI)
467 /* create an EGL window surface */
468 success = graphics_get_display_size (0 /* LCD */ , &state->screen_width,
469 &state->screen_height);
470 assert (success >= 0);
474 dst_rect.width = state->screen_width;
475 dst_rect.height = state->screen_height;
479 src_rect.width = state->screen_width << 16;
480 src_rect.height = state->screen_height << 16;
482 state->dispman_display = vc_dispmanx_display_open (0 /* LCD */ );
483 dispman_update = vc_dispmanx_update_start (0);
485 state->dispman_element =
486 vc_dispmanx_element_add (dispman_update, state->dispman_display,
487 0 /*layer */ , &dst_rect, 0 /*src */ ,
488 &src_rect, DISPMANX_PROTECTION_NONE, &alpha, 0 /*clamp */ ,
491 nativewindow.element = state->dispman_element;
492 nativewindow.width = state->screen_width;
493 nativewindow.height = state->screen_height;
494 vc_dispmanx_update_submit_sync (dispman_update);
496 window_handle = &nativewindow;
497 #elif defined(HAVE_X11)
498 state->screen_width = 1280;
499 state->screen_height = 720;
500 state->xdisplay = XOpenDisplay (NULL);
501 screen_num = DefaultScreen (state->xdisplay);
502 black_pixel = XBlackPixel (state->xdisplay, screen_num);
503 state->xwindow = XCreateSimpleWindow (state->xdisplay,
504 DefaultRootWindow (state->xdisplay), 0, 0, state->screen_width,
505 state->screen_height, 0, 0, black_pixel);
506 XSetWindowBackgroundPixmap (state->xdisplay, state->xwindow, None);
507 XMapRaised (state->xdisplay, state->xwindow);
508 XSync (state->xdisplay, FALSE);
509 window_handle = state->xwindow;
513 eglCreateWindowSurface (state->display, config, window_handle, NULL);
514 assert (state->surface != EGL_NO_SURFACE);
516 /* connect the context to the surface */
518 eglMakeCurrent (state->display, state->surface, state->surface,
520 assert (EGL_FALSE != result);
522 state->gst_display = gst_gl_display_egl_new_with_egl_display (state->display);
524 gst_gl_context_new_wrapped (GST_GL_DISPLAY (state->gst_display),
525 (guintptr) state->context, GST_GL_PLATFORM_EGL, GST_GL_API_GLES2);
528 /***********************************************************
529 * Name: init_model_proj
532 * APP_STATE_T *state - holds OGLES model info
534 * Description: Sets the OpenGL|ES model to default values
538 ***********************************************************/
540 init_model_proj (APP_STATE_T * state)
544 state->vshader = glCreateShader (GL_VERTEX_SHADER);
546 glShaderSource (state->vshader, 1, &cube_v_src, NULL);
547 glCompileShader (state->vshader);
548 assert (glGetError () == GL_NO_ERROR);
550 state->fshader = glCreateShader (GL_FRAGMENT_SHADER);
552 glShaderSource (state->fshader, 1, &cube_f_src, NULL);
553 glCompileShader (state->fshader);
554 assert (glGetError () == GL_NO_ERROR);
556 state->program = glCreateProgram ();
558 glAttachShader (state->program, state->vshader);
559 glAttachShader (state->program, state->fshader);
561 glBindAttribLocation (state->program, 0, "a_position");
562 glBindAttribLocation (state->program, 1, "a_texCoord");
564 glLinkProgram (state->program);
566 glGetProgramiv (state->program, GL_LINK_STATUS, &ret);
567 assert (ret == GL_TRUE);
569 glUseProgram (state->program);
571 state->u_rotx = glGetUniformLocation (state->program, "u_rotx");
572 state->u_roty = glGetUniformLocation (state->program, "u_roty");
573 state->u_rotz = glGetUniformLocation (state->program, "u_rotz");
575 state->u_modelviewmatrix =
576 glGetUniformLocation (state->program, "u_modelview");
578 state->u_projectionmatrix =
579 glGetUniformLocation (state->program, "u_projection");
581 state->s_texture = glGetUniformLocation (state->program, "s_texture");
583 glViewport (0, 0, (GLsizei) state->screen_width,
584 (GLsizei) state->screen_height);
587 state->distance = 5.0f;
589 (GLfloat) state->screen_width / (GLfloat) state->screen_height;
591 gst_gl_matrix_load_identity (&state->projection);
592 gst_gl_matrix_perspective (&state->projection, state->fov, state->aspect,
595 gst_gl_matrix_load_identity (&state->modelview);
596 gst_gl_matrix_translate (&state->modelview, 0.0f, 0.0f, -state->distance);
601 /***********************************************************
605 * APP_STATE_T *state - holds OGLES model info
607 * Description: Resets the Model projection and rotation direction
611 ***********************************************************/
613 reset_model (APP_STATE_T * state)
615 /* reset model rotation */
616 state->rot_angle_x = 45.f;
617 state->rot_angle_y = 30.f;
618 state->rot_angle_z = 0.f;
619 state->rot_angle_x_inc = 0.5f;
620 state->rot_angle_y_inc = 0.5f;
621 state->rot_angle_z_inc = 0.f;
624 /***********************************************************
628 * APP_STATE_T *state - holds OGLES model info
630 * Description: Updates model projection to current position/rotation
634 ***********************************************************/
636 update_model (APP_STATE_T * state)
638 if (state->animate) {
639 /* update position */
641 inc_and_wrap_angle (state->rot_angle_x, state->rot_angle_x_inc);
643 inc_and_wrap_angle (state->rot_angle_y, state->rot_angle_y_inc);
645 inc_and_wrap_angle (state->rot_angle_z, state->rot_angle_z_inc);
649 /***********************************************************
650 * Name: inc_and_wrap_angle
653 * GLfloat angle current angle
654 * GLfloat angle_inc angle increment
656 * Description: Increments or decrements angle by angle_inc degrees
657 * Wraps to 0 at 360 deg.
659 * Returns: new value of angle
661 ***********************************************************/
663 inc_and_wrap_angle (GLfloat angle, GLfloat angle_inc)
675 /***********************************************************
679 * APP_STATE_T *state - holds OGLES model info
681 * Description: Draws the model and calls eglSwapBuffers
682 * to render to screen
686 ***********************************************************/
688 redraw_scene (APP_STATE_T * state)
690 glBindFramebuffer (GL_FRAMEBUFFER, 0);
692 glEnable (GL_CULL_FACE);
693 glEnable (GL_DEPTH_TEST);
695 /* Set background color and clear buffers */
696 glClearColor (0.15f, 0.25f, 0.35f, 1.0f);
697 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
699 glUseProgram (state->program);
701 glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, quadx);
702 glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
704 glEnableVertexAttribArray (0);
705 glEnableVertexAttribArray (1);
707 glActiveTexture (GL_TEXTURE0);
708 glBindTexture (GL_TEXTURE_2D, state->tex);
709 glUniform1i (state->s_texture, 0);
711 glUniform1f (state->u_rotx, state->rot_angle_x);
712 glUniform1f (state->u_roty, state->rot_angle_y);
713 glUniform1f (state->u_rotz, state->rot_angle_z);
715 glUniformMatrix4fv (state->u_modelviewmatrix, 1, GL_FALSE,
716 &state->modelview.m[0][0]);
718 glUniformMatrix4fv (state->u_projectionmatrix, 1, GL_FALSE,
719 &state->projection.m[0][0]);
721 /* draw first 4 vertices */
722 glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
723 glDrawArrays (GL_TRIANGLE_STRIP, 4, 4);
724 glDrawArrays (GL_TRIANGLE_STRIP, 8, 4);
725 glDrawArrays (GL_TRIANGLE_STRIP, 12, 4);
726 glDrawArrays (GL_TRIANGLE_STRIP, 16, 4);
727 glDrawArrays (GL_TRIANGLE_STRIP, 20, 4);
729 if (!eglSwapBuffers (state->display, state->surface)) {
730 g_main_loop_quit (state->main_loop);
734 glDisable (GL_DEPTH_TEST);
735 glDisable (GL_CULL_FACE);
738 /***********************************************************
739 * Name: init_textures
742 * APP_STATE_T *state - holds OGLES model info
744 * Description: Initialise OGL|ES texture surfaces to use image
749 ***********************************************************/
751 init_textures (APP_STATE_T * state, GstBuffer * buffer)
753 GstCapsFeatures *feature = gst_caps_get_features (state->caps, 0);
755 if (gst_caps_features_contains (feature, "memory:GLMemory")) {
756 g_print ("Prepare texture for GLMemory\n");
757 state->can_avoid_upload = TRUE;
759 } else if (gst_caps_features_contains (feature,
760 "meta:GstVideoGLTextureUploadMeta")) {
761 GstVideoMeta *meta = NULL;
762 guint internal_format =
763 gst_gl_sized_gl_format_from_gl_format_type (state->gl_context,
764 GL_RGBA, GL_UNSIGNED_BYTE);
766 g_print ("Prepare texture for GstVideoGLTextureUploadMeta\n");
767 meta = gst_buffer_get_video_meta (buffer);
768 state->can_avoid_upload = FALSE;
769 glGenTextures (1, &state->tex);
770 glBindTexture (GL_TEXTURE_2D, state->tex);
771 glTexImage2D (GL_TEXTURE_2D, 0, internal_format, meta->width, meta->height,
772 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
774 g_assert_not_reached ();
778 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
779 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
781 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
782 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
785 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
786 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
788 assert (glGetError () == GL_NO_ERROR);
792 render_scene (APP_STATE_T * state)
794 update_model (state);
795 redraw_scene (state);
796 TRACE_VC_MEMORY_ONCE_FOR_ID ("after render_scene", gid2);
800 update_image (APP_STATE_T * state, GstBuffer * buffer)
802 GstVideoGLTextureUploadMeta *meta = NULL;
804 if (state->current_buffer) {
805 gst_buffer_unref (state->current_buffer);
807 /* Setup the model world */
808 init_model_proj (state);
809 TRACE_VC_MEMORY ("after init_model_proj");
811 /* initialize the OGLES texture(s) */
812 init_textures (state, buffer);
813 TRACE_VC_MEMORY ("after init_textures");
815 state->current_buffer = gst_buffer_ref (buffer);
817 TRACE_VC_MEMORY_ONCE_FOR_ID ("before GstVideoGLTextureUploadMeta", gid0);
819 if (state->can_avoid_upload) {
820 GstMemory *mem = gst_buffer_peek_memory (state->current_buffer, 0);
821 g_assert (gst_is_gl_memory (mem));
822 state->tex = ((GstGLMemory *) mem)->tex_id;
823 } else if ((meta = gst_buffer_get_video_gl_texture_upload_meta (buffer))) {
824 if (meta->n_textures == 1) {
825 guint ids[4] = { state->tex, 0, 0, 0 };
826 if (!gst_video_gl_texture_upload_meta_upload (meta, ids)) {
827 GST_WARNING ("failed to upload to texture");
832 TRACE_VC_MEMORY_ONCE_FOR_ID ("after GstVideoGLTextureUploadMeta", gid1);
836 init_intercom (APP_STATE_T * state)
839 g_async_queue_new_full ((GDestroyNotify) gst_mini_object_unref);
840 g_mutex_init (&state->queue_lock);
841 g_cond_init (&state->cond);
845 terminate_intercom (APP_STATE_T * state)
847 /* Release intercom */
849 g_async_queue_unref (state->queue);
852 g_mutex_clear (&state->queue_lock);
853 g_cond_clear (&state->cond);
857 flush_internal (APP_STATE_T * state)
859 if (state->current_buffer) {
860 gst_buffer_unref (state->current_buffer);
862 state->current_buffer = NULL;
866 flush_start (APP_STATE_T * state)
868 GstMiniObject *object = NULL;
870 g_mutex_lock (&state->queue_lock);
871 state->flushing = TRUE;
872 g_cond_broadcast (&state->cond);
873 g_mutex_unlock (&state->queue_lock);
875 while ((object = g_async_queue_try_pop (state->queue))) {
876 gst_mini_object_unref (object);
878 g_mutex_lock (&state->queue_lock);
879 flush_internal (state);
880 state->popped_obj = NULL;
881 g_mutex_unlock (&state->queue_lock);
885 flush_stop (APP_STATE_T * state)
887 GstMiniObject *object = NULL;
889 g_mutex_lock (&state->queue_lock);
890 while ((object = GST_MINI_OBJECT_CAST (g_async_queue_try_pop (state->queue)))) {
891 gst_mini_object_unref (object);
893 flush_internal (state);
894 state->popped_obj = NULL;
895 state->flushing = FALSE;
896 g_mutex_unlock (&state->queue_lock);
900 pipeline_pause (APP_STATE_T * state)
902 gst_element_set_state (state->pipeline, GST_STATE_PAUSED);
906 pipeline_play (APP_STATE_T * state)
908 gst_element_set_state (state->pipeline, GST_STATE_PLAYING);
912 pipeline_get_position (APP_STATE_T * state)
914 gint64 position = -1;
916 if (state->pipeline) {
917 gst_element_query_position (state->vsink, GST_FORMAT_TIME, &position);
924 pipeline_get_duration (APP_STATE_T * state)
926 gint64 duration = -1;
928 if (state->pipeline) {
929 gst_element_query_duration (state->pipeline, GST_FORMAT_TIME, &duration);
936 pipeline_seek (APP_STATE_T * state, gint64 position)
938 if (state->pipeline) {
940 event = gst_event_new_seek (1.0,
941 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
942 GST_SEEK_TYPE_SET, position, GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE);
943 if (!gst_element_send_event (state->vsink, event)) {
944 g_print ("seek failed\n");
950 handle_queued_objects (APP_STATE_T * state)
952 GstMiniObject *object = NULL;
954 g_mutex_lock (&state->queue_lock);
955 if (state->flushing) {
956 g_cond_broadcast (&state->cond);
958 } else if (g_async_queue_length (state->queue) == 0) {
962 if ((object = g_async_queue_try_pop (state->queue))) {
963 if (GST_IS_BUFFER (object)) {
964 GstBuffer *buffer = GST_BUFFER_CAST (object);
965 update_image (state, buffer);
966 render_scene (state);
967 gst_buffer_unref (buffer);
971 } else if (GST_IS_EVENT (object)) {
972 GstEvent *event = GST_EVENT_CAST (object);
973 g_print ("\nevent %p %s\n", event,
974 gst_event_type_get_name (GST_EVENT_TYPE (event)));
976 switch (GST_EVENT_TYPE (event)) {
978 flush_internal (state);
983 gst_event_unref (event);
989 state->popped_obj = object;
990 g_cond_broadcast (&state->cond);
994 g_mutex_unlock (&state->queue_lock);
1000 queue_object (APP_STATE_T * state, GstMiniObject * obj, gboolean synchronous)
1002 gboolean res = TRUE;
1004 g_mutex_lock (&state->queue_lock);
1005 if (state->flushing) {
1006 gst_mini_object_unref (obj);
1011 g_async_queue_push (state->queue, obj);
1014 /* Waiting for object to be handled */
1016 g_cond_wait (&state->cond, &state->queue_lock);
1017 } while (!state->flushing && state->popped_obj != obj);
1021 g_mutex_unlock (&state->queue_lock);
1026 preroll_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad,
1029 APP_STATE_T *state = (APP_STATE_T *) user_data;
1030 queue_object (state, GST_MINI_OBJECT_CAST (gst_buffer_ref (buffer)), FALSE);
1034 buffers_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad,
1037 APP_STATE_T *state = (APP_STATE_T *) user_data;
1038 queue_object (state, GST_MINI_OBJECT_CAST (gst_buffer_ref (buffer)),
1042 static GstPadProbeReturn
1043 events_cb (GstPad * pad, GstPadProbeInfo * probe_info, gpointer user_data)
1045 APP_STATE_T *state = (APP_STATE_T *) user_data;
1046 GstEvent *event = GST_PAD_PROBE_INFO_EVENT (probe_info);
1048 switch (GST_EVENT_TYPE (event)) {
1049 case GST_EVENT_CAPS:
1052 gst_caps_unref (state->caps);
1055 gst_event_parse_caps (event, &state->caps);
1057 gst_caps_ref (state->caps);
1060 case GST_EVENT_FLUSH_START:
1061 flush_start (state);
1063 case GST_EVENT_FLUSH_STOP:
1067 queue_object (state, GST_MINI_OBJECT_CAST (gst_event_ref (event)), FALSE);
1073 return GST_PAD_PROBE_OK;
1076 static GstPadProbeReturn
1077 query_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
1079 APP_STATE_T *state = (APP_STATE_T *) user_data;
1080 GstQuery *query = GST_PAD_PROBE_INFO_QUERY (info);
1082 switch (GST_QUERY_TYPE (query)) {
1083 case GST_QUERY_CONTEXT:
1085 if (gst_gl_handle_context_query (state->pipeline, query,
1086 (GstGLDisplay *) state->gst_display, NULL,
1087 (GstGLContext *) state->gl_context))
1088 return GST_PAD_PROBE_HANDLED;
1091 case GST_QUERY_DRAIN:
1093 flush_internal (state);
1100 return GST_PAD_PROBE_OK;
1104 init_playbin_player (APP_STATE_T * state, const gchar * uri)
1107 GstPad *ghostpad = NULL;
1108 GstElement *vbin = gst_bin_new ("vbin");
1110 /* insert a gl filter so that the GstGLBufferPool
1111 * is managed automatically */
1112 GstElement *glfilter = gst_element_factory_make ("glupload", "glfilter");
1113 GstElement *capsfilter = gst_element_factory_make ("capsfilter", NULL);
1114 GstElement *vsink = gst_element_factory_make ("fakesink", "vsink");
1116 g_object_set (capsfilter, "caps",
1117 gst_caps_from_string ("video/x-raw(memory:GLMemory), format=RGBA"), NULL);
1118 g_object_set (vsink, "sync", TRUE, "silent", TRUE, "qos", TRUE,
1119 "enable-last-sample", FALSE, "max-lateness", 20 * GST_MSECOND,
1120 "signal-handoffs", TRUE, NULL);
1122 g_signal_connect (vsink, "preroll-handoff", G_CALLBACK (preroll_cb), state);
1123 g_signal_connect (vsink, "handoff", G_CALLBACK (buffers_cb), state);
1125 gst_bin_add_many (GST_BIN (vbin), glfilter, capsfilter, vsink, NULL);
1127 pad = gst_element_get_static_pad (glfilter, "sink");
1128 ghostpad = gst_ghost_pad_new ("sink", pad);
1129 gst_object_unref (pad);
1130 gst_element_add_pad (vbin, ghostpad);
1132 pad = gst_element_get_static_pad (vsink, "sink");
1133 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, events_cb, state,
1135 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM, query_cb, state,
1137 gst_object_unref (pad);
1139 gst_element_link (glfilter, capsfilter);
1140 gst_element_link (capsfilter, vsink);
1142 /* Instantiate and configure playbin */
1143 state->pipeline = gst_element_factory_make ("playbin", "player");
1144 g_object_set (state->pipeline, "uri", uri,
1145 "video-sink", vbin, "flags",
1146 GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_AUDIO, NULL);
1148 state->vsink = gst_object_ref (vsink);
1153 init_parse_launch_player (APP_STATE_T * state, const gchar * spipeline)
1156 GError *error = NULL;
1160 ./testegl "filesrc location=big_buck_bunny_720p_h264.mov ! qtdemux ! \
1161 h264parse ! omxh264dec ! glcolorscale ! fakesink name=vsink"
1163 ./testegl "filesrc location=big_buck_bunny_720p_h264.mov ! qtdemux ! \
1164 h264parse ! omxh264dec ! glcolorscale ! \
1165 video/x-raw(memory:GLMemory) ! fakesink name=vsink"
1167 ./testegl "filesrc location=big_buck_bunny_720p_h264.mov ! qtdemux ! \
1168 h264parse ! omxh264dec ! glcolorscale ! \
1169 video/x-raw(meta:GstVideoGLTextureUploadMeta) ! \
1170 fakesink name=vsink"
1174 /* pipeline 1 and 2 are the same and the most efficient as glcolorscale
1175 * will enter in passthrough mode and testegl will just bind the eglimage
1176 * to a gl texture without any copy. */
1178 state->pipeline = gst_parse_launch (spipeline, &error);
1180 if (!state->pipeline) {
1181 g_printerr ("Unable to instatiate pipeline '%s': %s\n",
1182 spipeline, error->message);
1186 vsink = gst_bin_get_by_name (GST_BIN (state->pipeline), "vsink");
1189 g_printerr ("Unable to find a fakesink named 'vsink'");
1193 g_object_set (vsink, "sync", TRUE, "silent", TRUE, "qos", TRUE,
1194 "enable-last-sample", FALSE,
1195 "max-lateness", 20 * GST_MSECOND, "signal-handoffs", TRUE, NULL);
1197 g_signal_connect (vsink, "preroll-handoff", G_CALLBACK (preroll_cb), state);
1198 g_signal_connect (vsink, "handoff", G_CALLBACK (buffers_cb), state);
1200 gst_pad_add_probe (gst_element_get_static_pad (vsink, "sink"),
1201 GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, events_cb, state, NULL);
1202 gst_pad_add_probe (gst_element_get_static_pad (vsink, "sink"),
1203 GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM, query_cb, state, NULL);
1205 state->vsink = gst_object_ref (vsink);
1209 //------------------------------------------------------------------------------
1212 report_position_duration (APP_STATE_T * state)
1214 gint64 position, duration;
1216 duration = pipeline_get_duration (state);
1217 position = pipeline_get_position (state);
1219 if (position != -1) {
1220 g_print ("\n position / duration: %" GST_TIME_FORMAT,
1221 GST_TIME_ARGS (position));
1223 g_print ("\n position / duration: unknown");
1226 if (duration != -1) {
1227 g_print (" / %" GST_TIME_FORMAT, GST_TIME_ARGS (duration));
1229 g_print (" / unknown");
1235 seek_forward (APP_STATE_T * state)
1237 gint64 position, duration;
1239 duration = pipeline_get_duration (state);
1240 position = pipeline_get_position (state);
1242 if (position != -1) {
1243 position += 30 * GST_SECOND;
1244 if (duration != -1) {
1245 position = MIN (position, duration);
1247 pipeline_seek (state, position);
1252 seek_backward (APP_STATE_T * state)
1256 position = pipeline_get_position (state);
1258 if (position != -1) {
1259 position -= 30 * GST_SECOND;
1260 position = MAX (position, 0);
1261 pipeline_seek (state, position);
1267 if ((*t == ' ') || (*t == '\n') || (*t == '\t') || (*t == '\r')) \
1273 /* Process keyboard input */
1275 handle_keyboard (GIOChannel * source, GIOCondition cond, APP_STATE_T * state)
1280 if (g_io_channel_read_line (source, &str, NULL, NULL,
1281 NULL) == G_IO_STATUS_NORMAL) {
1289 if (state->animate) {
1290 state->animate = FALSE;
1292 state->animate = TRUE;
1296 pipeline_pause (state);
1299 pipeline_play (state);
1302 report_position_duration (state);
1305 seek_forward (state);
1308 seek_backward (state);
1311 flush_start (state);
1312 gst_element_set_state (state->pipeline, GST_STATE_READY);
1320 static GstBusSyncReply
1321 bus_sync_handler (GstBus * bus, GstMessage * message, GstPipeline * data)
1323 return GST_BUS_PASS;
1326 /* on error print the error and quit the application */
1328 error_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
1333 gst_message_parse_error (msg, &err, &debug_info);
1334 g_printerr ("Error received from element %s: %s\n",
1335 GST_OBJECT_NAME (msg->src), err->message);
1336 g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
1337 g_clear_error (&err);
1338 g_free (debug_info);
1339 flush_start (state);
1340 gst_element_set_state (state->pipeline, GST_STATE_READY);
1345 buffering_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
1349 gst_message_parse_buffering (msg, &percent);
1350 g_print ("Buffering %3d%%\r", percent);
1352 pipeline_pause (state);
1355 pipeline_play (state);
1359 /* on EOS just quit the application */
1361 eos_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
1363 if (GST_MESSAGE_SRC (msg) == GST_OBJECT (state->pipeline)) {
1364 g_print ("End-Of-Stream reached.\n");
1365 gst_element_set_state (state->pipeline, GST_STATE_READY);
1370 state_changed_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
1372 GstState old_state, new_state, pending_state;
1373 if (GST_MESSAGE_SRC (msg) == GST_OBJECT (state->pipeline)) {
1374 gst_message_parse_state_changed (msg, &old_state, &new_state,
1376 g_print ("State changed to %s\n", gst_element_state_get_name (new_state));
1377 if (old_state == GST_STATE_PAUSED && new_state == GST_STATE_READY) {
1378 g_main_loop_quit (state->main_loop);
1384 qos_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
1386 GstFormat fmt = GST_FORMAT_BUFFERS;
1387 gchar *name = gst_element_get_name (GST_MESSAGE_SRC (msg));
1388 gst_message_parse_qos_stats (msg, &fmt, &state->rendered, &state->dropped);
1389 g_print ("%s rendered: %" G_GUINT64_FORMAT " dropped: %" G_GUINT64_FORMAT
1391 name, state->rendered, state->dropped,
1392 (fmt == GST_FORMAT_BUFFERS ? "frames" : "samples"));
1396 //==============================================================================
1401 #if defined (USE_OMX_TARGET_RPI)
1402 DISPMANX_UPDATE_HANDLE_T dispman_update;
1405 if (state->fshader) {
1406 glDeleteShader (state->fshader);
1407 glDetachShader (state->program, state->fshader);
1410 if (state->vshader) {
1411 glDeleteShader (state->vshader);
1412 glDetachShader (state->program, state->vshader);
1416 glDeleteProgram (state->program);
1419 glDeleteTextures (1, &state->tex);
1422 glClear (GL_COLOR_BUFFER_BIT);
1423 eglSwapBuffers (state->display, state->surface);
1425 /* Release OpenGL resources */
1426 eglMakeCurrent (state->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
1428 eglDestroySurface (state->display, state->surface);
1429 eglDestroyContext (state->display, state->context);
1430 gst_object_unref (state->gl_context);
1431 gst_object_unref (state->gst_display);
1433 #if defined (USE_OMX_TARGET_RPI)
1434 dispman_update = vc_dispmanx_update_start (0);
1435 vc_dispmanx_element_remove (dispman_update, state->dispman_element);
1436 vc_dispmanx_update_submit_sync (dispman_update);
1437 vc_dispmanx_display_close (state->dispman_display);
1438 #elif defined(HAVE_X11)
1439 XSync (state->xdisplay, FALSE);
1440 XUnmapWindow (state->xdisplay, state->xwindow);
1441 XDestroyWindow (state->xdisplay, state->xwindow);
1442 XSync (state->xdisplay, FALSE);
1443 XCloseDisplay (state->xdisplay);
1447 //==============================================================================
1452 TRACE_VC_MEMORY ("state 0");
1454 #if defined (USE_OMX_TARGET_RPI)
1456 TRACE_VC_MEMORY ("after bcm_host_init");
1459 /* Create surface and gl context */
1461 TRACE_VC_MEMORY ("after init_ogl");
1465 render_func (gpointer data)
1468 state->running = TRUE;
1471 handle_queued_objects (state);
1473 } while (state->running == TRUE);
1480 main (int argc, char **argv)
1483 GOptionContext *ctx;
1484 GIOChannel *io_stdin;
1487 GOptionEntry options[] = {
1492 /* Clear application state */
1493 memset (state, 0, sizeof (*state));
1494 state->animate = TRUE;
1495 state->current_buffer = NULL;
1498 ctx = g_option_context_new ("[ADDITIONAL ARGUMENTS]");
1499 g_option_context_add_main_entries (ctx, options, NULL);
1500 g_option_context_add_group (ctx, gst_init_get_option_group ());
1501 if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
1502 g_print ("Error initializing: %s\n", GST_STR_NULL (err->message));
1503 g_option_context_free (ctx);
1504 g_clear_error (&err);
1507 g_option_context_free (ctx);
1510 g_print ("Usage: %s <URI> or <PIPELINE-DESCRIPTION>\n", argv[0]);
1514 /* Initialize GStreamer */
1515 gst_init (&argc, &argv);
1517 /* initialize inter thread comunnication */
1518 init_intercom (state);
1520 TRACE_VC_MEMORY ("state 0");
1522 if (!(rthread = g_thread_new ("render", (GThreadFunc) render_func, NULL))) {
1523 g_print ("Render thread create failed\n");
1527 /* Initialize player */
1528 if (gst_uri_is_valid (argv[1])) {
1529 res = init_playbin_player (state, argv[1]);
1531 res = init_parse_launch_player (state, argv[1]);
1537 /* Create a GLib Main Loop */
1538 state->main_loop = g_main_loop_new (NULL, FALSE);
1540 /* Add a keyboard watch so we get notified of keystrokes */
1541 io_stdin = g_io_channel_unix_new (fileno (stdin));
1542 g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc) handle_keyboard, state);
1543 g_io_channel_unref (io_stdin);
1546 g_print ("Available commands: \n"
1547 " a - Toggle animation \n"
1548 " p - Pause playback \n"
1549 " r - Resume playback \n"
1550 " l - Query position/duration\n"
1551 " f - Seek 30 seconds forward \n"
1552 " b - Seek 30 seconds backward \n"
1556 /* Connect the bus handlers */
1557 bus = gst_element_get_bus (state->pipeline);
1559 gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bus_sync_handler, state,
1562 gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
1563 gst_bus_enable_sync_message_emission (bus);
1565 g_signal_connect (G_OBJECT (bus), "message::error", (GCallback) error_cb,
1567 g_signal_connect (G_OBJECT (bus), "message::buffering",
1568 (GCallback) buffering_cb, state);
1569 g_signal_connect (G_OBJECT (bus), "message::eos", (GCallback) eos_cb, state);
1570 g_signal_connect (G_OBJECT (bus), "message::qos", (GCallback) qos_cb, state);
1571 g_signal_connect (G_OBJECT (bus), "message::state-changed",
1572 (GCallback) state_changed_cb, state);
1573 gst_object_unref (bus);
1575 /* Make player start playing */
1576 gst_element_set_state (state->pipeline, GST_STATE_PLAYING);
1578 /* Start the mainloop */
1579 g_main_loop_run (state->main_loop);
1582 /* Release pipeline */
1583 if (state->pipeline) {
1584 gst_element_set_state (state->pipeline, GST_STATE_NULL);
1586 gst_object_unref (state->vsink);
1587 state->vsink = NULL;
1590 gst_object_unref (state->pipeline);
1593 /* Unref the mainloop */
1594 if (state->main_loop) {
1595 g_main_loop_unref (state->main_loop);
1598 /* Stop rendering thread */
1599 state->running = FALSE;
1600 g_thread_join (rthread);
1603 gst_caps_unref (state->caps);
1607 terminate_intercom (state);
1609 TRACE_VC_MEMORY ("at exit");