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