cdda6d62dc23281d6659977b31a1b8ea48e3b154
[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  *   @author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  *
25  * Alternatively, the contents of this file may be used under the
26  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
27  * which case the following provisions apply instead of the ones
28  * mentioned above:
29  *
30  * This library is free software; you can redistribute it and/or
31  * modify it under the terms of the GNU Library General Public
32  * License as published by the Free Software Foundation; either
33  * version 2 of the License, or (at your option) any later version.
34  *
35  * This library is distributed in the hope that it will be useful,
36  * but WITHOUT ANY WARRANTY; without even the implied warranty of
37  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
38  * Library General Public License for more details.
39  *
40  * You should have received a copy of the GNU Library General Public
41  * License along with this library; if not, write to the
42  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
43  * Boston, MA 02110-1301, USA.
44  */
45
46 /**
47  * SECTION:element-eglglessink
48  *
49  * EglGlesSink renders video frames on a EGL surface it sets up
50  * from a window it either creates (on X11) or gets a handle to
51  * through it's xOverlay interface. All the display/surface logic
52  * in this sink uses EGL to interact with the native window system.
53  * The rendering logic, in turn, uses OpenGL ES v2.
54  *
55  * This sink has been tested to work on X11/Mesa and on Android
56  * (From Gingerbread on to Jelly Bean) and while it's currently
57  * using an slow copy-over rendering path it has proven to be fast
58  * enough on the devices we have tried it on. 
59  *
60  * <refsect2>
61  * <title>Supported EGL/OpenGL ES versions</title>
62  * <para>
63  * This Sink uses EGLv1 and GLESv2
64  * </para>
65  * </refsect2>
66  *
67  * <refsect2>
68  * <title>Example launch line</title>
69  * |[
70  * gst-launch -v -m videotestsrc ! eglglessink
71  * ]|
72  * </refsect2>
73  *
74  * <refsect2>
75  * <title>Example launch line with internal window creation disabled</title>
76  * <para>
77  * By setting the can_create_window property to FALSE you can force the
78  * sink to wait for a window handle through it's xOverlay interface even
79  * if internal window creation is supported by the platform. Window creation
80  * is only supported in X11 right now but it should be trivial to add support
81  * for different platforms.
82  * </para>
83  * |[
84  * gst-launch -v -m videotestsrc ! eglglessink can_create_window=FALSE
85  * ]|
86  * </refsect2>
87  *
88  * <refsect2>
89  * <title>Scaling</title>
90  * <para>
91  * The sink will try it's best to consider the incoming frame's and display's
92  * pixel aspect ratio and fill the corresponding surface without altering the
93  * decoded frame's geometry when scaling. You can disable this logic by setting
94  * the force_aspect_ratio property to FALSE, in which case the sink will just
95  * fill the entire surface it has access to regardles of the PAR/DAR relationship.
96  * </para>
97  * <para>
98  * Querying the display aspect ratio is only supported with EGL versions >= 1.2.
99  * The sink will just assume the DAR to be 1/1 if it can't get access to this
100  * information.
101  * </para>
102  * <para>
103  * Here is an example launch line with the PAR/DAR aware scaling disabled:
104  * </para>
105  * |[
106  * gst-launch -v -m videotestsrc ! eglglessink force_aspect_ratio=FALSE
107  * ]|
108  * </refsect2>
109  */
110
111 #ifdef HAVE_CONFIG_H
112 #  include <config.h>
113 #endif
114
115 #define EGL_EGLEXT_PROTOTYPES
116 #define GL_GLEXT_PROTOTYPES
117
118 #include <string.h>
119 #include <gst/gst.h>
120 #include <gst/video/video.h>
121 #include <gst/video/video-frame.h>
122 #include <gst/video/gstvideosink.h>
123 #include <gst/video/gstvideometa.h>
124 #include <gst/video/gstvideopool.h>
125 #include <gst/video/videooverlay.h>
126 #include <gst/egl/egl.h>
127
128 #include <EGL/egl.h>
129 #include <EGL/eglext.h>
130 #include <GLES2/gl2.h>
131 #include <GLES2/gl2ext.h>
132
133 #ifdef USE_EGL_RPI
134 #include <bcm_host.h>
135 #endif
136
137 #include "gstegladaptation.h"
138 #include "gsteglglessink.h"
139
140 GST_DEBUG_CATEGORY_STATIC (gst_eglglessink_debug);
141 #define GST_CAT_DEFAULT gst_eglglessink_debug
142
143 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
144
145 /* Input capabilities. */
146 static GstStaticPadTemplate gst_eglglessink_sink_template_factory =
147     GST_STATIC_PAD_TEMPLATE ("sink",
148     GST_PAD_SINK,
149     GST_PAD_ALWAYS,
150     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
151         (GST_CAPS_FEATURE_MEMORY_EGL_IMAGE,
152             "{ " "RGBA, BGRA, ARGB, ABGR, " "RGBx, BGRx, xRGB, xBGR, "
153             "AYUV, Y444, I420, YV12, " "NV12, NV21, Y42B, Y41B, "
154             "RGB, BGR, RGB16 }") ";"
155         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
156         (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META,
157             "{ " "RGBA, BGRA, ARGB, ABGR, " "RGBx, BGRx, xRGB, xBGR, "
158             "AYUV, Y444, I420, YV12, " "NV12, NV21, Y42B, Y41B, "
159             "RGB, BGR, RGB16 }") ";" GST_VIDEO_CAPS_MAKE ("{ "
160             "RGBA, BGRA, ARGB, ABGR, " "RGBx, BGRx, xRGB, xBGR, "
161             "AYUV, Y444, I420, YV12, " "NV12, NV21, Y42B, Y41B, "
162             "RGB, BGR, RGB16 }")));
163
164 /* Filter signals and args */
165 enum
166 {
167   /* FILL ME */
168   LAST_SIGNAL
169 };
170
171 enum
172 {
173   PROP_0,
174   PROP_CREATE_WINDOW,
175   PROP_FORCE_ASPECT_RATIO,
176 };
177
178 static void gst_eglglessink_finalize (GObject * object);
179 static void gst_eglglessink_get_property (GObject * object, guint prop_id,
180     GValue * value, GParamSpec * pspec);
181 static void gst_eglglessink_set_property (GObject * object, guint prop_id,
182     const GValue * value, GParamSpec * pspec);
183 static GstStateChangeReturn gst_eglglessink_change_state (GstElement * element,
184     GstStateChange transition);
185 static void gst_eglglessink_set_context (GstElement * element,
186     GstContext * context);
187 static GstFlowReturn gst_eglglessink_prepare (GstBaseSink * bsink,
188     GstBuffer * buf);
189 static GstFlowReturn gst_eglglessink_show_frame (GstVideoSink * vsink,
190     GstBuffer * buf);
191 static gboolean gst_eglglessink_setcaps (GstBaseSink * bsink, GstCaps * caps);
192 static GstCaps *gst_eglglessink_getcaps (GstBaseSink * bsink, GstCaps * filter);
193 static gboolean gst_eglglessink_propose_allocation (GstBaseSink * bsink,
194     GstQuery * query);
195 static gboolean gst_eglglessink_query (GstBaseSink * bsink, GstQuery * query);
196 static gboolean gst_eglglessink_event (GstBaseSink * bsink, GstEvent * event);
197
198 /* VideoOverlay interface cruft */
199 static void gst_eglglessink_videooverlay_init (GstVideoOverlayInterface *
200     iface);
201
202 /* Actual VideoOverlay interface funcs */
203 static void gst_eglglessink_expose (GstVideoOverlay * overlay);
204 static void gst_eglglessink_set_window_handle (GstVideoOverlay * overlay,
205     guintptr id);
206 static void gst_eglglessink_set_render_rectangle (GstVideoOverlay * overlay,
207     gint x, gint y, gint width, gint height);
208
209 /* Utility */
210 static gboolean gst_eglglessink_create_window (GstEglGlesSink *
211     eglglessink, gint width, gint height);
212 static gboolean gst_eglglessink_setup_vbo (GstEglGlesSink * eglglessink,
213     gboolean reset);
214 static gboolean
215 gst_eglglessink_configure_caps (GstEglGlesSink * eglglessink, GstCaps * caps);
216 static GstFlowReturn gst_eglglessink_upload (GstEglGlesSink * sink,
217     GstBuffer * buf);
218 static GstFlowReturn gst_eglglessink_render (GstEglGlesSink * sink);
219 static GstFlowReturn gst_eglglessink_queue_object (GstEglGlesSink * sink,
220     GstMiniObject * obj);
221 static inline gboolean egl_init (GstEglGlesSink * eglglessink);
222 static GstBufferPool *gst_egl_image_buffer_pool_new (GstEglGlesSink *
223     eglglessink, GstEGLDisplay * display);
224
225 /* EGLImage memory, buffer pool, etc */
226 typedef struct
227 {
228   GstVideoBufferPool parent;
229
230   GstEglGlesSink *sink;
231   GstAllocator *allocator;
232   GstAllocationParams params;
233   GstVideoInfo info;
234   gboolean add_metavideo;
235   gboolean want_eglimage;
236   GstEGLDisplay *display;
237 } GstEGLImageBufferPool;
238
239 typedef GstVideoBufferPoolClass GstEGLImageBufferPoolClass;
240
241 #define GST_EGL_IMAGE_BUFFER_POOL(p) ((GstEGLImageBufferPool*)(p))
242
243 GType gst_egl_image_buffer_pool_get_type (void);
244
245 G_DEFINE_TYPE (GstEGLImageBufferPool, gst_egl_image_buffer_pool,
246     GST_TYPE_VIDEO_BUFFER_POOL);
247
248 static const gchar **
249 gst_egl_image_buffer_pool_get_options (GstBufferPool * bpool)
250 {
251   static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL
252   };
253
254   return options;
255 }
256
257 static gboolean
258 gst_egl_image_buffer_pool_set_config (GstBufferPool * bpool,
259     GstStructure * config)
260 {
261   GstEGLImageBufferPool *pool = GST_EGL_IMAGE_BUFFER_POOL (bpool);
262   GstCaps *caps;
263   GstVideoInfo info;
264
265   if (pool->allocator)
266     gst_object_unref (pool->allocator);
267   pool->allocator = NULL;
268
269   if (!GST_BUFFER_POOL_CLASS
270       (gst_egl_image_buffer_pool_parent_class)->set_config (bpool, config))
271     return FALSE;
272
273   if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)
274       || !caps)
275     return FALSE;
276
277   if (!gst_video_info_from_caps (&info, caps))
278     return FALSE;
279
280   if (!gst_buffer_pool_config_get_allocator (config, &pool->allocator,
281           &pool->params))
282     return FALSE;
283   if (pool->allocator)
284     gst_object_ref (pool->allocator);
285
286   pool->add_metavideo =
287       gst_buffer_pool_config_has_option (config,
288       GST_BUFFER_POOL_OPTION_VIDEO_META);
289
290   pool->want_eglimage = (pool->allocator
291       && g_strcmp0 (pool->allocator->mem_type, GST_EGL_IMAGE_MEMORY_TYPE) == 0);
292
293   pool->info = info;
294
295   return TRUE;
296 }
297
298 static GstFlowReturn
299 gst_egl_image_buffer_pool_alloc_buffer (GstBufferPool * bpool,
300     GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
301 {
302   GstEGLImageBufferPool *pool = GST_EGL_IMAGE_BUFFER_POOL (bpool);
303
304   *buffer = NULL;
305
306   if (!pool->add_metavideo || !pool->want_eglimage)
307     return
308         GST_BUFFER_POOL_CLASS
309         (gst_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool,
310         buffer, params);
311
312   if (!pool->allocator)
313     return GST_FLOW_NOT_NEGOTIATED;
314
315   switch (pool->info.finfo->format) {
316     case GST_VIDEO_FORMAT_RGB:
317     case GST_VIDEO_FORMAT_BGR:
318     case GST_VIDEO_FORMAT_RGB16:
319     case GST_VIDEO_FORMAT_NV12:
320     case GST_VIDEO_FORMAT_NV21:
321     case GST_VIDEO_FORMAT_RGBA:
322     case GST_VIDEO_FORMAT_BGRA:
323     case GST_VIDEO_FORMAT_ARGB:
324     case GST_VIDEO_FORMAT_ABGR:
325     case GST_VIDEO_FORMAT_RGBx:
326     case GST_VIDEO_FORMAT_BGRx:
327     case GST_VIDEO_FORMAT_xRGB:
328     case GST_VIDEO_FORMAT_xBGR:
329     case GST_VIDEO_FORMAT_AYUV:
330     case GST_VIDEO_FORMAT_YV12:
331     case GST_VIDEO_FORMAT_I420:
332     case GST_VIDEO_FORMAT_Y444:
333     case GST_VIDEO_FORMAT_Y42B:
334     case GST_VIDEO_FORMAT_Y41B:{
335       GstFlowReturn ret;
336       GstQuery *query;
337       GstStructure *s;
338       const GValue *v;
339
340       s = gst_structure_new ("eglglessink-allocate-eglimage",
341           "format", GST_TYPE_VIDEO_FORMAT, pool->info.finfo->format,
342           "width", G_TYPE_INT, pool->info.width,
343           "height", G_TYPE_INT, pool->info.height, NULL);
344       query = gst_query_new_custom (GST_QUERY_CUSTOM, s);
345
346       ret =
347           gst_eglglessink_queue_object (pool->sink,
348           GST_MINI_OBJECT_CAST (query));
349
350       if (ret != GST_FLOW_OK || !gst_structure_has_field (s, "buffer")) {
351         GST_WARNING ("Fallback memory allocation");
352         gst_query_unref (query);
353         return
354             GST_BUFFER_POOL_CLASS
355             (gst_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool,
356             buffer, params);
357       }
358
359       v = gst_structure_get_value (s, "buffer");
360       *buffer = GST_BUFFER_CAST (g_value_get_pointer (v));
361       gst_query_unref (query);
362
363       if (!*buffer) {
364         GST_WARNING ("Fallback memory allocation");
365         return
366             GST_BUFFER_POOL_CLASS
367             (gst_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool,
368             buffer, params);
369       }
370
371       return GST_FLOW_OK;
372       break;
373     }
374     default:
375       return
376           GST_BUFFER_POOL_CLASS
377           (gst_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool,
378           buffer, params);
379       break;
380   }
381
382   return GST_FLOW_ERROR;
383 }
384
385 static GstFlowReturn
386 gst_egl_image_buffer_pool_acquire_buffer (GstBufferPool * bpool,
387     GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
388 {
389   GstFlowReturn ret;
390   GstEGLImageBufferPool *pool;
391
392   ret =
393       GST_BUFFER_POOL_CLASS
394       (gst_egl_image_buffer_pool_parent_class)->acquire_buffer (bpool,
395       buffer, params);
396   if (ret != GST_FLOW_OK || !*buffer)
397     return ret;
398
399   pool = GST_EGL_IMAGE_BUFFER_POOL (bpool);
400
401   /* XXX: Don't return the memory we just rendered, glEGLImageTargetTexture2DOES()
402    * keeps the EGLImage unmappable until the next one is uploaded
403    */
404   if (*buffer && *buffer == pool->sink->last_buffer) {
405     GstBuffer *oldbuf = *buffer;
406
407     ret =
408         GST_BUFFER_POOL_CLASS
409         (gst_egl_image_buffer_pool_parent_class)->acquire_buffer (bpool,
410         buffer, params);
411     gst_object_replace ((GstObject **) & oldbuf->pool, (GstObject *) pool);
412     gst_buffer_unref (oldbuf);
413   }
414
415   return ret;
416 }
417
418 static void
419 gst_egl_image_buffer_pool_finalize (GObject * object)
420 {
421   GstEGLImageBufferPool *pool = GST_EGL_IMAGE_BUFFER_POOL (object);
422
423   if (pool->allocator)
424     gst_object_unref (pool->allocator);
425   pool->allocator = NULL;
426
427   if (pool->sink)
428     gst_object_unref (pool->sink);
429   pool->sink = NULL;
430
431   if (pool->display)
432     gst_egl_display_unref (pool->display);
433   pool->display = NULL;
434
435   G_OBJECT_CLASS (gst_egl_image_buffer_pool_parent_class)->finalize (object);
436 }
437
438 static void
439 gst_egl_image_buffer_pool_class_init (GstEGLImageBufferPoolClass * klass)
440 {
441   GObjectClass *gobject_class = (GObjectClass *) klass;
442   GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
443
444   gobject_class->finalize = gst_egl_image_buffer_pool_finalize;
445   gstbufferpool_class->get_options = gst_egl_image_buffer_pool_get_options;
446   gstbufferpool_class->set_config = gst_egl_image_buffer_pool_set_config;
447   gstbufferpool_class->alloc_buffer = gst_egl_image_buffer_pool_alloc_buffer;
448   gstbufferpool_class->acquire_buffer =
449       gst_egl_image_buffer_pool_acquire_buffer;
450 }
451
452 static void
453 gst_egl_image_buffer_pool_init (GstEGLImageBufferPool * pool)
454 {
455 }
456
457 #define parent_class gst_eglglessink_parent_class
458 G_DEFINE_TYPE_WITH_CODE (GstEglGlesSink, gst_eglglessink, GST_TYPE_VIDEO_SINK,
459     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
460         gst_eglglessink_videooverlay_init));
461
462 static inline gboolean
463 egl_init (GstEglGlesSink * eglglessink)
464 {
465   GstCaps *caps;
466
467   if (!gst_egl_adaptation_init_egl_display (eglglessink->egl_context)) {
468     GST_ERROR_OBJECT (eglglessink, "Couldn't init EGL display");
469     goto HANDLE_ERROR;
470   }
471
472   caps =
473       gst_egl_adaptation_fill_supported_fbuffer_configs
474       (eglglessink->egl_context);
475   if (!caps) {
476     GST_ERROR_OBJECT (eglglessink, "Display support NONE of our configs");
477     goto HANDLE_ERROR;
478   } else {
479     GST_OBJECT_LOCK (eglglessink);
480     gst_caps_replace (&eglglessink->sinkcaps, caps);
481     GST_OBJECT_UNLOCK (eglglessink);
482     gst_caps_unref (caps);
483   }
484
485   eglglessink->egl_started = TRUE;
486
487   return TRUE;
488
489 HANDLE_ERROR:
490   GST_ERROR_OBJECT (eglglessink, "Failed to perform EGL init");
491   return FALSE;
492 }
493
494 static gpointer
495 render_thread_func (GstEglGlesSink * eglglessink)
496 {
497   GstMessage *message;
498   GValue val = { 0 };
499   GstDataQueueItem *item = NULL;
500   GstFlowReturn last_flow = GST_FLOW_OK;
501
502   g_value_init (&val, GST_TYPE_G_THREAD);
503   g_value_set_boxed (&val, g_thread_self ());
504   message = gst_message_new_stream_status (GST_OBJECT_CAST (eglglessink),
505       GST_STREAM_STATUS_TYPE_ENTER, GST_ELEMENT_CAST (eglglessink));
506   gst_message_set_stream_status_object (message, &val);
507   GST_DEBUG_OBJECT (eglglessink, "posting ENTER stream status");
508   gst_element_post_message (GST_ELEMENT_CAST (eglglessink), message);
509   g_value_unset (&val);
510
511   eglBindAPI (EGL_OPENGL_ES_API);
512
513   while (gst_data_queue_pop (eglglessink->queue, &item)) {
514     GstMiniObject *object = item->object;
515
516     GST_DEBUG_OBJECT (eglglessink, "Handling object %" GST_PTR_FORMAT, object);
517
518     if (GST_IS_CAPS (object)) {
519       GstCaps *caps = GST_CAPS_CAST (object);
520
521       if (caps != eglglessink->configured_caps) {
522         if (!gst_eglglessink_configure_caps (eglglessink, caps)) {
523           last_flow = GST_FLOW_NOT_NEGOTIATED;
524         }
525       }
526     } else if (GST_IS_QUERY (object)) {
527       GstQuery *query = GST_QUERY_CAST (object);
528       GstStructure *s = (GstStructure *) gst_query_get_structure (query);
529
530       if (gst_structure_has_name (s, "eglglessink-allocate-eglimage")) {
531         GstBuffer *buffer;
532         GstVideoFormat format;
533         gint width, height;
534         GValue v = { 0, };
535
536         if (!gst_structure_get_enum (s, "format", GST_TYPE_VIDEO_FORMAT,
537                 (gint *) & format)
538             || !gst_structure_get_int (s, "width", &width)
539             || !gst_structure_get_int (s, "height", &height)) {
540           g_assert_not_reached ();
541         }
542
543         buffer =
544             gst_egl_adaptation_allocate_eglimage (eglglessink->egl_context,
545             GST_EGL_IMAGE_BUFFER_POOL (eglglessink->pool)->allocator, format,
546             width, height);
547         g_value_init (&v, G_TYPE_POINTER);
548         g_value_set_pointer (&v, buffer);
549         gst_structure_set_value (s, "buffer", &v);
550         g_value_unset (&v);
551       } else {
552         g_assert_not_reached ();
553       }
554       last_flow = GST_FLOW_OK;
555     } else if (GST_IS_BUFFER (object)) {
556       GstBuffer *buf = GST_BUFFER_CAST (item->object);
557
558       if (eglglessink->configured_caps) {
559         last_flow = gst_eglglessink_upload (eglglessink, buf);
560       } else {
561         last_flow = GST_FLOW_OK;
562         GST_DEBUG_OBJECT (eglglessink,
563             "No caps configured yet, not drawing anything");
564       }
565     } else if (!object) {
566       if (eglglessink->configured_caps) {
567         last_flow = gst_eglglessink_render (eglglessink);
568       } else {
569         last_flow = GST_FLOW_OK;
570         GST_DEBUG_OBJECT (eglglessink,
571             "No caps configured yet, not drawing anything");
572       }
573     } else {
574       g_assert_not_reached ();
575     }
576
577     item->destroy (item);
578     g_mutex_lock (&eglglessink->render_lock);
579     eglglessink->last_flow = last_flow;
580     eglglessink->dequeued_object = object;
581     g_cond_broadcast (&eglglessink->render_cond);
582     g_mutex_unlock (&eglglessink->render_lock);
583
584     if (last_flow != GST_FLOW_OK)
585       break;
586     GST_DEBUG_OBJECT (eglglessink, "Successfully handled object");
587   }
588
589   if (last_flow == GST_FLOW_OK) {
590     g_mutex_lock (&eglglessink->render_lock);
591     eglglessink->last_flow = GST_FLOW_FLUSHING;
592     eglglessink->dequeued_object = NULL;
593     g_cond_broadcast (&eglglessink->render_cond);
594     g_mutex_unlock (&eglglessink->render_lock);
595   }
596
597   GST_DEBUG_OBJECT (eglglessink, "Shutting down thread");
598
599   /* EGL/GLES cleanup */
600   gst_egl_adaptation_cleanup (eglglessink->egl_context);
601
602   if (eglglessink->configured_caps) {
603     gst_caps_unref (eglglessink->configured_caps);
604     eglglessink->configured_caps = NULL;
605   }
606
607   g_value_init (&val, GST_TYPE_G_THREAD);
608   g_value_set_boxed (&val, g_thread_self ());
609   message = gst_message_new_stream_status (GST_OBJECT_CAST (eglglessink),
610       GST_STREAM_STATUS_TYPE_LEAVE, GST_ELEMENT_CAST (eglglessink));
611   gst_message_set_stream_status_object (message, &val);
612   GST_DEBUG_OBJECT (eglglessink, "posting LEAVE stream status");
613   gst_element_post_message (GST_ELEMENT_CAST (eglglessink), message);
614   g_value_unset (&val);
615
616   return NULL;
617 }
618
619 static gboolean
620 gst_eglglessink_start (GstEglGlesSink * eglglessink)
621 {
622   GError *error = NULL;
623
624   GST_DEBUG_OBJECT (eglglessink, "Starting");
625
626   if (!eglglessink->egl_started) {
627     GST_ERROR_OBJECT (eglglessink, "EGL uninitialized. Bailing out");
628     goto HANDLE_ERROR;
629   }
630
631   /* Ask for a window to render to */
632   if (!eglglessink->have_window)
633     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (eglglessink));
634
635   if (!eglglessink->have_window && !eglglessink->create_window) {
636     GST_ERROR_OBJECT (eglglessink, "Window handle unavailable and we "
637         "were instructed not to create an internal one. Bailing out.");
638     goto HANDLE_ERROR;
639   }
640
641   eglglessink->last_flow = GST_FLOW_OK;
642   eglglessink->display_region.w = 0;
643   eglglessink->display_region.h = 0;
644
645   gst_data_queue_set_flushing (eglglessink->queue, FALSE);
646
647 #if !GLIB_CHECK_VERSION (2, 31, 0)
648   eglglessink->thread =
649       g_thread_create ((GThreadFunc) render_thread_func, eglglessink, TRUE,
650       &error);
651 #else
652   eglglessink->thread = g_thread_try_new ("eglglessink-render",
653       (GThreadFunc) render_thread_func, eglglessink, &error);
654 #endif
655
656   if (!eglglessink->thread || error != NULL)
657     goto HANDLE_ERROR;
658
659   GST_DEBUG_OBJECT (eglglessink, "Started");
660
661   return TRUE;
662
663 HANDLE_ERROR:
664   GST_ERROR_OBJECT (eglglessink, "Couldn't start");
665   g_clear_error (&error);
666   return FALSE;
667 }
668
669 static gboolean
670 gst_eglglessink_stop (GstEglGlesSink * eglglessink)
671 {
672   GST_DEBUG_OBJECT (eglglessink, "Stopping");
673
674   gst_data_queue_set_flushing (eglglessink->queue, TRUE);
675   g_mutex_lock (&eglglessink->render_lock);
676   g_cond_broadcast (&eglglessink->render_cond);
677   g_mutex_unlock (&eglglessink->render_lock);
678
679   if (eglglessink->thread) {
680     g_thread_join (eglglessink->thread);
681     eglglessink->thread = NULL;
682   }
683   eglglessink->last_flow = GST_FLOW_FLUSHING;
684
685   gst_buffer_replace (&eglglessink->last_buffer, NULL);
686
687   if (eglglessink->using_own_window) {
688     gst_egl_adaptation_destroy_native_window (eglglessink->egl_context,
689         &eglglessink->own_window_data);
690     eglglessink->have_window = FALSE;
691   }
692   eglglessink->egl_context->used_window = 0;
693   if (eglglessink->current_caps) {
694     gst_caps_unref (eglglessink->current_caps);
695     eglglessink->current_caps = NULL;
696   }
697
698   GST_DEBUG_OBJECT (eglglessink, "Stopped");
699
700   return TRUE;
701 }
702
703 static void
704 gst_eglglessink_videooverlay_init (GstVideoOverlayInterface * iface)
705 {
706   iface->set_window_handle = gst_eglglessink_set_window_handle;
707   iface->expose = gst_eglglessink_expose;
708   iface->set_render_rectangle = gst_eglglessink_set_render_rectangle;
709 }
710
711 static gboolean
712 gst_eglglessink_create_window (GstEglGlesSink * eglglessink, gint width,
713     gint height)
714 {
715   EGLNativeWindowType window = 0;
716   gboolean window_created = FALSE;
717
718   if (!eglglessink->create_window) {
719     GST_ERROR_OBJECT (eglglessink, "This sink can't create a window by itself");
720     return window;
721   } else
722     GST_INFO_OBJECT (eglglessink, "Attempting internal window creation");
723
724   window_created =
725       gst_egl_adaptation_create_native_window (eglglessink->egl_context, width,
726       height, &eglglessink->own_window_data);
727   if (!window_created) {
728     GST_ERROR_OBJECT (eglglessink, "Could not create window");
729   }
730   return window_created;
731 }
732
733 static void
734 gst_eglglessink_expose (GstVideoOverlay * overlay)
735 {
736   GstEglGlesSink *eglglessink;
737   GstFlowReturn ret;
738
739   eglglessink = GST_EGLGLESSINK (overlay);
740   GST_DEBUG_OBJECT (eglglessink, "Expose catched, redisplay");
741
742   /* Render from last seen buffer */
743   ret = gst_eglglessink_queue_object (eglglessink, NULL);
744   if (ret == GST_FLOW_ERROR)
745     GST_ERROR_OBJECT (eglglessink, "Redisplay failed");
746 }
747
748 static gboolean
749 gst_eglglessink_setup_vbo (GstEglGlesSink * eglglessink, gboolean reset)
750 {
751   gdouble render_width, render_height;
752   gdouble texture_width, texture_height;
753   gdouble x1, x2, y1, y2;
754   gdouble tx1, tx2, ty1, ty2;
755
756   GST_INFO_OBJECT (eglglessink, "VBO setup. have_vbo:%d, should reset %d",
757       eglglessink->egl_context->have_vbo, reset);
758
759   if (eglglessink->egl_context->have_vbo && reset) {
760     glDeleteBuffers (1, &eglglessink->egl_context->position_buffer);
761     glDeleteBuffers (1, &eglglessink->egl_context->index_buffer);
762     eglglessink->egl_context->have_vbo = FALSE;
763   }
764
765   render_width = eglglessink->render_region.w;
766   render_height = eglglessink->render_region.h;
767
768   texture_width = eglglessink->configured_info.width;
769   texture_height = eglglessink->configured_info.height;
770
771   GST_DEBUG_OBJECT (eglglessink, "Performing VBO setup");
772
773   x1 = (eglglessink->display_region.x / render_width) * 2.0 - 1;
774   y1 = (eglglessink->display_region.y / render_height) * 2.0 - 1;
775   x2 = ((eglglessink->display_region.x +
776           eglglessink->display_region.w) / render_width) * 2.0 - 1;
777   y2 = ((eglglessink->display_region.y +
778           eglglessink->display_region.h) / render_height) * 2.0 - 1;
779
780   tx1 = (eglglessink->crop.x / texture_width);
781   tx2 = ((eglglessink->crop.x + eglglessink->crop.w) / texture_width);
782   ty1 = (eglglessink->crop.y / texture_height);
783   ty2 = ((eglglessink->crop.y + eglglessink->crop.h) / texture_height);
784
785   /* X-normal, Y-normal orientation */
786   eglglessink->egl_context->position_array[0].x = x2;
787   eglglessink->egl_context->position_array[0].y = y2;
788   eglglessink->egl_context->position_array[0].z = 0;
789   eglglessink->egl_context->position_array[0].a = tx2;
790   eglglessink->egl_context->position_array[0].b = ty1;
791
792   eglglessink->egl_context->position_array[1].x = x2;
793   eglglessink->egl_context->position_array[1].y = y1;
794   eglglessink->egl_context->position_array[1].z = 0;
795   eglglessink->egl_context->position_array[1].a = tx2;
796   eglglessink->egl_context->position_array[1].b = ty2;
797
798   eglglessink->egl_context->position_array[2].x = x1;
799   eglglessink->egl_context->position_array[2].y = y2;
800   eglglessink->egl_context->position_array[2].z = 0;
801   eglglessink->egl_context->position_array[2].a = tx1;
802   eglglessink->egl_context->position_array[2].b = ty1;
803
804   eglglessink->egl_context->position_array[3].x = x1;
805   eglglessink->egl_context->position_array[3].y = y1;
806   eglglessink->egl_context->position_array[3].z = 0;
807   eglglessink->egl_context->position_array[3].a = tx1;
808   eglglessink->egl_context->position_array[3].b = ty2;
809
810   /* X-normal, Y-flip orientation */
811   eglglessink->egl_context->position_array[4 + 0].x = x2;
812   eglglessink->egl_context->position_array[4 + 0].y = y2;
813   eglglessink->egl_context->position_array[4 + 0].z = 0;
814   eglglessink->egl_context->position_array[4 + 0].a = tx2;
815   eglglessink->egl_context->position_array[4 + 0].b = ty2;
816
817   eglglessink->egl_context->position_array[4 + 1].x = x2;
818   eglglessink->egl_context->position_array[4 + 1].y = y1;
819   eglglessink->egl_context->position_array[4 + 1].z = 0;
820   eglglessink->egl_context->position_array[4 + 1].a = tx2;
821   eglglessink->egl_context->position_array[4 + 1].b = ty1;
822
823   eglglessink->egl_context->position_array[4 + 2].x = x1;
824   eglglessink->egl_context->position_array[4 + 2].y = y2;
825   eglglessink->egl_context->position_array[4 + 2].z = 0;
826   eglglessink->egl_context->position_array[4 + 2].a = tx1;
827   eglglessink->egl_context->position_array[4 + 2].b = ty2;
828
829   eglglessink->egl_context->position_array[4 + 3].x = x1;
830   eglglessink->egl_context->position_array[4 + 3].y = y1;
831   eglglessink->egl_context->position_array[4 + 3].z = 0;
832   eglglessink->egl_context->position_array[4 + 3].a = tx1;
833   eglglessink->egl_context->position_array[4 + 3].b = ty1;
834
835
836   if (eglglessink->display_region.x == 0) {
837     /* Borders top/bottom */
838
839     eglglessink->egl_context->position_array[8 + 0].x = 1;
840     eglglessink->egl_context->position_array[8 + 0].y = 1;
841     eglglessink->egl_context->position_array[8 + 0].z = 0;
842
843     eglglessink->egl_context->position_array[8 + 1].x = x2;
844     eglglessink->egl_context->position_array[8 + 1].y = y2;
845     eglglessink->egl_context->position_array[8 + 1].z = 0;
846
847     eglglessink->egl_context->position_array[8 + 2].x = -1;
848     eglglessink->egl_context->position_array[8 + 2].y = 1;
849     eglglessink->egl_context->position_array[8 + 2].z = 0;
850
851     eglglessink->egl_context->position_array[8 + 3].x = x1;
852     eglglessink->egl_context->position_array[8 + 3].y = y2;
853     eglglessink->egl_context->position_array[8 + 3].z = 0;
854
855     eglglessink->egl_context->position_array[12 + 0].x = 1;
856     eglglessink->egl_context->position_array[12 + 0].y = y1;
857     eglglessink->egl_context->position_array[12 + 0].z = 0;
858
859     eglglessink->egl_context->position_array[12 + 1].x = 1;
860     eglglessink->egl_context->position_array[12 + 1].y = -1;
861     eglglessink->egl_context->position_array[12 + 1].z = 0;
862
863     eglglessink->egl_context->position_array[12 + 2].x = x1;
864     eglglessink->egl_context->position_array[12 + 2].y = y1;
865     eglglessink->egl_context->position_array[12 + 2].z = 0;
866
867     eglglessink->egl_context->position_array[12 + 3].x = -1;
868     eglglessink->egl_context->position_array[12 + 3].y = -1;
869     eglglessink->egl_context->position_array[12 + 3].z = 0;
870   } else {
871     /* Borders left/right */
872
873     eglglessink->egl_context->position_array[8 + 0].x = x1;
874     eglglessink->egl_context->position_array[8 + 0].y = 1;
875     eglglessink->egl_context->position_array[8 + 0].z = 0;
876
877     eglglessink->egl_context->position_array[8 + 1].x = x1;
878     eglglessink->egl_context->position_array[8 + 1].y = -1;
879     eglglessink->egl_context->position_array[8 + 1].z = 0;
880
881     eglglessink->egl_context->position_array[8 + 2].x = -1;
882     eglglessink->egl_context->position_array[8 + 2].y = 1;
883     eglglessink->egl_context->position_array[8 + 2].z = 0;
884
885     eglglessink->egl_context->position_array[8 + 3].x = -1;
886     eglglessink->egl_context->position_array[8 + 3].y = -1;
887     eglglessink->egl_context->position_array[8 + 3].z = 0;
888
889     eglglessink->egl_context->position_array[12 + 0].x = 1;
890     eglglessink->egl_context->position_array[12 + 0].y = 1;
891     eglglessink->egl_context->position_array[12 + 0].z = 0;
892
893     eglglessink->egl_context->position_array[12 + 1].x = 1;
894     eglglessink->egl_context->position_array[12 + 1].y = -1;
895     eglglessink->egl_context->position_array[12 + 1].z = 0;
896
897     eglglessink->egl_context->position_array[12 + 2].x = x2;
898     eglglessink->egl_context->position_array[12 + 2].y = y2;
899     eglglessink->egl_context->position_array[12 + 2].z = 0;
900
901     eglglessink->egl_context->position_array[12 + 3].x = x2;
902     eglglessink->egl_context->position_array[12 + 3].y = -1;
903     eglglessink->egl_context->position_array[12 + 3].z = 0;
904   }
905
906   eglglessink->egl_context->index_array[0] = 0;
907   eglglessink->egl_context->index_array[1] = 1;
908   eglglessink->egl_context->index_array[2] = 2;
909   eglglessink->egl_context->index_array[3] = 3;
910
911   glGenBuffers (1, &eglglessink->egl_context->position_buffer);
912   glGenBuffers (1, &eglglessink->egl_context->index_buffer);
913   if (got_gl_error ("glGenBuffers"))
914     goto HANDLE_ERROR_LOCKED;
915
916   glBindBuffer (GL_ARRAY_BUFFER, eglglessink->egl_context->position_buffer);
917   if (got_gl_error ("glBindBuffer position_buffer"))
918     goto HANDLE_ERROR_LOCKED;
919
920   glBufferData (GL_ARRAY_BUFFER,
921       sizeof (eglglessink->egl_context->position_array),
922       eglglessink->egl_context->position_array, GL_STATIC_DRAW);
923   if (got_gl_error ("glBufferData position_buffer"))
924     goto HANDLE_ERROR_LOCKED;
925
926   glBindBuffer (GL_ELEMENT_ARRAY_BUFFER,
927       eglglessink->egl_context->index_buffer);
928   if (got_gl_error ("glBindBuffer index_buffer"))
929     goto HANDLE_ERROR_LOCKED;
930
931   glBufferData (GL_ELEMENT_ARRAY_BUFFER,
932       sizeof (eglglessink->egl_context->index_array),
933       eglglessink->egl_context->index_array, GL_STATIC_DRAW);
934   if (got_gl_error ("glBufferData index_buffer"))
935     goto HANDLE_ERROR_LOCKED;
936
937   eglglessink->egl_context->have_vbo = TRUE;
938   GST_DEBUG_OBJECT (eglglessink, "VBO setup done");
939
940   return TRUE;
941
942 HANDLE_ERROR_LOCKED:
943   GST_ERROR_OBJECT (eglglessink, "Unable to perform VBO setup");
944   return FALSE;
945 }
946
947 static void
948 gst_eglglessink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
949 {
950   GstEglGlesSink *eglglessink = GST_EGLGLESSINK (overlay);
951
952   g_return_if_fail (GST_IS_EGLGLESSINK (eglglessink));
953   GST_DEBUG_OBJECT (eglglessink, "We got a window handle: %p", (gpointer) id);
954
955   /* OK, we have a new window */
956   GST_OBJECT_LOCK (eglglessink);
957   eglglessink->egl_context->window = (EGLNativeWindowType) id;
958   eglglessink->have_window = ((gpointer) id != NULL);
959   GST_OBJECT_UNLOCK (eglglessink);
960
961   return;
962 }
963
964 static void
965 gst_eglglessink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
966     gint width, gint height)
967 {
968   GstEglGlesSink *eglglessink = GST_EGLGLESSINK (overlay);
969
970   g_return_if_fail (GST_IS_EGLGLESSINK (eglglessink));
971
972   GST_OBJECT_LOCK (eglglessink);
973   eglglessink->render_region.x = x;
974   eglglessink->render_region.y = y;
975   eglglessink->render_region.w = width;
976   eglglessink->render_region.h = height;
977   eglglessink->render_region_changed = TRUE;
978   eglglessink->render_region_user = (width != -1 && height != -1);
979   GST_OBJECT_UNLOCK (eglglessink);
980
981   return;
982 }
983
984 static void
985 queue_item_destroy (GstDataQueueItem * item)
986 {
987   if (item->object && !GST_IS_QUERY (item->object))
988     gst_mini_object_unref (item->object);
989   g_slice_free (GstDataQueueItem, item);
990 }
991
992 static GstFlowReturn
993 gst_eglglessink_queue_object (GstEglGlesSink * eglglessink, GstMiniObject * obj)
994 {
995   GstDataQueueItem *item;
996   GstFlowReturn last_flow;
997
998   g_mutex_lock (&eglglessink->render_lock);
999   last_flow = eglglessink->last_flow;
1000   g_mutex_unlock (&eglglessink->render_lock);
1001
1002   if (last_flow != GST_FLOW_OK)
1003     return last_flow;
1004
1005   item = g_slice_new0 (GstDataQueueItem);
1006
1007   if (obj == NULL)
1008     item->object = NULL;
1009   else if (GST_IS_QUERY (obj))
1010     item->object = obj;
1011   else
1012     item->object = gst_mini_object_ref (obj);
1013   item->size = 0;
1014   item->duration = GST_CLOCK_TIME_NONE;
1015   item->visible = TRUE;
1016   item->destroy = (GDestroyNotify) queue_item_destroy;
1017
1018   GST_DEBUG_OBJECT (eglglessink, "Queueing object %" GST_PTR_FORMAT, obj);
1019
1020   g_mutex_lock (&eglglessink->render_lock);
1021   if (!gst_data_queue_push (eglglessink->queue, item)) {
1022     item->destroy (item);
1023     g_mutex_unlock (&eglglessink->render_lock);
1024     GST_DEBUG_OBJECT (eglglessink, "Flushing");
1025     return GST_FLOW_FLUSHING;
1026   }
1027
1028   GST_DEBUG_OBJECT (eglglessink, "Waiting for object to be handled");
1029   do {
1030     g_cond_wait (&eglglessink->render_cond, &eglglessink->render_lock);
1031   } while (eglglessink->dequeued_object != obj
1032       && eglglessink->last_flow != GST_FLOW_FLUSHING);
1033   GST_DEBUG_OBJECT (eglglessink, "Object handled: %s",
1034       gst_flow_get_name (eglglessink->last_flow));
1035   last_flow = eglglessink->last_flow;
1036   g_mutex_unlock (&eglglessink->render_lock);
1037
1038   return (obj ? last_flow : GST_FLOW_OK);
1039 }
1040
1041 static gboolean
1042 gst_eglglessink_crop_changed (GstEglGlesSink * eglglessink,
1043     GstVideoCropMeta * crop)
1044 {
1045   if (crop) {
1046     return (crop->x != eglglessink->crop.x ||
1047         crop->y != eglglessink->crop.y ||
1048         crop->width != eglglessink->crop.w ||
1049         crop->height != eglglessink->crop.h);
1050   }
1051
1052   return (eglglessink->crop.x != 0 || eglglessink->crop.y != 0 ||
1053       eglglessink->crop.w != eglglessink->configured_info.width ||
1054       eglglessink->crop.h != eglglessink->configured_info.height);
1055 }
1056
1057 static gboolean
1058 gst_eglglessink_fill_texture (GstEglGlesSink * eglglessink, GstBuffer * buf)
1059 {
1060   GstVideoFrame vframe;
1061   gint w, h;
1062
1063   memset (&vframe, 0, sizeof (vframe));
1064
1065   if (!gst_video_frame_map (&vframe, &eglglessink->configured_info, buf,
1066           GST_MAP_READ)) {
1067     GST_ERROR_OBJECT (eglglessink, "Couldn't map frame");
1068     goto HANDLE_ERROR;
1069   }
1070
1071   w = GST_VIDEO_FRAME_WIDTH (&vframe);
1072   h = GST_VIDEO_FRAME_HEIGHT (&vframe);
1073
1074   GST_DEBUG_OBJECT (eglglessink,
1075       "Got buffer %p: %dx%d size %" G_GSIZE_FORMAT, buf, w, h,
1076       gst_buffer_get_size (buf));
1077
1078   switch (eglglessink->configured_info.finfo->format) {
1079     case GST_VIDEO_FORMAT_BGR:
1080     case GST_VIDEO_FORMAT_RGB:{
1081       gint stride;
1082       gint stride_width;
1083       gint c_w;
1084
1085       stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1086       stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe);
1087
1088       glActiveTexture (GL_TEXTURE0);
1089
1090       if (GST_ROUND_UP_8 (c_w * 3) == stride) {
1091         glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1092       } else if (GST_ROUND_UP_4 (c_w * 3) == stride) {
1093         glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1094       } else if (GST_ROUND_UP_2 (c_w * 3) == stride) {
1095         glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1096       } else if (c_w * 3 == stride) {
1097         glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1098       } else {
1099         stride_width = stride;
1100
1101         if (GST_ROUND_UP_8 (stride_width * 3) == stride) {
1102           glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1103         } else if (GST_ROUND_UP_4 (stride_width * 3) == stride) {
1104           glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1105         } else if (GST_ROUND_UP_2 (stride_width * 3) == stride) {
1106           glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1107         } else if (stride_width * 3 == stride) {
1108           glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1109         } else {
1110           GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1111           goto HANDLE_ERROR;
1112         }
1113       }
1114       if (got_gl_error ("glPixelStorei"))
1115         goto HANDLE_ERROR;
1116
1117       eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1118
1119       glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1120       glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, stride_width, h, 0, GL_RGB,
1121           GL_UNSIGNED_BYTE, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0));
1122       break;
1123     }
1124     case GST_VIDEO_FORMAT_RGB16:{
1125       gint stride;
1126       gint stride_width;
1127       gint c_w;
1128
1129       stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1130       stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe);
1131
1132       glActiveTexture (GL_TEXTURE0);
1133
1134       if (GST_ROUND_UP_8 (c_w * 2) == stride) {
1135         glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1136       } else if (GST_ROUND_UP_4 (c_w * 2) == stride) {
1137         glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1138       } else if (c_w * 2 == stride) {
1139         glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1140       } else {
1141         stride_width = stride;
1142
1143         if (GST_ROUND_UP_8 (stride_width * 4) == stride) {
1144           glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1145         } else if (GST_ROUND_UP_4 (stride_width * 2) == stride) {
1146           glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1147         } else if (stride_width * 2 == stride) {
1148           glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1149         } else {
1150           GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1151           goto HANDLE_ERROR;
1152         }
1153       }
1154       if (got_gl_error ("glPixelStorei"))
1155         goto HANDLE_ERROR;
1156
1157       eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1158
1159       glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1160       glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, stride_width, h, 0, GL_RGB,
1161           GL_UNSIGNED_SHORT_5_6_5, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0));
1162       break;
1163     }
1164     case GST_VIDEO_FORMAT_RGBA:
1165     case GST_VIDEO_FORMAT_BGRA:
1166     case GST_VIDEO_FORMAT_ARGB:
1167     case GST_VIDEO_FORMAT_ABGR:
1168     case GST_VIDEO_FORMAT_RGBx:
1169     case GST_VIDEO_FORMAT_BGRx:
1170     case GST_VIDEO_FORMAT_xRGB:
1171     case GST_VIDEO_FORMAT_xBGR:{
1172       gint stride;
1173       gint stride_width;
1174       gint c_w;
1175
1176       stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1177       stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe);
1178
1179       glActiveTexture (GL_TEXTURE0);
1180
1181       if (GST_ROUND_UP_8 (c_w * 4) == stride) {
1182         glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1183       } else if (c_w * 4 == stride) {
1184         glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1185       } else {
1186         stride_width = stride;
1187
1188         if (GST_ROUND_UP_8 (stride_width * 4) == stride) {
1189           glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1190         } else if (stride_width * 4 == stride) {
1191           glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1192         } else {
1193           GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1194           goto HANDLE_ERROR;
1195         }
1196       }
1197       if (got_gl_error ("glPixelStorei"))
1198         goto HANDLE_ERROR;
1199
1200       eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1201
1202       glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1203       glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, stride_width, h, 0,
1204           GL_RGBA, GL_UNSIGNED_BYTE, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0));
1205       break;
1206     }
1207     case GST_VIDEO_FORMAT_AYUV:{
1208       gint stride;
1209       gint stride_width;
1210       gint c_w;
1211
1212       stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1213       stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe);
1214
1215       glActiveTexture (GL_TEXTURE0);
1216
1217       if (GST_ROUND_UP_8 (c_w * 4) == stride) {
1218         glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1219       } else if (c_w * 4 == stride) {
1220         glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1221       } else {
1222         stride_width = stride;
1223
1224         if (GST_ROUND_UP_8 (stride_width * 4) == stride) {
1225           glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1226         } else if (stride_width * 4 == stride) {
1227           glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1228         } else {
1229           GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1230           goto HANDLE_ERROR;
1231         }
1232       }
1233       if (got_gl_error ("glPixelStorei"))
1234         goto HANDLE_ERROR;
1235
1236       eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1237
1238       glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1239       glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, stride_width, h, 0,
1240           GL_RGBA, GL_UNSIGNED_BYTE, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0));
1241       break;
1242     }
1243     case GST_VIDEO_FORMAT_Y444:
1244     case GST_VIDEO_FORMAT_I420:
1245     case GST_VIDEO_FORMAT_YV12:
1246     case GST_VIDEO_FORMAT_Y42B:
1247     case GST_VIDEO_FORMAT_Y41B:{
1248       gint stride;
1249       gint stride_width;
1250       gint c_w;
1251
1252       stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1253       stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 0);
1254
1255       glActiveTexture (GL_TEXTURE0);
1256
1257       if (GST_ROUND_UP_8 (c_w) == stride) {
1258         glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1259       } else if (GST_ROUND_UP_4 (c_w) == stride) {
1260         glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1261       } else if (GST_ROUND_UP_2 (c_w) == stride) {
1262         glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1263       } else if (c_w == stride) {
1264         glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1265       } else {
1266         stride_width = stride;
1267
1268         if (GST_ROUND_UP_8 (stride_width) == stride) {
1269           glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1270         } else if (GST_ROUND_UP_4 (stride_width) == stride) {
1271           glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1272         } else if (GST_ROUND_UP_2 (stride_width) == stride) {
1273           glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1274         } else if (stride_width == stride) {
1275           glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1276         } else {
1277           GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1278           goto HANDLE_ERROR;
1279         }
1280       }
1281       if (got_gl_error ("glPixelStorei"))
1282         goto HANDLE_ERROR;
1283
1284       eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1285
1286       glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1287       glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1288           stride_width,
1289           GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 0),
1290           0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
1291           GST_VIDEO_FRAME_COMP_DATA (&vframe, 0));
1292
1293
1294       stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 1);
1295       stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 1);
1296
1297       glActiveTexture (GL_TEXTURE1);
1298
1299       if (GST_ROUND_UP_8 (c_w) == stride) {
1300         glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1301       } else if (GST_ROUND_UP_4 (c_w) == stride) {
1302         glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1303       } else if (GST_ROUND_UP_2 (c_w) == stride) {
1304         glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1305       } else if (c_w == stride) {
1306         glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1307       } else {
1308         stride_width = stride;
1309
1310         if (GST_ROUND_UP_8 (stride_width) == stride) {
1311           glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1312         } else if (GST_ROUND_UP_4 (stride_width) == stride) {
1313           glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1314         } else if (GST_ROUND_UP_2 (stride_width) == stride) {
1315           glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1316         } else if (stride_width == stride) {
1317           glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1318         } else {
1319           GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1320           goto HANDLE_ERROR;
1321         }
1322       }
1323       if (got_gl_error ("glPixelStorei"))
1324         goto HANDLE_ERROR;
1325
1326       eglglessink->stride[1] = ((gdouble) stride_width) / ((gdouble) c_w);
1327
1328       glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[1]);
1329       glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1330           stride_width,
1331           GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 1),
1332           0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
1333           GST_VIDEO_FRAME_COMP_DATA (&vframe, 1));
1334
1335
1336       stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 2);
1337       stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 2);
1338
1339       glActiveTexture (GL_TEXTURE2);
1340
1341       if (GST_ROUND_UP_8 (c_w) == stride) {
1342         glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1343       } else if (GST_ROUND_UP_4 (c_w) == stride) {
1344         glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1345       } else if (GST_ROUND_UP_2 (c_w) == stride) {
1346         glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1347       } else if (c_w == stride) {
1348         glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1349       } else {
1350         stride_width = stride;
1351
1352         if (GST_ROUND_UP_8 (stride_width) == stride) {
1353           glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1354         } else if (GST_ROUND_UP_4 (stride_width) == stride) {
1355           glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1356         } else if (GST_ROUND_UP_2 (stride_width) == stride) {
1357           glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1358         } else if (stride_width == stride) {
1359           glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1360         } else {
1361           GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1362           goto HANDLE_ERROR;
1363         }
1364       }
1365       if (got_gl_error ("glPixelStorei"))
1366         goto HANDLE_ERROR;
1367
1368       eglglessink->stride[2] = ((gdouble) stride_width) / ((gdouble) c_w);
1369
1370       glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[2]);
1371       glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1372           stride_width,
1373           GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 2),
1374           0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
1375           GST_VIDEO_FRAME_COMP_DATA (&vframe, 2));
1376       break;
1377     }
1378     case GST_VIDEO_FORMAT_NV12:
1379     case GST_VIDEO_FORMAT_NV21:{
1380       gint stride;
1381       gint stride_width;
1382       gint c_w;
1383
1384       stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1385       stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 0);
1386
1387       glActiveTexture (GL_TEXTURE0);
1388
1389       if (GST_ROUND_UP_8 (c_w) == stride) {
1390         glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1391       } else if (GST_ROUND_UP_4 (c_w) == stride) {
1392         glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1393       } else if (GST_ROUND_UP_2 (c_w) == stride) {
1394         glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1395       } else if (c_w == stride) {
1396         glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1397       } else {
1398         stride_width = stride;
1399
1400         if (GST_ROUND_UP_8 (stride_width) == stride) {
1401           glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1402         } else if (GST_ROUND_UP_4 (stride_width) == stride) {
1403           glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1404         } else if (GST_ROUND_UP_2 (stride_width) == stride) {
1405           glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1406         } else if (stride_width == stride) {
1407           glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1408         } else {
1409           GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1410           goto HANDLE_ERROR;
1411         }
1412       }
1413       if (got_gl_error ("glPixelStorei"))
1414         goto HANDLE_ERROR;
1415
1416       eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1417
1418       glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1419       glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1420           stride_width,
1421           GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 0),
1422           0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
1423           GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0));
1424
1425
1426       stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 1);
1427       stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 1);
1428
1429       glActiveTexture (GL_TEXTURE1);
1430
1431       if (GST_ROUND_UP_8 (c_w * 2) == stride) {
1432         glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1433       } else if (GST_ROUND_UP_4 (c_w * 2) == stride) {
1434         glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1435       } else if (c_w * 2 == stride) {
1436         glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1437       } else {
1438         stride_width = stride / 2;
1439
1440         if (GST_ROUND_UP_8 (stride_width * 2) == stride) {
1441           glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1442         } else if (GST_ROUND_UP_4 (stride_width * 2) == stride) {
1443           glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1444         } else if (stride_width * 2 == stride) {
1445           glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1446         } else {
1447           GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1448           goto HANDLE_ERROR;
1449         }
1450       }
1451       if (got_gl_error ("glPixelStorei"))
1452         goto HANDLE_ERROR;
1453
1454       eglglessink->stride[1] = ((gdouble) stride_width) / ((gdouble) c_w);
1455
1456       glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[1]);
1457       glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
1458           stride_width,
1459           GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 1),
1460           0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,
1461           GST_VIDEO_FRAME_PLANE_DATA (&vframe, 1));
1462       break;
1463     }
1464     default:
1465       g_assert_not_reached ();
1466       break;
1467   }
1468
1469   if (got_gl_error ("glTexImage2D"))
1470     goto HANDLE_ERROR;
1471
1472   gst_video_frame_unmap (&vframe);
1473
1474   return TRUE;
1475
1476 HANDLE_ERROR:
1477   {
1478     if (vframe.buffer)
1479       gst_video_frame_unmap (&vframe);
1480     return FALSE;
1481   }
1482 }
1483
1484 /* Rendering and display */
1485 static GstFlowReturn
1486 gst_eglglessink_upload (GstEglGlesSink * eglglessink, GstBuffer * buf)
1487 {
1488   GstVideoCropMeta *crop = NULL;
1489
1490   if (!buf) {
1491     GST_DEBUG_OBJECT (eglglessink, "Rendering previous buffer again");
1492   } else if (buf) {
1493     GstMemory *mem;
1494     GstVideoGLTextureUploadMeta *upload_meta;
1495
1496     crop = gst_buffer_get_video_crop_meta (buf);
1497
1498     upload_meta = gst_buffer_get_video_gl_texture_upload_meta (buf);
1499
1500     if (gst_eglglessink_crop_changed (eglglessink, crop)) {
1501       if (crop) {
1502         eglglessink->crop.x = crop->x;
1503         eglglessink->crop.y = crop->y;
1504         eglglessink->crop.w = crop->width;
1505         eglglessink->crop.h = crop->height;
1506       } else {
1507         eglglessink->crop.x = 0;
1508         eglglessink->crop.y = 0;
1509         eglglessink->crop.w = eglglessink->configured_info.width;
1510         eglglessink->crop.h = eglglessink->configured_info.height;
1511       }
1512       eglglessink->crop_changed = TRUE;
1513     }
1514
1515     if (upload_meta) {
1516       gint i;
1517
1518       if (upload_meta->n_textures != eglglessink->egl_context->n_textures)
1519         goto HANDLE_ERROR;
1520
1521       for (i = 0; i < eglglessink->egl_context->n_textures; i++) {
1522         if (i == 0)
1523           glActiveTexture (GL_TEXTURE0);
1524         else if (i == 1)
1525           glActiveTexture (GL_TEXTURE1);
1526         else if (i == 2)
1527           glActiveTexture (GL_TEXTURE2);
1528
1529         glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[i]);
1530       }
1531
1532       if (!gst_video_gl_texture_upload_meta_upload (upload_meta,
1533               eglglessink->egl_context->texture))
1534         goto HANDLE_ERROR;
1535
1536       eglglessink->orientation = upload_meta->texture_orientation;
1537       eglglessink->stride[0] = 1;
1538       eglglessink->stride[1] = 1;
1539       eglglessink->stride[2] = 1;
1540     } else if (gst_buffer_n_memory (buf) >= 1 &&
1541         (mem = gst_buffer_peek_memory (buf, 0))
1542         && gst_is_egl_image_memory (mem)) {
1543       guint n, i;
1544
1545       n = gst_buffer_n_memory (buf);
1546
1547       for (i = 0; i < n; i++) {
1548         mem = gst_buffer_peek_memory (buf, i);
1549
1550         g_assert (gst_is_egl_image_memory (mem));
1551
1552         if (i == 0)
1553           glActiveTexture (GL_TEXTURE0);
1554         else if (i == 1)
1555           glActiveTexture (GL_TEXTURE1);
1556         else if (i == 2)
1557           glActiveTexture (GL_TEXTURE2);
1558
1559         glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[i]);
1560         glEGLImageTargetTexture2DOES (GL_TEXTURE_2D,
1561             gst_egl_image_memory_get_image (mem));
1562         if (got_gl_error ("glEGLImageTargetTexture2DOES"))
1563           goto HANDLE_ERROR;
1564         eglglessink->orientation = gst_egl_image_memory_get_orientation (mem);
1565         if (eglglessink->orientation !=
1566             GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL
1567             && eglglessink->orientation !=
1568             GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_FLIP) {
1569           GST_ERROR_OBJECT (eglglessink, "Unsupported EGLImage orientation");
1570           return GST_FLOW_ERROR;
1571         }
1572       }
1573       gst_buffer_replace (&eglglessink->last_buffer, buf);
1574       eglglessink->stride[0] = 1;
1575       eglglessink->stride[1] = 1;
1576       eglglessink->stride[2] = 1;
1577     } else {
1578       eglglessink->orientation =
1579           GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL;
1580       if (!gst_eglglessink_fill_texture (eglglessink, buf))
1581         goto HANDLE_ERROR;
1582     }
1583   }
1584
1585   return GST_FLOW_OK;
1586
1587 HANDLE_ERROR:
1588   {
1589     GST_ERROR_OBJECT (eglglessink, "Failed to upload texture");
1590     return GST_FLOW_ERROR;
1591   }
1592 }
1593
1594 static GstFlowReturn
1595 gst_eglglessink_render (GstEglGlesSink * eglglessink)
1596 {
1597   guint dar_n, dar_d;
1598   gint i;
1599
1600   /* If no one has set a display rectangle on us initialize
1601    * a sane default. According to the docs on the xOverlay
1602    * interface we are supposed to fill the overlay 100%. We
1603    * do this trying to take PAR/DAR into account unless the
1604    * calling party explicitly ask us not to by setting
1605    * force_aspect_ratio to FALSE.
1606    */
1607   if (gst_egl_adaptation_update_surface_dimensions (eglglessink->egl_context) ||
1608       eglglessink->render_region_changed ||
1609       !eglglessink->display_region.w || !eglglessink->display_region.h ||
1610       eglglessink->crop_changed) {
1611     GST_OBJECT_LOCK (eglglessink);
1612
1613     if (!eglglessink->render_region_user) {
1614       eglglessink->render_region.x = 0;
1615       eglglessink->render_region.y = 0;
1616       eglglessink->render_region.w = eglglessink->egl_context->surface_width;
1617       eglglessink->render_region.h = eglglessink->egl_context->surface_height;
1618     }
1619     eglglessink->render_region_changed = FALSE;
1620     eglglessink->crop_changed = FALSE;
1621
1622     if (!eglglessink->force_aspect_ratio) {
1623       eglglessink->display_region.x = 0;
1624       eglglessink->display_region.y = 0;
1625       eglglessink->display_region.w = eglglessink->render_region.w;
1626       eglglessink->display_region.h = eglglessink->render_region.h;
1627     } else {
1628       GstVideoRectangle frame;
1629
1630       frame.x = 0;
1631       frame.y = 0;
1632
1633       if (!gst_video_calculate_display_ratio (&dar_n, &dar_d,
1634               eglglessink->crop.w, eglglessink->crop.h,
1635               eglglessink->configured_info.par_n,
1636               eglglessink->configured_info.par_d,
1637               eglglessink->egl_context->pixel_aspect_ratio_n,
1638               eglglessink->egl_context->pixel_aspect_ratio_d)) {
1639         GST_WARNING_OBJECT (eglglessink, "Could not compute resulting DAR");
1640         frame.w = eglglessink->crop.w;
1641         frame.h = eglglessink->crop.h;
1642       } else {
1643         /* Find suitable matching new size acording to dar & par
1644          * rationale for prefering leaving the height untouched
1645          * comes from interlacing considerations.
1646          * XXX: Move this to gstutils?
1647          */
1648         if (eglglessink->crop.h % dar_d == 0) {
1649           frame.w =
1650               gst_util_uint64_scale_int (eglglessink->crop.h, dar_n, dar_d);
1651           frame.h = eglglessink->crop.h;
1652         } else if (eglglessink->crop.w % dar_n == 0) {
1653           frame.h =
1654               gst_util_uint64_scale_int (eglglessink->crop.w, dar_d, dar_n);
1655           frame.w = eglglessink->crop.w;
1656         } else {
1657           /* Neither width nor height can be precisely scaled.
1658            * Prefer to leave height untouched. See comment above.
1659            */
1660           frame.w =
1661               gst_util_uint64_scale_int (eglglessink->crop.h, dar_n, dar_d);
1662           frame.h = eglglessink->crop.h;
1663         }
1664       }
1665
1666       gst_video_sink_center_rect (frame, eglglessink->render_region,
1667           &eglglessink->display_region, TRUE);
1668     }
1669
1670     glViewport (eglglessink->render_region.x,
1671         eglglessink->egl_context->surface_height -
1672         eglglessink->render_region.y -
1673         eglglessink->render_region.h,
1674         eglglessink->render_region.w, eglglessink->render_region.h);
1675
1676     /* Clear the surface once if its content is preserved */
1677     if (eglglessink->egl_context->buffer_preserved) {
1678       glClearColor (0.0, 0.0, 0.0, 1.0);
1679       glClear (GL_COLOR_BUFFER_BIT);
1680     }
1681
1682     if (!gst_eglglessink_setup_vbo (eglglessink, FALSE)) {
1683       GST_OBJECT_UNLOCK (eglglessink);
1684       GST_ERROR_OBJECT (eglglessink, "VBO setup failed");
1685       goto HANDLE_ERROR;
1686     }
1687     GST_OBJECT_UNLOCK (eglglessink);
1688   }
1689
1690   if (!eglglessink->egl_context->buffer_preserved) {
1691     /* Draw black borders */
1692     GST_DEBUG_OBJECT (eglglessink, "Drawing black border 1");
1693     glUseProgram (eglglessink->egl_context->glslprogram[1]);
1694
1695     glVertexAttribPointer (eglglessink->egl_context->position_loc[1], 3,
1696         GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (8 * sizeof (coord5)));
1697     if (got_gl_error ("glVertexAttribPointer"))
1698       goto HANDLE_ERROR;
1699
1700     glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);
1701     if (got_gl_error ("glDrawElements"))
1702       goto HANDLE_ERROR;
1703
1704     GST_DEBUG_OBJECT (eglglessink, "Drawing black border 2");
1705
1706     glVertexAttribPointer (eglglessink->egl_context->position_loc[1], 3,
1707         GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (12 * sizeof (coord5)));
1708     if (got_gl_error ("glVertexAttribPointer"))
1709       goto HANDLE_ERROR;
1710
1711     glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);
1712     if (got_gl_error ("glDrawElements"))
1713       goto HANDLE_ERROR;
1714   }
1715
1716   /* Draw video frame */
1717   GST_DEBUG_OBJECT (eglglessink, "Drawing video frame");
1718
1719   glUseProgram (eglglessink->egl_context->glslprogram[0]);
1720
1721   glUniform2f (eglglessink->egl_context->tex_scale_loc[0][0],
1722       eglglessink->stride[0], 1);
1723   glUniform2f (eglglessink->egl_context->tex_scale_loc[0][1],
1724       eglglessink->stride[1], 1);
1725   glUniform2f (eglglessink->egl_context->tex_scale_loc[0][2],
1726       eglglessink->stride[2], 1);
1727
1728   for (i = 0; i < eglglessink->egl_context->n_textures; i++) {
1729     glUniform1i (eglglessink->egl_context->tex_loc[0][i], i);
1730     if (got_gl_error ("glUniform1i"))
1731       goto HANDLE_ERROR;
1732   }
1733
1734   if (eglglessink->orientation ==
1735       GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL) {
1736     glVertexAttribPointer (eglglessink->egl_context->position_loc[0], 3,
1737         GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (0 * sizeof (coord5)));
1738     if (got_gl_error ("glVertexAttribPointer"))
1739       goto HANDLE_ERROR;
1740
1741     glVertexAttribPointer (eglglessink->egl_context->texpos_loc[0], 2,
1742         GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (3 * sizeof (gfloat)));
1743     if (got_gl_error ("glVertexAttribPointer"))
1744       goto HANDLE_ERROR;
1745   } else if (eglglessink->orientation ==
1746       GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_FLIP) {
1747     glVertexAttribPointer (eglglessink->egl_context->position_loc[0], 3,
1748         GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (4 * sizeof (coord5)));
1749     if (got_gl_error ("glVertexAttribPointer"))
1750       goto HANDLE_ERROR;
1751
1752     glVertexAttribPointer (eglglessink->egl_context->texpos_loc[0], 2,
1753         GL_FLOAT, GL_FALSE, sizeof (coord5),
1754         (gpointer) (4 * sizeof (coord5) + 3 * sizeof (gfloat)));
1755     if (got_gl_error ("glVertexAttribPointer"))
1756       goto HANDLE_ERROR;
1757   } else {
1758     g_assert_not_reached ();
1759   }
1760
1761   glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);
1762   if (got_gl_error ("glDrawElements"))
1763     goto HANDLE_ERROR;
1764
1765   if (!gst_egl_adaptation_context_swap_buffers (eglglessink->egl_context)) {
1766     goto HANDLE_ERROR;
1767   }
1768
1769
1770   GST_DEBUG_OBJECT (eglglessink, "Succesfully rendered 1 frame");
1771   return GST_FLOW_OK;
1772
1773 HANDLE_ERROR:
1774   GST_ERROR_OBJECT (eglglessink, "Rendering disabled for this frame");
1775
1776   return GST_FLOW_ERROR;
1777 }
1778
1779 static GstFlowReturn
1780 gst_eglglessink_prepare (GstBaseSink * bsink, GstBuffer * buf)
1781 {
1782   GstEglGlesSink *eglglessink;
1783
1784   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1785
1786   eglglessink = GST_EGLGLESSINK (bsink);
1787   GST_DEBUG_OBJECT (eglglessink, "Got buffer: %p", buf);
1788
1789   return gst_eglglessink_queue_object (eglglessink, GST_MINI_OBJECT_CAST (buf));
1790 }
1791
1792 static GstFlowReturn
1793 gst_eglglessink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1794 {
1795   GstEglGlesSink *eglglessink;
1796
1797   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1798
1799   eglglessink = GST_EGLGLESSINK (vsink);
1800   GST_DEBUG_OBJECT (eglglessink, "Got buffer: %p", buf);
1801
1802   return gst_eglglessink_queue_object (eglglessink, NULL);
1803 }
1804
1805 static GstCaps *
1806 gst_eglglessink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1807 {
1808   GstEglGlesSink *eglglessink;
1809   GstCaps *ret = NULL;
1810
1811   eglglessink = GST_EGLGLESSINK (bsink);
1812
1813   GST_OBJECT_LOCK (eglglessink);
1814   if (eglglessink->sinkcaps) {
1815     ret = gst_caps_ref (eglglessink->sinkcaps);
1816   } else {
1817     ret =
1818         gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD
1819             (bsink)));
1820   }
1821   GST_OBJECT_UNLOCK (eglglessink);
1822
1823   if (filter) {
1824     GstCaps *tmp =
1825         gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
1826
1827     gst_caps_unref (ret);
1828     ret = tmp;
1829   }
1830
1831   return ret;
1832 }
1833
1834 static gboolean
1835 gst_eglglessink_event (GstBaseSink * bsink, GstEvent * event)
1836 {
1837   GstEglGlesSink *eglglessink;
1838
1839   eglglessink = GST_EGLGLESSINK (bsink);
1840
1841   switch (GST_EVENT_TYPE (event)) {
1842     case GST_EVENT_CONTEXT:{
1843       GstContext *context;
1844       GstEGLDisplay *display;
1845
1846       gst_event_parse_context (event, &context);
1847
1848       if (gst_context_get_egl_display (context, &display)) {
1849         GST_OBJECT_LOCK (eglglessink);
1850         if (eglglessink->egl_context->set_display)
1851           gst_egl_display_unref (eglglessink->egl_context->set_display);
1852         eglglessink->egl_context->set_display = display;
1853         GST_OBJECT_UNLOCK (eglglessink);
1854       }
1855
1856       gst_context_unref (context);
1857
1858       return GST_BASE_SINK_CLASS (gst_eglglessink_parent_class)->event (bsink,
1859           event);
1860       break;
1861     }
1862     default:
1863       return GST_BASE_SINK_CLASS (gst_eglglessink_parent_class)->event (bsink,
1864           event);
1865       break;
1866   }
1867 }
1868
1869 static gboolean
1870 gst_eglglessink_query (GstBaseSink * bsink, GstQuery * query)
1871 {
1872   GstEglGlesSink *eglglessink;
1873
1874   eglglessink = GST_EGLGLESSINK (bsink);
1875
1876   switch (GST_QUERY_TYPE (query)) {
1877     case GST_QUERY_CONTEXT:{
1878       guint i, n;
1879
1880       GST_BASE_SINK_CLASS (gst_eglglessink_parent_class)->query (bsink, query);
1881
1882       n = gst_query_get_n_context_types (query);
1883       for (i = 0; i < n; i++) {
1884         const gchar *context_type = NULL;
1885
1886         gst_query_parse_nth_context_type (query, i, &context_type);
1887         if (g_strcmp0 (context_type, GST_EGL_DISPLAY_CONTEXT_TYPE) == 0) {
1888           GstContext *context, *old_context;
1889
1890           gst_query_parse_context (query, &old_context);
1891           if (old_context)
1892             context = gst_context_copy (old_context);
1893           else
1894             context = gst_context_new ();
1895
1896           gst_context_set_egl_display (context,
1897               eglglessink->egl_context->display);
1898           gst_query_set_context (query, context);
1899           gst_context_unref (context);
1900           break;
1901         }
1902       }
1903
1904       return TRUE;
1905       break;
1906     }
1907     default:
1908       return GST_BASE_SINK_CLASS (gst_eglglessink_parent_class)->query (bsink,
1909           query);
1910       break;
1911   }
1912 }
1913
1914 static void
1915 gst_eglglessink_set_context (GstElement * element, GstContext * context)
1916 {
1917   GstEglGlesSink *eglglessink;
1918   GstEGLDisplay *display = NULL;
1919
1920   eglglessink = GST_EGLGLESSINK (element);
1921
1922   if (gst_context_get_egl_display (context, &display)) {
1923     GST_OBJECT_LOCK (eglglessink);
1924     if (eglglessink->egl_context->set_display)
1925       gst_egl_display_unref (eglglessink->egl_context->set_display);
1926     eglglessink->egl_context->set_display = display;
1927     GST_OBJECT_UNLOCK (eglglessink);
1928   }
1929
1930   GST_OBJECT_LOCK (eglglessink);
1931   context = gst_context_copy (context);
1932   gst_context_set_egl_display (context, eglglessink->egl_context->display);
1933   GST_OBJECT_UNLOCK (eglglessink);
1934
1935   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
1936   gst_context_unref (context);
1937 }
1938
1939 static gboolean
1940 gst_eglglessink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1941 {
1942   GstEglGlesSink *eglglessink;
1943   GstBufferPool *pool;
1944   GstStructure *config;
1945   GstCaps *caps;
1946   GstVideoInfo info;
1947   gboolean need_pool;
1948   guint size;
1949   GstAllocator *allocator;
1950   GstAllocationParams params;
1951
1952   eglglessink = GST_EGLGLESSINK (bsink);
1953
1954   gst_allocation_params_init (&params);
1955
1956   gst_query_parse_allocation (query, &caps, &need_pool);
1957   if (!caps) {
1958     GST_ERROR_OBJECT (eglglessink, "allocation query without caps");
1959     return FALSE;
1960   }
1961
1962   if (!gst_video_info_from_caps (&info, caps)) {
1963     GST_ERROR_OBJECT (eglglessink, "allocation query with invalid caps");
1964     return FALSE;
1965   }
1966
1967   GST_OBJECT_LOCK (eglglessink);
1968   pool = eglglessink->pool ? gst_object_ref (eglglessink->pool) : NULL;
1969   GST_OBJECT_UNLOCK (eglglessink);
1970
1971   if (pool) {
1972     GstCaps *pcaps;
1973
1974     /* we had a pool, check caps */
1975     GST_DEBUG_OBJECT (eglglessink, "check existing pool caps");
1976     config = gst_buffer_pool_get_config (pool);
1977     gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1978
1979     if (!gst_caps_is_equal (caps, pcaps)) {
1980       GST_DEBUG_OBJECT (eglglessink, "pool has different caps");
1981       /* different caps, we can't use this pool */
1982       gst_object_unref (pool);
1983       pool = NULL;
1984     }
1985     gst_structure_free (config);
1986   }
1987
1988   if (pool == NULL && need_pool) {
1989     GstVideoInfo info;
1990
1991     if (!gst_video_info_from_caps (&info, caps)) {
1992       GST_ERROR_OBJECT (eglglessink, "allocation query has invalid caps %"
1993           GST_PTR_FORMAT, caps);
1994       return FALSE;
1995     }
1996
1997     GST_DEBUG_OBJECT (eglglessink, "create new pool");
1998     pool =
1999         gst_egl_image_buffer_pool_new (eglglessink,
2000         eglglessink->egl_context->display);
2001
2002     /* the normal size of a frame */
2003     size = info.size;
2004
2005     config = gst_buffer_pool_get_config (pool);
2006     /* we need at least 2 buffer because we hold on to the last one */
2007     gst_buffer_pool_config_set_params (config, caps, size, 2, 0);
2008     gst_buffer_pool_config_set_allocator (config, NULL, &params);
2009     if (!gst_buffer_pool_set_config (pool, config)) {
2010       gst_object_unref (pool);
2011       GST_ERROR_OBJECT (eglglessink, "failed to set pool configuration");
2012       return FALSE;
2013     }
2014   }
2015
2016   if (pool) {
2017     /* we need at least 2 buffer because we hold on to the last one */
2018     gst_query_add_allocation_pool (query, pool, size, 2, 0);
2019     gst_object_unref (pool);
2020   }
2021
2022   /* First the default allocator */
2023   if (!gst_egl_image_memory_is_mappable ()) {
2024     allocator = gst_allocator_find (NULL);
2025     gst_query_add_allocation_param (query, allocator, &params);
2026     gst_object_unref (allocator);
2027   }
2028
2029   allocator = gst_egl_image_allocator_obtain ();
2030   if (!gst_egl_image_memory_is_mappable ())
2031     params.flags |= GST_MEMORY_FLAG_NOT_MAPPABLE;
2032   gst_query_add_allocation_param (query, allocator, &params);
2033   gst_object_unref (allocator);
2034
2035   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
2036   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
2037   gst_query_add_allocation_meta (query,
2038       GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, NULL);
2039
2040   return TRUE;
2041 }
2042
2043 static gboolean
2044 gst_eglglessink_configure_caps (GstEglGlesSink * eglglessink, GstCaps * caps)
2045 {
2046   gboolean ret = TRUE;
2047   GstVideoInfo info;
2048
2049   gst_video_info_init (&info);
2050   if (!(ret = gst_video_info_from_caps (&info, caps))) {
2051     GST_ERROR_OBJECT (eglglessink, "Couldn't parse caps");
2052     goto HANDLE_ERROR;
2053   }
2054
2055   eglglessink->configured_info = info;
2056   GST_VIDEO_SINK_WIDTH (eglglessink) = info.width;
2057   GST_VIDEO_SINK_HEIGHT (eglglessink) = info.height;
2058
2059   if (eglglessink->configured_caps) {
2060     GST_DEBUG_OBJECT (eglglessink, "Caps were already set");
2061     if (gst_caps_can_intersect (caps, eglglessink->configured_caps)) {
2062       GST_DEBUG_OBJECT (eglglessink, "Caps are compatible anyway");
2063       goto SUCCEED;
2064     }
2065
2066     GST_DEBUG_OBJECT (eglglessink, "Caps are not compatible, reconfiguring");
2067
2068     /* EGL/GLES cleanup */
2069     gst_egl_adaptation_cleanup (eglglessink->egl_context);
2070
2071     gst_caps_unref (eglglessink->configured_caps);
2072     eglglessink->configured_caps = NULL;
2073   }
2074
2075   if (!gst_egl_adaptation_choose_config (eglglessink->egl_context)) {
2076     GST_ERROR_OBJECT (eglglessink, "Couldn't choose EGL config");
2077     goto HANDLE_ERROR;
2078   }
2079
2080   gst_caps_replace (&eglglessink->configured_caps, caps);
2081
2082   /* By now the application should have set a window
2083    * if it meant to do so
2084    */
2085   GST_OBJECT_LOCK (eglglessink);
2086   if (!eglglessink->have_window) {
2087
2088     GST_INFO_OBJECT (eglglessink,
2089         "No window. Will attempt internal window creation");
2090     if (!gst_eglglessink_create_window (eglglessink, info.width, info.height)) {
2091       GST_ERROR_OBJECT (eglglessink, "Internal window creation failed!");
2092       GST_OBJECT_UNLOCK (eglglessink);
2093       goto HANDLE_ERROR;
2094     }
2095     eglglessink->using_own_window = TRUE;
2096     eglglessink->have_window = TRUE;
2097   }
2098   GST_DEBUG_OBJECT (eglglessink, "Using window handle %p",
2099       (gpointer) eglglessink->egl_context->window);
2100   eglglessink->egl_context->used_window = eglglessink->egl_context->window;
2101   GST_OBJECT_UNLOCK (eglglessink);
2102   gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (eglglessink),
2103       (guintptr) eglglessink->egl_context->used_window);
2104
2105   if (!eglglessink->egl_context->have_surface) {
2106     if (!gst_egl_adaptation_init_egl_surface (eglglessink->egl_context,
2107             eglglessink->configured_info.finfo->format)) {
2108       GST_ERROR_OBJECT (eglglessink, "Couldn't init EGL surface from window");
2109       goto HANDLE_ERROR;
2110     }
2111   }
2112
2113   gst_egl_adaptation_init_egl_exts (eglglessink->egl_context);
2114
2115 SUCCEED:
2116   GST_INFO_OBJECT (eglglessink, "Configured caps successfully");
2117   return TRUE;
2118
2119 HANDLE_ERROR:
2120   GST_ERROR_OBJECT (eglglessink, "Configuring caps failed");
2121   return FALSE;
2122 }
2123
2124 static gboolean
2125 gst_eglglessink_setcaps (GstBaseSink * bsink, GstCaps * caps)
2126 {
2127   GstEglGlesSink *eglglessink;
2128   GstVideoInfo info;
2129   GstBufferPool *newpool, *oldpool;
2130   GstStructure *config;
2131   GstAllocationParams params = { 0, };
2132
2133   eglglessink = GST_EGLGLESSINK (bsink);
2134
2135   GST_DEBUG_OBJECT (eglglessink,
2136       "Current caps %" GST_PTR_FORMAT ", setting caps %"
2137       GST_PTR_FORMAT, eglglessink->current_caps, caps);
2138
2139   if (gst_eglglessink_queue_object (eglglessink,
2140           GST_MINI_OBJECT_CAST (caps)) != GST_FLOW_OK) {
2141     GST_ERROR_OBJECT (eglglessink, "Failed to configure caps");
2142     return FALSE;
2143   }
2144
2145   if (!gst_video_info_from_caps (&info, caps)) {
2146     GST_ERROR_OBJECT (eglglessink, "Invalid caps %" GST_PTR_FORMAT, caps);
2147     return FALSE;
2148   }
2149
2150   newpool =
2151       gst_egl_image_buffer_pool_new (eglglessink,
2152       eglglessink->egl_context->display);
2153   config = gst_buffer_pool_get_config (newpool);
2154   /* we need at least 2 buffer because we hold on to the last one */
2155   gst_buffer_pool_config_set_params (config, caps, info.size, 2, 0);
2156   gst_buffer_pool_config_set_allocator (config, NULL, &params);
2157   if (!gst_buffer_pool_set_config (newpool, config)) {
2158     gst_object_unref (newpool);
2159     GST_ERROR_OBJECT (eglglessink, "Failed to set buffer pool configuration");
2160     return FALSE;
2161   }
2162
2163   GST_OBJECT_LOCK (eglglessink);
2164   oldpool = eglglessink->pool;
2165   eglglessink->pool = newpool;
2166   GST_OBJECT_UNLOCK (eglglessink);
2167
2168   if (oldpool)
2169     gst_object_unref (oldpool);
2170
2171   gst_caps_replace (&eglglessink->current_caps, caps);
2172
2173   return TRUE;
2174 }
2175
2176 static gboolean
2177 gst_eglglessink_open (GstEglGlesSink * eglglessink)
2178 {
2179   if (!egl_init (eglglessink)) {
2180     return FALSE;
2181   }
2182
2183   return TRUE;
2184 }
2185
2186 static gboolean
2187 gst_eglglessink_close (GstEglGlesSink * eglglessink)
2188 {
2189   if (eglglessink->egl_context->display) {
2190     gst_egl_display_unref (eglglessink->egl_context->display);
2191     eglglessink->egl_context->display = NULL;
2192   }
2193
2194   gst_caps_unref (eglglessink->sinkcaps);
2195   eglglessink->sinkcaps = NULL;
2196   eglglessink->egl_started = FALSE;
2197
2198   GST_OBJECT_LOCK (eglglessink);
2199   if (eglglessink->pool)
2200     gst_object_unref (eglglessink->pool);
2201   eglglessink->pool = NULL;
2202   GST_OBJECT_UNLOCK (eglglessink);
2203
2204   return TRUE;
2205 }
2206
2207 static GstStateChangeReturn
2208 gst_eglglessink_change_state (GstElement * element, GstStateChange transition)
2209 {
2210   GstEglGlesSink *eglglessink;
2211   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2212
2213   eglglessink = GST_EGLGLESSINK (element);
2214
2215   switch (transition) {
2216     case GST_STATE_CHANGE_NULL_TO_READY:
2217       if (!gst_eglglessink_open (eglglessink)) {
2218         ret = GST_STATE_CHANGE_FAILURE;
2219         goto done;
2220       }
2221       break;
2222     case GST_STATE_CHANGE_READY_TO_PAUSED:
2223       if (!gst_eglglessink_start (eglglessink)) {
2224         ret = GST_STATE_CHANGE_FAILURE;
2225         goto done;
2226       }
2227       break;
2228     default:
2229       break;
2230   }
2231
2232   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2233   if (ret == GST_STATE_CHANGE_FAILURE)
2234     return ret;
2235
2236   switch (transition) {
2237     case GST_STATE_CHANGE_READY_TO_NULL:
2238       if (!gst_eglglessink_close (eglglessink)) {
2239         ret = GST_STATE_CHANGE_FAILURE;
2240         goto done;
2241       }
2242       break;
2243     case GST_STATE_CHANGE_PAUSED_TO_READY:
2244       if (!gst_eglglessink_stop (eglglessink)) {
2245         ret = GST_STATE_CHANGE_FAILURE;
2246         goto done;
2247       }
2248       break;
2249     default:
2250       break;
2251   }
2252
2253 done:
2254   return ret;
2255 }
2256
2257 static void
2258 gst_eglglessink_finalize (GObject * object)
2259 {
2260   GstEglGlesSink *eglglessink;
2261
2262   g_return_if_fail (GST_IS_EGLGLESSINK (object));
2263
2264   eglglessink = GST_EGLGLESSINK (object);
2265
2266   gst_egl_adaptation_context_free (eglglessink->egl_context);
2267
2268   if (eglglessink->queue)
2269     g_object_unref (eglglessink->queue);
2270   eglglessink->queue = NULL;
2271
2272   g_cond_clear (&eglglessink->render_cond);
2273   g_mutex_clear (&eglglessink->render_lock);
2274
2275   G_OBJECT_CLASS (parent_class)->finalize (object);
2276 }
2277
2278 static void
2279 gst_eglglessink_set_property (GObject * object, guint prop_id,
2280     const GValue * value, GParamSpec * pspec)
2281 {
2282   GstEglGlesSink *eglglessink;
2283
2284   g_return_if_fail (GST_IS_EGLGLESSINK (object));
2285
2286   eglglessink = GST_EGLGLESSINK (object);
2287
2288   switch (prop_id) {
2289     case PROP_CREATE_WINDOW:
2290       eglglessink->create_window = g_value_get_boolean (value);
2291       break;
2292     case PROP_FORCE_ASPECT_RATIO:
2293       eglglessink->force_aspect_ratio = g_value_get_boolean (value);
2294       break;
2295     default:
2296       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2297       break;
2298   }
2299 }
2300
2301 static void
2302 gst_eglglessink_get_property (GObject * object, guint prop_id,
2303     GValue * value, GParamSpec * pspec)
2304 {
2305   GstEglGlesSink *eglglessink;
2306
2307   g_return_if_fail (GST_IS_EGLGLESSINK (object));
2308
2309   eglglessink = GST_EGLGLESSINK (object);
2310
2311   switch (prop_id) {
2312     case PROP_CREATE_WINDOW:
2313       g_value_set_boolean (value, eglglessink->create_window);
2314       break;
2315     case PROP_FORCE_ASPECT_RATIO:
2316       g_value_set_boolean (value, eglglessink->force_aspect_ratio);
2317       break;
2318     default:
2319       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2320       break;
2321   }
2322 }
2323
2324 /* initialize the eglglessink's class */
2325 static void
2326 gst_eglglessink_class_init (GstEglGlesSinkClass * klass)
2327 {
2328   GObjectClass *gobject_class;
2329   GstElementClass *gstelement_class;
2330   GstBaseSinkClass *gstbasesink_class;
2331   GstVideoSinkClass *gstvideosink_class;
2332
2333   gobject_class = (GObjectClass *) klass;
2334   gstelement_class = (GstElementClass *) klass;
2335   gstbasesink_class = (GstBaseSinkClass *) klass;
2336   gstvideosink_class = (GstVideoSinkClass *) klass;
2337
2338   gobject_class->set_property = gst_eglglessink_set_property;
2339   gobject_class->get_property = gst_eglglessink_get_property;
2340   gobject_class->finalize = gst_eglglessink_finalize;
2341
2342   gstelement_class->change_state = gst_eglglessink_change_state;
2343   gstelement_class->set_context = gst_eglglessink_set_context;
2344
2345   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_eglglessink_setcaps);
2346   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_eglglessink_getcaps);
2347   gstbasesink_class->propose_allocation =
2348       GST_DEBUG_FUNCPTR (gst_eglglessink_propose_allocation);
2349   gstbasesink_class->prepare = GST_DEBUG_FUNCPTR (gst_eglglessink_prepare);
2350   gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_eglglessink_query);
2351   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_eglglessink_event);
2352
2353   gstvideosink_class->show_frame =
2354       GST_DEBUG_FUNCPTR (gst_eglglessink_show_frame);
2355
2356   g_object_class_install_property (gobject_class, PROP_CREATE_WINDOW,
2357       g_param_spec_boolean ("create-window", "Create Window",
2358           "If set to true, the sink will attempt to create it's own window to "
2359           "render to if none is provided. This is currently only supported "
2360           "when the sink is used under X11",
2361           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2362   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2363       g_param_spec_boolean ("force-aspect-ratio",
2364           "Respect aspect ratio when scaling",
2365           "If set to true, the sink will attempt to preserve the incoming "
2366           "frame's geometry while scaling, taking both the storage's and "
2367           "display's pixel aspect ratio into account",
2368           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2369
2370   gst_element_class_set_static_metadata (gstelement_class,
2371       "EGL/GLES vout Sink",
2372       "Sink/Video",
2373       "An EGL/GLES Video Output Sink Implementing the VideoOverlay interface",
2374       "Reynaldo H. Verdejo Pinochet <reynaldo@collabora.com>, "
2375       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
2376
2377   gst_element_class_add_pad_template (gstelement_class,
2378       gst_static_pad_template_get (&gst_eglglessink_sink_template_factory));
2379 }
2380
2381 static gboolean
2382 queue_check_full_func (GstDataQueue * queue, guint visible, guint bytes,
2383     guint64 time, gpointer checkdata)
2384 {
2385   return visible != 0;
2386 }
2387
2388 static void
2389 gst_eglglessink_init (GstEglGlesSink * eglglessink)
2390 {
2391   eglglessink->egl_context =
2392       gst_egl_adaptation_context_new (GST_ELEMENT_CAST (eglglessink));
2393
2394   /* Init defaults */
2395
2396   /** Flags */
2397   eglglessink->have_window = FALSE;
2398   eglglessink->egl_context->have_surface = FALSE;
2399   eglglessink->egl_context->have_vbo = FALSE;
2400   eglglessink->egl_context->have_texture = FALSE;
2401   eglglessink->egl_started = FALSE;
2402   eglglessink->using_own_window = FALSE;
2403
2404   /** Props */
2405   eglglessink->create_window = TRUE;
2406   eglglessink->force_aspect_ratio = TRUE;
2407
2408   g_mutex_init (&eglglessink->render_lock);
2409   g_cond_init (&eglglessink->render_cond);
2410   eglglessink->queue =
2411       gst_data_queue_new (queue_check_full_func, NULL, NULL, NULL);
2412   eglglessink->last_flow = GST_FLOW_FLUSHING;
2413
2414   eglglessink->render_region.x = 0;
2415   eglglessink->render_region.y = 0;
2416   eglglessink->render_region.w = -1;
2417   eglglessink->render_region.h = -1;
2418   eglglessink->render_region_changed = TRUE;
2419   eglglessink->render_region_user = FALSE;
2420 }
2421
2422 static GstBufferPool *
2423 gst_egl_image_buffer_pool_new (GstEglGlesSink *
2424     eglglessink, GstEGLDisplay * display)
2425 {
2426   GstEGLImageBufferPool *pool;
2427
2428   pool = g_object_new (gst_egl_image_buffer_pool_get_type (), NULL);
2429   pool->display = gst_egl_display_ref (display);
2430   pool->sink = gst_object_ref (eglglessink);
2431
2432   return (GstBufferPool *) pool;
2433 }
2434
2435 /* entry point to initialize the plug-in
2436  * initialize the plug-in itself
2437  * register the element factories and other features
2438  */
2439 static gboolean
2440 eglglessink_plugin_init (GstPlugin * plugin)
2441 {
2442   /* debug category for fltering log messages */
2443   GST_DEBUG_CATEGORY_INIT (gst_eglglessink_debug, "eglglessink",
2444       0, "Simple EGL/GLES Sink");
2445
2446   gst_egl_adaption_init ();
2447
2448 #ifdef USE_EGL_RPI
2449   GST_DEBUG ("Initialize BCM host");
2450   bcm_host_init ();
2451 #endif
2452
2453   return gst_element_register (plugin, "eglglessink", GST_RANK_PRIMARY,
2454       GST_TYPE_EGLGLESSINK);
2455 }
2456
2457 /* gstreamer looks for this structure to register eglglessinks */
2458 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
2459     GST_VERSION_MINOR,
2460     eglglessink,
2461     "EGL/GLES sink",
2462     eglglessink_plugin_init,
2463     VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)