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 #elif defined(HAVE_X11)
71 #include <X11/Xutil.h>
74 #if defined (USE_OMX_TARGET_RPI) && defined (__GNUC__)
75 #pragma GCC reset_options
76 #pragma GCC diagnostic pop
79 #include "cube_texture_and_coords.h"
82 #define M_PI 3.141592654
85 #define SYNC_BUFFERS TRUE
87 #define TRACE_VC_MEMORY_ENABLED 0
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")
94 #define TRACE_VC_MEMORY_DEFINE_ID(id) \
97 #define TRACE_VC_MEMORY_RESET_ID(id) \
102 #define TRACE_VC_MEMORY_ONCE_FOR_ID(str,id) \
105 fprintf (stderr, "\n\n" str "\n"); \
106 system ("vcdbg reloc >&2"); \
111 #define TRACE_VC_MEMORY_ONCE(str,id) \
115 fprintf (stderr, "\n\n" str "\n"); \
116 system ("vcdbg reloc >&2"); \
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)
129 /* some helpers that we should provide in libgstgl */
137 gst_gl_matrix_load_identity (GstGLMatrix * matrix)
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;
147 gst_gl_matrix_multiply (GstGLMatrix * matrix, GstGLMatrix * srcA,
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]);
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]);
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]);
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]);
171 memcpy (matrix, &tmp, sizeof (GstGLMatrix));
175 gst_gl_matrix_translate (GstGLMatrix * matrix, GLfloat tx, GLfloat ty,
179 (matrix->m[0][0] * tx + matrix->m[1][0] * ty + matrix->m[2][0] * tz);
181 (matrix->m[0][1] * tx + matrix->m[1][1] * ty + matrix->m[2][1] * tz);
183 (matrix->m[0][2] * tx + matrix->m[1][2] * ty + matrix->m[2][2] * tz);
185 (matrix->m[0][3] * tx + matrix->m[1][3] * ty + matrix->m[2][3] * tz);
189 gst_gl_matrix_frustum (GstGLMatrix * matrix, GLfloat left, GLfloat right,
190 GLfloat bottom, GLfloat top, GLfloat nearZ, GLfloat farZ)
192 GLfloat deltaX = right - left;
193 GLfloat deltaY = top - bottom;
194 GLfloat deltaZ = farZ - nearZ;
197 if ((nearZ <= 0.0f) || (farZ <= 0.0f) ||
198 (deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f))
201 frust.m[0][0] = 2.0f * nearZ / deltaX;
202 frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;
204 frust.m[1][1] = 2.0f * nearZ / deltaY;
205 frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;
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;
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;
215 gst_gl_matrix_multiply (matrix, &frust, matrix);
219 gst_gl_matrix_perspective (GstGLMatrix * matrix, GLfloat fovy, GLfloat aspect,
220 GLfloat nearZ, GLfloat farZ)
222 GLfloat frustumW, frustumH;
224 frustumH = tanf (fovy / 360.0f * M_PI) * nearZ;
225 frustumW = frustumH * aspect;
227 gst_gl_matrix_frustum (matrix, -frustumW, frustumW, -frustumH, frustumH,
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"
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"
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"
275 " gl_FragColor = texture2D (s_texture, v_texCoord); \n"
281 #if defined (USE_OMX_TARGET_RPI)
282 DISPMANX_DISPLAY_HANDLE_T dispman_display;
283 DISPMANX_ELEMENT_HANDLE_T dispman_element;
286 uint32_t screen_width;
287 uint32_t screen_height;
292 /* OpenGL|ES objects */
302 GLint u_modelviewmatrix;
303 GLint u_projectionmatrix;
309 GstGLMatrix modelview;
310 GstGLMatrix projection;
314 /* model rotation vector and direction */
315 GLfloat rot_angle_x_inc;
316 GLfloat rot_angle_y_inc;
317 GLfloat rot_angle_z_inc;
319 /* current model rotation angles */
324 /* current distance from camera */
326 GLfloat distance_inc;
328 /* GStreamer related resources */
329 GstElement *pipeline;
331 GstGLDisplayEGL *gst_display;
332 GstGLContext *gl_context;
333 gboolean can_avoid_upload;
335 /* Interthread comunication */
340 GstMiniObject *popped_obj;
341 GstBuffer *current_buffer;
344 GMainLoop *main_loop;
345 GstBuffer *last_buffer;
347 /* Rendering thread state */
350 /* number of rendered and dropped frames */
354 #if !defined (USE_OMX_TARGET_RPI) && defined(HAVE_X11)
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);
371 TRACE_VC_MEMORY_DEFINE_ID (gid0);
372 TRACE_VC_MEMORY_DEFINE_ID (gid1);
373 TRACE_VC_MEMORY_DEFINE_ID (gid2);
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)
390 /***********************************************************
394 * APP_STATE_T *state - holds OGLES model info
396 * Description: Sets the display, OpenGL|ES context and screen stuff
400 ***********************************************************/
402 init_ogl (APP_STATE_T * state)
404 #if defined (USE_OMX_TARGET_RPI)
408 gulong black_pixel = 0;
412 EGLNativeWindowType window_handle = (EGLNativeWindowType) 0;
414 #if defined (USE_OMX_TARGET_RPI)
415 static EGL_DISPMANX_WINDOW_T nativewindow;
417 DISPMANX_UPDATE_HANDLE_T dispman_update;
421 VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0 };
424 static const EGLint attribute_list[] = {
426 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
427 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
431 static const EGLint context_attributes[] = {
432 EGL_CONTEXT_CLIENT_VERSION, 2,
438 /* get an EGL display connection */
439 state->display = eglGetDisplay (EGL_DEFAULT_DISPLAY);
440 assert (state->display != EGL_NO_DISPLAY);
442 /* initialize the EGL display connection */
443 result = eglInitialize (state->display, NULL, NULL);
444 assert (EGL_FALSE != result);
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. */
451 eglSaneChooseConfigBRCM (state->display, attribute_list, &config, 1,
453 assert (EGL_FALSE != result);
456 eglChooseConfig (state->display, attribute_list, &config, 1, &num_config);
459 /* create an EGL rendering context */
461 eglCreateContext (state->display, config, EGL_NO_CONTEXT,
463 assert (state->context != EGL_NO_CONTEXT);
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);
473 dst_rect.width = state->screen_width;
474 dst_rect.height = state->screen_height;
478 src_rect.width = state->screen_width << 16;
479 src_rect.height = state->screen_height << 16;
481 state->dispman_display = vc_dispmanx_display_open (0 /* LCD */ );
482 dispman_update = vc_dispmanx_update_start (0);
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 */ ,
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);
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;
512 eglCreateWindowSurface (state->display, config, window_handle, NULL);
513 assert (state->surface != EGL_NO_SURFACE);
515 /* connect the context to the surface */
517 eglMakeCurrent (state->display, state->surface, state->surface,
519 assert (EGL_FALSE != result);
521 state->gst_display = gst_gl_display_egl_new_with_egl_display (state->display);
523 gst_gl_context_new_wrapped (GST_GL_DISPLAY (state->gst_display),
524 (guintptr) state->context, GST_GL_PLATFORM_EGL, GST_GL_API_GLES2);
527 /***********************************************************
528 * Name: init_model_proj
531 * APP_STATE_T *state - holds OGLES model info
533 * Description: Sets the OpenGL|ES model to default values
537 ***********************************************************/
539 init_model_proj (APP_STATE_T * state)
543 state->vshader = glCreateShader (GL_VERTEX_SHADER);
545 glShaderSource (state->vshader, 1, &cube_v_src, NULL);
546 glCompileShader (state->vshader);
547 assert (glGetError () == GL_NO_ERROR);
549 state->fshader = glCreateShader (GL_FRAGMENT_SHADER);
551 glShaderSource (state->fshader, 1, &cube_f_src, NULL);
552 glCompileShader (state->fshader);
553 assert (glGetError () == GL_NO_ERROR);
555 state->program = glCreateProgram ();
557 glAttachShader (state->program, state->vshader);
558 glAttachShader (state->program, state->fshader);
560 glBindAttribLocation (state->program, 0, "a_position");
561 glBindAttribLocation (state->program, 1, "a_texCoord");
563 glLinkProgram (state->program);
565 glGetProgramiv (state->program, GL_LINK_STATUS, &ret);
566 assert (ret == GL_TRUE);
568 glUseProgram (state->program);
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");
574 state->u_modelviewmatrix =
575 glGetUniformLocation (state->program, "u_modelview");
577 state->u_projectionmatrix =
578 glGetUniformLocation (state->program, "u_projection");
580 state->s_texture = glGetUniformLocation (state->program, "s_texture");
582 glViewport (0, 0, (GLsizei) state->screen_width,
583 (GLsizei) state->screen_height);
586 state->distance = 5.0f;
588 (GLfloat) state->screen_width / (GLfloat) state->screen_height;
590 gst_gl_matrix_load_identity (&state->projection);
591 gst_gl_matrix_perspective (&state->projection, state->fov, state->aspect,
594 gst_gl_matrix_load_identity (&state->modelview);
595 gst_gl_matrix_translate (&state->modelview, 0.0f, 0.0f, -state->distance);
600 /***********************************************************
604 * APP_STATE_T *state - holds OGLES model info
606 * Description: Resets the Model projection and rotation direction
610 ***********************************************************/
612 reset_model (APP_STATE_T * state)
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;
623 /***********************************************************
627 * APP_STATE_T *state - holds OGLES model info
629 * Description: Updates model projection to current position/rotation
633 ***********************************************************/
635 update_model (APP_STATE_T * state)
637 if (state->animate) {
638 /* update position */
640 inc_and_wrap_angle (state->rot_angle_x, state->rot_angle_x_inc);
642 inc_and_wrap_angle (state->rot_angle_y, state->rot_angle_y_inc);
644 inc_and_wrap_angle (state->rot_angle_z, state->rot_angle_z_inc);
648 /***********************************************************
649 * Name: inc_and_wrap_angle
652 * GLfloat angle current angle
653 * GLfloat angle_inc angle increment
655 * Description: Increments or decrements angle by angle_inc degrees
656 * Wraps to 0 at 360 deg.
658 * Returns: new value of angle
660 ***********************************************************/
662 inc_and_wrap_angle (GLfloat angle, GLfloat angle_inc)
674 /***********************************************************
678 * APP_STATE_T *state - holds OGLES model info
680 * Description: Draws the model and calls eglSwapBuffers
681 * to render to screen
685 ***********************************************************/
687 redraw_scene (APP_STATE_T * state)
689 glBindFramebuffer (GL_FRAMEBUFFER, 0);
691 glEnable (GL_CULL_FACE);
692 glEnable (GL_DEPTH_TEST);
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);
698 glUseProgram (state->program);
700 glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, quadx);
701 glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
703 glEnableVertexAttribArray (0);
704 glEnableVertexAttribArray (1);
706 glActiveTexture (GL_TEXTURE0);
707 glBindTexture (GL_TEXTURE_2D, state->tex);
708 glUniform1i (state->s_texture, 0);
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);
714 glUniformMatrix4fv (state->u_modelviewmatrix, 1, GL_FALSE,
715 &state->modelview.m[0][0]);
717 glUniformMatrix4fv (state->u_projectionmatrix, 1, GL_FALSE,
718 &state->projection.m[0][0]);
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);
728 if (!eglSwapBuffers (state->display, state->surface)) {
729 g_main_loop_quit (state->main_loop);
733 glDisable (GL_DEPTH_TEST);
734 glDisable (GL_CULL_FACE);
737 /***********************************************************
738 * Name: init_textures
741 * APP_STATE_T *state - holds OGLES model info
743 * Description: Initialise OGL|ES texture surfaces to use image
748 ***********************************************************/
750 init_textures (APP_STATE_T * state, GstBuffer * buffer)
752 GstCapsFeatures *feature = gst_caps_get_features (state->caps, 0);
754 if (gst_caps_features_contains (feature, "memory:GLMemory")) {
755 g_print ("Prepare texture for GLMemory\n");
756 state->can_avoid_upload = TRUE;
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);
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);
773 g_assert_not_reached ();
777 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
778 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
780 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
781 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
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);
787 assert (glGetError () == GL_NO_ERROR);
791 render_scene (APP_STATE_T * state)
793 update_model (state);
794 redraw_scene (state);
795 TRACE_VC_MEMORY_ONCE_FOR_ID ("after render_scene", gid2);
799 update_image (APP_STATE_T * state, GstBuffer * buffer)
801 GstVideoGLTextureUploadMeta *meta = NULL;
803 if (state->current_buffer) {
804 gst_buffer_unref (state->current_buffer);
806 /* Setup the model world */
807 init_model_proj (state);
808 TRACE_VC_MEMORY ("after init_model_proj");
810 /* initialize the OGLES texture(s) */
811 init_textures (state, buffer);
812 TRACE_VC_MEMORY ("after init_textures");
814 state->current_buffer = gst_buffer_ref (buffer);
816 TRACE_VC_MEMORY_ONCE_FOR_ID ("before GstVideoGLTextureUploadMeta", gid0);
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");
831 TRACE_VC_MEMORY_ONCE_FOR_ID ("after GstVideoGLTextureUploadMeta", gid1);
835 init_intercom (APP_STATE_T * state)
838 g_async_queue_new_full ((GDestroyNotify) gst_mini_object_unref);
839 g_mutex_init (&state->queue_lock);
840 g_cond_init (&state->cond);
844 terminate_intercom (APP_STATE_T * state)
846 /* Release intercom */
848 g_async_queue_unref (state->queue);
851 g_mutex_clear (&state->queue_lock);
852 g_cond_clear (&state->cond);
856 flush_internal (APP_STATE_T * state)
858 if (state->current_buffer) {
859 gst_buffer_unref (state->current_buffer);
861 state->current_buffer = NULL;
865 flush_start (APP_STATE_T * state)
867 GstMiniObject *object = NULL;
869 g_mutex_lock (&state->queue_lock);
870 state->flushing = TRUE;
871 g_cond_broadcast (&state->cond);
872 g_mutex_unlock (&state->queue_lock);
874 while ((object = g_async_queue_try_pop (state->queue))) {
875 gst_mini_object_unref (object);
877 g_mutex_lock (&state->queue_lock);
878 flush_internal (state);
879 state->popped_obj = NULL;
880 g_mutex_unlock (&state->queue_lock);
884 flush_stop (APP_STATE_T * state)
886 GstMiniObject *object = NULL;
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);
892 flush_internal (state);
893 state->popped_obj = NULL;
894 state->flushing = FALSE;
895 g_mutex_unlock (&state->queue_lock);
899 pipeline_pause (APP_STATE_T * state)
901 gst_element_set_state (state->pipeline, GST_STATE_PAUSED);
905 pipeline_play (APP_STATE_T * state)
907 gst_element_set_state (state->pipeline, GST_STATE_PLAYING);
911 pipeline_get_position (APP_STATE_T * state)
913 gint64 position = -1;
915 if (state->pipeline) {
916 gst_element_query_position (state->vsink, GST_FORMAT_TIME, &position);
923 pipeline_get_duration (APP_STATE_T * state)
925 gint64 duration = -1;
927 if (state->pipeline) {
928 gst_element_query_duration (state->pipeline, GST_FORMAT_TIME, &duration);
935 pipeline_seek (APP_STATE_T * state, gint64 position)
937 if (state->pipeline) {
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");
949 handle_queued_objects (APP_STATE_T * state)
951 GstMiniObject *object = NULL;
953 g_mutex_lock (&state->queue_lock);
954 if (state->flushing) {
955 g_cond_broadcast (&state->cond);
957 } else if (g_async_queue_length (state->queue) == 0) {
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);
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)));
975 switch (GST_EVENT_TYPE (event)) {
977 flush_internal (state);
982 gst_event_unref (event);
988 state->popped_obj = object;
989 g_cond_broadcast (&state->cond);
993 g_mutex_unlock (&state->queue_lock);
999 queue_object (APP_STATE_T * state, GstMiniObject * obj, gboolean synchronous)
1001 gboolean res = TRUE;
1003 g_mutex_lock (&state->queue_lock);
1004 if (state->flushing) {
1005 gst_mini_object_unref (obj);
1010 g_async_queue_push (state->queue, obj);
1013 /* Waiting for object to be handled */
1015 g_cond_wait (&state->cond, &state->queue_lock);
1016 } while (!state->flushing && state->popped_obj != obj);
1020 g_mutex_unlock (&state->queue_lock);
1025 preroll_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad,
1028 APP_STATE_T *state = (APP_STATE_T *) user_data;
1029 queue_object (state, GST_MINI_OBJECT_CAST (gst_buffer_ref (buffer)), FALSE);
1033 buffers_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad,
1036 APP_STATE_T *state = (APP_STATE_T *) user_data;
1037 queue_object (state, GST_MINI_OBJECT_CAST (gst_buffer_ref (buffer)),
1041 static GstPadProbeReturn
1042 events_cb (GstPad * pad, GstPadProbeInfo * probe_info, gpointer user_data)
1044 APP_STATE_T *state = (APP_STATE_T *) user_data;
1045 GstEvent *event = GST_PAD_PROBE_INFO_EVENT (probe_info);
1047 switch (GST_EVENT_TYPE (event)) {
1048 case GST_EVENT_CAPS:
1051 gst_caps_unref (state->caps);
1054 gst_event_parse_caps (event, &state->caps);
1056 gst_caps_ref (state->caps);
1059 case GST_EVENT_FLUSH_START:
1060 flush_start (state);
1062 case GST_EVENT_FLUSH_STOP:
1066 queue_object (state, GST_MINI_OBJECT_CAST (gst_event_ref (event)), FALSE);
1072 return GST_PAD_PROBE_OK;
1075 static GstPadProbeReturn
1076 query_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
1078 APP_STATE_T *state = (APP_STATE_T *) user_data;
1079 GstQuery *query = GST_PAD_PROBE_INFO_QUERY (info);
1081 switch (GST_QUERY_TYPE (query)) {
1082 case GST_QUERY_CONTEXT:
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;
1090 case GST_QUERY_DRAIN:
1092 flush_internal (state);
1099 return GST_PAD_PROBE_OK;
1103 init_playbin_player (APP_STATE_T * state, const gchar * uri)
1106 GstPad *ghostpad = NULL;
1107 GstElement *vbin = gst_bin_new ("vbin");
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");
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);
1121 g_signal_connect (vsink, "preroll-handoff", G_CALLBACK (preroll_cb), state);
1122 g_signal_connect (vsink, "handoff", G_CALLBACK (buffers_cb), state);
1124 gst_bin_add_many (GST_BIN (vbin), glfilter, capsfilter, vsink, NULL);
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);
1131 pad = gst_element_get_static_pad (vsink, "sink");
1132 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, events_cb, state,
1134 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM, query_cb, state,
1136 gst_object_unref (pad);
1138 gst_element_link (glfilter, capsfilter);
1139 gst_element_link (capsfilter, vsink);
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);
1147 state->vsink = gst_object_ref (vsink);
1152 init_parse_launch_player (APP_STATE_T * state, const gchar * spipeline)
1155 GError *error = NULL;
1159 ./testegl "filesrc location=big_buck_bunny_720p_h264.mov ! qtdemux ! \
1160 h264parse ! omxh264dec ! glcolorscale ! fakesink name=vsink"
1162 ./testegl "filesrc location=big_buck_bunny_720p_h264.mov ! qtdemux ! \
1163 h264parse ! omxh264dec ! glcolorscale ! \
1164 video/x-raw(memory:GLMemory) ! fakesink name=vsink"
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"
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. */
1177 state->pipeline = gst_parse_launch (spipeline, &error);
1179 if (!state->pipeline) {
1180 g_printerr ("Unable to instatiate pipeline '%s': %s\n",
1181 spipeline, error->message);
1185 vsink = gst_bin_get_by_name (GST_BIN (state->pipeline), "vsink");
1188 g_printerr ("Unable to find a fakesink named 'vsink'");
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);
1196 g_signal_connect (vsink, "preroll-handoff", G_CALLBACK (preroll_cb), state);
1197 g_signal_connect (vsink, "handoff", G_CALLBACK (buffers_cb), state);
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);
1204 state->vsink = gst_object_ref (vsink);
1208 //------------------------------------------------------------------------------
1211 report_position_duration (APP_STATE_T * state)
1213 gint64 position, duration;
1215 duration = pipeline_get_duration (state);
1216 position = pipeline_get_position (state);
1218 if (position != -1) {
1219 g_print ("\n position / duration: %" GST_TIME_FORMAT,
1220 GST_TIME_ARGS (position));
1222 g_print ("\n position / duration: unknown");
1225 if (duration != -1) {
1226 g_print (" / %" GST_TIME_FORMAT, GST_TIME_ARGS (duration));
1228 g_print (" / unknown");
1234 seek_forward (APP_STATE_T * state)
1236 gint64 position, duration;
1238 duration = pipeline_get_duration (state);
1239 position = pipeline_get_position (state);
1241 if (position != -1) {
1242 position += 30 * GST_SECOND;
1243 if (duration != -1) {
1244 position = MIN (position, duration);
1246 pipeline_seek (state, position);
1251 seek_backward (APP_STATE_T * state)
1255 position = pipeline_get_position (state);
1257 if (position != -1) {
1258 position -= 30 * GST_SECOND;
1259 position = MAX (position, 0);
1260 pipeline_seek (state, position);
1266 if ((*t == ' ') || (*t == '\n') || (*t == '\t') || (*t == '\r')) \
1272 /* Process keyboard input */
1274 handle_keyboard (GIOChannel * source, GIOCondition cond, APP_STATE_T * state)
1279 if (g_io_channel_read_line (source, &str, NULL, NULL,
1280 NULL) == G_IO_STATUS_NORMAL) {
1288 if (state->animate) {
1289 state->animate = FALSE;
1291 state->animate = TRUE;
1295 pipeline_pause (state);
1298 pipeline_play (state);
1301 report_position_duration (state);
1304 seek_forward (state);
1307 seek_backward (state);
1310 flush_start (state);
1311 gst_element_set_state (state->pipeline, GST_STATE_READY);
1319 static GstBusSyncReply
1320 bus_sync_handler (GstBus * bus, GstMessage * message, GstPipeline * data)
1322 return GST_BUS_PASS;
1325 /* on error print the error and quit the application */
1327 error_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
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);
1344 buffering_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
1348 gst_message_parse_buffering (msg, &percent);
1349 g_print ("Buffering %3d%%\r", percent);
1351 pipeline_pause (state);
1354 pipeline_play (state);
1358 /* on EOS just quit the application */
1360 eos_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
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);
1369 state_changed_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
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,
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);
1383 qos_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
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
1390 name, state->rendered, state->dropped,
1391 (fmt == GST_FORMAT_BUFFERS ? "frames" : "samples"));
1395 //==============================================================================
1400 #if defined (USE_OMX_TARGET_RPI)
1401 DISPMANX_UPDATE_HANDLE_T dispman_update;
1404 if (state->fshader) {
1405 glDeleteShader (state->fshader);
1406 glDetachShader (state->program, state->fshader);
1409 if (state->vshader) {
1410 glDeleteShader (state->vshader);
1411 glDetachShader (state->program, state->vshader);
1415 glDeleteProgram (state->program);
1418 glDeleteTextures (1, &state->tex);
1421 glClear (GL_COLOR_BUFFER_BIT);
1422 eglSwapBuffers (state->display, state->surface);
1424 /* Release OpenGL resources */
1425 eglMakeCurrent (state->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
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);
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);
1446 //==============================================================================
1451 TRACE_VC_MEMORY ("state 0");
1453 #if defined (USE_OMX_TARGET_RPI)
1455 TRACE_VC_MEMORY ("after bcm_host_init");
1458 /* Create surface and gl context */
1460 TRACE_VC_MEMORY ("after init_ogl");
1464 render_func (gpointer data)
1467 state->running = TRUE;
1470 handle_queued_objects (state);
1472 } while (state->running == TRUE);
1479 main (int argc, char **argv)
1482 GOptionContext *ctx;
1483 GIOChannel *io_stdin;
1486 GOptionEntry options[] = {
1491 /* Clear application state */
1492 memset (state, 0, sizeof (*state));
1493 state->animate = TRUE;
1494 state->current_buffer = NULL;
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);
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);
1512 g_option_context_free (ctx);
1515 g_print ("Usage: %s <URI> or <PIPELINE-DESCRIPTION>\n", argv[0]);
1519 /* Initialize GStreamer */
1520 gst_init (&argc, &argv);
1522 /* initialize inter thread comunnication */
1523 init_intercom (state);
1525 TRACE_VC_MEMORY ("state 0");
1527 if (!(rthread = g_thread_new ("render", (GThreadFunc) render_func, NULL))) {
1528 g_print ("Render thread create failed\n");
1532 /* Initialize player */
1533 if (gst_uri_is_valid (argv[1])) {
1534 res = init_playbin_player (state, argv[1]);
1536 res = init_parse_launch_player (state, argv[1]);
1542 /* Create a GLib Main Loop */
1543 state->main_loop = g_main_loop_new (NULL, FALSE);
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);
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"
1561 /* Connect the bus handlers */
1562 bus = gst_element_get_bus (state->pipeline);
1564 gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bus_sync_handler, state,
1567 gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
1568 gst_bus_enable_sync_message_emission (bus);
1570 g_signal_connect (G_OBJECT (bus), "message::error", (GCallback) error_cb,
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);
1580 /* Make player start playing */
1581 gst_element_set_state (state->pipeline, GST_STATE_PLAYING);
1583 /* Start the mainloop */
1584 g_main_loop_run (state->main_loop);
1587 /* Release pipeline */
1588 if (state->pipeline) {
1589 gst_element_set_state (state->pipeline, GST_STATE_NULL);
1591 gst_object_unref (state->vsink);
1592 state->vsink = NULL;
1595 gst_object_unref (state->pipeline);
1598 /* Unref the mainloop */
1599 if (state->main_loop) {
1600 g_main_loop_unref (state->main_loop);
1603 /* Stop rendering thread */
1604 state->running = FALSE;
1605 g_thread_join (rthread);
1608 gst_caps_unref (state->caps);
1612 terminate_intercom (state);
1614 TRACE_VC_MEMORY ("at exit");