eglimage: Add compatibility define for DRM_FORMAT_NV24
[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
68 #ifndef DRM_FORMAT_NV24
69 #define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4')
70 #endif
71 #endif
72
73 #ifndef EGL_LINUX_DMA_BUF_EXT
74 #define EGL_LINUX_DMA_BUF_EXT 0x3270
75 #endif
76
77 #ifndef EGL_LINUX_DRM_FOURCC_EXT
78 #define EGL_LINUX_DRM_FOURCC_EXT 0x3271
79 #endif
80
81 #ifndef EGL_DMA_BUF_PLANE0_FD_EXT
82 #define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272
83 #endif
84
85 #ifndef EGL_DMA_BUF_PLANE0_OFFSET_EXT
86 #define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273
87 #endif
88
89 #ifndef EGL_DMA_BUF_PLANE0_PITCH_EXT
90 #define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274
91 #endif
92
93 #ifndef EGL_DMA_BUF_PLANE1_FD_EXT
94 #define EGL_DMA_BUF_PLANE1_FD_EXT 0x3275
95 #endif
96
97 #ifndef EGL_DMA_BUF_PLANE1_OFFSET_EXT
98 #define EGL_DMA_BUF_PLANE1_OFFSET_EXT 0x3276
99 #endif
100
101 #ifndef EGL_DMA_BUF_PLANE1_PITCH_EXT
102 #define EGL_DMA_BUF_PLANE1_PITCH_EXT 0x3277
103 #endif
104
105 #ifndef EGL_DMA_BUF_PLANE2_FD_EXT
106 #define EGL_DMA_BUF_PLANE2_FD_EXT 0x3278
107 #endif
108
109 #ifndef EGL_DMA_BUF_PLANE2_OFFSET_EXT
110 #define EGL_DMA_BUF_PLANE2_OFFSET_EXT 0x3279
111 #endif
112
113 #ifndef EGL_DMA_BUF_PLANE2_PITCH_EXT
114 #define EGL_DMA_BUF_PLANE2_PITCH_EXT 0x327A
115 #endif
116
117 #ifndef DRM_FORMAT_MOD_LINEAR
118 #define DRM_FORMAT_MOD_LINEAR 0ULL
119 #endif
120
121 #ifndef EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT
122 #define EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT 0x3443
123 #endif
124
125 #ifndef EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT
126 #define EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT 0x3444
127 #endif
128
129 #ifndef EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT
130 #define EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT 0x3445
131 #endif
132
133 #ifndef EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT
134 #define EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT 0x3446
135 #endif
136
137 #ifndef EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT
138 #define EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT 0x3447
139 #endif
140
141 #ifndef EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT
142 #define EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT 0x3448
143 #endif
144
145 #ifndef EGL_ITU_REC601_EXT
146 #define EGL_ITU_REC601_EXT 0x327F
147 #endif
148
149 #ifndef EGL_ITU_REC709_EXT
150 #define EGL_ITU_REC709_EXT 0x3280
151 #endif
152
153 #ifndef EGL_ITU_REC2020_EXT
154 #define EGL_ITU_REC2020_EXT 0x3281
155 #endif
156
157 #ifndef EGL_SAMPLE_RANGE_HINT_EXT
158 #define EGL_SAMPLE_RANGE_HINT_EXT 0x327C
159 #endif
160
161 #ifndef EGL_YUV_COLOR_SPACE_HINT_EXT
162 #define EGL_YUV_COLOR_SPACE_HINT_EXT 0x327B
163 #endif
164
165 #ifndef EGL_YUV_FULL_RANGE_EXT
166 #define EGL_YUV_FULL_RANGE_EXT 0x3282
167 #endif
168
169 #ifndef EGL_YUV_NARROW_RANGE_EXT
170 #define EGL_YUV_NARROW_RANGE_EXT 0x3283
171 #endif
172
173 #if !GST_GL_HAVE_EGLUINT64KHR
174 typedef khronos_uint64_t EGLuint64KHR;
175 #endif
176
177 GST_DEFINE_MINI_OBJECT_TYPE (GstEGLImage, gst_egl_image);
178
179 #ifndef GST_DISABLE_GST_DEBUG
180 #define GST_CAT_DEFAULT gst_egl_image_ensure_debug_category()
181
182 static GstDebugCategory *
183 gst_egl_image_ensure_debug_category (void)
184 {
185   static gsize cat_gonce = 0;
186
187   if (g_once_init_enter (&cat_gonce)) {
188     GstDebugCategory *cat = NULL;
189
190     GST_DEBUG_CATEGORY_INIT (cat, "gleglimage", 0, "EGLImage wrapper");
191
192     g_once_init_leave (&cat_gonce, (gsize) cat);
193   }
194
195   return (GstDebugCategory *) cat_gonce;
196 }
197 #endif /* GST_DISABLE_GST_DEBUG */
198
199 /**
200  * gst_egl_image_get_image:
201  * @image: a #GstEGLImage
202  *
203  * Returns: the #EGLImageKHR of @image
204  */
205 gpointer
206 gst_egl_image_get_image (GstEGLImage * image)
207 {
208   g_return_val_if_fail (GST_IS_EGL_IMAGE (image), EGL_NO_IMAGE_KHR);
209
210   return image->image;
211 }
212
213 static void
214 _gst_egl_image_free_thread (GstGLContext * context, GstEGLImage * image)
215 {
216   if (image->destroy_notify)
217     image->destroy_notify (image, image->destroy_data);
218 }
219
220 static void
221 _gst_egl_image_free (GstMiniObject * object)
222 {
223   GstEGLImage *image = GST_EGL_IMAGE (object);
224
225   if (image->context) {
226     gst_gl_context_thread_add (GST_GL_CONTEXT (image->context),
227         (GstGLContextThreadFunc) _gst_egl_image_free_thread, image);
228     gst_object_unref (image->context);
229   }
230 }
231
232 static GstMiniObject *
233 _gst_egl_image_copy (GstMiniObject * obj)
234 {
235   return gst_mini_object_ref (obj);
236 }
237
238 /**
239  * gst_egl_image_new_wrapped:
240  * @context: a #GstGLContext (must be an EGL context)
241  * @image: the image to wrap
242  * @format: the #GstGLFormat
243  * @user_data: user data
244  * @user_data_destroy: (destroy user_data): called when @user_data is no longer needed
245  *
246  * Returns: a new #GstEGLImage wrapping @image
247  */
248 GstEGLImage *
249 gst_egl_image_new_wrapped (GstGLContext * context, gpointer image,
250     GstGLFormat format, gpointer user_data,
251     GstEGLImageDestroyNotify user_data_destroy)
252 {
253   GstEGLImage *img = NULL;
254
255   g_return_val_if_fail (context != NULL, NULL);
256   g_return_val_if_fail ((gst_gl_context_get_gl_platform (context) &
257           GST_GL_PLATFORM_EGL) != 0, NULL);
258   g_return_val_if_fail (image != EGL_NO_IMAGE_KHR, NULL);
259
260   img = g_new0 (GstEGLImage, 1);
261   gst_mini_object_init (GST_MINI_OBJECT_CAST (img), 0, GST_TYPE_EGL_IMAGE,
262       (GstMiniObjectCopyFunction) _gst_egl_image_copy, NULL,
263       (GstMiniObjectFreeFunction) _gst_egl_image_free);
264
265   img->context = gst_object_ref (context);
266   img->image = image;
267   img->format = format;
268
269   img->destroy_data = user_data;
270   img->destroy_notify = user_data_destroy;
271
272   return img;
273 }
274
275 static EGLImageKHR
276 _gst_egl_image_create (GstGLContext * context, guint target,
277     EGLClientBuffer buffer, guintptr * attribs)
278 {
279   EGLDisplay egl_display = EGL_DEFAULT_DISPLAY;
280   EGLContext egl_context = EGL_NO_CONTEXT;
281   EGLImageKHR img = EGL_NO_IMAGE_KHR;
282   GstGLDisplayEGL *display_egl;
283   gint plat_major, plat_minor;
284   guint attrib_len = 0;
285
286   gst_gl_context_get_gl_platform_version (context, &plat_major, &plat_minor);
287
288   display_egl = gst_gl_display_egl_from_gl_display (context->display);
289   if (!display_egl) {
290     GST_WARNING_OBJECT (context, "Failed to retrieve GstGLDisplayEGL from %"
291         GST_PTR_FORMAT, context->display);
292     return EGL_NO_IMAGE_KHR;
293   }
294   egl_display =
295       (EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (display_egl));
296   gst_object_unref (display_egl);
297
298   if (target != EGL_LINUX_DMA_BUF_EXT)
299     egl_context = (EGLContext) gst_gl_context_get_gl_context (context);
300
301   if (attribs)
302     while (attribs[attrib_len++] != EGL_NONE) {
303     }
304 #ifdef EGL_VERSION_1_5
305   if (GST_GL_CHECK_GL_VERSION (plat_major, plat_minor, 1, 5)) {
306     EGLImageKHR (*gst_eglCreateImage) (EGLDisplay dpy, EGLContext ctx,
307         EGLenum target, EGLClientBuffer buffer, const EGLAttrib * attrib_list);
308     EGLAttrib *egl_attribs = NULL;
309     guint i;
310
311     gst_eglCreateImage = gst_gl_context_get_proc_address (context,
312         "eglCreateImage");
313     if (!gst_eglCreateImage) {
314       GST_ERROR_OBJECT (context, "\"eglCreateImage\" not exposed by the "
315           "implementation as required by EGL >= 1.5");
316       return EGL_NO_IMAGE_KHR;
317     }
318
319     if (attribs) {
320       egl_attribs = g_new0 (EGLAttrib, attrib_len);
321       for (i = 0; i < attrib_len; i++)
322         egl_attribs[i] = (EGLAttrib) attribs[i];
323     }
324
325     img = gst_eglCreateImage (egl_display, egl_context, target, buffer,
326         egl_attribs);
327
328     g_free (egl_attribs);
329   } else
330 #endif
331   {
332     EGLImageKHR (*gst_eglCreateImageKHR) (EGLDisplay dpy, EGLContext ctx,
333         EGLenum target, EGLClientBuffer buffer, const EGLint * attrib_list);
334     EGLint *egl_attribs = NULL;
335     gint i;
336
337     gst_eglCreateImageKHR = gst_gl_context_get_proc_address (context,
338         "eglCreateImageKHR");
339     if (!gst_eglCreateImageKHR) {
340       GST_WARNING_OBJECT (context, "\"eglCreateImageKHR\" not exposed by the "
341           "implementation");
342       return EGL_NO_IMAGE_KHR;
343     }
344
345     if (attribs) {
346       egl_attribs = g_new0 (EGLint, attrib_len);
347       for (i = 0; i < attrib_len; i++)
348         egl_attribs[i] = (EGLint) attribs[i];
349     }
350
351     img = gst_eglCreateImageKHR (egl_display, egl_context, target, buffer,
352         egl_attribs);
353
354     g_free (egl_attribs);
355   }
356
357   return img;
358 }
359
360 static void
361 _gst_egl_image_destroy (GstGLContext * context, EGLImageKHR image)
362 {
363   EGLBoolean (*gst_eglDestroyImage) (EGLDisplay dpy, EGLImageKHR image);
364   EGLDisplay egl_display = EGL_DEFAULT_DISPLAY;
365   GstGLDisplayEGL *display_egl;
366
367   gst_eglDestroyImage = gst_gl_context_get_proc_address (context,
368       "eglDestroyImage");
369   if (!gst_eglDestroyImage) {
370     gst_eglDestroyImage = gst_gl_context_get_proc_address (context,
371         "eglDestroyImageKHR");
372     if (!gst_eglDestroyImage) {
373       GST_ERROR_OBJECT (context, "\"eglDestroyImage\" not exposed by the "
374           "implementation");
375       return;
376     }
377   }
378
379   display_egl = gst_gl_display_egl_from_gl_display (context->display);
380   if (!display_egl) {
381     GST_WARNING_OBJECT (context, "Failed to retrieve GstGLDisplayEGL from %"
382         GST_PTR_FORMAT, context->display);
383     return;
384   }
385   egl_display =
386       (EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (display_egl));
387   gst_object_unref (display_egl);
388
389   if (!gst_eglDestroyImage (egl_display, image))
390     GST_WARNING_OBJECT (context, "eglDestroyImage failed");
391 }
392
393 static void
394 _destroy_egl_image (GstEGLImage * image, gpointer user_data)
395 {
396   _gst_egl_image_destroy (image->context, image->image);
397 }
398
399 /**
400  * gst_egl_image_from_texture:
401  * @context: a #GstGLContext (must be an EGL context)
402  * @gl_mem: a #GstGLMemory
403  * @attribs: additional attributes to add to the eglCreateImage() call.
404  *
405  * Returns: (transfer full): a #GstEGLImage wrapping @gl_mem or %NULL on failure
406  */
407 GstEGLImage *
408 gst_egl_image_from_texture (GstGLContext * context, GstGLMemory * gl_mem,
409     guintptr * attribs)
410 {
411   EGLenum egl_target;
412   EGLImageKHR img;
413
414   if (gl_mem->tex_target != GST_GL_TEXTURE_TARGET_2D) {
415     GST_FIXME_OBJECT (context, "Only know how to create EGLImage's from 2D "
416         "textures");
417     return NULL;
418   }
419
420   egl_target = EGL_GL_TEXTURE_2D_KHR;
421
422   img = _gst_egl_image_create (context, egl_target,
423       (EGLClientBuffer) (guintptr) gl_mem->tex_id, attribs);
424   if (!img)
425     return NULL;
426
427   return gst_egl_image_new_wrapped (context, img, gl_mem->tex_format, NULL,
428       (GstEGLImageDestroyNotify) _destroy_egl_image);
429 }
430
431 #if GST_GL_HAVE_DMABUF
432 /*
433  * GStreamer format descriptions differ from DRM formats as the representation
434  * is relative to a register, hence in native endianness. To reduce the driver
435  * requirement, we only import with a subset of texture formats and use
436  * shaders to convert. This way we avoid having to use external texture
437  * target.
438  */
439 static int
440 _drm_rgba_fourcc_from_info (GstVideoInfo * info, int plane,
441     GstGLFormat * out_format)
442 {
443   GstVideoFormat format = GST_VIDEO_INFO_FORMAT (info);
444 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
445   const gint rgba_fourcc = DRM_FORMAT_ABGR8888;
446   const gint rgb_fourcc = DRM_FORMAT_BGR888;
447   const gint rg_fourcc = DRM_FORMAT_GR88;
448 #else
449   const gint rgba_fourcc = DRM_FORMAT_RGBA8888;
450   const gint rgb_fourcc = DRM_FORMAT_RGB888;
451   const gint rg_fourcc = DRM_FORMAT_RG88;
452 #endif
453
454   GST_DEBUG ("Getting DRM fourcc for %s plane %i",
455       gst_video_format_to_string (format), plane);
456
457   switch (format) {
458     case GST_VIDEO_FORMAT_RGB16:
459     case GST_VIDEO_FORMAT_BGR16:
460       *out_format = GST_GL_RGB565;
461       return DRM_FORMAT_RGB565;
462
463     case GST_VIDEO_FORMAT_RGB:
464     case GST_VIDEO_FORMAT_BGR:
465       *out_format = GST_GL_RGB;
466       return rgb_fourcc;
467
468     case GST_VIDEO_FORMAT_RGBA:
469     case GST_VIDEO_FORMAT_RGBx:
470     case GST_VIDEO_FORMAT_BGRA:
471     case GST_VIDEO_FORMAT_BGRx:
472     case GST_VIDEO_FORMAT_ARGB:
473     case GST_VIDEO_FORMAT_xRGB:
474     case GST_VIDEO_FORMAT_ABGR:
475     case GST_VIDEO_FORMAT_xBGR:
476     case GST_VIDEO_FORMAT_AYUV:
477       *out_format = GST_GL_RGBA;
478       return rgba_fourcc;
479
480     case GST_VIDEO_FORMAT_GRAY8:
481       *out_format = GST_GL_RED;
482       return DRM_FORMAT_R8;
483
484     case GST_VIDEO_FORMAT_YUY2:
485     case GST_VIDEO_FORMAT_UYVY:
486     case GST_VIDEO_FORMAT_GRAY16_LE:
487     case GST_VIDEO_FORMAT_GRAY16_BE:
488       *out_format = GST_GL_RG;
489       return rg_fourcc;
490
491     case GST_VIDEO_FORMAT_NV12:
492     case GST_VIDEO_FORMAT_NV21:
493       *out_format = plane == 0 ? GST_GL_RED : GST_GL_RG;
494       return plane == 0 ? DRM_FORMAT_R8 : rg_fourcc;
495
496     case GST_VIDEO_FORMAT_I420:
497     case GST_VIDEO_FORMAT_YV12:
498     case GST_VIDEO_FORMAT_Y41B:
499     case GST_VIDEO_FORMAT_Y42B:
500     case GST_VIDEO_FORMAT_Y444:
501       *out_format = GST_GL_RED;
502       return DRM_FORMAT_R8;
503
504     default:
505       GST_ERROR ("Unsupported format for DMABuf.");
506       return -1;
507   }
508 }
509
510 /**
511  * gst_egl_image_from_dmabuf:
512  * @context: a #GstGLContext (must be an EGL context)
513  * @dmabuf: the DMA-Buf file descriptor
514  * @in_info: the #GstVideoInfo in @dmabuf
515  * @plane: the plane in @in_info to create and #GstEGLImage for
516  * @offset: the byte-offset in the data
517  *
518  * Creates an EGL image that imports the dmabuf FD. The dmabuf data
519  * is passed as RGBA data. Shaders later take this "RGBA" data and
520  * convert it from its true format (described by in_info) to actual
521  * RGBA output. For example, with I420, three EGL images are created,
522  * one for each plane, each EGL image with a single-channel R format.
523  * With NV12, two EGL images are created, one with R format, one
524  * with RG format etc.
525  *
526  * Returns: a #GstEGLImage wrapping @dmabuf or %NULL on failure
527  */
528 GstEGLImage *
529 gst_egl_image_from_dmabuf (GstGLContext * context,
530     gint dmabuf, GstVideoInfo * in_info, gint plane, gsize offset)
531 {
532   GstGLFormat format = 0;
533   guintptr attribs[13];
534   EGLImageKHR img;
535   gint atti = 0;
536   gint fourcc;
537   gint i;
538
539   fourcc = _drm_rgba_fourcc_from_info (in_info, plane, &format);
540   GST_DEBUG ("fourcc %.4s (%d) plane %d (%dx%d)",
541       (char *) &fourcc, fourcc, plane,
542       GST_VIDEO_INFO_COMP_WIDTH (in_info, plane),
543       GST_VIDEO_INFO_COMP_HEIGHT (in_info, plane));
544
545   attribs[atti++] = EGL_WIDTH;
546   attribs[atti++] = GST_VIDEO_INFO_COMP_WIDTH (in_info, plane);
547   attribs[atti++] = EGL_HEIGHT;
548   attribs[atti++] = GST_VIDEO_INFO_COMP_HEIGHT (in_info, plane);
549   attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
550   attribs[atti++] = fourcc;
551   attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT;
552   attribs[atti++] = dmabuf;
553   attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
554   attribs[atti++] = offset;
555   attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
556   attribs[atti++] = GST_VIDEO_INFO_PLANE_STRIDE (in_info, plane);
557   attribs[atti] = EGL_NONE;
558   g_assert (atti == G_N_ELEMENTS (attribs) - 1);
559
560   for (i = 0; i < atti; i++)
561     GST_LOG ("attr %i: %" G_GINTPTR_FORMAT, i, attribs[i]);
562
563   img = _gst_egl_image_create (context, EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
564   if (!img) {
565     GST_WARNING ("eglCreateImage failed: %s",
566         gst_egl_get_error_string (eglGetError ()));
567     return NULL;
568   }
569
570   return gst_egl_image_new_wrapped (context, img, format, NULL,
571       (GstEGLImageDestroyNotify) _destroy_egl_image);
572 }
573
574 /*
575  * Variant of _drm_rgba_fourcc_from_info() that is used in case the GPU can
576  * handle YUV formats directly (by using internal shaders, or hardwired
577  * YUV->RGB conversion matrices etc.)
578  */
579 static int
580 _drm_direct_fourcc_from_info (GstVideoInfo * info)
581 {
582   GstVideoFormat format = GST_VIDEO_INFO_FORMAT (info);
583
584   GST_DEBUG ("Getting DRM fourcc for %s", gst_video_format_to_string (format));
585
586   switch (format) {
587     case GST_VIDEO_FORMAT_YUY2:
588       return DRM_FORMAT_YUYV;
589
590     case GST_VIDEO_FORMAT_YVYU:
591       return DRM_FORMAT_YVYU;
592
593     case GST_VIDEO_FORMAT_UYVY:
594       return DRM_FORMAT_UYVY;
595
596     case GST_VIDEO_FORMAT_VYUY:
597       return DRM_FORMAT_VYUY;
598
599     case GST_VIDEO_FORMAT_AYUV:
600       return DRM_FORMAT_AYUV;
601
602     case GST_VIDEO_FORMAT_NV12:
603       return DRM_FORMAT_NV12;
604
605     case GST_VIDEO_FORMAT_NV21:
606       return DRM_FORMAT_NV21;
607
608     case GST_VIDEO_FORMAT_NV16:
609       return DRM_FORMAT_NV16;
610
611     case GST_VIDEO_FORMAT_NV61:
612       return DRM_FORMAT_NV61;
613
614     case GST_VIDEO_FORMAT_NV24:
615       return DRM_FORMAT_NV24;
616
617     case GST_VIDEO_FORMAT_YUV9:
618       return DRM_FORMAT_YUV410;
619
620     case GST_VIDEO_FORMAT_YVU9:
621       return DRM_FORMAT_YVU410;
622
623     case GST_VIDEO_FORMAT_Y41B:
624       return DRM_FORMAT_YUV411;
625
626     case GST_VIDEO_FORMAT_I420:
627       return DRM_FORMAT_YUV420;
628
629     case GST_VIDEO_FORMAT_YV12:
630       return DRM_FORMAT_YVU420;
631
632     case GST_VIDEO_FORMAT_Y42B:
633       return DRM_FORMAT_YUV422;
634
635     case GST_VIDEO_FORMAT_Y444:
636       return DRM_FORMAT_YUV444;
637
638     case GST_VIDEO_FORMAT_RGB16:
639       return DRM_FORMAT_RGB565;
640
641     case GST_VIDEO_FORMAT_BGR16:
642       return DRM_FORMAT_BGR565;
643
644     case GST_VIDEO_FORMAT_RGBA:
645       return DRM_FORMAT_ABGR8888;
646
647     case GST_VIDEO_FORMAT_RGBx:
648       return DRM_FORMAT_XBGR8888;
649
650     case GST_VIDEO_FORMAT_BGRA:
651       return DRM_FORMAT_ARGB8888;
652
653     case GST_VIDEO_FORMAT_BGRx:
654       return DRM_FORMAT_XRGB8888;
655
656     case GST_VIDEO_FORMAT_ARGB:
657       return DRM_FORMAT_BGRA8888;
658
659     case GST_VIDEO_FORMAT_xRGB:
660       return DRM_FORMAT_BGRX8888;
661
662     case GST_VIDEO_FORMAT_ABGR:
663       return DRM_FORMAT_RGBA8888;
664
665     case GST_VIDEO_FORMAT_xBGR:
666       return DRM_FORMAT_RGBX8888;
667
668     default:
669       GST_INFO ("Unsupported format for direct DMABuf.");
670       return -1;
671   }
672 }
673
674 static gboolean
675 _gst_egl_image_check_dmabuf_direct (GstGLContext * context, int fourcc)
676 {
677   EGLDisplay egl_display = EGL_DEFAULT_DISPLAY;
678   GstGLDisplayEGL *display_egl;
679   EGLint *formats;
680   EGLint num_formats;
681   EGLuint64KHR *modifiers;
682   EGLBoolean *external_only;
683   int num_modifiers;
684   gboolean ret;
685   int i;
686
687   EGLBoolean (*gst_eglQueryDmaBufFormatsEXT) (EGLDisplay dpy,
688       EGLint max_formats, EGLint * formats, EGLint * num_formats);
689   EGLBoolean (*gst_eglQueryDmaBufModifiersEXT) (EGLDisplay dpy,
690       int format, int max_modifiers, EGLuint64KHR * modifiers,
691       EGLBoolean * external_only, int *num_modifiers);
692
693   gst_eglQueryDmaBufFormatsEXT =
694       gst_gl_context_get_proc_address (context, "eglQueryDmaBufFormatsEXT");
695   gst_eglQueryDmaBufModifiersEXT =
696       gst_gl_context_get_proc_address (context, "eglQueryDmaBufModifiersEXT");
697
698   if (!gst_eglQueryDmaBufFormatsEXT || !gst_eglQueryDmaBufModifiersEXT)
699     return FALSE;
700
701   display_egl = gst_gl_display_egl_from_gl_display (context->display);
702   if (!display_egl) {
703     GST_WARNING_OBJECT (context,
704         "Failed to retrieve GstGLDisplayEGL from %" GST_PTR_FORMAT,
705         context->display);
706     return FALSE;
707   }
708   egl_display =
709       (EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (display_egl));
710   gst_object_unref (display_egl);
711
712   ret = gst_eglQueryDmaBufFormatsEXT (egl_display, 0, NULL, &num_formats);
713   if (!ret || num_formats == 0)
714     return FALSE;
715
716   formats = g_new (EGLint, num_formats);
717
718   ret = gst_eglQueryDmaBufFormatsEXT (egl_display, num_formats, formats,
719       &num_formats);
720   if (!ret || num_formats == 0) {
721     g_free (formats);
722     return FALSE;
723   }
724
725   for (i = 0; i < num_formats; i++) {
726     if (formats[i] == fourcc)
727       break;
728   }
729   g_free (formats);
730   if (i == num_formats) {
731     GST_DEBUG ("driver does not support importing fourcc %" GST_FOURCC_FORMAT,
732         GST_FOURCC_ARGS (fourcc));
733     return FALSE;
734   }
735
736   ret = gst_eglQueryDmaBufModifiersEXT (egl_display, fourcc, 0, NULL, NULL,
737       &num_modifiers);
738   if (!ret || num_modifiers == 0) {
739     GST_DEBUG ("driver does not report modifiers for fourcc %"
740         GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
741     return FALSE;
742   }
743
744   modifiers = g_new (EGLuint64KHR, num_modifiers);
745   external_only = g_new (EGLBoolean, num_modifiers);
746
747   ret = gst_eglQueryDmaBufModifiersEXT (egl_display, fourcc, num_modifiers,
748       modifiers, external_only, &num_modifiers);
749   if (!ret || num_modifiers == 0) {
750     g_free (modifiers);
751     g_free (external_only);
752     return FALSE;
753   }
754
755   for (i = 0; i < num_modifiers; ++i) {
756     if (modifiers[i] == DRM_FORMAT_MOD_LINEAR) {
757       if (external_only[i]) {
758         GST_DEBUG ("driver only supports external import of fourcc %"
759             GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
760       }
761       ret = !external_only[i];
762       g_free (modifiers);
763       g_free (external_only);
764       return ret;
765     }
766   }
767   GST_DEBUG ("driver only supports non-linear import of fourcc %"
768       GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
769   g_free (modifiers);
770   g_free (external_only);
771   return FALSE;
772 }
773
774 /**
775  * gst_egl_image_from_dmabuf_direct:
776  * @context: a #GstGLContext (must be an EGL context)
777  * @fd: Array of DMABuf file descriptors
778  * @offset: Array of offsets, relative to the DMABuf
779  * @in_info: the #GstVideoInfo
780  *
781  * Creates an EGL image that imports the dmabuf FD. The dmabuf data
782  * is passed directly as the format described in in_info. This is
783  * useful if the hardware is capable of performing color space conversions
784  * internally. The appropriate DRM format is picked, and the EGL image
785  * is created with this DRM format.
786  *
787  * Another notable difference to gst_egl_image_from_dmabuf()
788  * is that this function creates one EGL image for all planes, not one for
789  * a single plane.
790  *
791  * Returns: a #GstEGLImage wrapping @dmabuf or %NULL on failure
792  */
793 GstEGLImage *
794 gst_egl_image_from_dmabuf_direct (GstGLContext * context,
795     gint * fd, gsize * offset, GstVideoInfo * in_info)
796 {
797
798   EGLImageKHR img;
799   guint n_planes = GST_VIDEO_INFO_N_PLANES (in_info);
800   gint fourcc;
801   gint i;
802   gboolean with_modifiers;
803
804   /* Explanation of array length:
805    * - 6 plane independent values are at the start (width, height, format FourCC)
806    * - 10 values per plane, and there are up to MAX_NUM_DMA_BUF_PLANES planes
807    * - 4 values for color space and range
808    * - 1 extra value for the EGL_NONE sentinel
809    */
810   guintptr attribs[41];         /* 6 + 10 * 3 + 4 + 1 */
811   gint atti = 0;
812
813   fourcc = _drm_direct_fourcc_from_info (in_info);
814   if (fourcc == -1)
815     return NULL;
816
817   if (!_gst_egl_image_check_dmabuf_direct (context, fourcc))
818     return NULL;
819
820   with_modifiers = gst_gl_context_check_feature (context,
821       "EGL_EXT_image_dma_buf_import_with_modifiers");
822
823   /* EGL DMABuf importation supports a maximum of 3 planes */
824   if (G_UNLIKELY (n_planes > 3))
825     return NULL;
826
827   attribs[atti++] = EGL_WIDTH;
828   attribs[atti++] = GST_VIDEO_INFO_WIDTH (in_info);
829   attribs[atti++] = EGL_HEIGHT;
830   attribs[atti++] = GST_VIDEO_INFO_HEIGHT (in_info);
831   attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
832   attribs[atti++] = fourcc;
833
834   /* first plane */
835   {
836     attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT;
837     attribs[atti++] = fd[0];
838     attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
839     attribs[atti++] = offset[0];
840     attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
841     attribs[atti++] = in_info->stride[0];
842     if (with_modifiers) {
843       attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
844       attribs[atti++] = DRM_FORMAT_MOD_LINEAR & 0xffffffff;
845       attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
846       attribs[atti++] = (DRM_FORMAT_MOD_LINEAR >> 32) & 0xffffffff;
847     }
848   }
849
850   /* second plane */
851   if (n_planes >= 2) {
852     attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT;
853     attribs[atti++] = fd[1];
854     attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
855     attribs[atti++] = offset[1];
856     attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
857     attribs[atti++] = in_info->stride[1];
858     if (with_modifiers) {
859       attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT;
860       attribs[atti++] = DRM_FORMAT_MOD_LINEAR & 0xffffffff;
861       attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT;
862       attribs[atti++] = (DRM_FORMAT_MOD_LINEAR >> 32) & 0xffffffff;
863     }
864   }
865
866   /* third plane */
867   if (n_planes == 3) {
868     attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT;
869     attribs[atti++] = fd[2];
870     attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
871     attribs[atti++] = offset[2];
872     attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
873     attribs[atti++] = in_info->stride[2];
874     if (with_modifiers) {
875       attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT;
876       attribs[atti++] = DRM_FORMAT_MOD_LINEAR & 0xffffffff;
877       attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT;
878       attribs[atti++] = (DRM_FORMAT_MOD_LINEAR >> 32) & 0xffffffff;
879     }
880   }
881
882   {
883     uint32_t color_space;
884     switch (in_info->colorimetry.matrix) {
885       case GST_VIDEO_COLOR_MATRIX_BT601:
886         color_space = EGL_ITU_REC601_EXT;
887         break;
888       case GST_VIDEO_COLOR_MATRIX_BT709:
889         color_space = EGL_ITU_REC709_EXT;
890         break;
891       case GST_VIDEO_COLOR_MATRIX_BT2020:
892         color_space = EGL_ITU_REC2020_EXT;
893         break;
894       default:
895         color_space = 0;
896         break;
897     }
898     if (color_space != 0) {
899       attribs[atti++] = EGL_YUV_COLOR_SPACE_HINT_EXT;
900       attribs[atti++] = color_space;
901     }
902   }
903
904   {
905     uint32_t range;
906     switch (in_info->colorimetry.range) {
907       case GST_VIDEO_COLOR_RANGE_0_255:
908         range = EGL_YUV_FULL_RANGE_EXT;
909         break;
910       case GST_VIDEO_COLOR_RANGE_16_235:
911         range = EGL_YUV_NARROW_RANGE_EXT;
912         break;
913       default:
914         range = 0;
915         break;
916     }
917     if (range != 0) {
918       attribs[atti++] = EGL_SAMPLE_RANGE_HINT_EXT;
919       attribs[atti++] = range;
920     }
921   }
922
923   /* Add the EGL_NONE sentinel */
924   attribs[atti] = EGL_NONE;
925   g_assert (atti <= G_N_ELEMENTS (attribs) - 1);
926
927   for (i = 0; i < atti; i++)
928     GST_LOG ("attr %i: %" G_GINTPTR_FORMAT, i, attribs[i]);
929
930   img = _gst_egl_image_create (context, EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
931   if (!img) {
932     GST_WARNING ("eglCreateImage failed: %s",
933         gst_egl_get_error_string (eglGetError ()));
934     return NULL;
935   }
936
937   return gst_egl_image_new_wrapped (context, img, GST_GL_RGBA, NULL,
938       (GstEGLImageDestroyNotify) _destroy_egl_image);
939 }
940
941 gboolean
942 gst_egl_image_export_dmabuf (GstEGLImage * image, int *fd, gint * stride,
943     gsize * offset)
944 {
945   EGLBoolean (*gst_eglExportDMABUFImageQueryMESA) (EGLDisplay dpy,
946       EGLImageKHR image, int *fourcc, int *num_planes,
947       EGLuint64KHR * modifiers);
948   EGLBoolean (*gst_eglExportDMABUFImageMESA) (EGLDisplay dpy, EGLImageKHR image,
949       int *fds, EGLint * strides, EGLint * offsets);
950   GstGLDisplayEGL *display_egl;
951   EGLDisplay egl_display = EGL_DEFAULT_DISPLAY;
952   int num_planes = 0;
953   int egl_fd = 0;
954   EGLint egl_stride = 0;
955   EGLint egl_offset = 0;
956
957   gst_eglExportDMABUFImageQueryMESA =
958       gst_gl_context_get_proc_address (image->context,
959       "eglExportDMABUFImageQueryMESA");
960   gst_eglExportDMABUFImageMESA =
961       gst_gl_context_get_proc_address (image->context,
962       "eglExportDMABUFImageMESA");
963
964   if (!gst_eglExportDMABUFImageQueryMESA || !gst_eglExportDMABUFImageMESA)
965     return FALSE;
966
967   display_egl =
968       (GstGLDisplayEGL *) gst_gl_display_egl_from_gl_display (image->
969       context->display);
970   if (!display_egl) {
971     GST_WARNING_OBJECT (image->context,
972         "Failed to retrieve GstGLDisplayEGL from %" GST_PTR_FORMAT,
973         image->context->display);
974     return FALSE;
975   }
976   egl_display =
977       (EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (display_egl));
978   gst_object_unref (display_egl);
979
980   if (!gst_eglExportDMABUFImageQueryMESA (egl_display, image->image,
981           NULL, &num_planes, NULL))
982     return FALSE;
983
984   /* Don't allow multi-plane dmabufs */
985   if (num_planes > 1)
986     return FALSE;
987
988   if (!gst_eglExportDMABUFImageMESA (egl_display, image->image, &egl_fd,
989           &egl_stride, &egl_offset))
990     return FALSE;
991
992   *fd = egl_fd;
993   *stride = egl_stride;
994   *offset = egl_offset;
995
996   return TRUE;
997 }
998
999 #endif /* GST_GL_HAVE_DMABUF */