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