8cc90d5f5acb505a5a123c666835155afe847403
[platform/upstream/gstreamer.git] / ext / eglgles / gsteglglessink.c
1 /*
2  * GStreamer EGL/GLES Sink
3  * Copyright (C) 2012 Collabora Ltd.
4  *   @author: Reynaldo H. Verdejo Pinochet <reynaldo@collabora.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  *
24  * Alternatively, the contents of this file may be used under the
25  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
26  * which case the following provisions apply instead of the ones
27  * mentioned above:
28  *
29  * This library is free software; you can redistribute it and/or
30  * modify it under the terms of the GNU Library General Public
31  * License as published by the Free Software Foundation; either
32  * version 2 of the License, or (at your option) any later version.
33  *
34  * This library is distributed in the hope that it will be useful,
35  * but WITHOUT ANY WARRANTY; without even the implied warranty of
36  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
37  * Library General Public License for more details.
38  *
39  * You should have received a copy of the GNU Library General Public
40  * License along with this library; if not, write to the
41  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
42  * Boston, MA 02111-1307, USA.
43  */
44
45 /**
46  * SECTION:element-eglglessink
47  *
48  * This is a vout sink using EGL/GLES.
49  *
50  * <refsect2>
51  * <title>Rationale on OpenGL ES version</title>
52  * <para>
53  * This Sink uses GLESv2
54  * </para>
55  * </refsect2>
56  *
57  * <refsect2>
58  * <title>Example launch line</title>
59  * |[
60  * gst-launch -v -m videotestsrc ! eglglessink
61  * ]|
62  * </refsect2>
63  *
64  * <refsect2>
65  * <title>Example launch line with forced slow path rendering</title>
66  * <para>
67  * The sink will chose a buffer copy-over slow rendering path even
68  * if needed EGL/GLES extensions to use a fast rendering path are
69  * available.
70  * </para>
71  * |[
72  * gst-launch -v -m videotestsrc ! eglglessink force_rendering_slow=TRUE
73  * ]|
74  * </refsect2>
75  *
76  * <refsect2>
77  * <title>Example launch line with internal window creation disabled</title>
78  * <para>
79  * The sink will wait for a window handle through it's xOverlay interface
80  * even if internal window creation is supported by the platform and
81  * implemented.
82  * </para>
83  * |[
84  * gst-launch -v -m videotestsrc ! eglglessink can_create_window=FALSE
85  * ]|
86  * </refsect2>
87  */
88
89 #ifdef HAVE_CONFIG_H
90 #  include <config.h>
91 #endif
92
93 #include <string.h>
94 #include <gst/gst.h>
95 #include <gst/video/video.h>
96 #include <gst/video/gstvideosink.h>
97 #include <gst/interfaces/xoverlay.h>
98
99 #include <EGL/egl.h>
100 #include <EGL/eglext.h>
101 #include <GLES2/gl2.h>
102 #include <GLES2/gl2ext.h>
103
104 #include "video_platform_wrapper.h"
105
106 #include "gsteglglessink.h"
107
108 GST_DEBUG_CATEGORY_STATIC (gst_eglglessink_debug);
109 #define GST_CAT_DEFAULT gst_eglglessink_debug
110
111 /* XXX: These should be defined per model someway
112  * but the Galaxy Nexus's were taken as a reference
113  * for now on:
114  */
115 #define EGLGLESSINK_MAX_FRAME_WIDTH 1280
116 #define EGLGLESSINK_MAX_FRAME_HEIGHT 720
117
118 /* These are only needed for the fast rendering path */
119 #ifdef EGL_KHR_image
120 static PFNEGLCREATEIMAGEKHRPROC my_eglCreateImageKHR;
121 static PFNEGLDESTROYIMAGEKHRPROC my_eglDestroyImageKHR;
122
123 #ifdef EGL_KHR_lock_surface
124 static PFNEGLLOCKSURFACEKHRPROC my_eglLockSurfaceKHR;
125 static PFNEGLUNLOCKSURFACEKHRPROC my_eglUnlockSurfaceKHR;
126
127 static EGLint lock_attribs[] = {
128   EGL_MAP_PRESERVE_PIXELS_KHR, EGL_TRUE,
129   EGL_LOCK_USAGE_HINT_KHR, EGL_READ_SURFACE_BIT_KHR | EGL_WRITE_SURFACE_BIT_KHR,
130   EGL_NONE
131 };
132
133 #ifdef GL_OES_EGL_image
134 static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC my_glEGLImageTargetTexture2DOES;
135 #define EGL_FAST_RENDERING_POSSIBLE 1
136 #endif
137 #endif
138 #endif
139
140 /* *INDENT-OFF* */
141 static const char *vert_COPY_prog = {
142       "attribute vec3 position;"
143       "attribute vec2 texpos;"
144       "varying vec2 opos;"
145       "void main(void)"
146       "{"
147       " opos = texpos;"
148       " gl_Position = vec4(position, 1.0);"
149       "}"
150 };
151
152 static const char *frag_COPY_prog = {
153   "precision mediump float;"
154       "varying vec2 opos;"
155       "uniform sampler2D tex;"
156       "void main(void)"
157       "{"
158       " vec4 t = texture2D(tex, opos);"
159       " gl_FragColor = vec4(t.xyz, 1.0);"
160       "}"
161 };
162
163 static const char *frag_REORDER_prog = {
164   "precision mediump float;"
165       "varying vec2 opos;"
166       "uniform sampler2D tex;"
167       "void main(void)"
168       "{"
169       " float r, g, b;"
170       " r = texture2D(tex, opos).%c;"
171       " g = texture2D(tex, opos).%c;"
172       " b = texture2D(tex, opos).%c;"
173       " gl_FragColor = vec4(r, g, b, 1.0);"
174       "}"
175 };
176
177 /* From gst-plugins-gl */
178 static const char *frag_AYUV_prog = {
179       "precision mediump float;"
180       "varying vec2 opos;"
181       "uniform sampler2D tex;"
182       "void main(void) {"
183       "  float r,g,b,y,u,v;"
184       "  vec2 nxy = opos.xy;"
185       "  y=texture2D(tex,nxy).g;"
186       "  u=texture2D(tex,nxy).b;"
187       "  v=texture2D(tex,nxy).a;"
188       "  y=1.1643*(y-0.0625);"
189       "  u=u-0.5;"
190       "  v=v-0.5;"
191       "  r=y+1.5958*v;"
192       "  g=y-0.39173*u-0.81290*v;"
193       "  b=y+2.017*u;"
194       "  gl_FragColor=vec4(r,g,b,1.0);"
195       "}"
196 };
197
198 static const char *frag_PLANAR_YUV_prog = {
199       "precision mediump float;"
200       "varying vec2 opos;"
201       "uniform sampler2D Ytex,Utex,Vtex;"
202       "void main(void) {"
203       "  float r,g,b,y,u,v;"
204       "  vec2 nxy = opos.xy;"
205       "  y=texture2D(Ytex,nxy).r;"
206       "  u=texture2D(Utex,nxy).r;"
207       "  v=texture2D(Vtex,nxy).r;"
208       "  y=1.1643*(y-0.0625);"
209       "  u=u-0.5;"
210       "  v=v-0.5;"
211       "  r=y+1.5958*v;"
212       "  g=y-0.39173*u-0.81290*v;"
213       "  b=y+2.017*u;"
214       "  gl_FragColor=vec4(r,g,b,1.0);"
215       "}"
216 };
217
218 static const char *frag_NV12_NV21_prog = {
219       "precision mediump float;"
220       "varying vec2 opos;"
221       "uniform sampler2D Ytex,UVtex;"
222       "void main(void) {"
223       "  float r,g,b,y,u,v;"
224       "  vec2 nxy = opos.xy;"
225       "  y=texture2D(Ytex,nxy).r;"
226       "  u=texture2D(UVtex,nxy).%c;"
227       "  v=texture2D(UVtex,nxy).%c;"
228       "  y=1.1643*(y-0.0625);"
229       "  u=u-0.5;"
230       "  v=v-0.5;"
231       "  r=y+1.5958*v;"
232       "  g=y-0.39173*u-0.81290*v;"
233       "  b=y+2.017*u;"
234       "  gl_FragColor=vec4(r,g,b,1.0);"
235       "}"
236 };
237
238 static const char *frag_YUY2_UYVY_prog = {
239       "precision mediump float;"
240       "varying vec2 opos;"
241       "uniform sampler2D Ytex, UVtex;"
242       "void main(void) {"
243       "  float fx, fy, y, u, v, r, g, b;"
244       "  fx = opos.x;"
245       "  fy = opos.y;"
246       "  y = texture2D(Ytex,vec2(fx,fy)).%c;"
247       "  u = texture2D(UVtex,vec2(fx,fy)).%c;"
248       "  v = texture2D(UVtex,vec2(fx,fy)).%c;"
249       "  y=1.1643*(y-0.0625);"
250       "  u=u-0.5;"
251       "  v=v-0.5;"
252       "  r=y+1.5958*v;"
253       "  g=y-0.39173*u-0.81290*v;"
254       "  b=y+2.017*u;"
255       "  gl_FragColor=vec4(r,g,b,1.0);"
256       "}"
257 };
258
259
260 /* *INDENT-ON* */
261
262 /* Input capabilities.
263  *
264  * Note: OpenGL ES Standard does not mandate YUV support.
265  */
266 static GstStaticPadTemplate gst_eglglessink_sink_template_factory =
267     GST_STATIC_PAD_TEMPLATE ("sink",
268     GST_PAD_SINK,
269     GST_PAD_ALWAYS,
270     GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_BGRA ";"
271         GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_ABGR ";"
272         GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx ";"
273         GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";"
274         GST_VIDEO_CAPS_YUV
275         ("{ AYUV, Y444, I420, YV12, NV12, NV21, YUY2, UYVY, Y42B, Y41B }") ";"
276         GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";" GST_VIDEO_CAPS_RGB_16));
277
278 /* Filter signals and args */
279 enum
280 {
281   /* FILL ME */
282   LAST_SIGNAL
283 };
284
285 enum
286 {
287   PROP_0,
288   PROP_SILENT,
289   PROP_CREATE_WINDOW,
290   PROP_FORCE_ASPECT_RATIO,
291   PROP_DEFAULT_HEIGHT,
292   PROP_DEFAULT_WIDTH,
293   PROP_FORCE_RENDERING_SLOW
294 };
295
296 /* will probably move elsewhere */
297 static const EGLint eglglessink_RGBA8888_attribs[] = {
298   EGL_RED_SIZE, 8,
299   EGL_GREEN_SIZE, 8,
300   EGL_BLUE_SIZE, 8,
301   EGL_ALPHA_SIZE, 8,
302   EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
303   EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
304   EGL_NONE
305 };
306
307 static const EGLint eglglessink_RGB888_attribs[] = {
308   EGL_RED_SIZE, 8,
309   EGL_GREEN_SIZE, 8,
310   EGL_BLUE_SIZE, 8,
311   EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
312   EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
313   EGL_NONE
314 };
315
316 static const EGLint eglglessink_RGB565_attribs[] = {
317   EGL_RED_SIZE, 5,
318   EGL_GREEN_SIZE, 6,
319   EGL_BLUE_SIZE, 5,
320   EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
321   EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
322   EGL_NONE
323 };
324
325 static void gst_eglglessink_finalize (GObject * object);
326 static void gst_eglglessink_get_property (GObject * object, guint prop_id,
327     GValue * value, GParamSpec * pspec);
328 static void gst_eglglessink_set_property (GObject * object, guint prop_id,
329     const GValue * value, GParamSpec * pspec);
330 static GstStateChangeReturn gst_eglglessink_change_state (GstElement * element,
331     GstStateChange transition);
332 static GstFlowReturn gst_eglglessink_show_frame (GstVideoSink * vsink,
333     GstBuffer * buf);
334 static gboolean gst_eglglessink_setcaps (GstBaseSink * bsink, GstCaps * caps);
335 static gboolean gst_eglglessink_start (GstBaseSink * sink);
336 static gboolean gst_eglglessink_stop (GstBaseSink * sink);
337 static GstFlowReturn gst_eglglessink_buffer_alloc (GstBaseSink * sink,
338     guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
339 static GstCaps *gst_eglglessink_getcaps (GstBaseSink * bsink);
340
341 /* XOverlay interface cruft */
342 static gboolean gst_eglglessink_interface_supported
343     (GstImplementsInterface * iface, GType type);
344 static void gst_eglglessink_implements_init
345     (GstImplementsInterfaceClass * klass);
346 static void gst_eglglessink_xoverlay_init (GstXOverlayClass * iface);
347 static void gst_eglglessink_init_interfaces (GType type);
348
349 /* Actual XOverlay interface funcs */
350 static void gst_eglglessink_expose (GstXOverlay * overlay);
351 static void gst_eglglessink_set_window_handle (GstXOverlay * overlay,
352     guintptr id);
353 static void gst_eglglessink_set_render_rectangle (GstXOverlay * overlay, gint x,
354     gint y, gint width, gint height);
355
356 /* Custom Buffer funcs */
357 static void gst_eglglesbuffer_destroy (GstEglGlesBuffer * eglglessink);
358 static void gst_eglglesbuffer_init (GstEglGlesBuffer * eglglessink,
359     gpointer g_class);
360 static GType gst_eglglesbuffer_get_type (void);
361 static GstEglGlesImageFmt *gst_eglglessink_get_compat_format_from_caps
362     (GstEglGlesSink * eglglessink, GstCaps * caps);
363 static void gst_eglglesbuffer_finalize (GstEglGlesBuffer * eglglessink);
364 static void gst_eglglesbuffer_class_init (gpointer g_class,
365     gpointer class_data);
366 static void gst_eglglesbuffer_free (GstEglGlesBuffer * eglglesbuffer);
367 static GstEglGlesBuffer *gst_eglglesbuffer_new (GstEglGlesSink * eglglessink,
368     GstCaps * caps);
369 static EGLint *gst_eglglesbuffer_create_native (EGLNativeWindowType win,
370     EGLConfig config, EGLNativeDisplayType display, const EGLint * egl_attribs);
371
372 /* Utility */
373 static EGLNativeWindowType gst_eglglessink_create_window (GstEglGlesSink *
374     eglglessink, gint width, gint height);
375 static inline gint
376 gst_eglglessink_fill_supported_fbuffer_configs (GstEglGlesSink * eglglessink);
377 static gboolean gst_eglglessink_init_egl_display (GstEglGlesSink * eglglessink);
378 static gboolean gst_eglglessink_choose_config (GstEglGlesSink * eglglessink);
379 static gboolean gst_eglglessink_init_egl_surface (GstEglGlesSink * eglglessink);
380 static void gst_eglglessink_init_egl_exts (GstEglGlesSink * eglglessink);
381 static gboolean gst_eglglessink_setup_vbo (GstEglGlesSink * eglglessink,
382     gboolean reset);
383 static GstFlowReturn gst_eglglessink_render_and_display (GstEglGlesSink * sink,
384     GstBuffer * buf);
385 static inline gboolean got_gl_error (const char *wtf);
386 static inline void show_egl_error (const char *wtf);
387 static void gst_eglglessink_wipe_fmt (gpointer data);
388 static inline gboolean egl_init (GstEglGlesSink * eglglessink);
389
390 static GstBufferClass *gsteglglessink_buffer_parent_class = NULL;
391 #define GST_TYPE_EGLGLESBUFFER (gst_eglglesbuffer_get_type())
392 #define GST_IS_EGLGLESBUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_EGLGLESBUFFER))
393 #define GST_EGLGLESBUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_EGLGLESBUFFER, GstEglGlesBuffer))
394 #define GST_EGLGLESBUFFER_CAST(obj) ((GstEglGlesBuffer *)(obj))
395
396
397 GST_BOILERPLATE_FULL (GstEglGlesSink, gst_eglglessink, GstVideoSink,
398     GST_TYPE_VIDEO_SINK, gst_eglglessink_init_interfaces);
399
400 /* Custom Buffer Funcs */
401 /* XXX: Drafted implementation */
402 static EGLint *
403 gst_eglglesbuffer_create_native (EGLNativeWindowType win,
404     EGLConfig config, EGLNativeDisplayType display, const EGLint * egl_attribs)
405 {
406   EGLNativePixmapType pix = 0;
407   EGLSurface pix_surface;
408   EGLint *buffer = NULL;
409
410   /* XXX: Need to figure out how to create an egl_native_pixmap_t to
411    * feed to eglCreatePixmapSurface. An option on android: create an
412    * android_native_buffer_t to pass straight to eglCreateImageKHR.
413    */
414
415   pix_surface = eglCreatePixmapSurface (display, config, pix, egl_attribs);
416
417   if (pix_surface == EGL_NO_SURFACE) {
418     show_egl_error ("eglCreatePixmapSurface");
419     GST_CAT_ERROR (GST_CAT_DEFAULT, "Unable to create pixmap surface");
420     goto EGL_ERROR;
421   }
422
423   if (my_eglLockSurfaceKHR (display, pix_surface, lock_attribs) == EGL_FALSE) {
424     show_egl_error ("eglLockSurfaceKHR");
425     GST_CAT_ERROR (GST_CAT_DEFAULT, "Unable to lock surface");
426     goto EGL_ERROR;
427   }
428
429   if (eglQuerySurface (display, pix_surface, EGL_BITMAP_POINTER_KHR, buffer)
430       == EGL_FALSE) {
431     show_egl_error ("eglQuerySurface");
432     GST_CAT_ERROR (GST_CAT_DEFAULT,
433         "Unable to query surface for bitmap pointer");
434     goto EGL_ERROR_LOCKED;
435   }
436
437   return buffer;
438
439 EGL_ERROR_LOCKED:
440   my_eglUnlockSurfaceKHR (display, pix_surface);
441 EGL_ERROR:
442   GST_CAT_ERROR (GST_CAT_DEFAULT, "EGL call returned error %x", eglGetError ());
443   if (!eglDestroySurface (display, pix_surface)) {
444     show_egl_error ("eglDestroySurface");
445     GST_CAT_ERROR (GST_CAT_DEFAULT, "Couldn't destroy surface");
446   }
447   return NULL;
448 }
449
450 static GstEglGlesBuffer *
451 gst_eglglesbuffer_new (GstEglGlesSink * eglglessink, GstCaps * caps)
452 {
453   GstEglGlesBuffer *eglglesbuffer = NULL;
454   GstStructure *structure = NULL;
455   GstEglGlesImageFmt *format;
456
457   g_return_val_if_fail (GST_IS_EGLGLESSINK (eglglessink), NULL);
458   g_return_val_if_fail (caps, NULL);
459
460   eglglesbuffer =
461       (GstEglGlesBuffer *) gst_mini_object_new (GST_TYPE_EGLGLESBUFFER);
462   GST_DEBUG_OBJECT (eglglesbuffer, "Creating new GstEglGlesBuffer");
463
464   structure = gst_caps_get_structure (caps, 0);
465
466   if (!gst_structure_get_int (structure, "width", &eglglesbuffer->width) ||
467       !gst_structure_get_int (structure, "height", &eglglesbuffer->height)) {
468     GST_WARNING ("Failed getting geometry from caps %" GST_PTR_FORMAT, caps);
469   }
470
471   GST_LOG_OBJECT (eglglessink, "creating %dx%d", eglglesbuffer->width,
472       eglglesbuffer->height);
473
474   format = gst_eglglessink_get_compat_format_from_caps (eglglessink, caps);
475
476   if (!format) {
477     GST_WARNING_OBJECT (eglglessink,
478         "Failed to get format from caps %" GST_PTR_FORMAT, caps);
479     GST_ERROR_OBJECT (eglglessink,
480         "Invalid input caps. Failed to create  %dx%d buffer",
481         eglglesbuffer->width, eglglesbuffer->height);
482     goto BEACH_UNLOCKED;
483   }
484
485   eglglesbuffer->format = format->fmt;
486   eglglesbuffer->eglglessink = gst_object_ref (eglglessink);
487
488   eglglesbuffer->image = gst_eglglesbuffer_create_native
489       (eglglessink->window, eglglessink->config, eglglessink->display, NULL);
490   if (!eglglesbuffer->image) {
491     GST_ERROR_OBJECT (eglglessink,
492         "Failed to create native %dx%d image buffer", eglglesbuffer->width,
493         eglglesbuffer->height);
494     goto BEACH_UNLOCKED;
495   }
496
497   GST_BUFFER_DATA (eglglesbuffer) = (guchar *) eglglesbuffer->image;
498   GST_BUFFER_SIZE (eglglesbuffer) = eglglesbuffer->size;
499
500   return eglglesbuffer;
501
502 BEACH_UNLOCKED:
503   gst_eglglesbuffer_free (eglglesbuffer);
504   eglglesbuffer = NULL;
505   return NULL;
506 }
507
508 static void
509 gst_eglglesbuffer_destroy (GstEglGlesBuffer * eglglesbuffer)
510 {
511
512   GstEglGlesSink *eglglessink;
513
514   GST_DEBUG_OBJECT (eglglesbuffer, "Destroying buffer");
515
516   eglglessink = eglglesbuffer->eglglessink;
517   if (G_UNLIKELY (eglglessink == NULL))
518     goto NO_SINK;
519
520   g_return_if_fail (GST_IS_EGLGLESSINK (eglglessink));
521
522   GST_OBJECT_LOCK (eglglessink);
523   GST_DEBUG_OBJECT (eglglessink, "Destroying image");
524
525   if (eglglesbuffer->image) {
526     if (GST_BUFFER_DATA (eglglesbuffer)) {
527       g_free (GST_BUFFER_DATA (eglglesbuffer));
528     }
529     eglglesbuffer->image = NULL;
530     /* XXX: Unallocate EGL/GL especific resources asociated with this
531      * Image here
532      */
533   }
534
535   GST_OBJECT_UNLOCK (eglglessink);
536   eglglesbuffer->eglglessink = NULL;
537   gst_object_unref (eglglessink);
538
539   GST_MINI_OBJECT_CLASS (gsteglglessink_buffer_parent_class)->finalize
540       (GST_MINI_OBJECT (eglglesbuffer));
541
542   return;
543
544 NO_SINK:
545   GST_WARNING ("No sink found");
546   return;
547 }
548
549 /* XXX: Missing implementation.
550  * This function will have the code for maintaing the pool. readding or
551  * destroying the buffers on size or runing/status change. Right now all
552  * it does is to call _destroy.
553  * for a proper implementation take a look at xvimagesink's image buffer
554  * destroy func.
555  */
556 static void
557 gst_eglglesbuffer_finalize (GstEglGlesBuffer * eglglesbuffer)
558 {
559   GstEglGlesSink *eglglessink;
560
561   eglglessink = eglglesbuffer->eglglessink;
562   if (G_UNLIKELY (eglglessink == NULL))
563     goto NO_SINK;
564
565   g_return_if_fail (GST_IS_EGLGLESSINK (eglglessink));
566
567   gst_eglglesbuffer_destroy (eglglesbuffer);
568
569   return;
570
571 NO_SINK:
572   GST_WARNING ("No sink found");
573   return;
574 }
575
576 static void
577 gst_eglglesbuffer_free (GstEglGlesBuffer * eglglesbuffer)
578 {
579   /* Make sure it is not recycled. This is meaningless without
580    * a pool but was left here as a reference
581    */
582   eglglesbuffer->width = -1;
583   eglglesbuffer->height = -1;
584   gst_buffer_unref (GST_BUFFER (eglglesbuffer));
585 }
586
587 static void
588 gst_eglglesbuffer_init (GstEglGlesBuffer * eglglesbuffer, gpointer g_class)
589 {
590   eglglesbuffer->width = 0;
591   eglglesbuffer->height = 0;
592   eglglesbuffer->size = 0;
593   eglglesbuffer->image = NULL;
594   eglglesbuffer->format = GST_EGLGLESSINK_IMAGE_NOFMT;
595 }
596
597 static void
598 gst_eglglesbuffer_class_init (gpointer g_class, gpointer class_data)
599 {
600   GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
601
602   gsteglglessink_buffer_parent_class = g_type_class_peek_parent (g_class);
603
604   mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
605       gst_eglglesbuffer_finalize;
606 }
607
608 static GType
609 gst_eglglesbuffer_get_type (void)
610 {
611   static GType _gst_eglglessink_buffer_type;
612
613   if (G_UNLIKELY (_gst_eglglessink_buffer_type == 0)) {
614     static const GTypeInfo eglglessink_buffer_info = {
615       sizeof (GstBufferClass),
616       NULL,
617       NULL,
618       gst_eglglesbuffer_class_init,
619       NULL,
620       NULL,
621       sizeof (GstEglGlesBuffer),
622       0,
623       (GInstanceInitFunc) gst_eglglesbuffer_init,
624       NULL
625     };
626     _gst_eglglessink_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
627         "GstEglGlesBuffer", &eglglessink_buffer_info, 0);
628   }
629   return _gst_eglglessink_buffer_type;
630 }
631
632 static GstEglGlesImageFmt *
633 gst_eglglessink_get_compat_format_from_caps (GstEglGlesSink * eglglessink,
634     GstCaps * caps)
635 {
636
637   GList *list;
638   GstEglGlesImageFmt *format;
639
640   g_return_val_if_fail (GST_IS_EGLGLESSINK (eglglessink), 0);
641
642   list = eglglessink->supported_fmts;
643
644   /* Traverse the list trying to find a compatible format */
645   while (list) {
646     format = list->data;
647     GST_DEBUG_OBJECT (eglglessink, "Checking compatibility between listed %"
648         GST_PTR_FORMAT " and %" GST_PTR_FORMAT, format->caps, caps);
649     if (format) {
650       if (gst_caps_can_intersect (caps, format->caps)) {
651         GST_INFO_OBJECT (eglglessink, "Found compatible format %d",
652             format->fmt);
653         GST_DEBUG_OBJECT (eglglessink,
654             "Got caps %" GST_PTR_FORMAT " and this format can do %"
655             GST_PTR_FORMAT, caps, format->caps);
656         return format;
657       }
658     }
659     list = g_list_next (list);
660   }
661
662   return NULL;
663 }
664
665 static GstCaps *
666 gst_eglglessink_different_size_suggestion (GstEglGlesSink * eglglessink,
667     GstCaps * caps)
668 {
669   GstCaps *intersection;
670   GstCaps *new_caps;
671   GstStructure *s;
672   gint width, height;
673   gint par_n = 1, par_d = 1;
674   gint dar_n, dar_d;
675   gint w, h;
676
677   new_caps = gst_caps_copy (caps);
678
679   s = gst_caps_get_structure (new_caps, 0);
680
681   gst_structure_get_int (s, "width", &width);
682   gst_structure_get_int (s, "height", &height);
683   gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d);
684
685   gst_structure_remove_field (s, "width");
686   gst_structure_remove_field (s, "height");
687   gst_structure_remove_field (s, "pixel-aspect-ratio");
688
689   intersection = gst_caps_intersect (eglglessink->current_caps, new_caps);
690   gst_caps_unref (new_caps);
691
692   if (gst_caps_is_empty (intersection))
693     return intersection;
694
695   s = gst_caps_get_structure (intersection, 0);
696
697   gst_util_fraction_multiply (width, height, par_n, par_d, &dar_n, &dar_d);
698
699   /* XXX: xvimagesink supports all PARs not sure about our eglglessink
700    * though, need to review this afterwards.
701    */
702
703   gst_structure_fixate_field_nearest_int (s, "width", width);
704   gst_structure_fixate_field_nearest_int (s, "height", height);
705   gst_structure_get_int (s, "width", &w);
706   gst_structure_get_int (s, "height", &h);
707
708   gst_util_fraction_multiply (h, w, dar_n, dar_d, &par_n, &par_d);
709   gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d,
710       NULL);
711
712   return intersection;
713 }
714
715 static GstFlowReturn
716 gst_eglglessink_buffer_alloc (GstBaseSink * bsink, guint64 offset,
717     guint size, GstCaps * caps, GstBuffer ** buf)
718 {
719
720   GstEglGlesSink *eglglessink;
721   GstFlowReturn ret = GST_FLOW_OK;
722   GstEglGlesBuffer *eglglesbuffer = NULL;
723   GstCaps *intersection = NULL;
724   GstStructure *structure = NULL;
725   gint width, height;
726   GstEglGlesImageFmt *format;
727
728   eglglessink = GST_EGLGLESSINK (bsink);
729
730   /* No custom alloc for the slow rendering path */
731   if (eglglessink->rendering_path == GST_EGLGLESSINK_RENDER_SLOW) {
732     GST_INFO_OBJECT (eglglessink, "No custom alloc for slow rendering path");
733     *buf = NULL;
734     return GST_FLOW_OK;
735   }
736
737   if (G_UNLIKELY (!caps))
738     goto NO_CAPS;
739
740   if (G_LIKELY (gst_caps_is_equal (caps, eglglessink->current_caps))) {
741     GST_LOG_OBJECT (eglglessink,
742         "Buffer alloc for same last_caps, reusing caps");
743     intersection = gst_caps_ref (caps);
744     width = GST_VIDEO_SINK_WIDTH (eglglessink);
745     height = GST_VIDEO_SINK_HEIGHT (eglglessink);
746
747     goto REUSE_LAST_CAPS;
748   }
749
750   GST_DEBUG_OBJECT (eglglessink, "Buffer alloc requested size %d with caps %"
751       GST_PTR_FORMAT ", intersecting with our caps %" GST_PTR_FORMAT, size,
752       caps, eglglessink->current_caps);
753
754   /* Check the caps against our current caps */
755   intersection = gst_caps_intersect (eglglessink->current_caps, caps);
756
757   GST_DEBUG_OBJECT (eglglessink, "Intersection in buffer alloc returned %"
758       GST_PTR_FORMAT, intersection);
759
760   if (gst_caps_is_empty (intersection)) {
761     GstCaps *new_caps;
762
763     gst_caps_unref (intersection);
764
765     /* So we don't support this kind of buffer, let's define one we'd like */
766     new_caps = gst_caps_copy (caps);
767
768     structure = gst_caps_get_structure (new_caps, 0);
769     if (!gst_structure_has_field (structure, "width") ||
770         !gst_structure_has_field (structure, "height")) {
771       gst_caps_unref (new_caps);
772       goto INVALID;
773     }
774
775     /* Try different dimensions */
776     intersection =
777         gst_eglglessink_different_size_suggestion (eglglessink, new_caps);
778
779     /* YUV not implemented yet */
780     if (gst_caps_is_empty (intersection)) {
781
782       gst_structure_set_name (structure, "video/x-raw-rgb");
783
784       /* Remove format specific fields */
785       gst_structure_remove_field (structure, "format");
786       gst_structure_remove_field (structure, "endianness");
787       gst_structure_remove_field (structure, "depth");
788       gst_structure_remove_field (structure, "bpp");
789       gst_structure_remove_field (structure, "red_mask");
790       gst_structure_remove_field (structure, "green_mask");
791       gst_structure_remove_field (structure, "blue_mask");
792       gst_structure_remove_field (structure, "alpha_mask");
793
794       /* Reuse intersection with current_caps */
795       intersection = gst_caps_intersect (eglglessink->current_caps, new_caps);
796     }
797
798     /* Try with different dimensions and RGB formats */
799     if (gst_caps_is_empty (intersection))
800       intersection =
801           gst_eglglessink_different_size_suggestion (eglglessink, new_caps);
802
803     /* Clean this copy */
804     gst_caps_unref (new_caps);
805
806     if (gst_caps_is_empty (intersection))
807       goto INCOMPATIBLE;
808   }
809
810   /* Ensure the returned caps are fixed */
811   gst_caps_truncate (intersection);
812
813   GST_DEBUG_OBJECT (eglglessink, "Allocating a buffer with caps %"
814       GST_PTR_FORMAT, intersection);
815   if (gst_caps_is_equal (intersection, caps)) {
816     /* Things work better if we return a buffer with the same caps ptr
817      * as was asked for when we can */
818     gst_caps_replace (&intersection, caps);
819   }
820
821   /* Get image format from caps */
822   format = gst_eglglessink_get_compat_format_from_caps (eglglessink,
823       intersection);
824
825   if (!format)
826     GST_WARNING_OBJECT (eglglessink, "Can't get a compatible format from caps");
827
828   /* Get geometry from caps */
829   structure = gst_caps_get_structure (intersection, 0);
830   if (!gst_structure_get_int (structure, "width", &width) ||
831       !gst_structure_get_int (structure, "height", &height) || !format)
832     goto INVALID_CAPS;
833
834 REUSE_LAST_CAPS:
835
836   GST_DEBUG_OBJECT (eglglessink, "Creating eglglesbuffer");
837   eglglesbuffer = gst_eglglesbuffer_new (eglglessink, intersection);
838
839   if (eglglesbuffer) {
840     /* Make sure the buffer is cleared of any previously used flags */
841     GST_MINI_OBJECT_CAST (eglglesbuffer)->flags = 0;
842     gst_buffer_set_caps (GST_BUFFER_CAST (eglglesbuffer), intersection);
843   }
844
845   *buf = GST_BUFFER_CAST (eglglesbuffer);
846
847 BEACH:
848   if (intersection) {
849     gst_caps_unref (intersection);
850   }
851
852   return ret;
853
854   /* ERRORS */
855 INVALID:
856   {
857     GST_DEBUG_OBJECT (eglglessink, "No width/height on caps!?");
858     ret = GST_FLOW_WRONG_STATE;
859     goto BEACH;
860   }
861 INCOMPATIBLE:
862   {
863     GST_WARNING_OBJECT (eglglessink, "We were requested a buffer with "
864         "caps %" GST_PTR_FORMAT ", but our current caps %" GST_PTR_FORMAT
865         " are completely incompatible!", caps, eglglessink->current_caps);
866     ret = GST_FLOW_NOT_NEGOTIATED;
867     goto BEACH;
868   }
869 INVALID_CAPS:
870   {
871     GST_WARNING_OBJECT (eglglessink, "Invalid caps for buffer allocation %"
872         GST_PTR_FORMAT, intersection);
873     ret = GST_FLOW_NOT_NEGOTIATED;
874     goto BEACH;
875   }
876 NO_CAPS:
877   {
878     GST_WARNING_OBJECT (eglglessink, "Have no caps, doing fallback allocation");
879     *buf = NULL;
880     ret = GST_FLOW_OK;
881     goto BEACH;
882   }
883 }
884
885 static inline gint
886 gst_eglglessink_fill_supported_fbuffer_configs (GstEglGlesSink * eglglessink)
887 {
888   gint ret = 0;
889   EGLint cfg_number;
890   GstEglGlesImageFmt *format;
891   GstCaps *caps;
892
893   GST_DEBUG_OBJECT (eglglessink,
894       "Building initial list of wanted eglattribs per format");
895
896   /* Init supported format/caps list */
897   g_mutex_lock (eglglessink->flow_lock);
898
899   caps = gst_caps_new_empty ();
900
901   if (eglChooseConfig (eglglessink->display, eglglessink_RGBA8888_attribs,
902           NULL, 1, &cfg_number) != EGL_FALSE) {
903     format = g_new0 (GstEglGlesImageFmt, 1);
904     format->fmt = GST_EGLGLESSINK_IMAGE_RGBA8888;
905     format->attribs = eglglessink_RGBA8888_attribs;
906     format->caps = gst_video_format_new_template_caps (GST_VIDEO_FORMAT_RGBA);
907     gst_caps_append (format->caps,
908         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_BGRA));
909     gst_caps_append (format->caps,
910         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_ARGB));
911     gst_caps_append (format->caps,
912         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_ABGR));
913     gst_caps_append (format->caps,
914         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_RGBx));
915     gst_caps_append (format->caps,
916         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_BGRx));
917     gst_caps_append (format->caps,
918         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_xRGB));
919     gst_caps_append (format->caps,
920         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_xBGR));
921     gst_caps_append (format->caps,
922         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_AYUV));
923     gst_caps_append (format->caps,
924         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_Y444));
925     gst_caps_append (format->caps,
926         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_I420));
927     gst_caps_append (format->caps,
928         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_YV12));
929     gst_caps_append (format->caps,
930         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_NV12));
931     gst_caps_append (format->caps,
932         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_NV21));
933     gst_caps_append (format->caps,
934         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_YUY2));
935     gst_caps_append (format->caps,
936         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_UYVY));
937     gst_caps_append (format->caps,
938         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_Y42B));
939     gst_caps_append (format->caps,
940         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_Y41B));
941     eglglessink->supported_fmts =
942         g_list_append (eglglessink->supported_fmts, format);
943     ret++;
944     gst_caps_append (caps, gst_caps_ref (format->caps));
945   } else {
946     GST_INFO_OBJECT (eglglessink,
947         "EGL display doesn't support RGBA8888 config");
948   }
949
950   if (eglChooseConfig (eglglessink->display, eglglessink_RGB888_attribs,
951           NULL, 1, &cfg_number) != EGL_FALSE) {
952     format = g_new0 (GstEglGlesImageFmt, 1);
953     format->fmt = GST_EGLGLESSINK_IMAGE_RGB888;
954     format->attribs = eglglessink_RGB888_attribs;
955     format->caps = gst_video_format_new_template_caps (GST_VIDEO_FORMAT_RGB);
956     gst_caps_append (format->caps,
957         gst_video_format_new_template_caps (GST_VIDEO_FORMAT_BGR));
958     eglglessink->supported_fmts =
959         g_list_append (eglglessink->supported_fmts, format);
960     ret++;
961     gst_caps_append (caps, gst_caps_ref (format->caps));
962   } else {
963     GST_INFO_OBJECT (eglglessink, "EGL display doesn't support RGB888 config");
964   }
965
966   if (eglChooseConfig (eglglessink->display, eglglessink_RGB565_attribs,
967           NULL, 1, &cfg_number) != EGL_FALSE) {
968     format = g_new0 (GstEglGlesImageFmt, 1);
969     format->fmt = GST_EGLGLESSINK_IMAGE_RGB565;
970     format->attribs = eglglessink_RGB565_attribs;
971     format->caps = gst_video_format_new_template_caps (GST_VIDEO_FORMAT_RGB16);
972     eglglessink->supported_fmts =
973         g_list_append (eglglessink->supported_fmts, format);
974     ret++;
975     gst_caps_append (caps, gst_caps_ref (format->caps));
976   } else {
977     GST_INFO_OBJECT (eglglessink, "EGL display doesn't support RGB565 config");
978   }
979
980   gst_caps_replace (&eglglessink->sinkcaps, caps);
981   gst_caps_unref (caps);
982
983   g_mutex_unlock (eglglessink->flow_lock);
984
985   return ret;
986 }
987
988 static inline gboolean
989 egl_init (GstEglGlesSink * eglglessink)
990 {
991   if (!platform_wrapper_init ()) {
992     GST_ERROR_OBJECT (eglglessink, "Couldn't init EGL platform wrapper");
993     goto HANDLE_ERROR;
994   }
995
996   if (!gst_eglglessink_init_egl_display (eglglessink)) {
997     GST_ERROR_OBJECT (eglglessink, "Couldn't init EGL display");
998     goto HANDLE_ERROR;
999   }
1000
1001   gst_eglglessink_init_egl_exts (eglglessink);
1002
1003   if (!gst_eglglessink_fill_supported_fbuffer_configs (eglglessink)) {
1004     GST_ERROR_OBJECT (eglglessink, "Display support NONE of our configs");
1005     goto HANDLE_ERROR;
1006   }
1007
1008   g_mutex_lock (eglglessink->flow_lock);
1009   eglglessink->egl_started = TRUE;
1010   g_mutex_unlock (eglglessink->flow_lock);
1011
1012   return TRUE;
1013
1014 HANDLE_ERROR:
1015   GST_ERROR_OBJECT (eglglessink, "Failed to perform EGL init");
1016   return FALSE;
1017 }
1018
1019 gboolean
1020 gst_eglglessink_start (GstBaseSink * sink)
1021 {
1022   GstEglGlesSink *eglglessink = GST_EGLGLESSINK (sink);
1023
1024   if (!eglglessink->egl_started) {
1025     GST_ERROR_OBJECT (eglglessink, "EGL uninitialized. Bailing out");
1026     goto HANDLE_ERROR;
1027   }
1028
1029   /* Ask for a window to render to */
1030   if (!eglglessink->have_window)
1031     gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (eglglessink));
1032
1033   if (!eglglessink->have_window && !eglglessink->can_create_window) {
1034     GST_ERROR_OBJECT (eglglessink, "Window handle unavailable and we "
1035         "were instructed not to create an internal one. Bailing out.");
1036     goto HANDLE_ERROR;
1037   }
1038
1039   return TRUE;
1040
1041 HANDLE_ERROR:
1042   GST_ERROR_OBJECT (eglglessink, "Couldn't start");
1043   return FALSE;
1044 }
1045
1046 /* Drafted */
1047 gboolean
1048 gst_eglglessink_stop (GstBaseSink * sink)
1049 {
1050   GstEglGlesSink *eglglessink = GST_EGLGLESSINK (sink);
1051
1052   /* EGL/GLES2 cleanup */
1053
1054   if (eglglessink->rendering_path == GST_EGLGLESSINK_RENDER_SLOW) {
1055     glDeleteBuffers (1, &eglglessink->vdata);
1056     glDeleteBuffers (1, &eglglessink->tdata);
1057     glDeleteBuffers (1, &eglglessink->idata);
1058     eglglessink->have_vbo = FALSE;
1059
1060     glDeleteShader (eglglessink->fragshader);
1061     glDeleteShader (eglglessink->vertshader);
1062
1063     glDeleteTextures (eglglessink->n_textures, eglglessink->texture);
1064     eglglessink->have_texture = FALSE;
1065     eglglessink->n_textures = 0;
1066
1067     glDeleteProgram (eglglessink->program);
1068   }
1069
1070   if (eglglessink->surface) {
1071     eglDestroySurface (eglglessink->display, eglglessink->surface);
1072     eglglessink->surface = NULL;
1073     eglglessink->have_surface = FALSE;
1074   }
1075
1076   if (eglglessink->context) {
1077     eglDestroyContext (eglglessink->display, eglglessink->context);
1078     eglglessink->context = NULL;
1079   }
1080
1081   if (eglglessink->using_own_window) {
1082     platform_destroy_native_window (eglglessink->display, eglglessink->window);
1083     eglglessink->window = NULL;
1084     eglglessink->have_window = FALSE;
1085   }
1086
1087   if (eglglessink->current_caps) {
1088     gst_caps_unref (eglglessink->current_caps);
1089     eglglessink->current_caps = NULL;
1090   }
1091
1092   return TRUE;
1093 }
1094
1095 static void
1096 gst_eglglessink_xoverlay_init (GstXOverlayClass * iface)
1097 {
1098   iface->set_window_handle = gst_eglglessink_set_window_handle;
1099   iface->expose = gst_eglglessink_expose;
1100   iface->set_render_rectangle = gst_eglglessink_set_render_rectangle;
1101 }
1102
1103 static gboolean
1104 gst_eglglessink_interface_supported (GstImplementsInterface * iface, GType type)
1105 {
1106   return (type == GST_TYPE_X_OVERLAY);
1107 }
1108
1109 static void
1110 gst_eglglessink_implements_init (GstImplementsInterfaceClass * klass)
1111 {
1112   klass->supported = gst_eglglessink_interface_supported;
1113 }
1114
1115 static inline gboolean
1116 got_gl_error (const char *wtf)
1117 {
1118   GLuint error = GL_NO_ERROR;
1119
1120   if ((error = glGetError ()) != GL_NO_ERROR) {
1121     GST_CAT_ERROR (GST_CAT_DEFAULT, "GL ERROR: %s returned %x", wtf, error);
1122     return TRUE;
1123   }
1124   return FALSE;
1125 }
1126
1127 static inline void
1128 show_egl_error (const char *wtf)
1129 {
1130   EGLint error;
1131
1132   if ((error = eglGetError ()) != EGL_SUCCESS)
1133     GST_CAT_DEBUG (GST_CAT_DEFAULT, "EGL ERROR: %s returned %x", wtf, error);
1134 }
1135
1136 static EGLNativeWindowType
1137 gst_eglglessink_create_window (GstEglGlesSink * eglglessink, gint width,
1138     gint height)
1139 {
1140   EGLNativeWindowType window = 0;
1141
1142   if (!eglglessink->can_create_window) {
1143     GST_ERROR_OBJECT (eglglessink, "This sink can't create a window by itself");
1144     return window;
1145   } else
1146     GST_INFO_OBJECT (eglglessink, "Attempting internal window creation");
1147
1148   if (!width && !height) {      /* Create a default size window */
1149     width = eglglessink->window_default_width;
1150     height = eglglessink->window_default_height;
1151   }
1152
1153   window = platform_create_native_window (width, height);
1154   if (!window) {
1155     GST_ERROR_OBJECT (eglglessink, "Could not create window");
1156     return window;
1157   }
1158   gst_x_overlay_got_window_handle (GST_X_OVERLAY (eglglessink),
1159       (guintptr) window);
1160   return window;
1161 }
1162
1163 /* XXX: Should implement (redisplay)
1164  * We need at least the last buffer stored for this to work
1165  */
1166 static void
1167 gst_eglglessink_expose (GstXOverlay * overlay)
1168 {
1169   GstEglGlesSink *eglglessink;
1170   GstFlowReturn ret;
1171
1172   eglglessink = GST_EGLGLESSINK (overlay);
1173   GST_DEBUG_OBJECT (eglglessink, "Expose catched, redisplay");
1174
1175   /* Logic would be to get _render_and_display() to use
1176    * last seen buffer to render from when NULL it's
1177    * passed on */
1178   GST_WARNING_OBJECT (eglglessink, "_expose() not implemented");
1179   ret = gst_eglglessink_render_and_display (eglglessink, NULL);
1180   if (ret == GST_FLOW_ERROR)
1181     GST_ERROR_OBJECT (eglglessink, "Redisplay failed");
1182 }
1183
1184 /* Checks available egl/gles extensions and chooses
1185  * a suitable rendering path from GstEglGlesSinkRenderingPath
1186  * accordingly. This function can only be called after an
1187  * EGL context has been made current.
1188  */
1189 static void
1190 gst_eglglessink_init_egl_exts (GstEglGlesSink * eglglessink)
1191 {
1192   const char *eglexts;
1193   unsigned const char *glexts;
1194
1195   eglexts = eglQueryString (eglglessink->display, EGL_EXTENSIONS);
1196   glexts = glGetString (GL_EXTENSIONS);
1197
1198   GST_DEBUG_OBJECT (eglglessink, "Available EGL extensions: %s\n", eglexts);
1199   GST_DEBUG_OBJECT (eglglessink, "Available GLES extensions: %s\n", glexts);
1200
1201 #ifdef EGL_FAST_RENDERING_POSSIBLE
1202   /* OK Fast rendering should be possible from the declared
1203    * extensions on the eglexts/glexts.h headers
1204    */
1205
1206   /* Check for support from claimed EGL/GLES extensions */
1207
1208   if (!strstr (eglexts, "EGL_KHR_image"))
1209     goto KHR_IMAGE_NA;
1210   if (!strstr (eglexts, "EGL_KHR_lock_surface"))
1211     goto SURFACE_LOCK_NA;
1212   if (!strstr ((char *) glexts, "GL_OES_EGL_image"))
1213     goto TEXTURE_2DOES_NA;
1214
1215   /* Check for actual extension proc addresses */
1216
1217   my_eglCreateImageKHR =
1218       (PFNEGLCREATEIMAGEKHRPROC) eglGetProcAddress ("eglCreateImageKHR");
1219   my_eglDestroyImageKHR =
1220       (PFNEGLDESTROYIMAGEKHRPROC) eglGetProcAddress ("eglDestroyImageKHR");
1221
1222   if (!my_eglCreateImageKHR || !my_eglDestroyImageKHR) {
1223   KHR_IMAGE_NA:
1224     GST_INFO_OBJECT (eglglessink, "Extension missing: EGL_KHR_image");
1225     goto MISSING_EXTS;
1226   }
1227
1228   my_eglLockSurfaceKHR =
1229       (PFNEGLLOCKSURFACEKHRPROC) eglGetProcAddress ("eglLockSurfaceKHR");
1230   my_eglUnlockSurfaceKHR =
1231       (PFNEGLUNLOCKSURFACEKHRPROC) eglGetProcAddress ("eglUnlockSurfaceKHR");
1232
1233   if (!my_eglLockSurfaceKHR || !my_eglUnlockSurfaceKHR) {
1234   SURFACE_LOCK_NA:
1235     GST_INFO_OBJECT (eglglessink, "Extension missing: EGL_KHR_lock_surface");
1236     goto MISSING_EXTS;
1237   }
1238
1239   my_glEGLImageTargetTexture2DOES =
1240       (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) eglGetProcAddress
1241       ("glEGLImageTargetTexture2DOES");
1242
1243   if (!my_glEGLImageTargetTexture2DOES) {
1244   TEXTURE_2DOES_NA:
1245     GST_INFO_OBJECT (eglglessink, "Extension missing: GL_OES_EGL_image");
1246     goto MISSING_EXTS;
1247   }
1248
1249   if (!eglglessink->force_rendering_slow) {
1250     GST_INFO_OBJECT (eglglessink,
1251         "Have needed extensions for fast rendering path");
1252   } else {
1253     GST_WARNING_OBJECT (eglglessink,
1254         "Extension check passed but slow rendering path being forced");
1255     goto SLOW_PATH_SELECTED;
1256   }
1257
1258   /* Extension check passed. Enable fast rendering path */
1259   eglglessink->rendering_path = GST_EGLGLESSINK_RENDER_FAST;
1260   GST_INFO_OBJECT (eglglessink, "Using fast rendering path");
1261   return;
1262 #endif
1263
1264 MISSING_EXTS:
1265   GST_WARNING_OBJECT (eglglessink,
1266       "Extensions missing. Can't use fast rendering path");
1267 SLOW_PATH_SELECTED:
1268   eglglessink->rendering_path = GST_EGLGLESSINK_RENDER_SLOW;
1269   GST_INFO_OBJECT (eglglessink, "Using slow rendering path");
1270   return;
1271 }
1272
1273 static gboolean
1274 gst_eglglessink_setup_vbo (GstEglGlesSink * eglglessink, gboolean reset)
1275 {
1276
1277   g_mutex_lock (eglglessink->flow_lock);
1278
1279   GST_INFO_OBJECT (eglglessink, "VBO setup. have_vbo:%d, should reset %d",
1280       eglglessink->have_vbo, reset);
1281
1282   if (eglglessink->have_vbo && reset) {
1283     glDeleteBuffers (1, &eglglessink->vdata);
1284     glDeleteBuffers (1, &eglglessink->tdata);
1285     glDeleteBuffers (1, &eglglessink->idata);
1286     eglglessink->have_vbo = FALSE;
1287   }
1288
1289   if (!eglglessink->have_vbo) {
1290     GST_DEBUG_OBJECT (eglglessink, "Performing VBO setup");
1291     eglglessink->coordarray[0].x = 1;
1292     eglglessink->coordarray[0].y = 1;
1293     eglglessink->coordarray[0].z = 0;
1294
1295     eglglessink->coordarray[1].x = 1;
1296     eglglessink->coordarray[1].y = -1;
1297     eglglessink->coordarray[1].z = 0;
1298
1299     eglglessink->coordarray[2].x = -1;
1300     eglglessink->coordarray[2].y = 1;
1301     eglglessink->coordarray[2].z = 0;
1302
1303     eglglessink->coordarray[3].x = -1;
1304     eglglessink->coordarray[3].y = -1;
1305     eglglessink->coordarray[3].z = 0;
1306
1307     eglglessink->texarray[0].x = 1;
1308     eglglessink->texarray[0].y = 0;
1309
1310     eglglessink->texarray[1].x = 1;
1311     eglglessink->texarray[1].y = 1;
1312
1313     eglglessink->texarray[2].x = 0;
1314     eglglessink->texarray[2].y = 0;
1315
1316     eglglessink->texarray[3].x = 0;
1317     eglglessink->texarray[3].y = 1;
1318
1319     eglglessink->indexarray[0] = 0;
1320     eglglessink->indexarray[1] = 1;
1321     eglglessink->indexarray[2] = 2;
1322     eglglessink->indexarray[3] = 3;
1323
1324     glGenBuffers (1, &eglglessink->vdata);
1325     glGenBuffers (1, &eglglessink->tdata);
1326     glGenBuffers (1, &eglglessink->idata);
1327     if (got_gl_error ("glGenBuffers"))
1328       goto HANDLE_ERROR_LOCKED;
1329
1330     glBindBuffer (GL_ARRAY_BUFFER, eglglessink->vdata);
1331     if (got_gl_error ("glBindBuffer vdata"))
1332       goto HANDLE_ERROR_LOCKED;
1333
1334     glBufferData (GL_ARRAY_BUFFER, sizeof (eglglessink->coordarray),
1335         eglglessink->coordarray, GL_STATIC_DRAW);
1336     if (got_gl_error ("glBufferData vdata"))
1337       goto HANDLE_ERROR_LOCKED;
1338
1339     glVertexAttribPointer (eglglessink->coord_pos, 3, GL_FLOAT, GL_FALSE, 0, 0);
1340     if (got_gl_error ("glVertexAttribPointer"))
1341       goto HANDLE_ERROR_LOCKED;
1342
1343     glEnableVertexAttribArray (eglglessink->coord_pos);
1344     if (got_gl_error ("glEnableVertexAttribArray"))
1345       goto HANDLE_ERROR_LOCKED;
1346
1347     glBindBuffer (GL_ARRAY_BUFFER, eglglessink->tdata);
1348     if (got_gl_error ("glBindBuffer tdata"))
1349       goto HANDLE_ERROR_LOCKED;
1350
1351     glBufferData (GL_ARRAY_BUFFER, sizeof (eglglessink->texarray),
1352         eglglessink->texarray, GL_STATIC_DRAW);
1353     if (got_gl_error ("glBufferData tdata"))
1354       goto HANDLE_ERROR_LOCKED;
1355
1356     glVertexAttribPointer (eglglessink->tex_pos, 2, GL_FLOAT, GL_FALSE, 0, 0);
1357     if (got_gl_error ("glVertexAttribPointer"))
1358       goto HANDLE_ERROR_LOCKED;
1359
1360     glEnableVertexAttribArray (eglglessink->tex_pos);
1361     if (got_gl_error ("glEnableVertexAttribArray"))
1362       goto HANDLE_ERROR_LOCKED;
1363
1364     glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, eglglessink->idata);
1365     if (got_gl_error ("glBindBuffer idata"))
1366       goto HANDLE_ERROR_LOCKED;
1367
1368     glBufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (eglglessink->indexarray),
1369         eglglessink->indexarray, GL_STATIC_DRAW);
1370     if (got_gl_error ("glBufferData idata"))
1371       goto HANDLE_ERROR_LOCKED;
1372
1373     eglglessink->have_vbo = TRUE;
1374   } else {
1375     GST_INFO_OBJECT (eglglessink, "Won't perform VBO setup");
1376   }
1377
1378   g_mutex_unlock (eglglessink->flow_lock);
1379   return TRUE;
1380
1381 HANDLE_ERROR_LOCKED:
1382   g_mutex_unlock (eglglessink->flow_lock);
1383   GST_ERROR_OBJECT (eglglessink, "Unable to perform VBO setup");
1384   return FALSE;
1385 }
1386
1387 static gboolean
1388 gst_eglglessink_init_egl_surface (GstEglGlesSink * eglglessink)
1389 {
1390   GLint test;
1391   GLboolean ret;
1392   GLchar *info_log;
1393   const gchar *texnames[3] = { NULL, };
1394   gchar *tmp_prog = NULL;
1395
1396   GST_DEBUG_OBJECT (eglglessink, "Enter EGL surface setup");
1397
1398   g_mutex_lock (eglglessink->flow_lock);
1399
1400   eglglessink->surface = eglCreateWindowSurface (eglglessink->display,
1401       eglglessink->config, eglglessink->window, NULL);
1402
1403   if (eglglessink->surface == EGL_NO_SURFACE) {
1404     show_egl_error ("eglCreateWindowSurface");
1405     GST_ERROR_OBJECT (eglglessink, "Can't create surface");
1406     goto HANDLE_EGL_ERROR_LOCKED;
1407   }
1408
1409   if (!eglMakeCurrent (eglglessink->display, eglglessink->surface,
1410           eglglessink->surface, eglglessink->context)) {
1411     show_egl_error ("eglCreateWindowSurface");
1412     GST_ERROR_OBJECT (eglglessink, "Couldn't bind surface/context");
1413     goto HANDLE_EGL_ERROR_LOCKED;
1414   }
1415
1416   /* Save surface dims */
1417   eglQuerySurface (eglglessink->display, eglglessink->surface, EGL_WIDTH,
1418       &eglglessink->surface_width);
1419   eglQuerySurface (eglglessink->display, eglglessink->surface, EGL_HEIGHT,
1420       &eglglessink->surface_height);
1421
1422   GST_INFO_OBJECT (eglglessink, "Got surface of %dx%d pixels",
1423       eglglessink->surface_width, eglglessink->surface_height);
1424
1425   /* We have a surface! */
1426   eglglessink->have_surface = TRUE;
1427   g_mutex_unlock (eglglessink->flow_lock);
1428
1429   /* Init vertex and fragment progs.
1430    * XXX: Need to be runtime conditional or ifdefed
1431    */
1432
1433   /* Shader compiler support it's optional byt we
1434    * currently rely on it.
1435    */
1436
1437   glGetBooleanv (GL_SHADER_COMPILER, &ret);
1438   if (ret == GL_FALSE) {
1439     GST_ERROR_OBJECT (eglglessink, "Shader compiler support is unavailable!");
1440     goto HANDLE_ERROR;
1441   }
1442
1443   eglglessink->vertshader = glCreateShader (GL_VERTEX_SHADER);
1444   GST_DEBUG_OBJECT (eglglessink, "Sending %s to handle %d", vert_COPY_prog,
1445       eglglessink->vertshader);
1446   glShaderSource (eglglessink->vertshader, 1, &vert_COPY_prog, NULL);
1447   if (got_gl_error ("glShaderSource vertex"))
1448     goto HANDLE_ERROR;
1449
1450   glCompileShader (eglglessink->vertshader);
1451   if (got_gl_error ("glCompileShader vertex"))
1452     goto HANDLE_ERROR;
1453
1454   glGetShaderiv (eglglessink->vertshader, GL_COMPILE_STATUS, &test);
1455   if (test != GL_FALSE)
1456     GST_DEBUG_OBJECT (eglglessink, "Successfully compiled vertex shader");
1457   else {
1458     GST_ERROR_OBJECT (eglglessink, "Couldn't compile vertex shader");
1459     glGetShaderiv (eglglessink->vertshader, GL_INFO_LOG_LENGTH, &test);
1460     info_log = g_new0 (GLchar, test);
1461     glGetShaderInfoLog (eglglessink->vertshader, test, NULL, info_log);
1462     GST_INFO_OBJECT (eglglessink, "Compilation info log:\n%s", info_log);
1463     g_free (info_log);
1464     goto HANDLE_ERROR;
1465   }
1466
1467   eglglessink->fragshader = glCreateShader (GL_FRAGMENT_SHADER);
1468   switch (eglglessink->format) {
1469     case GST_VIDEO_FORMAT_AYUV:
1470       glShaderSource (eglglessink->fragshader, 1, &frag_AYUV_prog, NULL);
1471       eglglessink->n_textures = 1;
1472       texnames[0] = "tex";
1473       break;
1474     case GST_VIDEO_FORMAT_Y444:
1475     case GST_VIDEO_FORMAT_I420:
1476     case GST_VIDEO_FORMAT_YV12:
1477     case GST_VIDEO_FORMAT_Y42B:
1478     case GST_VIDEO_FORMAT_Y41B:
1479       glShaderSource (eglglessink->fragshader, 1, &frag_PLANAR_YUV_prog, NULL);
1480       eglglessink->n_textures = 3;
1481       texnames[0] = "Ytex";
1482       texnames[1] = "Utex";
1483       texnames[2] = "Vtex";
1484       break;
1485     case GST_VIDEO_FORMAT_YUY2:
1486       tmp_prog = g_strdup_printf (frag_YUY2_UYVY_prog, 'r', 'g', 'a');
1487       glShaderSource (eglglessink->fragshader, 1, (const GLchar **) &tmp_prog,
1488           NULL);
1489       eglglessink->n_textures = 2;
1490       texnames[0] = "Ytex";
1491       texnames[1] = "UVtex";
1492       break;
1493     case GST_VIDEO_FORMAT_UYVY:
1494       tmp_prog = g_strdup_printf (frag_YUY2_UYVY_prog, 'a', 'r', 'b');
1495       glShaderSource (eglglessink->fragshader, 1, (const GLchar **) &tmp_prog,
1496           NULL);
1497       eglglessink->n_textures = 2;
1498       texnames[0] = "Ytex";
1499       texnames[1] = "UVtex";
1500       break;
1501     case GST_VIDEO_FORMAT_NV12:
1502       tmp_prog = g_strdup_printf (frag_NV12_NV21_prog, 'r', 'a');
1503       glShaderSource (eglglessink->fragshader, 1, (const GLchar **) &tmp_prog,
1504           NULL);
1505       eglglessink->n_textures = 2;
1506       texnames[0] = "Ytex";
1507       texnames[1] = "UVtex";
1508       break;
1509     case GST_VIDEO_FORMAT_NV21:
1510       tmp_prog = g_strdup_printf (frag_NV12_NV21_prog, 'a', 'r');
1511       glShaderSource (eglglessink->fragshader, 1, (const GLchar **) &tmp_prog,
1512           NULL);
1513       eglglessink->n_textures = 2;
1514       texnames[0] = "Ytex";
1515       texnames[1] = "UVtex";
1516       break;
1517     case GST_VIDEO_FORMAT_BGR:
1518     case GST_VIDEO_FORMAT_BGRx:
1519     case GST_VIDEO_FORMAT_BGRA:
1520       tmp_prog = g_strdup_printf (frag_REORDER_prog, 'b', 'g', 'r');
1521       glShaderSource (eglglessink->fragshader, 1, (const GLchar **) &tmp_prog,
1522           NULL);
1523       eglglessink->n_textures = 1;
1524       texnames[0] = "tex";
1525       break;
1526     case GST_VIDEO_FORMAT_xRGB:
1527     case GST_VIDEO_FORMAT_ARGB:
1528       tmp_prog = g_strdup_printf (frag_REORDER_prog, 'g', 'b', 'a');
1529       glShaderSource (eglglessink->fragshader, 1, (const GLchar **) &tmp_prog,
1530           NULL);
1531       eglglessink->n_textures = 1;
1532       texnames[0] = "tex";
1533       break;
1534     case GST_VIDEO_FORMAT_xBGR:
1535     case GST_VIDEO_FORMAT_ABGR:
1536       tmp_prog = g_strdup_printf (frag_REORDER_prog, 'a', 'b', 'g');
1537       glShaderSource (eglglessink->fragshader, 1, (const GLchar **) &tmp_prog,
1538           NULL);
1539       eglglessink->n_textures = 1;
1540       texnames[0] = "tex";
1541       break;
1542     case GST_VIDEO_FORMAT_RGB:
1543     case GST_VIDEO_FORMAT_RGBx:
1544     case GST_VIDEO_FORMAT_RGBA:
1545     case GST_VIDEO_FORMAT_RGB16:
1546       glShaderSource (eglglessink->fragshader, 1, &frag_COPY_prog, NULL);
1547       eglglessink->n_textures = 1;
1548       texnames[0] = "tex";
1549       break;
1550     default:
1551       g_assert_not_reached ();
1552       break;
1553   }
1554
1555   if (got_gl_error ("glShaderSource fragment"))
1556     goto HANDLE_ERROR;
1557
1558   glCompileShader (eglglessink->fragshader);
1559   if (got_gl_error ("glCompileShader fragment"))
1560     goto HANDLE_ERROR;
1561
1562   glGetShaderiv (eglglessink->fragshader, GL_COMPILE_STATUS, &test);
1563   if (test != GL_FALSE)
1564     GST_DEBUG_OBJECT (eglglessink, "Successfully compiled fragment shader");
1565   else {
1566     GST_ERROR_OBJECT (eglglessink, "Couldn't compile fragment shader");
1567     glGetShaderiv (eglglessink->fragshader, GL_INFO_LOG_LENGTH, &test);
1568     info_log = g_new0 (GLchar, test);
1569     glGetShaderInfoLog (eglglessink->fragshader, test, NULL, info_log);
1570     GST_INFO_OBJECT (eglglessink, "Compilation info log:\n%s", info_log);
1571     g_free (info_log);
1572     goto HANDLE_ERROR;
1573   }
1574
1575   eglglessink->program = glCreateProgram ();
1576   if (got_gl_error ("glCreateProgram"))
1577     goto HANDLE_ERROR;
1578   glAttachShader (eglglessink->program, eglglessink->vertshader);
1579   if (got_gl_error ("glAttachShader vertices"))
1580     goto HANDLE_ERROR;
1581   glAttachShader (eglglessink->program, eglglessink->fragshader);
1582   if (got_gl_error ("glAttachShader fragments"))
1583     goto HANDLE_ERROR;
1584   glLinkProgram (eglglessink->program);
1585   glGetProgramiv (eglglessink->program, GL_LINK_STATUS, &test);
1586   if (test != GL_FALSE)
1587     GST_DEBUG_OBJECT (eglglessink, "GLES: Successfully linked program");
1588   else {
1589     GST_ERROR_OBJECT (eglglessink, "Couldn't link program");
1590     goto HANDLE_ERROR;
1591   }
1592
1593   glUseProgram (eglglessink->program);
1594   if (got_gl_error ("glUseProgram"))
1595     goto HANDLE_ERROR;
1596
1597   eglglessink->coord_pos =
1598       glGetAttribLocation (eglglessink->program, "position");
1599   eglglessink->tex_pos = glGetAttribLocation (eglglessink->program, "texpos");
1600
1601   /* Generate and bind texture */
1602   if (!eglglessink->have_texture) {
1603     gint i;
1604
1605     GST_INFO_OBJECT (eglglessink, "Performing initial texture setup");
1606
1607     g_mutex_lock (eglglessink->flow_lock);
1608
1609     for (i = 0; i < eglglessink->n_textures; i++) {
1610       if (i == 0)
1611         glActiveTexture (GL_TEXTURE0);
1612       else if (i == 1)
1613         glActiveTexture (GL_TEXTURE1);
1614       else if (i == 2)
1615         glActiveTexture (GL_TEXTURE2);
1616
1617       glGenTextures (1, &eglglessink->texture[i]);
1618       if (got_gl_error ("glGenTextures"))
1619         goto HANDLE_ERROR_LOCKED;
1620
1621       glBindTexture (GL_TEXTURE_2D, eglglessink->texture[i]);
1622       if (got_gl_error ("glBindTexture"))
1623         goto HANDLE_ERROR_LOCKED;
1624
1625       eglglessink->tex_uniform[i] = glGetUniformLocation (eglglessink->program,
1626           texnames[i]);
1627       glUniform1i (eglglessink->tex_uniform[i], i);
1628
1629       /* Set 2D resizing params */
1630       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1631       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1632       /* The following two calls are for non-POT width/height cases. If these
1633        * are not set the texture image unit returns (R, G, B, A) = black
1634        * on glTexImage2D. For a deeper explanation take a look at
1635        * the OpenGl ES docs for glTexParameter */
1636       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1637       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1638       if (got_gl_error ("glTexParameteri"))
1639         goto HANDLE_ERROR_LOCKED;
1640     }
1641
1642     eglglessink->have_texture = TRUE;
1643     g_mutex_unlock (eglglessink->flow_lock);
1644   }
1645
1646   g_free (tmp_prog);
1647
1648   return TRUE;
1649
1650   /* Errors */
1651 HANDLE_EGL_ERROR_LOCKED:
1652   GST_ERROR_OBJECT (eglglessink, "EGL call returned error %x", eglGetError ());
1653 HANDLE_ERROR_LOCKED:
1654   g_mutex_unlock (eglglessink->flow_lock);
1655 HANDLE_ERROR:
1656   GST_ERROR_OBJECT (eglglessink, "Couldn't setup EGL surface");
1657   g_free (tmp_prog);
1658   return FALSE;
1659 }
1660
1661 static gboolean
1662 gst_eglglessink_init_egl_display (GstEglGlesSink * eglglessink)
1663 {
1664   EGLint egl_major, egl_minor;
1665
1666   GST_DEBUG_OBJECT (eglglessink, "Enter EGL initial configuration");
1667
1668   eglglessink->display = eglGetDisplay (EGL_DEFAULT_DISPLAY);
1669   if (eglglessink->display == EGL_NO_DISPLAY) {
1670     GST_ERROR_OBJECT (eglglessink, "Could not get EGL display connection");
1671     goto HANDLE_ERROR;          /* No EGL error is set by eglGetDisplay() */
1672   }
1673
1674   if (!eglInitialize (eglglessink->display, &egl_major, &egl_minor)) {
1675     show_egl_error ("eglInitialize");
1676     GST_ERROR_OBJECT (eglglessink, "Could not init EGL display connection");
1677     goto HANDLE_EGL_ERROR;
1678   }
1679
1680   /* Check against required EGL version */
1681   if (egl_major < GST_EGLGLESSINK_EGL_MIN_VERSION) {
1682     GST_ERROR_OBJECT (eglglessink, "EGL v%d\n needed, but you only have v%d.%d",
1683         GST_EGLGLESSINK_EGL_MIN_VERSION, egl_major, egl_minor);
1684     goto HANDLE_ERROR;
1685   }
1686
1687   GST_INFO_OBJECT (eglglessink, "System reports supported EGL version v%d.%d",
1688       egl_major, egl_minor);
1689
1690   eglBindAPI (EGL_OPENGL_ES_API);
1691
1692   return TRUE;
1693
1694   /* Errors */
1695 HANDLE_EGL_ERROR:
1696   GST_ERROR_OBJECT (eglglessink, "EGL call returned error %x", eglGetError ());
1697 HANDLE_ERROR:
1698   GST_ERROR_OBJECT (eglglessink, "Couldn't setup window/surface from handle");
1699   return FALSE;
1700 }
1701
1702 static gboolean
1703 gst_eglglessink_choose_config (GstEglGlesSink * eglglessink)
1704 {
1705   EGLint con_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
1706   GLint egl_configs;
1707
1708   if ((eglChooseConfig (eglglessink->display,
1709               eglglessink->selected_fmt->attribs, &eglglessink->config, 1,
1710               &egl_configs)) == EGL_FALSE) {
1711     show_egl_error ("eglChooseConfig");
1712     GST_ERROR_OBJECT (eglglessink, "eglChooseConfig failed");
1713     goto HANDLE_EGL_ERROR;
1714   }
1715
1716   if (egl_configs < 1) {
1717     GST_ERROR_OBJECT (eglglessink,
1718         "Could not find matching framebuffer config");
1719     goto HANDLE_ERROR;
1720   }
1721
1722   eglglessink->context = eglCreateContext (eglglessink->display,
1723       eglglessink->config, EGL_NO_CONTEXT, con_attribs);
1724
1725   if (eglglessink->context == EGL_NO_CONTEXT) {
1726     GST_ERROR_OBJECT (eglglessink, "Error getting context, eglCreateContext");
1727     goto HANDLE_EGL_ERROR;
1728   }
1729
1730   GST_DEBUG_OBJECT (eglglessink, "EGL Context: %p", eglglessink->context);
1731
1732   return TRUE;
1733
1734   /* Errors */
1735 HANDLE_EGL_ERROR:
1736   GST_ERROR_OBJECT (eglglessink, "EGL call returned error %x", eglGetError ());
1737 HANDLE_ERROR:
1738   GST_ERROR_OBJECT (eglglessink, "Couldn't choose an usable config");
1739   return FALSE;
1740 }
1741
1742 static void
1743 gst_eglglessink_set_window_handle (GstXOverlay * overlay, guintptr id)
1744 {
1745   GstEglGlesSink *eglglessink = GST_EGLGLESSINK (overlay);
1746
1747   g_return_if_fail (GST_IS_EGLGLESSINK (eglglessink));
1748   GST_DEBUG_OBJECT (eglglessink, "We got a window handle!");
1749
1750   if (eglglessink->have_window) {
1751     GST_WARNING_OBJECT (eglglessink,
1752         "We already have a window. Ignoring request");
1753     return;
1754   }
1755
1756   if (!id) {
1757     GST_ERROR_OBJECT (eglglessink, "Window handle is invalid");
1758     goto HANDLE_ERROR;
1759   }
1760
1761   /* OK, we have a new window */
1762   g_mutex_lock (eglglessink->flow_lock);
1763   eglglessink->window = (EGLNativeWindowType) id;
1764   eglglessink->have_window = TRUE;
1765   g_mutex_unlock (eglglessink->flow_lock);
1766
1767   return;
1768
1769   /* Errors */
1770 HANDLE_ERROR:
1771   GST_ERROR_OBJECT (eglglessink, "Couldn't setup window/surface from handle");
1772   return;
1773 }
1774
1775 /* Drafted */
1776 static void
1777 gst_eglglessink_set_render_rectangle (GstXOverlay * overlay, gint x, gint y,
1778     gint width, gint height)
1779 {
1780   GstEglGlesSink *eglglessink = GST_EGLGLESSINK (overlay);
1781
1782   g_return_if_fail (GST_IS_EGLGLESSINK (eglglessink));
1783
1784   g_mutex_lock (eglglessink->flow_lock);
1785
1786   if (width == -1 && height == -1) {
1787     /* This is the set_defaults condition according to
1788      * the xOverlay interface docs
1789      */
1790     eglglessink->display_region.w = 0;
1791     eglglessink->display_region.h = 0;
1792   } else {
1793     g_mutex_lock (eglglessink->flow_lock);
1794     eglglessink->display_region.x = x;
1795     eglglessink->display_region.y = y;
1796     eglglessink->display_region.w = width;
1797     eglglessink->display_region.h = height;
1798   }
1799
1800   g_mutex_unlock (eglglessink->flow_lock);
1801
1802   return;
1803 }
1804
1805 /* Rendering and display */
1806 static GstFlowReturn
1807 gst_eglglessink_render_and_display (GstEglGlesSink * eglglessink,
1808     GstBuffer * buf)
1809 {
1810   GstVideoRectangle frame, surface;
1811   gint w, h;
1812
1813 #ifdef EGL_FAST_RENDERING_POSSIBLE
1814   EGLImageKHR img = EGL_NO_IMAGE_KHR;
1815   EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR,
1816     EGL_FALSE, EGL_NONE, EGL_NONE
1817   };
1818 #endif
1819
1820   if (!buf) {
1821     GST_ERROR_OBJECT (eglglessink, "Null buffer, no past queue implemented");
1822     goto HANDLE_ERROR;
1823   }
1824
1825   w = GST_VIDEO_SINK_WIDTH (eglglessink);
1826   h = GST_VIDEO_SINK_HEIGHT (eglglessink);
1827
1828   GST_DEBUG_OBJECT (eglglessink,
1829       "Got good buffer %p. Sink geometry is %dx%d size %d", buf, w, h,
1830       GST_BUFFER_SIZE (buf));
1831
1832   switch (eglglessink->rendering_path) {
1833 #ifdef EGL_FAST_RENDERING_POSSIBLE
1834     case GST_EGLGLESSINK_RENDER_FAST:
1835       /* XXX: Not Fully implemented */
1836       img = my_eglCreateImageKHR (eglglessink->display, EGL_NO_CONTEXT,
1837           EGL_NATIVE_PIXMAP_KHR, (EGLClientBuffer) GST_BUFFER_DATA (buf),
1838           attrs);
1839
1840       if (img == EGL_NO_IMAGE_KHR) {
1841         GST_ERROR_OBJECT (eglglessink, "my_eglCreateImageKHR failed");
1842         goto HANDLE_EGL_ERROR;
1843       }
1844
1845       my_glEGLImageTargetTexture2DOES (GL_TEXTURE_2D, img);
1846
1847       break;
1848 #endif
1849     default:                   /* case GST_EGLGLESSINK_RENDER_SLOW */
1850
1851       switch (eglglessink->selected_fmt->fmt) {
1852         case GST_EGLGLESSINK_IMAGE_RGB888:
1853           glActiveTexture (GL_TEXTURE0);
1854           glBindTexture (GL_TEXTURE_2D, eglglessink->texture[0]);
1855           glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
1856               GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf));
1857           break;
1858         case GST_EGLGLESSINK_IMAGE_RGB565:
1859           glActiveTexture (GL_TEXTURE0);
1860           glBindTexture (GL_TEXTURE_2D, eglglessink->texture[0]);
1861           glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
1862               GL_UNSIGNED_SHORT_5_6_5, GST_BUFFER_DATA (buf));
1863           break;
1864         case GST_EGLGLESSINK_IMAGE_RGBA8888:
1865           switch (eglglessink->format) {
1866             case GST_VIDEO_FORMAT_RGBA:
1867             case GST_VIDEO_FORMAT_BGRA:
1868             case GST_VIDEO_FORMAT_ARGB:
1869             case GST_VIDEO_FORMAT_ABGR:
1870             case GST_VIDEO_FORMAT_RGBx:
1871             case GST_VIDEO_FORMAT_BGRx:
1872             case GST_VIDEO_FORMAT_xRGB:
1873             case GST_VIDEO_FORMAT_xBGR:
1874               glActiveTexture (GL_TEXTURE0);
1875               glBindTexture (GL_TEXTURE_2D, eglglessink->texture[0]);
1876               glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
1877                   GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf));
1878               break;
1879             case GST_VIDEO_FORMAT_AYUV:
1880               glActiveTexture (GL_TEXTURE0);
1881               glBindTexture (GL_TEXTURE_2D, eglglessink->texture[0]);
1882               glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
1883                   GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf));
1884               break;
1885             case GST_VIDEO_FORMAT_Y444:
1886             case GST_VIDEO_FORMAT_I420:
1887             case GST_VIDEO_FORMAT_YV12:
1888             case GST_VIDEO_FORMAT_Y42B:
1889             case GST_VIDEO_FORMAT_Y41B:{
1890               gint coffset, cw, ch;
1891
1892               coffset =
1893                   gst_video_format_get_component_offset (eglglessink->format, 0,
1894                   w, h);
1895               cw = gst_video_format_get_component_width (eglglessink->format, 0,
1896                   w);
1897               ch = gst_video_format_get_component_height (eglglessink->format,
1898                   0, h);
1899               glActiveTexture (GL_TEXTURE0);
1900               glBindTexture (GL_TEXTURE_2D, eglglessink->texture[0]);
1901               glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1902                   cw, ch, 0, GL_LUMINANCE,
1903                   GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf) + coffset);
1904               coffset =
1905                   gst_video_format_get_component_offset (eglglessink->format, 1,
1906                   w, h);
1907               cw = gst_video_format_get_component_width (eglglessink->format, 1,
1908                   w);
1909               ch = gst_video_format_get_component_height (eglglessink->format,
1910                   1, h);
1911               glActiveTexture (GL_TEXTURE1);
1912               glBindTexture (GL_TEXTURE_2D, eglglessink->texture[1]);
1913               glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1914                   cw, ch, 0, GL_LUMINANCE,
1915                   GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf) + coffset);
1916               coffset =
1917                   gst_video_format_get_component_offset (eglglessink->format, 2,
1918                   w, h);
1919               cw = gst_video_format_get_component_width (eglglessink->format, 2,
1920                   w);
1921               ch = gst_video_format_get_component_height (eglglessink->format,
1922                   2, h);
1923               glActiveTexture (GL_TEXTURE2);
1924               glBindTexture (GL_TEXTURE_2D, eglglessink->texture[2]);
1925               glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1926                   cw, ch, 0, GL_LUMINANCE,
1927                   GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf) + coffset);
1928               break;
1929             }
1930             case GST_VIDEO_FORMAT_YUY2:
1931             case GST_VIDEO_FORMAT_UYVY:
1932               glActiveTexture (GL_TEXTURE0);
1933               glBindTexture (GL_TEXTURE_2D, eglglessink->texture[0]);
1934               glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
1935                   w, h, 0, GL_LUMINANCE_ALPHA,
1936                   GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf));
1937               glActiveTexture (GL_TEXTURE1);
1938               glBindTexture (GL_TEXTURE_2D, eglglessink->texture[1]);
1939               glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
1940                   GST_ROUND_UP_2 (w) / 2, h, 0, GL_RGBA,
1941                   GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf));
1942               break;
1943             case GST_VIDEO_FORMAT_NV12:
1944             case GST_VIDEO_FORMAT_NV21:{
1945               gint coffset, cw, ch;
1946
1947               coffset =
1948                   gst_video_format_get_component_offset (eglglessink->format, 0,
1949                   w, h);
1950               cw = gst_video_format_get_component_width (eglglessink->format, 0,
1951                   w);
1952               ch = gst_video_format_get_component_height (eglglessink->format,
1953                   0, h);
1954               glActiveTexture (GL_TEXTURE0);
1955               glBindTexture (GL_TEXTURE_2D, eglglessink->texture[0]);
1956               glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1957                   cw, ch, 0, GL_LUMINANCE,
1958                   GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf) + coffset);
1959
1960               coffset =
1961                   gst_video_format_get_component_offset (eglglessink->format,
1962                   (eglglessink->format == GST_VIDEO_FORMAT_NV12 ? 1 : 2), w, h);
1963               cw = gst_video_format_get_component_width (eglglessink->format, 1,
1964                   w);
1965               ch = gst_video_format_get_component_height (eglglessink->format,
1966                   1, h);
1967               glActiveTexture (GL_TEXTURE1);
1968               glBindTexture (GL_TEXTURE_2D, eglglessink->texture[1]);
1969               glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
1970                   cw, ch, 0, GL_LUMINANCE_ALPHA,
1971                   GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf) + coffset);
1972               break;
1973             }
1974             default:
1975               g_assert_not_reached ();
1976               break;
1977           }
1978       }
1979
1980       if (got_gl_error ("glTexImage2D"))
1981         goto HANDLE_ERROR;
1982
1983       /* If no one has set a display rectangle on us initialize
1984        * a sane default. According to the docs on the xOverlay
1985        * interface we are supposed to fill the overlay 100%
1986        */
1987       if (!eglglessink->display_region.w || !eglglessink->display_region.h) {
1988         g_mutex_lock (eglglessink->flow_lock);
1989         if (!eglglessink->keep_aspect_ratio) {
1990           eglglessink->display_region.x = 0;
1991           eglglessink->display_region.y = 0;
1992           eglglessink->display_region.w = eglglessink->surface_width;
1993           eglglessink->display_region.h = eglglessink->surface_height;
1994         } else {
1995           /* XXX: Proly consider display pixel aspect ratio too? */
1996           frame.w = w;
1997           frame.h = h;
1998           surface.w = eglglessink->surface_width;
1999           surface.h = eglglessink->surface_height;
2000           gst_video_sink_center_rect (frame, surface,
2001               &eglglessink->display_region, TRUE);
2002         }
2003         g_mutex_unlock (eglglessink->flow_lock);
2004         glViewport (eglglessink->display_region.x,
2005             eglglessink->display_region.y, eglglessink->display_region.w,
2006             eglglessink->display_region.h);
2007       }
2008
2009       glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);
2010       if (got_gl_error ("glDrawElements"))
2011         goto HANDLE_ERROR;
2012
2013       if ((eglSwapBuffers (eglglessink->display, eglglessink->surface))
2014           == EGL_FALSE) {
2015         show_egl_error ("eglSwapBuffers");
2016         goto HANDLE_ERROR;
2017       }
2018   }
2019
2020   GST_DEBUG_OBJECT (eglglessink, "Succesfully rendered 1 frame");
2021   return GST_FLOW_OK;
2022
2023 HANDLE_EGL_ERROR:
2024   GST_ERROR_OBJECT (eglglessink, "EGL call returned error %x", eglGetError ());
2025 HANDLE_ERROR:
2026   GST_ERROR_OBJECT (eglglessink, "Rendering disabled for this frame");
2027   return GST_FLOW_ERROR;
2028 }
2029
2030 static GstFlowReturn
2031 gst_eglglessink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
2032 {
2033   GstEglGlesSink *eglglessink;
2034
2035   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
2036
2037   eglglessink = GST_EGLGLESSINK (vsink);
2038   GST_DEBUG_OBJECT (eglglessink, "Got buffer: %p", buf);
2039
2040   if (!eglglessink->have_window) {
2041     GST_ERROR_OBJECT (eglglessink, "I don't have a window to render to");
2042     return GST_FLOW_ERROR;
2043   }
2044
2045   if (!eglglessink->have_surface) {
2046     GST_ERROR_OBJECT (eglglessink, "I don't have a surface to render to");
2047     return GST_FLOW_ERROR;
2048   }
2049 #ifndef EGL_ANDROID_image_native_buffer
2050   GST_WARNING_OBJECT (eglglessink, "EGL_ANDROID_image_native_buffer not "
2051       "available");
2052 #endif
2053
2054   return gst_eglglessink_render_and_display (eglglessink, buf);
2055 }
2056
2057 static GstCaps *
2058 gst_eglglessink_getcaps (GstBaseSink * bsink)
2059 {
2060   GstEglGlesSink *eglglessink;
2061   GstCaps *ret = NULL;
2062
2063   eglglessink = GST_EGLGLESSINK (bsink);
2064
2065   g_mutex_lock (eglglessink->flow_lock);
2066   if (eglglessink->sinkcaps) {
2067     ret = gst_caps_ref (eglglessink->sinkcaps);
2068   } else {
2069     ret =
2070         gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD
2071             (bsink)));
2072   }
2073
2074   g_mutex_unlock (eglglessink->flow_lock);
2075
2076   return ret;
2077 }
2078
2079 static gboolean
2080 gst_eglglessink_setcaps (GstBaseSink * bsink, GstCaps * caps)
2081 {
2082   GstEglGlesSink *eglglessink;
2083   gboolean ret = TRUE;
2084   gint width, height;
2085   EGLNativeWindowType window;
2086   GstEglGlesImageFmt *format;
2087
2088   eglglessink = GST_EGLGLESSINK (bsink);
2089
2090   GST_DEBUG_OBJECT (eglglessink,
2091       "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
2092       GST_PTR_FORMAT, eglglessink->current_caps, caps);
2093
2094   if (!(ret = gst_video_format_parse_caps (caps, &eglglessink->format, &width,
2095               &height))) {
2096     GST_ERROR_OBJECT (eglglessink, "Got weird and/or incomplete caps");
2097     goto HANDLE_ERROR;
2098   }
2099
2100   format = gst_eglglessink_get_compat_format_from_caps (eglglessink, caps);
2101   if (!format) {
2102     GST_ERROR_OBJECT (eglglessink,
2103         "No supported and compatible egl/gles format " "found for given caps");
2104     goto HANDLE_ERROR;
2105   } else
2106     GST_INFO_OBJECT (eglglessink, "Selected compatible egl/gles format %d",
2107         format->fmt);
2108
2109   g_mutex_lock (eglglessink->flow_lock);
2110   eglglessink->selected_fmt = format;
2111   GST_VIDEO_SINK_WIDTH (eglglessink) = width;
2112   GST_VIDEO_SINK_HEIGHT (eglglessink) = height;
2113   g_mutex_unlock (eglglessink->flow_lock);
2114
2115   if (eglglessink->current_caps) {
2116     GST_ERROR_OBJECT (eglglessink, "Caps already set. Won't do it again");
2117     if (gst_caps_can_intersect (caps, eglglessink->current_caps)) {
2118       GST_INFO_OBJECT (eglglessink, "Caps are compatible anyway");
2119       goto SUCCEED;
2120     }
2121
2122     GST_DEBUG_OBJECT (eglglessink, "Caps are not compatible, reconfiguring");
2123     if (eglglessink->rendering_path == GST_EGLGLESSINK_RENDER_SLOW) {
2124       glDeleteBuffers (1, &eglglessink->vdata);
2125       glDeleteBuffers (1, &eglglessink->tdata);
2126       glDeleteBuffers (1, &eglglessink->idata);
2127       eglglessink->have_vbo = FALSE;
2128
2129       glDeleteShader (eglglessink->fragshader);
2130       glDeleteShader (eglglessink->vertshader);
2131
2132       glDeleteTextures (eglglessink->n_textures, eglglessink->texture);
2133       eglglessink->have_texture = FALSE;
2134       eglglessink->n_textures = 0;
2135
2136       glDeleteProgram (eglglessink->program);
2137     }
2138
2139     if (eglglessink->surface) {
2140       eglDestroySurface (eglglessink->display, eglglessink->surface);
2141       eglglessink->surface = NULL;
2142       eglglessink->have_surface = FALSE;
2143     }
2144
2145     if (eglglessink->context) {
2146       eglDestroyContext (eglglessink->display, eglglessink->context);
2147       eglglessink->context = NULL;
2148     }
2149
2150     g_mutex_lock (eglglessink->flow_lock);
2151     gst_caps_unref (eglglessink->current_caps);
2152     eglglessink->current_caps = NULL;
2153     g_mutex_unlock (eglglessink->flow_lock);
2154   }
2155
2156   if (!gst_eglglessink_choose_config (eglglessink)) {
2157     GST_ERROR_OBJECT (eglglessink, "Couldn't choose EGL config");
2158     goto HANDLE_ERROR;
2159   }
2160
2161   g_mutex_lock (eglglessink->flow_lock);
2162   eglglessink->current_caps = gst_caps_ref (caps);
2163   g_mutex_unlock (eglglessink->flow_lock);
2164
2165   /* By now the application should have set a window
2166    * already if it meant to do so
2167    */
2168   if (!eglglessink->have_window) {
2169     GST_INFO_OBJECT (eglglessink,
2170         "No window. Will attempt internal window creation");
2171     if (!(window = gst_eglglessink_create_window (eglglessink, width, height))) {
2172       GST_ERROR_OBJECT (eglglessink, "Internal window creation failed!");
2173       goto HANDLE_ERROR;
2174     }
2175     g_mutex_lock (eglglessink->flow_lock);
2176     eglglessink->using_own_window = TRUE;
2177     g_mutex_unlock (eglglessink->flow_lock);
2178     gst_eglglessink_set_window_handle (GST_X_OVERLAY (eglglessink),
2179         (guintptr) window);
2180   }
2181
2182   if (!eglglessink->have_surface) {
2183     if (!gst_eglglessink_init_egl_surface (eglglessink)) {
2184       GST_ERROR_OBJECT (eglglessink, "Couldn't init EGL surface from window");
2185       goto HANDLE_ERROR;
2186     }
2187   }
2188
2189   if (!eglglessink->have_vbo) {
2190     if (!gst_eglglessink_setup_vbo (eglglessink, FALSE)) {
2191       GST_ERROR_OBJECT (eglglessink, "VBO setup failed");
2192       goto HANDLE_ERROR;
2193     }
2194   }
2195
2196 SUCCEED:
2197   GST_INFO_OBJECT (eglglessink, "Setcaps succeed");
2198   return TRUE;
2199
2200 /* Errors */
2201 HANDLE_ERROR:
2202   GST_ERROR_OBJECT (eglglessink, "Setcaps failed");
2203   return FALSE;
2204 }
2205
2206 static void
2207 gst_eglglessink_wipe_fmt (gpointer data)
2208 {
2209   GstEglGlesImageFmt *format = data;
2210   gst_caps_unref (format->caps);
2211   g_free (format);
2212 }
2213
2214 static GstStateChangeReturn
2215 gst_eglglessink_change_state (GstElement * element, GstStateChange transition)
2216 {
2217   GstEglGlesSink *eglglessink;
2218   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2219
2220   eglglessink = GST_EGLGLESSINK (element);
2221
2222   switch (transition) {
2223     case GST_STATE_CHANGE_NULL_TO_READY:
2224       if (!egl_init (eglglessink)) {
2225         ret = GST_STATE_CHANGE_FAILURE;
2226         goto done;
2227       }
2228       break;
2229     default:
2230       break;
2231   }
2232
2233   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2234   if (ret == GST_STATE_CHANGE_FAILURE)
2235     return ret;
2236
2237   switch (transition) {
2238     case GST_STATE_CHANGE_READY_TO_NULL:
2239       g_mutex_lock (eglglessink->flow_lock);
2240       if (eglglessink->display) {
2241         eglTerminate (eglglessink->display);
2242         eglglessink->display = NULL;
2243       }
2244
2245       eglglessink->selected_fmt = NULL;
2246       g_list_free_full (eglglessink->supported_fmts, gst_eglglessink_wipe_fmt);
2247       eglglessink->supported_fmts = NULL;
2248       gst_caps_unref (eglglessink->sinkcaps);
2249       eglglessink->sinkcaps = NULL;
2250       eglglessink->egl_started = FALSE;
2251       g_mutex_unlock (eglglessink->flow_lock);
2252       break;
2253     default:
2254       break;
2255   }
2256
2257 done:
2258   return ret;
2259 }
2260
2261 static void
2262 gst_eglglessink_finalize (GObject * object)
2263 {
2264   GstEglGlesSink *eglglessink;
2265
2266   g_return_if_fail (GST_IS_EGLGLESSINK (object));
2267
2268   eglglessink = GST_EGLGLESSINK (object);
2269
2270   g_mutex_free (eglglessink->flow_lock);
2271   eglglessink->flow_lock = NULL;
2272
2273   G_OBJECT_CLASS (parent_class)->finalize (object);
2274 }
2275
2276 static void
2277 gst_eglglessink_set_property (GObject * object, guint prop_id,
2278     const GValue * value, GParamSpec * pspec)
2279 {
2280   GstEglGlesSink *eglglessink;
2281
2282   g_return_if_fail (GST_IS_EGLGLESSINK (object));
2283
2284   eglglessink = GST_EGLGLESSINK (object);
2285
2286   switch (prop_id) {
2287     case PROP_SILENT:
2288       eglglessink->silent = g_value_get_boolean (value);
2289       break;
2290     case PROP_CREATE_WINDOW:
2291       eglglessink->can_create_window = g_value_get_boolean (value);
2292       break;
2293     case PROP_DEFAULT_HEIGHT:
2294       eglglessink->window_default_height = g_value_get_int (value);
2295       break;
2296     case PROP_DEFAULT_WIDTH:
2297       eglglessink->window_default_width = g_value_get_int (value);
2298       break;
2299     case PROP_FORCE_RENDERING_SLOW:
2300       eglglessink->force_rendering_slow = g_value_get_boolean (value);
2301       break;
2302     case PROP_FORCE_ASPECT_RATIO:
2303       eglglessink->keep_aspect_ratio = g_value_get_boolean (value);
2304       break;
2305     default:
2306       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2307       break;
2308   }
2309 }
2310
2311 static void
2312 gst_eglglessink_get_property (GObject * object, guint prop_id,
2313     GValue * value, GParamSpec * pspec)
2314 {
2315   GstEglGlesSink *eglglessink;
2316
2317   g_return_if_fail (GST_IS_EGLGLESSINK (object));
2318
2319   eglglessink = GST_EGLGLESSINK (object);
2320
2321   switch (prop_id) {
2322     case PROP_SILENT:
2323       g_value_set_boolean (value, eglglessink->silent);
2324       break;
2325     case PROP_CREATE_WINDOW:
2326       g_value_set_boolean (value, eglglessink->can_create_window);
2327       break;
2328     case PROP_DEFAULT_HEIGHT:
2329       g_value_set_int (value, eglglessink->window_default_height);
2330       break;
2331     case PROP_DEFAULT_WIDTH:
2332       g_value_set_int (value, eglglessink->window_default_width);
2333       break;
2334     case PROP_FORCE_RENDERING_SLOW:
2335       g_value_set_boolean (value, eglglessink->force_rendering_slow);
2336       break;
2337     case PROP_FORCE_ASPECT_RATIO:
2338       g_value_set_boolean (value, eglglessink->keep_aspect_ratio);
2339       break;
2340     default:
2341       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2342       break;
2343   }
2344 }
2345
2346 static void
2347 gst_eglglessink_base_init (gpointer gclass)
2348 {
2349   GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
2350
2351   gst_element_class_set_details_simple (element_class,
2352       "EGL/GLES vout Sink",
2353       "Sink/Video",
2354       "An EGL/GLES Video Output Sink Implementing the XOverlay interface",
2355       "Reynaldo H. Verdejo Pinochet <reynaldo@collabora.com>");
2356
2357   gst_element_class_add_pad_template (element_class,
2358       gst_static_pad_template_get (&gst_eglglessink_sink_template_factory));
2359 }
2360
2361 /* initialize the eglglessink's class */
2362 static void
2363 gst_eglglessink_class_init (GstEglGlesSinkClass * klass)
2364 {
2365   GObjectClass *gobject_class;
2366   GstElementClass *gstelement_class;
2367   GstBaseSinkClass *gstbasesink_class;
2368   GstVideoSinkClass *gstvideosink_class;
2369
2370   gobject_class = (GObjectClass *) klass;
2371   gstelement_class = (GstElementClass *) klass;
2372   gstbasesink_class = (GstBaseSinkClass *) klass;
2373   gstvideosink_class = (GstVideoSinkClass *) klass;
2374
2375   gobject_class->set_property = gst_eglglessink_set_property;
2376   gobject_class->get_property = gst_eglglessink_get_property;
2377   gobject_class->finalize = gst_eglglessink_finalize;
2378
2379   gstelement_class->change_state = gst_eglglessink_change_state;
2380
2381   gstbasesink_class->start = gst_eglglessink_start;
2382   gstbasesink_class->stop = gst_eglglessink_stop;
2383   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_eglglessink_setcaps);
2384   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_eglglessink_getcaps);
2385   gstbasesink_class->buffer_alloc = GST_DEBUG_FUNCPTR
2386       (gst_eglglessink_buffer_alloc);
2387
2388   gstvideosink_class->show_frame =
2389       GST_DEBUG_FUNCPTR (gst_eglglessink_show_frame);
2390
2391   g_object_class_install_property (gobject_class, PROP_SILENT,
2392       g_param_spec_boolean ("silent", "Silent", "Produce no output",
2393           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2394   g_object_class_install_property (gobject_class, PROP_CREATE_WINDOW,
2395       g_param_spec_boolean ("create-window", "Create Window",
2396           "Attempt to create a window if none is provided",
2397           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2398   g_object_class_install_property (gobject_class, PROP_FORCE_RENDERING_SLOW,
2399       g_param_spec_boolean ("force-rendering-slow", "Force Slow Rendering",
2400           "Force slow rendering path", FALSE,
2401           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2402   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2403       g_param_spec_boolean ("force-aspect-ratio", "Force Aspect Ratio",
2404           "When enabled, scaling will respect original aspect ratio",
2405           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2406   g_object_class_install_property (gobject_class, PROP_DEFAULT_WIDTH,
2407       g_param_spec_int ("window-default-width", "Default Width",
2408           "Default width for self created windows", 0,
2409           EGLGLESSINK_MAX_FRAME_WIDTH, EGLGLESSINK_MAX_FRAME_WIDTH,
2410           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2411   g_object_class_install_property (gobject_class, PROP_DEFAULT_HEIGHT,
2412       g_param_spec_int ("window-default-height", "Default Height",
2413           "Default height for self created windows", 0,
2414           EGLGLESSINK_MAX_FRAME_HEIGHT, EGLGLESSINK_MAX_FRAME_HEIGHT,
2415           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2416 }
2417
2418
2419 static void
2420 gst_eglglessink_init (GstEglGlesSink * eglglessink,
2421     GstEglGlesSinkClass * gclass)
2422 {
2423   /* Init defaults */
2424   eglglessink->have_window = FALSE;
2425   eglglessink->have_surface = FALSE;
2426   eglglessink->have_vbo = FALSE;
2427   eglglessink->have_texture = FALSE;
2428   eglglessink->egl_started = FALSE;
2429   eglglessink->can_create_window = TRUE;
2430   eglglessink->force_rendering_slow = FALSE;
2431   eglglessink->keep_aspect_ratio = TRUE;
2432   eglglessink->using_own_window = FALSE;
2433   eglglessink->flow_lock = g_mutex_new ();
2434 }
2435
2436 /* Interface initializations. Used here for initializing the XOverlay
2437  * Interface.
2438  */
2439 static void
2440 gst_eglglessink_init_interfaces (GType type)
2441 {
2442   static const GInterfaceInfo implements_info = {
2443     (GInterfaceInitFunc) gst_eglglessink_implements_init, NULL, NULL
2444   };
2445
2446   static const GInterfaceInfo xoverlay_info = {
2447     (GInterfaceInitFunc) gst_eglglessink_xoverlay_init, NULL, NULL
2448   };
2449
2450   g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE,
2451       &implements_info);
2452   g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xoverlay_info);
2453
2454 }
2455
2456 /* entry point to initialize the plug-in
2457  * initialize the plug-in itself
2458  * register the element factories and other features
2459  */
2460 static gboolean
2461 eglglessink_plugin_init (GstPlugin * plugin)
2462 {
2463   /* debug category for fltering log messages */
2464   GST_DEBUG_CATEGORY_INIT (gst_eglglessink_debug, "eglglessink",
2465       0, "Simple EGL/GLES Sink");
2466
2467   return gst_element_register (plugin, "eglglessink", GST_RANK_PRIMARY,
2468       GST_TYPE_EGLGLESSINK);
2469 }
2470
2471 /* PACKAGE: this is usually set by autotools depending on some _INIT macro
2472  * in configure.ac and then written into and defined in config.h, but we can
2473  * just set it ourselves here in case someone doesn't use autotools to
2474  * compile this code. GST_PLUGIN_DEFINE needs PACKAGE to be defined.
2475  */
2476 #ifndef PACKAGE
2477 #define PACKAGE "EGL/GLES Sink"
2478 #endif
2479
2480 #ifndef VERSION
2481 #define VERSION "0.911"
2482 #endif
2483
2484 /* gstreamer looks for this structure to register eglglessinks */
2485 GST_PLUGIN_DEFINE2 (GST_VERSION_MAJOR,
2486     GST_VERSION_MINOR,
2487     eglglessink,
2488     "EGL/GLES sink",
2489     eglglessink_plugin_init,
2490     VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")