eglglessink: Use gst_egl_display_get() instead of passing a GstEGLDisplay to EGL...
[platform/upstream/gstreamer.git] / ext / eglgles / gstegladaptation_egl.c
1 /*
2  * Copyright (C) 2012-2013 Collabora Ltd.
3  *   @author: Reynaldo H. Verdejo Pinochet <reynaldo@collabora.com>
4  *   @author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
5  *   @author: Thiago Santos <thiago.sousa.santos@collabora.com>
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., 59 Temple Place - Suite 330,
43  * Boston, MA 02111-1307, USA.
44  */
45
46 #include "gstegladaptation.h"
47 #include "video_platform_wrapper.h"
48 #include <string.h>
49
50 #include <EGL/egl.h>
51 #include <EGL/eglext.h>
52 #include <GLES2/gl2.h>
53 #include <GLES2/gl2ext.h>
54 #include <gst/egl/egl.h>
55
56 #define GST_CAT_DEFAULT egladaption_debug
57
58 /* Some EGL implementations are reporting wrong
59  * values for the display's EGL_PIXEL_ASPECT_RATIO.
60  * They are required by the khronos specs to report
61  * this value as w/h * EGL_DISPLAY_SCALING (Which is
62  * a constant with value 10000) but at least the
63  * Galaxy SIII (Android) is reporting just 1 when
64  * w = h. We use these two to bound returned values to
65  * sanity.
66  */
67 #define EGL_SANE_DAR_MIN ((EGL_DISPLAY_SCALING)/10)
68 #define EGL_SANE_DAR_MAX ((EGL_DISPLAY_SCALING)*10)
69
70 /*
71  * GstEglGlesRenderContext:
72  * @config: Current EGL config
73  * @eglcontext: Current EGL context
74  * @egl_minor: EGL version (minor)
75  * @egl_major: EGL version (major)
76  *
77  * This struct holds the sink's EGL/GLES rendering context.
78  */
79 struct _GstEglGlesRenderContext
80 {
81   EGLConfig config;
82   EGLContext eglcontext;
83   EGLSurface surface;
84   EGLint egl_minor, egl_major;
85 };
86
87 gboolean
88 got_egl_error (const char *wtf)
89 {
90   EGLint error;
91
92   if ((error = eglGetError ()) != EGL_SUCCESS) {
93     GST_CAT_DEBUG (GST_CAT_DEFAULT, "EGL ERROR: %s returned 0x%04x", wtf,
94         error);
95     return TRUE;
96   }
97
98   return FALSE;
99 }
100
101 /* Prints available EGL/GLES extensions 
102  * If another rendering path is implemented this is the place
103  * where you want to check for the availability of its supporting
104  * EGL/GLES extensions.
105  */
106 void
107 gst_egl_adaptation_init_egl_exts (GstEglAdaptationContext * ctx)
108 {
109   const char *eglexts;
110   unsigned const char *glexts;
111
112   eglexts = eglQueryString (gst_egl_display_get (ctx->display), EGL_EXTENSIONS);
113   glexts = glGetString (GL_EXTENSIONS);
114
115   GST_DEBUG_OBJECT (ctx->element, "Available EGL extensions: %s\n",
116       GST_STR_NULL (eglexts));
117   GST_DEBUG_OBJECT (ctx->element, "Available GLES extensions: %s\n",
118       GST_STR_NULL ((const char *) glexts));
119
120   return;
121 }
122
123 gboolean
124 gst_egl_adaptation_init_egl_display (GstEglAdaptationContext * ctx)
125 {
126   GstMessage *msg;
127   EGLDisplay display;
128   GST_DEBUG_OBJECT (ctx->element, "Enter EGL initial configuration");
129
130   if (!platform_wrapper_init ()) {
131     GST_ERROR_OBJECT (ctx->element, "Couldn't init EGL platform wrapper");
132     goto HANDLE_ERROR;
133   }
134 #ifdef USE_EGL_RPI
135   /* See https://github.com/raspberrypi/firmware/issues/99 */
136   if (!eglMakeCurrent ((EGLDisplay) 1, EGL_NO_SURFACE, EGL_NO_SURFACE,
137           EGL_NO_CONTEXT)) {
138     got_egl_error ("eglMakeCurrent");
139     GST_ERROR_OBJECT (ctx->element, "Couldn't unbind context");
140     return FALSE;
141   }
142 #endif
143
144   msg = gst_message_new_need_context (GST_OBJECT_CAST (ctx->element));
145   gst_message_add_context_type (msg, GST_EGL_DISPLAY_CONTEXT_TYPE);
146   gst_element_post_message (GST_ELEMENT_CAST (ctx->element), msg);
147
148   GST_OBJECT_LOCK (ctx->element);
149   if (ctx->set_display) {
150     GstContext *context;
151
152     ctx->display = gst_egl_display_ref (ctx->set_display);
153     GST_OBJECT_UNLOCK (ctx->element);
154     context = gst_element_get_context (GST_ELEMENT_CAST (ctx->element));
155     if (!context)
156       context = gst_context_new ();
157     context = gst_context_make_writable (context);
158     gst_context_set_egl_display (context, ctx->display);
159     gst_element_set_context (GST_ELEMENT_CAST (ctx->element), context);
160     gst_context_unref (context);
161   } else {
162     GstContext *context;
163
164     GST_OBJECT_UNLOCK (ctx->element);
165
166     display = eglGetDisplay (EGL_DEFAULT_DISPLAY);
167     if (display == EGL_NO_DISPLAY) {
168       GST_ERROR_OBJECT (ctx->element, "Could not get EGL display connection");
169       goto HANDLE_ERROR;        /* No EGL error is set by eglGetDisplay() */
170     }
171     ctx->display = gst_egl_display_new (display);
172
173     context = gst_context_new ();
174     gst_context_set_egl_display (context, ctx->display);
175
176     msg = gst_message_new_have_context (GST_OBJECT (ctx->element), context);
177     gst_element_post_message (GST_ELEMENT_CAST (ctx->element), msg);
178     context = NULL;
179
180     context = gst_element_get_context (GST_ELEMENT_CAST (ctx->element));
181     if (!context)
182       context = gst_context_new ();
183     context = gst_context_make_writable (context);
184     gst_context_set_egl_display (context, ctx->display);
185     gst_element_set_context (GST_ELEMENT_CAST (ctx->element), context);
186     gst_context_unref (context);
187   }
188
189   if (!eglInitialize (gst_egl_display_get (ctx->display),
190           &ctx->eglglesctx->egl_major, &ctx->eglglesctx->egl_minor)) {
191     got_egl_error ("eglInitialize");
192     GST_ERROR_OBJECT (ctx->element, "Could not init EGL display connection");
193     goto HANDLE_EGL_ERROR;
194   }
195
196   /* Check against required EGL version
197    * XXX: Need to review the version requirement in terms of the needed API
198    */
199   if (ctx->eglglesctx->egl_major < GST_EGLGLESSINK_EGL_MIN_VERSION) {
200     GST_ERROR_OBJECT (ctx->element, "EGL v%d needed, but you only have v%d.%d",
201         GST_EGLGLESSINK_EGL_MIN_VERSION, ctx->eglglesctx->egl_major,
202         ctx->eglglesctx->egl_minor);
203     goto HANDLE_ERROR;
204   }
205
206   GST_INFO_OBJECT (ctx->element, "System reports supported EGL version v%d.%d",
207       ctx->eglglesctx->egl_major, ctx->eglglesctx->egl_minor);
208
209   eglBindAPI (EGL_OPENGL_ES_API);
210
211   return TRUE;
212
213   /* Errors */
214 HANDLE_EGL_ERROR:
215   GST_ERROR_OBJECT (ctx->element, "EGL call returned error %x", eglGetError ());
216 HANDLE_ERROR:
217   GST_ERROR_OBJECT (ctx->element, "Couldn't setup window/surface from handle");
218   return FALSE;
219 }
220
221 gboolean
222 gst_egl_adaptation_context_make_current (GstEglAdaptationContext * ctx,
223     gboolean bind)
224 {
225   g_assert (ctx->display != NULL);
226
227   if (bind && ctx->eglglesctx->surface && ctx->eglglesctx->eglcontext) {
228     EGLContext *cur_ctx = eglGetCurrentContext ();
229
230     if (cur_ctx == ctx->eglglesctx->eglcontext) {
231       GST_DEBUG_OBJECT (ctx->element,
232           "Already attached the context to thread %p", g_thread_self ());
233       return TRUE;
234     }
235
236     GST_DEBUG_OBJECT (ctx->element, "Attaching context to thread %p",
237         g_thread_self ());
238     if (!eglMakeCurrent (gst_egl_display_get (ctx->display),
239             ctx->eglglesctx->surface, ctx->eglglesctx->surface,
240             ctx->eglglesctx->eglcontext)) {
241       got_egl_error ("eglMakeCurrent");
242       GST_ERROR_OBJECT (ctx->element, "Couldn't bind context");
243       return FALSE;
244     }
245   } else {
246     GST_DEBUG_OBJECT (ctx->element, "Detaching context from thread %p",
247         g_thread_self ());
248     if (!eglMakeCurrent (gst_egl_display_get (ctx->display),
249             EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
250       got_egl_error ("eglMakeCurrent");
251       GST_ERROR_OBJECT (ctx->element, "Couldn't unbind context");
252       return FALSE;
253     }
254   }
255
256   return TRUE;
257 }
258
259 /* XXX: Lock eglgles context? */
260 gboolean
261 gst_egl_adaptation_update_surface_dimensions (GstEglAdaptationContext * ctx)
262 {
263   gint width, height;
264
265   /* Save surface dims */
266   eglQuerySurface (gst_egl_display_get (ctx->display),
267       ctx->eglglesctx->surface, EGL_WIDTH, &width);
268   eglQuerySurface (gst_egl_display_get (ctx->display),
269       ctx->eglglesctx->surface, EGL_HEIGHT, &height);
270
271   if (width != ctx->surface_width || height != ctx->surface_height) {
272     ctx->surface_width = width;
273     ctx->surface_height = height;
274     GST_INFO_OBJECT (ctx->element, "Got surface of %dx%d pixels", width,
275         height);
276     return TRUE;
277   }
278
279   return FALSE;
280 }
281
282 gboolean
283 gst_egl_adaptation_context_swap_buffers (GstEglAdaptationContext * ctx)
284 {
285   gboolean ret = eglSwapBuffers (gst_egl_display_get (ctx->display),
286       ctx->eglglesctx->surface);
287   if (ret == EGL_FALSE) {
288     got_egl_error ("eglSwapBuffers");
289   }
290   return ret;
291 }
292
293 gboolean
294 _gst_egl_choose_config (GstEglAdaptationContext * ctx, gboolean try_only,
295     gint * num_configs)
296 {
297   EGLint cfg_number;
298   gboolean ret;
299   EGLConfig *config = NULL;
300
301   if (!try_only)
302     config = &ctx->eglglesctx->config;
303
304   ret = eglChooseConfig (gst_egl_display_get (ctx->display),
305       eglglessink_RGBA8888_attribs, config, 1, &cfg_number) != EGL_FALSE;
306
307   if (!ret)
308     got_egl_error ("eglChooseConfig");
309   else if (num_configs)
310     *num_configs = cfg_number;
311   return ret;
312 }
313
314 gboolean
315 gst_egl_adaptation_create_surface (GstEglAdaptationContext * ctx)
316 {
317   ctx->eglglesctx->surface =
318       eglCreateWindowSurface (gst_egl_display_get (ctx->display),
319       ctx->eglglesctx->config, ctx->used_window, NULL);
320
321   if (ctx->eglglesctx->surface == EGL_NO_SURFACE) {
322     got_egl_error ("eglCreateWindowSurface");
323     GST_ERROR_OBJECT (ctx->element, "Can't create surface");
324     return FALSE;
325   }
326   return TRUE;
327 }
328
329 void
330 gst_egl_adaptation_query_buffer_preserved (GstEglAdaptationContext * ctx)
331 {
332   EGLint swap_behavior;
333
334   ctx->buffer_preserved = FALSE;
335   if (eglQuerySurface (gst_egl_display_get (ctx->display),
336           ctx->eglglesctx->surface, EGL_SWAP_BEHAVIOR, &swap_behavior)) {
337     GST_DEBUG_OBJECT (ctx->element, "Buffer swap behavior %x", swap_behavior);
338     ctx->buffer_preserved = swap_behavior == EGL_BUFFER_PRESERVED;
339   } else {
340     GST_DEBUG_OBJECT (ctx->element, "Can't query buffer swap behavior");
341   }
342 }
343
344 void
345 gst_egl_adaptation_query_par (GstEglAdaptationContext * ctx)
346 {
347   EGLint display_par;
348
349   /* fixed value */
350   ctx->pixel_aspect_ratio_d = EGL_DISPLAY_SCALING;
351
352   /* Save display's pixel aspect ratio
353    *
354    * DAR is reported as w/h * EGL_DISPLAY_SCALING wich is
355    * a constant with value 10000. This attribute is only
356    * supported if the EGL version is >= 1.2
357    * XXX: Setup this as a property.
358    * or some other one time check. Right now it's being called once
359    * per frame.
360    */
361   if (ctx->eglglesctx->egl_major == 1 && ctx->eglglesctx->egl_minor < 2) {
362     GST_DEBUG_OBJECT (ctx->element, "Can't query PAR. Using default: %dx%d",
363         EGL_DISPLAY_SCALING, EGL_DISPLAY_SCALING);
364     ctx->pixel_aspect_ratio_n = EGL_DISPLAY_SCALING;
365   } else {
366     eglQuerySurface (gst_egl_display_get (ctx->display),
367         ctx->eglglesctx->surface, EGL_PIXEL_ASPECT_RATIO, &display_par);
368     /* Fix for outbound DAR reporting on some implementations not
369      * honoring the 'should return w/h * EGL_DISPLAY_SCALING' spec
370      * requirement
371      */
372     if (display_par == EGL_UNKNOWN || display_par < EGL_SANE_DAR_MIN ||
373         display_par > EGL_SANE_DAR_MAX) {
374       GST_DEBUG_OBJECT (ctx->element, "Nonsensical PAR value returned: %d. "
375           "Bad EGL implementation? "
376           "Will use default: %d/%d", ctx->pixel_aspect_ratio_n,
377           EGL_DISPLAY_SCALING, EGL_DISPLAY_SCALING);
378       ctx->pixel_aspect_ratio_n = EGL_DISPLAY_SCALING;
379     } else {
380       ctx->pixel_aspect_ratio_n = display_par;
381     }
382   }
383 }
384
385 gboolean
386 gst_egl_adaptation_create_egl_context (GstEglAdaptationContext * ctx)
387 {
388   EGLint con_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
389
390   ctx->eglglesctx->eglcontext =
391       eglCreateContext (gst_egl_display_get (ctx->display),
392       ctx->eglglesctx->config, EGL_NO_CONTEXT, con_attribs);
393
394   if (ctx->eglglesctx->eglcontext == EGL_NO_CONTEXT) {
395     GST_ERROR_OBJECT (ctx->element, "EGL call returned error %x",
396         eglGetError ());
397     return FALSE;
398   }
399
400   GST_DEBUG_OBJECT (ctx->element, "EGL Context: %p",
401       ctx->eglglesctx->eglcontext);
402
403   return TRUE;
404 }
405
406 static void
407 gst_egl_gles_image_data_free (GstEGLGLESImageData * data)
408 {
409   glDeleteTextures (1, &data->texture);
410   g_slice_free (GstEGLGLESImageData, data);
411 }
412
413
414 GstBuffer *
415 gst_egl_adaptation_allocate_eglimage (GstEglAdaptationContext * ctx,
416     GstAllocator * allocator, GstVideoFormat format, gint width, gint height)
417 {
418   GstEGLGLESImageData *data = NULL;
419   GstBuffer *buffer;
420   GstVideoInfo info;
421   gint i;
422   gint stride[3];
423   gsize offset[3];
424   GstMemory *mem[3] = { NULL, NULL, NULL };
425   guint n_mem;
426   GstMemoryFlags flags = 0;
427
428   memset (stride, 0, sizeof (stride));
429   memset (offset, 0, sizeof (offset));
430
431   if (!gst_egl_image_memory_is_mappable ())
432     flags |= GST_MEMORY_FLAG_NOT_MAPPABLE;
433   /* See https://bugzilla.gnome.org/show_bug.cgi?id=695203 */
434   flags |= GST_MEMORY_FLAG_NO_SHARE;
435
436   gst_video_info_set_format (&info, format, width, height);
437
438   switch (format) {
439     case GST_VIDEO_FORMAT_RGB:
440     case GST_VIDEO_FORMAT_BGR:{
441       gsize size;
442       EGLImageKHR image;
443
444       mem[0] =
445           gst_egl_image_allocator_alloc (allocator, ctx->display,
446           GST_VIDEO_GL_TEXTURE_TYPE_RGB, GST_VIDEO_INFO_WIDTH (&info),
447           GST_VIDEO_INFO_HEIGHT (&info), &size);
448       if (mem[0]) {
449         stride[0] = size / GST_VIDEO_INFO_HEIGHT (&info);
450         n_mem = 1;
451         GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE);
452       } else {
453         data = g_slice_new0 (GstEGLGLESImageData);
454
455         stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_WIDTH (&info) * 3);
456         size = stride[0] * GST_VIDEO_INFO_HEIGHT (&info);
457
458         glGenTextures (1, &data->texture);
459         if (got_gl_error ("glGenTextures"))
460           goto mem_error;
461
462         glBindTexture (GL_TEXTURE_2D, data->texture);
463         if (got_gl_error ("glBindTexture"))
464           goto mem_error;
465
466         /* Set 2D resizing params */
467         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
468         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
469
470         /* If these are not set the texture image unit will return
471          * * (R, G, B, A) = black on glTexImage2D for non-POT width/height
472          * * frames. For a deeper explanation take a look at the OpenGL ES
473          * * documentation for glTexParameter */
474         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
475         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
476         if (got_gl_error ("glTexParameteri"))
477           goto mem_error;
478
479         glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB,
480             GST_VIDEO_INFO_WIDTH (&info),
481             GST_VIDEO_INFO_HEIGHT (&info), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
482         if (got_gl_error ("glTexImage2D"))
483           goto mem_error;
484
485         image =
486             eglCreateImageKHR (gst_egl_display_get (ctx->display),
487             ctx->eglglesctx->eglcontext, EGL_GL_TEXTURE_2D_KHR,
488             (EGLClientBuffer) (guintptr) data->texture, NULL);
489         if (got_egl_error ("eglCreateImageKHR"))
490           goto mem_error;
491
492         mem[0] =
493             gst_egl_image_allocator_wrap (allocator, ctx->display,
494             image, GST_VIDEO_GL_TEXTURE_TYPE_RGB,
495             flags, size, data, (GDestroyNotify) gst_egl_gles_image_data_free);
496         n_mem = 1;
497       }
498       break;
499     }
500     case GST_VIDEO_FORMAT_RGB16:{
501       EGLImageKHR image;
502       gsize size;
503
504       mem[0] =
505           gst_egl_image_allocator_alloc (allocator, ctx->display,
506           GST_VIDEO_GL_TEXTURE_TYPE_RGB, GST_VIDEO_INFO_WIDTH (&info),
507           GST_VIDEO_INFO_HEIGHT (&info), &size);
508       if (mem[0]) {
509         stride[0] = size / GST_VIDEO_INFO_HEIGHT (&info);
510         n_mem = 1;
511         GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE);
512       } else {
513         data = g_slice_new0 (GstEGLGLESImageData);
514
515         stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_WIDTH (&info) * 2);
516         size = stride[0] * GST_VIDEO_INFO_HEIGHT (&info);
517
518         glGenTextures (1, &data->texture);
519         if (got_gl_error ("glGenTextures"))
520           goto mem_error;
521
522         glBindTexture (GL_TEXTURE_2D, data->texture);
523         if (got_gl_error ("glBindTexture"))
524           goto mem_error;
525
526         /* Set 2D resizing params */
527         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
528         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
529
530         /* If these are not set the texture image unit will return
531          * * (R, G, B, A) = black on glTexImage2D for non-POT width/height
532          * * frames. For a deeper explanation take a look at the OpenGL ES
533          * * documentation for glTexParameter */
534         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
535         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
536         if (got_gl_error ("glTexParameteri"))
537           goto mem_error;
538
539         glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB,
540             GST_VIDEO_INFO_WIDTH (&info),
541             GST_VIDEO_INFO_HEIGHT (&info), 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
542             NULL);
543         if (got_gl_error ("glTexImage2D"))
544           goto mem_error;
545
546         image =
547             eglCreateImageKHR (gst_egl_display_get (ctx->display),
548             ctx->eglglesctx->eglcontext, EGL_GL_TEXTURE_2D_KHR,
549             (EGLClientBuffer) (guintptr) data->texture, NULL);
550         if (got_egl_error ("eglCreateImageKHR"))
551           goto mem_error;
552
553         mem[0] =
554             gst_egl_image_allocator_wrap (allocator, ctx->display,
555             image, GST_VIDEO_GL_TEXTURE_TYPE_RGB,
556             flags, size, data, (GDestroyNotify) gst_egl_gles_image_data_free);
557         n_mem = 1;
558       }
559       break;
560     }
561     case GST_VIDEO_FORMAT_NV12:
562     case GST_VIDEO_FORMAT_NV21:{
563       EGLImageKHR image;
564       gsize size[2];
565
566       mem[0] =
567           gst_egl_image_allocator_alloc (allocator, ctx->display,
568           GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info,
569               0), GST_VIDEO_INFO_COMP_HEIGHT (&info, 0), &size[0]);
570       mem[1] =
571           gst_egl_image_allocator_alloc (allocator, ctx->display,
572           GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE_ALPHA,
573           GST_VIDEO_INFO_COMP_WIDTH (&info, 1),
574           GST_VIDEO_INFO_COMP_HEIGHT (&info, 1), &size[1]);
575
576       if (mem[0] && mem[1]) {
577         stride[0] = size[0] / GST_VIDEO_INFO_HEIGHT (&info);
578         offset[1] = size[0];
579         stride[1] = size[1] / GST_VIDEO_INFO_HEIGHT (&info);
580         n_mem = 2;
581         GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE);
582         GST_MINI_OBJECT_FLAG_SET (mem[1], GST_MEMORY_FLAG_NO_SHARE);
583       } else {
584         if (mem[0])
585           gst_memory_unref (mem[0]);
586         if (mem[1])
587           gst_memory_unref (mem[1]);
588         mem[0] = mem[1] = NULL;
589
590         stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 0));
591         stride[1] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 1) * 2);
592         offset[1] = stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 0);
593         size[0] = offset[1];
594         size[1] = stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 1);
595
596         for (i = 0; i < 2; i++) {
597           data = g_slice_new0 (GstEGLGLESImageData);
598
599           glGenTextures (1, &data->texture);
600           if (got_gl_error ("glGenTextures"))
601             goto mem_error;
602
603           glBindTexture (GL_TEXTURE_2D, data->texture);
604           if (got_gl_error ("glBindTexture"))
605             goto mem_error;
606
607           /* Set 2D resizing params */
608           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
609           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
610
611           /* If these are not set the texture image unit will return
612            * * (R, G, B, A) = black on glTexImage2D for non-POT width/height
613            * * frames. For a deeper explanation take a look at the OpenGL ES
614            * * documentation for glTexParameter */
615           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
616           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
617           if (got_gl_error ("glTexParameteri"))
618             goto mem_error;
619
620           if (i == 0)
621             glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
622                 GST_VIDEO_INFO_COMP_WIDTH (&info, i),
623                 GST_VIDEO_INFO_COMP_HEIGHT (&info, i), 0, GL_LUMINANCE,
624                 GL_UNSIGNED_BYTE, NULL);
625           else
626             glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
627                 GST_VIDEO_INFO_COMP_WIDTH (&info, i),
628                 GST_VIDEO_INFO_COMP_HEIGHT (&info, i), 0, GL_LUMINANCE_ALPHA,
629                 GL_UNSIGNED_BYTE, NULL);
630
631           if (got_gl_error ("glTexImage2D"))
632             goto mem_error;
633
634           image =
635               eglCreateImageKHR (gst_egl_display_get (ctx->display),
636               ctx->eglglesctx->eglcontext, EGL_GL_TEXTURE_2D_KHR,
637               (EGLClientBuffer) (guintptr) data->texture, NULL);
638           if (got_egl_error ("eglCreateImageKHR"))
639             goto mem_error;
640
641           mem[i] =
642               gst_egl_image_allocator_wrap (allocator, ctx->display,
643               image,
644               (i ==
645                   0 ? GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE :
646                   GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE_ALPHA),
647               flags, size[i], data,
648               (GDestroyNotify) gst_egl_gles_image_data_free);
649         }
650
651         n_mem = 2;
652       }
653       break;
654     }
655     case GST_VIDEO_FORMAT_I420:
656     case GST_VIDEO_FORMAT_YV12:
657     case GST_VIDEO_FORMAT_Y444:
658     case GST_VIDEO_FORMAT_Y42B:
659     case GST_VIDEO_FORMAT_Y41B:{
660       EGLImageKHR image;
661       gsize size[3];
662
663       mem[0] =
664           gst_egl_image_allocator_alloc (allocator, ctx->display,
665           GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info,
666               0), GST_VIDEO_INFO_COMP_HEIGHT (&info, 0), &size[0]);
667       mem[1] =
668           gst_egl_image_allocator_alloc (allocator, ctx->display,
669           GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info,
670               1), GST_VIDEO_INFO_COMP_HEIGHT (&info, 1), &size[1]);
671       mem[2] =
672           gst_egl_image_allocator_alloc (allocator, ctx->display,
673           GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info,
674               2), GST_VIDEO_INFO_COMP_HEIGHT (&info, 2), &size[2]);
675
676       if (mem[0] && mem[1] && mem[2]) {
677         stride[0] = size[0] / GST_VIDEO_INFO_HEIGHT (&info);
678         offset[1] = size[0];
679         stride[1] = size[1] / GST_VIDEO_INFO_HEIGHT (&info);
680         offset[2] = size[1];
681         stride[2] = size[2] / GST_VIDEO_INFO_HEIGHT (&info);
682         n_mem = 3;
683         GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE);
684         GST_MINI_OBJECT_FLAG_SET (mem[1], GST_MEMORY_FLAG_NO_SHARE);
685         GST_MINI_OBJECT_FLAG_SET (mem[2], GST_MEMORY_FLAG_NO_SHARE);
686       } else {
687         if (mem[0])
688           gst_memory_unref (mem[0]);
689         if (mem[1])
690           gst_memory_unref (mem[1]);
691         if (mem[2])
692           gst_memory_unref (mem[2]);
693         mem[0] = mem[1] = mem[2] = NULL;
694
695         stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 0));
696         stride[1] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 1));
697         stride[2] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 2));
698         size[0] = stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 0);
699         size[1] = stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 1);
700         size[2] = stride[2] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 2);
701         offset[0] = 0;
702         offset[1] = size[0];
703         offset[2] = offset[1] + size[1];
704
705         for (i = 0; i < 3; i++) {
706           data = g_slice_new0 (GstEGLGLESImageData);
707
708           glGenTextures (1, &data->texture);
709           if (got_gl_error ("glGenTextures"))
710             goto mem_error;
711
712           glBindTexture (GL_TEXTURE_2D, data->texture);
713           if (got_gl_error ("glBindTexture"))
714             goto mem_error;
715
716           /* Set 2D resizing params */
717           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
718           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
719
720           /* If these are not set the texture image unit will return
721            * * (R, G, B, A) = black on glTexImage2D for non-POT width/height
722            * * frames. For a deeper explanation take a look at the OpenGL ES
723            * * documentation for glTexParameter */
724           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
725           glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
726           if (got_gl_error ("glTexParameteri"))
727             goto mem_error;
728
729           glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
730               GST_VIDEO_INFO_COMP_WIDTH (&info, i),
731               GST_VIDEO_INFO_COMP_HEIGHT (&info, i), 0, GL_LUMINANCE,
732               GL_UNSIGNED_BYTE, NULL);
733
734           if (got_gl_error ("glTexImage2D"))
735             goto mem_error;
736
737           image =
738               eglCreateImageKHR (gst_egl_display_get (ctx->display),
739               ctx->eglglesctx->eglcontext, EGL_GL_TEXTURE_2D_KHR,
740               (EGLClientBuffer) (guintptr) data->texture, NULL);
741           if (got_egl_error ("eglCreateImageKHR"))
742             goto mem_error;
743
744           mem[i] =
745               gst_egl_image_allocator_wrap (allocator, ctx->display,
746               image, GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE,
747               flags, size[i], data,
748               (GDestroyNotify) gst_egl_gles_image_data_free);
749         }
750
751         n_mem = 3;
752       }
753       break;
754     }
755     case GST_VIDEO_FORMAT_RGBA:
756     case GST_VIDEO_FORMAT_BGRA:
757     case GST_VIDEO_FORMAT_ARGB:
758     case GST_VIDEO_FORMAT_ABGR:
759     case GST_VIDEO_FORMAT_RGBx:
760     case GST_VIDEO_FORMAT_BGRx:
761     case GST_VIDEO_FORMAT_xRGB:
762     case GST_VIDEO_FORMAT_xBGR:
763     case GST_VIDEO_FORMAT_AYUV:{
764       gsize size;
765       EGLImageKHR image;
766
767       mem[0] =
768           gst_egl_image_allocator_alloc (allocator, ctx->display,
769           GST_VIDEO_GL_TEXTURE_TYPE_RGBA, GST_VIDEO_INFO_WIDTH (&info),
770           GST_VIDEO_INFO_HEIGHT (&info), &size);
771       if (mem[0]) {
772         stride[0] = size / GST_VIDEO_INFO_HEIGHT (&info);
773         n_mem = 1;
774         GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE);
775       } else {
776         data = g_slice_new0 (GstEGLGLESImageData);
777
778         stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_WIDTH (&info) * 4);
779         size = stride[0] * GST_VIDEO_INFO_HEIGHT (&info);
780
781         glGenTextures (1, &data->texture);
782         if (got_gl_error ("glGenTextures"))
783           goto mem_error;
784
785         glBindTexture (GL_TEXTURE_2D, data->texture);
786         if (got_gl_error ("glBindTexture"))
787           goto mem_error;
788
789         /* Set 2D resizing params */
790         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
791         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
792
793         /* If these are not set the texture image unit will return
794          * * (R, G, B, A) = black on glTexImage2D for non-POT width/height
795          * * frames. For a deeper explanation take a look at the OpenGL ES
796          * * documentation for glTexParameter */
797         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
798         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
799         if (got_gl_error ("glTexParameteri"))
800           goto mem_error;
801
802         glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
803             GST_VIDEO_INFO_WIDTH (&info),
804             GST_VIDEO_INFO_HEIGHT (&info), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
805         if (got_gl_error ("glTexImage2D"))
806           goto mem_error;
807
808         image =
809             eglCreateImageKHR (gst_egl_display_get (ctx->display),
810             ctx->eglglesctx->eglcontext, EGL_GL_TEXTURE_2D_KHR,
811             (EGLClientBuffer) (guintptr) data->texture, NULL);
812         if (got_egl_error ("eglCreateImageKHR"))
813           goto mem_error;
814
815         mem[0] =
816             gst_egl_image_allocator_wrap (allocator, ctx->display,
817             image, GST_VIDEO_GL_TEXTURE_TYPE_RGBA,
818             flags, size, data, (GDestroyNotify) gst_egl_gles_image_data_free);
819
820         n_mem = 1;
821       }
822       break;
823     }
824     default:
825       g_assert_not_reached ();
826       break;
827   }
828
829   buffer = gst_buffer_new ();
830   gst_buffer_add_video_meta_full (buffer, 0, format, width, height,
831       GST_VIDEO_INFO_N_PLANES (&info), offset, stride);
832
833   for (i = 0; i < n_mem; i++)
834     gst_buffer_append_memory (buffer, mem[i]);
835
836   return buffer;
837
838 mem_error:
839   {
840     GST_ERROR_OBJECT (ctx->element, "Failed to create EGLImage");
841
842     if (data)
843       gst_egl_gles_image_data_free (data);
844
845     if (mem[0])
846       gst_memory_unref (mem[0]);
847     if (mem[1])
848       gst_memory_unref (mem[1]);
849     if (mem[2])
850       gst_memory_unref (mem[2]);
851
852     return NULL;
853   }
854 }
855
856 void
857 gst_egl_adaptation_destroy_native_window (GstEglAdaptationContext * ctx,
858     gpointer * own_window_data)
859 {
860   platform_destroy_native_window (gst_egl_display_get
861       (ctx->display), ctx->used_window, own_window_data);
862   ctx->used_window = 0;
863 }
864
865 gboolean
866 gst_egl_adaptation_create_native_window (GstEglAdaptationContext * ctx,
867     gint width, gint height, gpointer * own_window_data)
868 {
869   EGLNativeWindowType window =
870       platform_create_native_window (width, height, own_window_data);
871   if (window)
872     gst_egl_adaptation_set_window (ctx, (guintptr) window);
873   GST_DEBUG_OBJECT (ctx->element, "Using window handle %p", (gpointer) window);
874   return window != 0;
875 }
876
877 void
878 gst_egl_adaptation_set_window (GstEglAdaptationContext * ctx, guintptr window)
879 {
880   ctx->window = (EGLNativeWindowType) window;
881 }
882
883 void
884 gst_egl_adaptation_init (GstEglAdaptationContext * ctx)
885 {
886   ctx->eglglesctx = g_new0 (GstEglGlesRenderContext, 1);
887 }
888
889 void
890 gst_egl_adaptation_deinit (GstEglAdaptationContext * ctx)
891 {
892   g_free (ctx->eglglesctx);
893 }
894
895 void
896 gst_egl_adaptation_destroy_surface (GstEglAdaptationContext * ctx)
897 {
898   if (ctx->eglglesctx->surface) {
899     eglDestroySurface (gst_egl_display_get (ctx->display),
900         ctx->eglglesctx->surface);
901     ctx->eglglesctx->surface = NULL;
902     ctx->have_surface = FALSE;
903   }
904 }
905
906 void
907 gst_egl_adaptation_destroy_context (GstEglAdaptationContext * ctx)
908 {
909   if (ctx->eglglesctx->eglcontext) {
910     eglDestroyContext (gst_egl_display_get (ctx->display),
911         ctx->eglglesctx->eglcontext);
912     ctx->eglglesctx->eglcontext = NULL;
913   }
914 }