gl/egl: Add gst_egl_image_from_dmabuf_direct() function
[platform/upstream/gstreamer.git] / gst-libs / gst / gl / egl / gsteglimage.c
1 /*
2  * GStreamer
3  * Copyright (C) 2012 Collabora Ltd.
4  *   @author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
5  * Copyright (C) 2014 Julien Isorce <julien.isorce@gmail.com>
6  * Copyright (C) 2016 Matthew Waters <matthew@centricular.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 /**
25  * SECTION:gsteglimage
26  * @short_description: EGLImage abstraction
27  * @title: GstEGLImage
28  * @see_also: #GstGLMemoryEGL, #GstGLContext
29  *
30  * #GstEGLImage represents and holds an #EGLImage handle.
31  *
32  * A #GstEGLImage can be created from a dmabuf with gst_egl_image_from_dmabuf(),
33  * or gst_egl_image_from_dmabuf_direct(), or #GstGLMemoryEGL provides a
34  * #GstAllocator to allocate #EGLImage's bound to and OpenGL texture.
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40
41 #include "gsteglimage.h"
42
43 #include <string.h>
44
45 #include <gst/gl/gstglfeature.h>
46 #include <gst/gl/gstglmemory.h>
47
48 #include "gst/gl/egl/gstegl.h"
49 #include "gst/gl/egl/gstglcontext_egl.h"
50 #include "gst/gl/egl/gstgldisplay_egl.h"
51
52 #if GST_GL_HAVE_DMABUF
53 #include <gst/allocators/gstdmabuf.h>
54 #include <libdrm/drm_fourcc.h>
55
56 #ifndef DRM_FORMAT_R8
57 #define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ')
58 #endif
59
60 #ifndef DRM_FORMAT_RG88
61 #define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8')
62 #endif
63
64 #ifndef DRM_FORMAT_GR88
65 #define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8')
66 #endif
67 #endif
68
69 #ifndef EGL_LINUX_DMA_BUF_EXT
70 #define EGL_LINUX_DMA_BUF_EXT 0x3270
71 #endif
72
73 #ifndef EGL_LINUX_DRM_FOURCC_EXT
74 #define EGL_LINUX_DRM_FOURCC_EXT 0x3271
75 #endif
76
77 #ifndef EGL_DMA_BUF_PLANE0_FD_EXT
78 #define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272
79 #endif
80
81 #ifndef EGL_DMA_BUF_PLANE0_OFFSET_EXT
82 #define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273
83 #endif
84
85 #ifndef EGL_DMA_BUF_PLANE0_PITCH_EXT
86 #define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274
87 #endif
88
89 #if !GST_GL_HAVE_EGLUINT64KHR
90 typedef khronos_uint64_t EGLuint64KHR;
91 #endif
92
93 GST_DEFINE_MINI_OBJECT_TYPE (GstEGLImage, gst_egl_image);
94
95 #ifndef GST_DISABLE_GST_DEBUG
96 #define GST_CAT_DEFAULT gst_egl_image_ensure_debug_category()
97
98 static GstDebugCategory *
99 gst_egl_image_ensure_debug_category (void)
100 {
101   static gsize cat_gonce = 0;
102
103   if (g_once_init_enter (&cat_gonce)) {
104     GstDebugCategory *cat = NULL;
105
106     GST_DEBUG_CATEGORY_INIT (cat, "gleglimage", 0, "EGLImage wrapper");
107
108     g_once_init_leave (&cat_gonce, (gsize) cat);
109   }
110
111   return (GstDebugCategory *) cat_gonce;
112 }
113 #endif /* GST_DISABLE_GST_DEBUG */
114
115 /**
116  * gst_egl_image_get_image:
117  * @image: a #GstEGLImage
118  *
119  * Returns: the #EGLImageKHR of @image
120  */
121 gpointer
122 gst_egl_image_get_image (GstEGLImage * image)
123 {
124   g_return_val_if_fail (GST_IS_EGL_IMAGE (image), EGL_NO_IMAGE_KHR);
125
126   return image->image;
127 }
128
129 static void
130 _gst_egl_image_free_thread (GstGLContext * context, GstEGLImage * image)
131 {
132   if (image->destroy_notify)
133     image->destroy_notify (image, image->destroy_data);
134 }
135
136 static void
137 _gst_egl_image_free (GstMiniObject * object)
138 {
139   GstEGLImage *image = GST_EGL_IMAGE (object);
140
141   if (image->context) {
142     gst_gl_context_thread_add (GST_GL_CONTEXT (image->context),
143         (GstGLContextThreadFunc) _gst_egl_image_free_thread, image);
144     gst_object_unref (image->context);
145   }
146 }
147
148 static GstMiniObject *
149 _gst_egl_image_copy (GstMiniObject * obj)
150 {
151   return gst_mini_object_ref (obj);
152 }
153
154 /**
155  * gst_egl_image_new_wrapped:
156  * @context: a #GstGLContext (must be an EGL context)
157  * @image: the image to wrap
158  * @format: the #GstGLFormat
159  * @user_data: user data
160  * @user_data_destroy: (destroy user_data): called when @user_data is no longer needed
161  *
162  * Returns: a new #GstEGLImage wrapping @image
163  */
164 GstEGLImage *
165 gst_egl_image_new_wrapped (GstGLContext * context, gpointer image,
166     GstGLFormat format, gpointer user_data,
167     GstEGLImageDestroyNotify user_data_destroy)
168 {
169   GstEGLImage *img = NULL;
170
171   g_return_val_if_fail (context != NULL, NULL);
172   g_return_val_if_fail ((gst_gl_context_get_gl_platform (context) &
173           GST_GL_PLATFORM_EGL) != 0, NULL);
174   g_return_val_if_fail (image != EGL_NO_IMAGE_KHR, NULL);
175
176   img = g_new0 (GstEGLImage, 1);
177   gst_mini_object_init (GST_MINI_OBJECT_CAST (img), 0, GST_TYPE_EGL_IMAGE,
178       (GstMiniObjectCopyFunction) _gst_egl_image_copy, NULL,
179       (GstMiniObjectFreeFunction) _gst_egl_image_free);
180
181   img->context = gst_object_ref (context);
182   img->image = image;
183   img->format = format;
184
185   img->destroy_data = user_data;
186   img->destroy_notify = user_data_destroy;
187
188   return img;
189 }
190
191 static EGLImageKHR
192 _gst_egl_image_create (GstGLContext * context, guint target,
193     EGLClientBuffer buffer, guintptr * attribs)
194 {
195   EGLDisplay egl_display = EGL_DEFAULT_DISPLAY;
196   EGLContext egl_context = EGL_NO_CONTEXT;
197   EGLImageKHR img = EGL_NO_IMAGE_KHR;
198   GstGLDisplayEGL *display_egl;
199   gint plat_major, plat_minor;
200   guint attrib_len = 0;
201
202   gst_gl_context_get_gl_platform_version (context, &plat_major, &plat_minor);
203
204   display_egl = gst_gl_display_egl_from_gl_display (context->display);
205   if (!display_egl) {
206     GST_WARNING_OBJECT (context, "Failed to retrieve GstGLDisplayEGL from %"
207         GST_PTR_FORMAT, context->display);
208     return EGL_NO_IMAGE_KHR;
209   }
210   egl_display =
211       (EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (display_egl));
212   gst_object_unref (display_egl);
213
214   if (target != EGL_LINUX_DMA_BUF_EXT)
215     egl_context = (EGLContext) gst_gl_context_get_gl_context (context);
216
217   if (attribs)
218     while (attribs[attrib_len++] != EGL_NONE) {
219     }
220 #ifdef EGL_VERSION_1_5
221   if (GST_GL_CHECK_GL_VERSION (plat_major, plat_minor, 1, 5)) {
222     EGLImageKHR (*gst_eglCreateImage) (EGLDisplay dpy, EGLContext ctx,
223         EGLenum target, EGLClientBuffer buffer, const EGLAttrib * attrib_list);
224     EGLAttrib *egl_attribs = NULL;
225     guint i;
226
227     gst_eglCreateImage = gst_gl_context_get_proc_address (context,
228         "eglCreateImage");
229     if (!gst_eglCreateImage) {
230       GST_ERROR_OBJECT (context, "\"eglCreateImage\" not exposed by the "
231           "implementation as required by EGL >= 1.5");
232       return EGL_NO_IMAGE_KHR;
233     }
234
235     if (attribs) {
236       egl_attribs = g_new0 (EGLAttrib, attrib_len);
237       for (i = 0; i < attrib_len; i++)
238         egl_attribs[i] = (EGLAttrib) attribs[i];
239     }
240
241     img = gst_eglCreateImage (egl_display, egl_context, target, buffer,
242         egl_attribs);
243
244     g_free (egl_attribs);
245   } else
246 #endif
247   {
248     EGLImageKHR (*gst_eglCreateImageKHR) (EGLDisplay dpy, EGLContext ctx,
249         EGLenum target, EGLClientBuffer buffer, const EGLint * attrib_list);
250     EGLint *egl_attribs = NULL;
251     gint i;
252
253     gst_eglCreateImageKHR = gst_gl_context_get_proc_address (context,
254         "eglCreateImageKHR");
255     if (!gst_eglCreateImageKHR) {
256       GST_WARNING_OBJECT (context, "\"eglCreateImageKHR\" not exposed by the "
257           "implementation");
258       return EGL_NO_IMAGE_KHR;
259     }
260
261     if (attribs) {
262       egl_attribs = g_new0 (EGLint, attrib_len);
263       for (i = 0; i < attrib_len; i++)
264         egl_attribs[i] = (EGLint) attribs[i];
265     }
266
267     img = gst_eglCreateImageKHR (egl_display, egl_context, target, buffer,
268         egl_attribs);
269
270     g_free (egl_attribs);
271   }
272
273   return img;
274 }
275
276 static void
277 _gst_egl_image_destroy (GstGLContext * context, EGLImageKHR image)
278 {
279   EGLBoolean (*gst_eglDestroyImage) (EGLDisplay dpy, EGLImageKHR image);
280   EGLDisplay egl_display = EGL_DEFAULT_DISPLAY;
281   GstGLDisplayEGL *display_egl;
282
283   gst_eglDestroyImage = gst_gl_context_get_proc_address (context,
284       "eglDestroyImage");
285   if (!gst_eglDestroyImage) {
286     gst_eglDestroyImage = gst_gl_context_get_proc_address (context,
287         "eglDestroyImageKHR");
288     if (!gst_eglDestroyImage) {
289       GST_ERROR_OBJECT (context, "\"eglDestroyImage\" not exposed by the "
290           "implementation");
291       return;
292     }
293   }
294
295   display_egl = gst_gl_display_egl_from_gl_display (context->display);
296   if (!display_egl) {
297     GST_WARNING_OBJECT (context, "Failed to retrieve GstGLDisplayEGL from %"
298         GST_PTR_FORMAT, context->display);
299     return;
300   }
301   egl_display =
302       (EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (display_egl));
303   gst_object_unref (display_egl);
304
305   if (!gst_eglDestroyImage (egl_display, image))
306     GST_WARNING_OBJECT (context, "eglDestroyImage failed");
307 }
308
309 static void
310 _destroy_egl_image (GstEGLImage * image, gpointer user_data)
311 {
312   _gst_egl_image_destroy (image->context, image->image);
313 }
314
315 /**
316  * gst_egl_image_from_texture:
317  * @context: a #GstGLContext (must be an EGL context)
318  * @gl_mem: a #GstGLMemory
319  * @attribs: additional attributes to add to the eglCreateImage() call.
320  *
321  * Returns: (transfer full): a #GstEGLImage wrapping @gl_mem or %NULL on failure
322  */
323 GstEGLImage *
324 gst_egl_image_from_texture (GstGLContext * context, GstGLMemory * gl_mem,
325     guintptr * attribs)
326 {
327   EGLenum egl_target;
328   EGLImageKHR img;
329
330   if (gl_mem->tex_target != GST_GL_TEXTURE_TARGET_2D) {
331     GST_FIXME_OBJECT (context, "Only know how to create EGLImage's from 2D "
332         "textures");
333     return NULL;
334   }
335
336   egl_target = EGL_GL_TEXTURE_2D_KHR;
337
338   img = _gst_egl_image_create (context, egl_target,
339       (EGLClientBuffer) (guintptr) gl_mem->tex_id, attribs);
340   if (!img)
341     return NULL;
342
343   return gst_egl_image_new_wrapped (context, img, gl_mem->tex_format, NULL,
344       (GstEGLImageDestroyNotify) _destroy_egl_image);
345 }
346
347 #if GST_GL_HAVE_DMABUF
348 /*
349  * GStreamer format descriptions differ from DRM formats as the representation
350  * is relative to a register, hence in native endianness. To reduce the driver
351  * requirement, we only import with a subset of texture formats and use
352  * shaders to convert. This way we avoid having to use external texture
353  * target.
354  */
355 static int
356 _drm_rgba_fourcc_from_info (GstVideoInfo * info, int plane)
357 {
358   GstVideoFormat format = GST_VIDEO_INFO_FORMAT (info);
359 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
360   const gint rgba_fourcc = DRM_FORMAT_ABGR8888;
361   const gint rgb_fourcc = DRM_FORMAT_BGR888;
362   const gint rg_fourcc = DRM_FORMAT_GR88;
363 #else
364   const gint rgba_fourcc = DRM_FORMAT_RGBA8888;
365   const gint rgb_fourcc = DRM_FORMAT_RGB888;
366   const gint rg_fourcc = DRM_FORMAT_RG88;
367 #endif
368
369   GST_DEBUG ("Getting DRM fourcc for %s plane %i",
370       gst_video_format_to_string (format), plane);
371
372   switch (format) {
373     case GST_VIDEO_FORMAT_RGB16:
374     case GST_VIDEO_FORMAT_BGR16:
375       return DRM_FORMAT_RGB565;
376
377     case GST_VIDEO_FORMAT_RGB:
378     case GST_VIDEO_FORMAT_BGR:
379       return rgb_fourcc;
380
381     case GST_VIDEO_FORMAT_RGBA:
382     case GST_VIDEO_FORMAT_RGBx:
383     case GST_VIDEO_FORMAT_BGRA:
384     case GST_VIDEO_FORMAT_BGRx:
385     case GST_VIDEO_FORMAT_ARGB:
386     case GST_VIDEO_FORMAT_xRGB:
387     case GST_VIDEO_FORMAT_ABGR:
388     case GST_VIDEO_FORMAT_xBGR:
389     case GST_VIDEO_FORMAT_AYUV:
390       return rgba_fourcc;
391
392     case GST_VIDEO_FORMAT_GRAY8:
393       return DRM_FORMAT_R8;
394
395     case GST_VIDEO_FORMAT_YUY2:
396     case GST_VIDEO_FORMAT_UYVY:
397     case GST_VIDEO_FORMAT_GRAY16_LE:
398     case GST_VIDEO_FORMAT_GRAY16_BE:
399       return rg_fourcc;
400
401     case GST_VIDEO_FORMAT_NV12:
402     case GST_VIDEO_FORMAT_NV21:
403       return plane == 0 ? DRM_FORMAT_R8 : rg_fourcc;
404
405     case GST_VIDEO_FORMAT_I420:
406     case GST_VIDEO_FORMAT_YV12:
407     case GST_VIDEO_FORMAT_Y41B:
408     case GST_VIDEO_FORMAT_Y42B:
409     case GST_VIDEO_FORMAT_Y444:
410       return DRM_FORMAT_R8;
411
412     default:
413       GST_ERROR ("Unsupported format for DMABuf.");
414       return -1;
415   }
416 }
417
418 /**
419  * gst_egl_image_from_dmabuf:
420  * @context: a #GstGLContext (must be an EGL context)
421  * @dmabuf: the DMA-Buf file descriptor
422  * @in_info: the #GstVideoInfo in @dmabuf
423  * @plane: the plane in @in_info to create and #GstEGLImage for
424  * @offset: the byte-offset in the data
425  *
426  * Creates an EGL image that imports the dmabuf FD. The dmabuf data
427  * is passed as RGBA data. Shaders later take this "RGBA" data and
428  * convert it from its true format (described by in_info) to actual
429  * RGBA output. For example, with I420, three EGL images are created,
430  * one for each plane, each EGL image with a single-channel R format.
431  * With NV12, two EGL images are created, one with R format, one
432  * with RG format etc.
433  *
434  * Returns: a #GstEGLImage wrapping @dmabuf or %NULL on failure
435  */
436 GstEGLImage *
437 gst_egl_image_from_dmabuf (GstGLContext * context,
438     gint dmabuf, GstVideoInfo * in_info, gint plane, gsize offset)
439 {
440   GstGLFormat format;
441   guintptr attribs[13];
442   EGLImageKHR img;
443   gint atti = 0;
444   gint fourcc;
445   gint i;
446
447   fourcc = _drm_rgba_fourcc_from_info (in_info, plane);
448   format = gst_gl_format_from_video_info (context, in_info, plane);
449
450   GST_DEBUG ("fourcc %.4s (%d) plane %d (%dx%d)",
451       (char *) &fourcc, fourcc, plane,
452       GST_VIDEO_INFO_COMP_WIDTH (in_info, plane),
453       GST_VIDEO_INFO_COMP_HEIGHT (in_info, plane));
454
455   attribs[atti++] = EGL_WIDTH;
456   attribs[atti++] = GST_VIDEO_INFO_COMP_WIDTH (in_info, plane);
457   attribs[atti++] = EGL_HEIGHT;
458   attribs[atti++] = GST_VIDEO_INFO_COMP_HEIGHT (in_info, plane);
459   attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
460   attribs[atti++] = fourcc;
461   attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT;
462   attribs[atti++] = dmabuf;
463   attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
464   attribs[atti++] = offset;
465   attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
466   attribs[atti++] = GST_VIDEO_INFO_PLANE_STRIDE (in_info, plane);
467   attribs[atti] = EGL_NONE;
468   g_assert (atti == G_N_ELEMENTS (attribs) - 1);
469
470   for (i = 0; i < atti; i++)
471     GST_LOG ("attr %i: %" G_GINTPTR_FORMAT, i, attribs[i]);
472
473   img = _gst_egl_image_create (context, EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
474   if (!img) {
475     GST_WARNING ("eglCreateImage failed: %s",
476         gst_egl_get_error_string (eglGetError ()));
477     return NULL;
478   }
479
480   return gst_egl_image_new_wrapped (context, img, format, NULL,
481       (GstEGLImageDestroyNotify) _destroy_egl_image);
482 }
483
484 /*
485  * Variant of _drm_rgba_fourcc_from_info() that is used in case the GPU can
486  * handle YUV formats directly (by using internal shaders, or hardwired
487  * YUV->RGB conversion matrices etc.)
488  */
489 static int
490 _drm_direct_fourcc_from_info (GstVideoInfo * info)
491 {
492   GstVideoFormat format = GST_VIDEO_INFO_FORMAT (info);
493
494   GST_DEBUG ("Getting DRM fourcc for %s", gst_video_format_to_string (format));
495
496   switch (format) {
497     case GST_VIDEO_FORMAT_YUY2:
498       return DRM_FORMAT_YUYV;
499
500     case GST_VIDEO_FORMAT_YVYU:
501       return DRM_FORMAT_YVYU;
502
503     case GST_VIDEO_FORMAT_UYVY:
504       return DRM_FORMAT_UYVY;
505
506     case GST_VIDEO_FORMAT_VYUY:
507       return DRM_FORMAT_VYUY;
508
509     case GST_VIDEO_FORMAT_AYUV:
510       return DRM_FORMAT_AYUV;
511
512     case GST_VIDEO_FORMAT_NV12:
513       return DRM_FORMAT_NV12;
514
515     case GST_VIDEO_FORMAT_NV21:
516       return DRM_FORMAT_NV21;
517
518     case GST_VIDEO_FORMAT_NV16:
519       return DRM_FORMAT_NV16;
520
521     case GST_VIDEO_FORMAT_NV61:
522       return DRM_FORMAT_NV61;
523
524     case GST_VIDEO_FORMAT_NV24:
525       return DRM_FORMAT_NV24;
526
527     case GST_VIDEO_FORMAT_YUV9:
528       return DRM_FORMAT_YUV410;
529
530     case GST_VIDEO_FORMAT_YVU9:
531       return DRM_FORMAT_YVU410;
532
533     case GST_VIDEO_FORMAT_Y41B:
534       return DRM_FORMAT_YUV411;
535
536     case GST_VIDEO_FORMAT_I420:
537       return DRM_FORMAT_YUV420;
538
539     case GST_VIDEO_FORMAT_YV12:
540       return DRM_FORMAT_YVU420;
541
542     case GST_VIDEO_FORMAT_Y42B:
543       return DRM_FORMAT_YUV422;
544
545     case GST_VIDEO_FORMAT_Y444:
546       return DRM_FORMAT_YUV444;
547
548     case GST_VIDEO_FORMAT_RGB16:
549       return DRM_FORMAT_RGB565;
550
551     case GST_VIDEO_FORMAT_BGR16:
552       return DRM_FORMAT_BGR565;
553
554     case GST_VIDEO_FORMAT_RGBA:
555       return DRM_FORMAT_ABGR8888;
556
557     case GST_VIDEO_FORMAT_RGBx:
558       return DRM_FORMAT_XBGR8888;
559
560     case GST_VIDEO_FORMAT_BGRA:
561       return DRM_FORMAT_ARGB8888;
562
563     case GST_VIDEO_FORMAT_BGRx:
564       return DRM_FORMAT_XRGB8888;
565
566     case GST_VIDEO_FORMAT_ARGB:
567       return DRM_FORMAT_BGRA8888;
568
569     case GST_VIDEO_FORMAT_xRGB:
570       return DRM_FORMAT_BGRX8888;
571
572     case GST_VIDEO_FORMAT_ABGR:
573       return DRM_FORMAT_RGBA8888;
574
575     case GST_VIDEO_FORMAT_xBGR:
576       return DRM_FORMAT_RGBX8888;
577
578     default:
579       GST_INFO ("Unsupported format for direct DMABuf.");
580       return -1;
581   }
582 }
583
584 static gboolean
585 _gst_egl_image_check_dmabuf_direct (GstGLContext * context,
586     GstVideoInfo * in_info)
587 {
588   EGLDisplay egl_display = EGL_DEFAULT_DISPLAY;
589   GstGLDisplayEGL *display_egl;
590   EGLuint64KHR *modifiers;
591   EGLBoolean *external_only;
592   int num_modifiers;
593   gboolean ret;
594   int i;
595
596   EGLBoolean (*gst_eglQueryDmaBufModifiersEXT) (EGLDisplay dpy,
597       int format, int max_modifiers, EGLuint64KHR * modifiers,
598       EGLBoolean * external_only, int *num_modifiers);
599
600   gst_eglQueryDmaBufModifiersEXT =
601       gst_gl_context_get_proc_address (context, "eglQueryDmaBufModifiersEXT");
602
603   if (!gst_eglQueryDmaBufModifiersEXT)
604     return FALSE;
605
606   display_egl = gst_gl_display_egl_from_gl_display (context->display);
607   if (!display_egl) {
608     GST_WARNING_OBJECT (context,
609         "Failed to retrieve GstGLDisplayEGL from %" GST_PTR_FORMAT,
610         context->display);
611     return FALSE;
612   }
613   egl_display =
614       (EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (display_egl));
615   gst_object_unref (display_egl);
616
617   ret = gst_eglQueryDmaBufModifiersEXT (egl_display,
618       _drm_direct_fourcc_from_info (in_info), 0, NULL, NULL, &num_modifiers);
619   if (!ret || num_modifiers == 0)
620     return FALSE;
621
622   modifiers = g_new (EGLuint64KHR, num_modifiers);
623   external_only = g_new (EGLBoolean, num_modifiers);
624
625   ret = gst_eglQueryDmaBufModifiersEXT (egl_display,
626       _drm_direct_fourcc_from_info (in_info), num_modifiers, modifiers,
627       external_only, &num_modifiers);
628   if (!ret || num_modifiers == 0) {
629     g_free (modifiers);
630     g_free (external_only);
631     return FALSE;
632   }
633
634   for (i = 0; i < num_modifiers; ++i) {
635     if (modifiers[i] == DRM_FORMAT_MOD_LINEAR) {
636       ret = !external_only[i];
637       g_free (modifiers);
638       g_free (external_only);
639       return ret;
640     }
641   }
642   g_free (modifiers);
643   g_free (external_only);
644   return FALSE;
645 }
646
647 /**
648  * gst_egl_image_from_dmabuf_direct:
649  * @context: a #GstGLContext (must be an EGL context)
650  * @fd: Array of DMABuf file descriptors
651  * @offset: Array of offsets, relative to the DMABuf
652  * @in_info: the #GstVideoInfo
653  *
654  * Creates an EGL image that imports the dmabuf FD. The dmabuf data
655  * is passed directly as the format described in in_info. This is
656  * useful if the hardware is capable of performing color space conversions
657  * internally. The appropriate DRM format is picked, and the EGL image
658  * is created with this DRM format.
659  *
660  * Another notable difference to gst_egl_image_from_dmabuf()
661  * is that this function creates one EGL image for all planes, not one for
662  * a single plane.
663  *
664  * Returns: a #GstEGLImage wrapping @dmabuf or %NULL on failure
665  */
666 GstEGLImage *
667 gst_egl_image_from_dmabuf_direct (GstGLContext * context,
668     gint * fd, gsize * offset, GstVideoInfo * in_info)
669 {
670
671   EGLImageKHR img;
672   guint n_planes = GST_VIDEO_INFO_N_PLANES (in_info);
673   gint i;
674   gboolean with_modifiers;
675
676   /* Explanation of array length:
677    * - 6 plane independent values are at the start (width, height, format FourCC)
678    * - 10 values per plane, and there are up to MAX_NUM_DMA_BUF_PLANES planes
679    * - 4 values for color space and range
680    * - 1 extra value for the EGL_NONE sentinel
681    */
682   guintptr attribs[41];         /* 6 + 10 * 3 + 4 + 1 */
683   gint atti = 0;
684
685   if (!_gst_egl_image_check_dmabuf_direct (context, in_info))
686     return NULL;
687
688   with_modifiers = gst_gl_context_check_feature (context,
689       "EGL_EXT_image_dma_buf_import_with_modifiers");
690
691   /* EGL DMABuf importation supports a maximum of 3 planes */
692   if (G_UNLIKELY (n_planes > 3))
693     return NULL;
694
695   attribs[atti++] = EGL_WIDTH;
696   attribs[atti++] = GST_VIDEO_INFO_WIDTH (in_info);
697   attribs[atti++] = EGL_HEIGHT;
698   attribs[atti++] = GST_VIDEO_INFO_HEIGHT (in_info);
699   attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
700   attribs[atti++] = _drm_direct_fourcc_from_info (in_info);
701
702   /* first plane */
703   {
704     attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT;
705     attribs[atti++] = fd[0];
706     attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
707     attribs[atti++] = offset[0];
708     attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
709     attribs[atti++] = in_info->stride[0];
710     if (with_modifiers) {
711       attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
712       attribs[atti++] = DRM_FORMAT_MOD_LINEAR & 0xffffffff;
713       attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
714       attribs[atti++] = (DRM_FORMAT_MOD_LINEAR >> 32) & 0xffffffff;
715     }
716   }
717
718   /* second plane */
719   if (n_planes >= 2) {
720     attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT;
721     attribs[atti++] = fd[1];
722     attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
723     attribs[atti++] = offset[1];
724     attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
725     attribs[atti++] = in_info->stride[1];
726     if (with_modifiers) {
727       attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT;
728       attribs[atti++] = DRM_FORMAT_MOD_LINEAR & 0xffffffff;
729       attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT;
730       attribs[atti++] = (DRM_FORMAT_MOD_LINEAR >> 32) & 0xffffffff;
731     }
732   }
733
734   /* third plane */
735   if (n_planes == 3) {
736     attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT;
737     attribs[atti++] = fd[2];
738     attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
739     attribs[atti++] = offset[2];
740     attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
741     attribs[atti++] = in_info->stride[2];
742     if (with_modifiers) {
743       attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT;
744       attribs[atti++] = DRM_FORMAT_MOD_LINEAR & 0xffffffff;
745       attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT;
746       attribs[atti++] = (DRM_FORMAT_MOD_LINEAR >> 32) & 0xffffffff;
747     }
748   }
749
750   {
751     uint32_t color_space;
752     switch (in_info->colorimetry.matrix) {
753       case GST_VIDEO_COLOR_MATRIX_BT601:
754         color_space = EGL_ITU_REC601_EXT;
755         break;
756       case GST_VIDEO_COLOR_MATRIX_BT709:
757         color_space = EGL_ITU_REC709_EXT;
758         break;
759       case GST_VIDEO_COLOR_MATRIX_BT2020:
760         color_space = EGL_ITU_REC2020_EXT;
761         break;
762       default:
763         color_space = 0;
764         break;
765     }
766     if (color_space != 0) {
767       attribs[atti++] = EGL_YUV_COLOR_SPACE_HINT_EXT;
768       attribs[atti++] = color_space;
769     }
770   }
771
772   {
773     uint32_t range;
774     switch (in_info->colorimetry.range) {
775       case GST_VIDEO_COLOR_RANGE_0_255:
776         range = EGL_YUV_FULL_RANGE_EXT;
777         break;
778       case GST_VIDEO_COLOR_RANGE_16_235:
779         range = EGL_YUV_NARROW_RANGE_EXT;
780         break;
781       default:
782         range = 0;
783         break;
784     }
785     if (range != 0) {
786       attribs[atti++] = EGL_SAMPLE_RANGE_HINT_EXT;
787       attribs[atti++] = range;
788     }
789   }
790
791   /* Add the EGL_NONE sentinel */
792   attribs[atti] = EGL_NONE;
793   g_assert (atti <= G_N_ELEMENTS (attribs) - 1);
794
795   for (i = 0; i < atti; i++)
796     GST_LOG ("attr %i: %" G_GINTPTR_FORMAT, i, attribs[i]);
797
798   img = _gst_egl_image_create (context, EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
799   if (!img) {
800     GST_WARNING ("eglCreateImage failed: %s",
801         gst_egl_get_error_string (eglGetError ()));
802     return NULL;
803   }
804
805   return gst_egl_image_new_wrapped (context, img, GST_GL_RGBA, NULL,
806       (GstEGLImageDestroyNotify) _destroy_egl_image);
807 }
808
809 gboolean
810 gst_egl_image_export_dmabuf (GstEGLImage * image, int *fd, gint * stride,
811     gsize * offset)
812 {
813   EGLBoolean (*gst_eglExportDMABUFImageQueryMESA) (EGLDisplay dpy,
814       EGLImageKHR image, int *fourcc, int *num_planes,
815       EGLuint64KHR * modifiers);
816   EGLBoolean (*gst_eglExportDMABUFImageMESA) (EGLDisplay dpy, EGLImageKHR image,
817       int *fds, EGLint * strides, EGLint * offsets);
818   GstGLDisplayEGL *display_egl;
819   EGLDisplay egl_display = EGL_DEFAULT_DISPLAY;
820   int num_planes = 0;
821   int egl_fd = 0;
822   EGLint egl_stride = 0;
823   EGLint egl_offset = 0;
824
825   gst_eglExportDMABUFImageQueryMESA =
826       gst_gl_context_get_proc_address (image->context,
827       "eglExportDMABUFImageQueryMESA");
828   gst_eglExportDMABUFImageMESA =
829       gst_gl_context_get_proc_address (image->context,
830       "eglExportDMABUFImageMESA");
831
832   if (!gst_eglExportDMABUFImageQueryMESA || !gst_eglExportDMABUFImageMESA)
833     return FALSE;
834
835   display_egl =
836       (GstGLDisplayEGL *) gst_gl_display_egl_from_gl_display (image->
837       context->display);
838   if (!display_egl) {
839     GST_WARNING_OBJECT (image->context,
840         "Failed to retrieve GstGLDisplayEGL from %" GST_PTR_FORMAT,
841         image->context->display);
842     return FALSE;
843   }
844   egl_display =
845       (EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (display_egl));
846   gst_object_unref (display_egl);
847
848   if (!gst_eglExportDMABUFImageQueryMESA (egl_display, image->image,
849           NULL, &num_planes, NULL))
850     return FALSE;
851
852   /* Don't allow multi-plane dmabufs */
853   if (num_planes > 1)
854     return FALSE;
855
856   if (!gst_eglExportDMABUFImageMESA (egl_display, image->image, &egl_fd,
857           &egl_stride, &egl_offset))
858     return FALSE;
859
860   *fd = egl_fd;
861   *stride = egl_stride;
862   *offset = egl_offset;
863
864   return TRUE;
865 }
866
867 #endif /* GST_GL_HAVE_DMABUF */