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