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