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