/* * GStreamer * Copyright (C) 2012 Collabora Ltd. * @author: Sebastian Dröge * Copyright (C) 2014 Julien Isorce * Copyright (C) 2016 Matthew Waters * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * SECTION:gsteglimage * @short_description: EGLImage abstraction * @title: GstEGLImage * @see_also: #GstGLMemoryEGL, #GstGLContext * * #GstEGLImage represents and holds an `EGLImage` handle. * * A #GstEGLImage can be created from a dmabuf with gst_egl_image_from_dmabuf(), * or gst_egl_image_from_dmabuf_direct(), or #GstGLMemoryEGL provides a * #GstAllocator to allocate `EGLImage`'s bound to and OpenGL texture. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gsteglimage.h" #include "gsteglimage_private.h" #include #include #include #include "gst/gl/egl/gstegl.h" #include "gst/gl/egl/gstglcontext_egl.h" #include "gst/gl/egl/gstgldisplay_egl.h" #if GST_GL_HAVE_DMABUF #include #ifdef HAVE_LIBDRM #include #endif #ifndef DRM_FORMAT_R8 #define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') #endif #ifndef DRM_FORMAT_RG88 #define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') #endif #ifndef DRM_FORMAT_GR88 #define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') #endif #ifndef DRM_FORMAT_NV24 #define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4') #endif #endif #ifndef DRM_FORMAT_BGRA1010102 #define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') #endif #ifndef DRM_FORMAT_RGBA1010102 #define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') #endif #ifndef DRM_FORMAT_R16 #define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') #endif #ifndef DRM_FORMAT_GR1616 #define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') #endif #ifndef DRM_FORMAT_RG1616 #define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') #endif #ifndef DRM_FORMAT_ABGR2101010 #define DRM_FORMAT_ABGR2101010 fourcc_code('A', 'B', '3', '0') #endif #ifndef DRM_FORMAT_RGBA1010102 #define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') #endif #ifndef DRM_FORMAT_ABGR16161616 #define DRM_FORMAT_ABGR16161616 fourcc_code('A', 'B', '4', '8') #endif #ifndef EGL_LINUX_DMA_BUF_EXT #define EGL_LINUX_DMA_BUF_EXT 0x3270 #endif #ifndef EGL_LINUX_DRM_FOURCC_EXT #define EGL_LINUX_DRM_FOURCC_EXT 0x3271 #endif #ifndef EGL_DMA_BUF_PLANE0_FD_EXT #define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272 #endif #ifndef EGL_DMA_BUF_PLANE0_OFFSET_EXT #define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273 #endif #ifndef EGL_DMA_BUF_PLANE0_PITCH_EXT #define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274 #endif #ifndef EGL_DMA_BUF_PLANE1_FD_EXT #define EGL_DMA_BUF_PLANE1_FD_EXT 0x3275 #endif #ifndef EGL_DMA_BUF_PLANE1_OFFSET_EXT #define EGL_DMA_BUF_PLANE1_OFFSET_EXT 0x3276 #endif #ifndef EGL_DMA_BUF_PLANE1_PITCH_EXT #define EGL_DMA_BUF_PLANE1_PITCH_EXT 0x3277 #endif #ifndef EGL_DMA_BUF_PLANE2_FD_EXT #define EGL_DMA_BUF_PLANE2_FD_EXT 0x3278 #endif #ifndef EGL_DMA_BUF_PLANE2_OFFSET_EXT #define EGL_DMA_BUF_PLANE2_OFFSET_EXT 0x3279 #endif #ifndef EGL_DMA_BUF_PLANE2_PITCH_EXT #define EGL_DMA_BUF_PLANE2_PITCH_EXT 0x327A #endif #ifndef DRM_FORMAT_MOD_LINEAR #define DRM_FORMAT_MOD_LINEAR 0ULL #endif #ifndef EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT #define EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT 0x3443 #endif #ifndef EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT #define EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT 0x3444 #endif #ifndef EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT #define EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT 0x3445 #endif #ifndef EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT #define EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT 0x3446 #endif #ifndef EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT #define EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT 0x3447 #endif #ifndef EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT #define EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT 0x3448 #endif #ifndef EGL_ITU_REC601_EXT #define EGL_ITU_REC601_EXT 0x327F #endif #ifndef EGL_ITU_REC709_EXT #define EGL_ITU_REC709_EXT 0x3280 #endif #ifndef EGL_ITU_REC2020_EXT #define EGL_ITU_REC2020_EXT 0x3281 #endif #ifndef EGL_SAMPLE_RANGE_HINT_EXT #define EGL_SAMPLE_RANGE_HINT_EXT 0x327C #endif #ifndef EGL_YUV_COLOR_SPACE_HINT_EXT #define EGL_YUV_COLOR_SPACE_HINT_EXT 0x327B #endif #ifndef EGL_YUV_FULL_RANGE_EXT #define EGL_YUV_FULL_RANGE_EXT 0x3282 #endif #ifndef EGL_YUV_NARROW_RANGE_EXT #define EGL_YUV_NARROW_RANGE_EXT 0x3283 #endif GST_DEFINE_MINI_OBJECT_TYPE (GstEGLImage, gst_egl_image); #ifndef GST_DISABLE_GST_DEBUG #define GST_CAT_DEFAULT gst_egl_image_ensure_debug_category() static GstDebugCategory * gst_egl_image_ensure_debug_category (void) { static gsize cat_gonce = 0; if (g_once_init_enter (&cat_gonce)) { GstDebugCategory *cat = NULL; GST_DEBUG_CATEGORY_INIT (cat, "gleglimage", 0, "EGLImage wrapper"); g_once_init_leave (&cat_gonce, (gsize) cat); } return (GstDebugCategory *) cat_gonce; } #endif /* GST_DISABLE_GST_DEBUG */ /** * gst_egl_image_get_image: * @image: a #GstEGLImage * * Returns: the `EGLImage` of @image */ gpointer gst_egl_image_get_image (GstEGLImage * image) { g_return_val_if_fail (GST_IS_EGL_IMAGE (image), EGL_NO_IMAGE_KHR); return image->image; } static void _gst_egl_image_free_thread (GstGLContext * context, GstEGLImage * image) { if (image->destroy_notify) image->destroy_notify (image, image->destroy_data); } static void _gst_egl_image_free (GstMiniObject * object) { GstEGLImage *image = GST_EGL_IMAGE (object); if (image->context) { gst_gl_context_thread_add (GST_GL_CONTEXT (image->context), (GstGLContextThreadFunc) _gst_egl_image_free_thread, image); gst_object_unref (image->context); } g_free (image); } static GstMiniObject * _gst_egl_image_copy (GstMiniObject * obj) { return gst_mini_object_ref (obj); } /** * gst_egl_image_new_wrapped: * @context: a #GstGLContext (must be an EGL context) * @image: the image to wrap * @format: the #GstGLFormat * @user_data: user data * @user_data_destroy: (destroy user_data) (scope async): called when @user_data is no longer needed * * Returns: a new #GstEGLImage wrapping @image */ GstEGLImage * gst_egl_image_new_wrapped (GstGLContext * context, gpointer image, GstGLFormat format, gpointer user_data, GstEGLImageDestroyNotify user_data_destroy) { GstEGLImage *img = NULL; g_return_val_if_fail (context != NULL, NULL); g_return_val_if_fail ((gst_gl_context_get_gl_platform (context) & GST_GL_PLATFORM_EGL) != 0, NULL); g_return_val_if_fail (image != EGL_NO_IMAGE_KHR, NULL); img = g_new0 (GstEGLImage, 1); gst_mini_object_init (GST_MINI_OBJECT_CAST (img), 0, GST_TYPE_EGL_IMAGE, (GstMiniObjectCopyFunction) _gst_egl_image_copy, NULL, (GstMiniObjectFreeFunction) _gst_egl_image_free); img->context = gst_object_ref (context); img->image = image; img->format = format; img->destroy_data = user_data; img->destroy_notify = user_data_destroy; return img; } static EGLImageKHR _gst_egl_image_create (GstGLContext * context, guint target, EGLClientBuffer buffer, guintptr * attribs) { EGLDisplay egl_display = EGL_DEFAULT_DISPLAY; EGLContext egl_context = EGL_NO_CONTEXT; EGLImageKHR img = EGL_NO_IMAGE_KHR; GstGLDisplayEGL *display_egl; guint attrib_len = 0; display_egl = gst_gl_display_egl_from_gl_display (context->display); if (!display_egl) { GST_WARNING_OBJECT (context, "Failed to retrieve GstGLDisplayEGL from %" GST_PTR_FORMAT, context->display); return EGL_NO_IMAGE_KHR; } egl_display = (EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (display_egl)); gst_object_unref (display_egl); if (target != EGL_LINUX_DMA_BUF_EXT) egl_context = (EGLContext) gst_gl_context_get_gl_context (context); if (attribs) while (attribs[attrib_len++] != EGL_NONE) { } #ifdef EGL_VERSION_1_5 gint plat_major, plat_minor; gst_gl_context_get_gl_platform_version (context, &plat_major, &plat_minor); if (GST_GL_CHECK_GL_VERSION (plat_major, plat_minor, 1, 5)) { EGLImageKHR (*gst_eglCreateImage) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib * attrib_list); EGLAttrib *egl_attribs = NULL; guint i; gst_eglCreateImage = gst_gl_context_get_proc_address (context, "eglCreateImage"); if (!gst_eglCreateImage) { GST_ERROR_OBJECT (context, "\"eglCreateImage\" not exposed by the " "implementation as required by EGL >= 1.5"); return EGL_NO_IMAGE_KHR; } if (attribs) { egl_attribs = g_new0 (EGLAttrib, attrib_len); for (i = 0; i < attrib_len; i++) egl_attribs[i] = (EGLAttrib) attribs[i]; } img = gst_eglCreateImage (egl_display, egl_context, target, buffer, egl_attribs); g_free (egl_attribs); } else #endif if (gst_gl_context_check_feature (context, "EGL_KHR_image_base")) { EGLImageKHR (*gst_eglCreateImageKHR) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint * attrib_list); EGLint *egl_attribs = NULL; gint i; gst_eglCreateImageKHR = gst_gl_context_get_proc_address (context, "eglCreateImageKHR"); if (!gst_eglCreateImageKHR) { GST_ERROR_OBJECT (context, "\"eglCreateImageKHR\" not exposed by the " "implementation as required by EGL_KHR_image_base"); return EGL_NO_IMAGE_KHR; } if (attribs) { egl_attribs = g_new0 (EGLint, attrib_len); for (i = 0; i < attrib_len; i++) egl_attribs[i] = (EGLint) attribs[i]; } img = gst_eglCreateImageKHR (egl_display, egl_context, target, buffer, egl_attribs); g_free (egl_attribs); } else { GST_INFO_OBJECT (context, "EGLImage creation not supported"); return EGL_NO_IMAGE_KHR; } return img; } static void _gst_egl_image_destroy (GstGLContext * context, EGLImageKHR image) { EGLBoolean (*gst_eglDestroyImage) (EGLDisplay dpy, EGLImageKHR image); EGLDisplay egl_display = EGL_DEFAULT_DISPLAY; GstGLDisplayEGL *display_egl; #ifdef EGL_VERSION_1_5 gint plat_major, plat_minor; gst_gl_context_get_gl_platform_version (context, &plat_major, &plat_minor); if (GST_GL_CHECK_GL_VERSION (plat_major, plat_minor, 1, 5)) { gst_eglDestroyImage = gst_gl_context_get_proc_address (context, "eglDestroyImage"); if (!gst_eglDestroyImage) { GST_ERROR_OBJECT (context, "\"eglDestroyImage\" not exposed by the " "implementation as required by EGL >= 1.5"); return; } } else #endif if (gst_gl_context_check_feature (context, "EGL_KHR_image_base")) { gst_eglDestroyImage = gst_gl_context_get_proc_address (context, "eglDestroyImageKHR"); if (!gst_eglDestroyImage) { GST_ERROR_OBJECT (context, "\"eglDestroyImage\" not exposed by the " "implementation as required by EGL_KHR_image_base"); return; } } else { GST_ERROR_OBJECT (context, "Destruction of EGLImage not supported."); return; } display_egl = gst_gl_display_egl_from_gl_display (context->display); if (!display_egl) { GST_WARNING_OBJECT (context, "Failed to retrieve GstGLDisplayEGL from %" GST_PTR_FORMAT, context->display); return; } egl_display = (EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (display_egl)); gst_object_unref (display_egl); if (!gst_eglDestroyImage (egl_display, image)) GST_WARNING_OBJECT (context, "eglDestroyImage failed"); } static void _destroy_egl_image (GstEGLImage * image, gpointer user_data) { _gst_egl_image_destroy (image->context, image->image); } /** * gst_egl_image_from_texture: * @context: a #GstGLContext (must be an EGL context) * @gl_mem: a #GstGLMemory * @attribs: additional attributes to add to the `eglCreateImage`() call. * * Returns: (transfer full) (nullable): a #GstEGLImage wrapping @gl_mem or %NULL on failure */ GstEGLImage * gst_egl_image_from_texture (GstGLContext * context, GstGLMemory * gl_mem, guintptr * attribs) { EGLenum egl_target; EGLImageKHR img; if (gl_mem->tex_target != GST_GL_TEXTURE_TARGET_2D) { GST_FIXME_OBJECT (context, "Only know how to create EGLImage's from 2D " "textures"); return NULL; } egl_target = EGL_GL_TEXTURE_2D_KHR; img = _gst_egl_image_create (context, egl_target, (EGLClientBuffer) (guintptr) gl_mem->tex_id, attribs); if (!img) return NULL; return gst_egl_image_new_wrapped (context, img, gl_mem->tex_format, NULL, (GstEGLImageDestroyNotify) _destroy_egl_image); } #if GST_GL_HAVE_DMABUF /* * GStreamer format descriptions differ from DRM formats as the representation * is relative to a register, hence in native endianness. To reduce the driver * requirement, we only import with a subset of texture formats and use * shaders to convert. This way we avoid having to use external texture * target. */ static int _drm_rgba_fourcc_from_format (GstVideoFormat format, int plane, GstGLFormat * out_format) { #if G_BYTE_ORDER == G_LITTLE_ENDIAN const gint rgba_fourcc = DRM_FORMAT_ABGR8888; const gint rgb_fourcc = DRM_FORMAT_BGR888; const gint rg_fourcc = DRM_FORMAT_GR88; const gint rg16_fourcc = DRM_FORMAT_GR1616; const gint rgb10a2_fourcc = DRM_FORMAT_ABGR2101010; #else const gint rgba_fourcc = DRM_FORMAT_RGBA8888; const gint rgb_fourcc = DRM_FORMAT_RGB888; const gint rg_fourcc = DRM_FORMAT_RG88; const gint rg16_fourcc = DRM_FORMAT_RG1616; const gint rgb10a2_fourcc = DRM_FORMAT_RGBA1010102; #endif GST_DEBUG ("Getting DRM fourcc for %s plane %i", gst_video_format_to_string (format), plane); switch (format) { case GST_VIDEO_FORMAT_RGB16: case GST_VIDEO_FORMAT_BGR16: *out_format = GST_GL_RGB565; return DRM_FORMAT_RGB565; case GST_VIDEO_FORMAT_RGB: case GST_VIDEO_FORMAT_BGR: *out_format = GST_GL_RGB; return rgb_fourcc; case GST_VIDEO_FORMAT_RGBA: case GST_VIDEO_FORMAT_RGBx: case GST_VIDEO_FORMAT_BGRA: case GST_VIDEO_FORMAT_BGRx: case GST_VIDEO_FORMAT_ARGB: case GST_VIDEO_FORMAT_xRGB: case GST_VIDEO_FORMAT_ABGR: case GST_VIDEO_FORMAT_xBGR: case GST_VIDEO_FORMAT_AYUV: case GST_VIDEO_FORMAT_VUYA: *out_format = GST_GL_RGBA; return rgba_fourcc; case GST_VIDEO_FORMAT_GRAY8: *out_format = GST_GL_RED; return DRM_FORMAT_R8; case GST_VIDEO_FORMAT_YUY2: case GST_VIDEO_FORMAT_UYVY: case GST_VIDEO_FORMAT_GRAY16_LE: case GST_VIDEO_FORMAT_GRAY16_BE: *out_format = GST_GL_RG; return rg_fourcc; case GST_VIDEO_FORMAT_NV12: case GST_VIDEO_FORMAT_NV21: case GST_VIDEO_FORMAT_NV16: case GST_VIDEO_FORMAT_NV61: case GST_VIDEO_FORMAT_NV12_16L32S: case GST_VIDEO_FORMAT_NV12_4L4: *out_format = plane == 0 ? GST_GL_RED : GST_GL_RG; return plane == 0 ? DRM_FORMAT_R8 : rg_fourcc; case GST_VIDEO_FORMAT_I420: case GST_VIDEO_FORMAT_YV12: case GST_VIDEO_FORMAT_Y41B: case GST_VIDEO_FORMAT_Y42B: case GST_VIDEO_FORMAT_Y444: *out_format = GST_GL_RED; return DRM_FORMAT_R8; case GST_VIDEO_FORMAT_BGR10A2_LE: *out_format = GST_GL_RGB10_A2; return DRM_FORMAT_BGRA1010102; case GST_VIDEO_FORMAT_RGB10A2_LE: *out_format = GST_GL_RGB10_A2; return DRM_FORMAT_RGBA1010102; case GST_VIDEO_FORMAT_P010_10LE: case GST_VIDEO_FORMAT_P012_LE: case GST_VIDEO_FORMAT_P016_LE: *out_format = plane == 0 ? GST_GL_R16 : GST_GL_RG16; return plane == 0 ? DRM_FORMAT_R16 : DRM_FORMAT_GR1616; case GST_VIDEO_FORMAT_P010_10BE: case GST_VIDEO_FORMAT_P012_BE: case GST_VIDEO_FORMAT_P016_BE: *out_format = plane == 0 ? GST_GL_R16 : GST_GL_RG16; return plane == 0 ? DRM_FORMAT_R16 : DRM_FORMAT_RG1616; case GST_VIDEO_FORMAT_AV12: *out_format = plane == 1 ? GST_GL_RED : GST_GL_RG; return plane == 1 ? rg_fourcc : DRM_FORMAT_R8; case GST_VIDEO_FORMAT_Y210: *out_format = GST_GL_RG16; return rg16_fourcc; case GST_VIDEO_FORMAT_Y212_LE: *out_format = GST_GL_RG16; return DRM_FORMAT_GR1616; case GST_VIDEO_FORMAT_Y212_BE: *out_format = GST_GL_RG16; return DRM_FORMAT_RG1616; case GST_VIDEO_FORMAT_Y410: *out_format = GST_GL_RGB10_A2; return rgb10a2_fourcc; case GST_VIDEO_FORMAT_Y412_LE: *out_format = GST_GL_RGBA16; return DRM_FORMAT_ABGR16161616; default: GST_ERROR ("Unsupported format for DMABuf."); return -1; } } static gint get_egl_stride (const GstVideoInfo * info, gint plane) { const GstVideoFormatInfo *finfo = info->finfo; gint stride = info->stride[plane]; if (!GST_VIDEO_FORMAT_INFO_IS_TILED (finfo)) return stride; return GST_VIDEO_TILE_X_TILES (stride) * GST_VIDEO_FORMAT_INFO_TILE_STRIDE (finfo, plane); } /** * gst_egl_image_from_dmabuf: * @context: a #GstGLContext (must be an EGL context) * @dmabuf: the DMA-Buf file descriptor * @in_info: the #GstVideoInfo in @dmabuf * @plane: the plane in @in_info to create and #GstEGLImage for * @offset: the byte-offset in the data * * Creates an EGL image that imports the dmabuf FD. The dmabuf data * is passed as RGBA data. Shaders later take this "RGBA" data and * convert it from its true format (described by in_info) to actual * RGBA output. For example, with I420, three EGL images are created, * one for each @plane, each EGL image with a single-channel R format. * With NV12, two EGL images are created, one with R format, one * with RG format etc. * * Returns: (nullable): a #GstEGLImage wrapping @dmabuf or %NULL on failure */ GstEGLImage * gst_egl_image_from_dmabuf (GstGLContext * context, gint dmabuf, const GstVideoInfo * in_info, gint plane, gsize offset) { gint comp[GST_VIDEO_MAX_COMPONENTS]; GstGLFormat format = 0; guintptr attribs[17]; EGLImageKHR img; gint atti = 0; gint fourcc; gint i; gboolean with_modifiers; gst_video_format_info_component (in_info->finfo, plane, comp); fourcc = _drm_rgba_fourcc_from_format (GST_VIDEO_INFO_FORMAT (in_info), plane, &format); GST_DEBUG ("fourcc %.4s (%d) plane %d (%dx%d)", (char *) &fourcc, fourcc, plane, GST_VIDEO_INFO_COMP_WIDTH (in_info, comp[0]), GST_VIDEO_INFO_COMP_HEIGHT (in_info, comp[0])); with_modifiers = gst_gl_context_check_feature (context, "EGL_EXT_image_dma_buf_import_modifiers"); attribs[atti++] = EGL_WIDTH; attribs[atti++] = GST_VIDEO_INFO_COMP_WIDTH (in_info, comp[0]); attribs[atti++] = EGL_HEIGHT; attribs[atti++] = GST_VIDEO_INFO_COMP_HEIGHT (in_info, comp[0]); attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; attribs[atti++] = fourcc; attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT; attribs[atti++] = dmabuf; attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; attribs[atti++] = offset; attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; attribs[atti++] = get_egl_stride (in_info, plane); if (with_modifiers) { attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; attribs[atti++] = DRM_FORMAT_MOD_LINEAR & 0xffffffff; attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; attribs[atti++] = (DRM_FORMAT_MOD_LINEAR >> 32) & 0xffffffff; } attribs[atti] = EGL_NONE; g_assert (atti <= G_N_ELEMENTS (attribs) - 1); for (i = 0; i < atti; i++) GST_LOG ("attr %i: %" G_GINTPTR_FORMAT, i, attribs[i]); img = _gst_egl_image_create (context, EGL_LINUX_DMA_BUF_EXT, NULL, attribs); if (!img) { GST_WARNING ("eglCreateImage failed: %s", gst_egl_get_error_string (eglGetError ())); return NULL; } return gst_egl_image_new_wrapped (context, img, format, NULL, (GstEGLImageDestroyNotify) _destroy_egl_image); } /** * gst_egl_image_check_dmabuf_direct: * @context: a #GstGLContext (must be an EGL context) * @in_info: a #GstVideoInfo * @target: a #GstGLTextureTarget * * Checks whether the video format specified by the given #GstVideoInfo is a * supported texture format for the given @target. * * Returns: %TRUE if the format is supported. */ gboolean gst_egl_image_check_dmabuf_direct (GstGLContext * context, const GstVideoInfo * in_info, GstGLTextureTarget target) { GstVideoInfoDmaDrm in_info_dma; if (!gst_video_info_dma_drm_from_video_info (&in_info_dma, in_info, DRM_FORMAT_MOD_LINEAR)) return FALSE; return gst_egl_image_check_dmabuf_direct_with_dma_drm (context, &in_info_dma, target); } /** * gst_egl_image_check_dmabuf_direct_with_dma_drm: * @context: a #GstGLContext (must be an EGL context) * @in_info_dma: a #GstVideoInfoDmaDrm * @target: a #GstGLTextureTarget * * Checks whether the video format specified by the given #GstVideoInfoDmaDrm is * a supported texture format for the given @target. * * Returns: %TRUE if the format is supported. * * Since: 1.24 */ gboolean gst_egl_image_check_dmabuf_direct_with_dma_drm (GstGLContext * context, const GstVideoInfoDmaDrm * in_info_dma, GstGLTextureTarget target) { GstGLDmaModifier *mods, linear_modifier = { 0, FALSE }; gsize len; const GArray *modifiers = NULL; gint32 fourcc; int i; gboolean ret = FALSE; fourcc = in_info_dma->drm_fourcc; if (fourcc == DRM_FORMAT_INVALID) { GST_INFO ("Unsupported format for direct DMABuf."); return FALSE; } if (!gst_gl_context_egl_get_format_modifiers (context, fourcc, &modifiers)) { GST_DEBUG ("driver does not support importing fourcc %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc)); return FALSE; } if (modifiers) { mods = (GstGLDmaModifier *) modifiers->data; len = modifiers->len; } else { mods = &linear_modifier; len = 1; } for (i = 0; i < len; ++i) { GstGLDmaModifier *modifier = &mods[i]; if (modifier->modifier == in_info_dma->drm_modifier) { if (modifier->external_only) { GST_DEBUG ("driver only supports external import of fourcc %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc)); } ret = !modifier->external_only || (target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES); break; } } if (!ret) { GST_DEBUG ("driver only supports non-linear import of fourcc %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc)); } return ret; } /** * gst_egl_image_from_dmabuf_direct_target: * @context: a #GstGLContext (must be an EGL context) * @fd: Array of DMABuf file descriptors * @offset: Array of offsets, relative to the DMABuf * @in_info: the #GstVideoInfo * @target: GL texture target this GstEGLImage is intended for * * Creates an EGL image that imports the dmabuf FD. The dmabuf data * is passed directly as the format described in @in_info. This is * useful if the hardware is capable of performing color space conversions * internally. The appropriate DRM format is picked, and the EGL image * is created with this DRM format. * * Another notable difference to gst_egl_image_from_dmabuf() * is that this function creates one EGL image for all planes, not one for * a single plane. * * Returns: (nullable): a #GstEGLImage wrapping @dmabuf or %NULL on failure * * Since: 1.18 */ GstEGLImage * gst_egl_image_from_dmabuf_direct_target (GstGLContext * context, gint * fd, const gsize * offset, const GstVideoInfo * in_info, GstGLTextureTarget target) { GstVideoInfoDmaDrm in_info_dma; if (!gst_video_info_dma_drm_from_video_info (&in_info_dma, in_info, DRM_FORMAT_MOD_LINEAR)) return NULL; return gst_egl_image_from_dmabuf_direct_target_with_dma_drm (context, GST_VIDEO_INFO_N_PLANES (in_info), fd, offset, &in_info_dma, target); } /** * gst_egl_image_from_dmabuf_direct_target_with_dma_drm: * @context: a #GstGLContext (must be an EGL context) * @n_planes: number of planes (obtained from a #GstVideoMeta) * @fd: Array of DMABuf file descriptors * @offset: Array of offsets, relative to the DMABuf * @in_info_dma: the #GstVideoInfoDmaDrm * @target: GL texture target this GstEGLImage is intended for * * Creates an EGL image that imports the dmabuf FD. The dmabuf data is passed * directly as the format described in @in_info. This is useful if the hardware * is capable of performing color space conversions internally. * * Another notable difference to gst_egl_image_from_dmabuf() is that this * function creates one EGL image for all planes, not one for a single plane. * * Returns: (nullable): a #GstEGLImage wrapping @dmabuf or %NULL on failure * * Since: 1.24 */ GstEGLImage * gst_egl_image_from_dmabuf_direct_target_with_dma_drm (GstGLContext * context, guint n_planes, gint * fd, const gsize * offset, const GstVideoInfoDmaDrm * in_info_dma, GstGLTextureTarget target) { EGLImageKHR img; const GstVideoInfo *in_info = &in_info_dma->vinfo; guint32 fourcc; guint64 modifier; gint i; gboolean with_modifiers; /* Explanation of array length: * - 6 plane independent values are at the start (width, height, format FourCC) * - 10 values per plane, and there are up to MAX_NUM_DMA_BUF_PLANES planes * - 4 values for color space and range * - 1 extra value for the EGL_NONE sentinel */ guintptr attribs[41]; /* 6 + 10 * 3 + 4 + 1 */ gint atti = 0; if (!gst_egl_image_check_dmabuf_direct_with_dma_drm (context, in_info_dma, target)) return NULL; fourcc = in_info_dma->drm_fourcc; modifier = in_info_dma->drm_modifier; with_modifiers = gst_gl_context_check_feature (context, "EGL_EXT_image_dma_buf_import_modifiers"); if (!with_modifiers && modifier != DRM_FORMAT_MOD_LINEAR) return NULL; /* EGL DMABuf importation supports a maximum of 3 planes */ if (G_UNLIKELY (n_planes > 3)) return NULL; attribs[atti++] = EGL_WIDTH; attribs[atti++] = GST_VIDEO_INFO_WIDTH (in_info); attribs[atti++] = EGL_HEIGHT; attribs[atti++] = GST_VIDEO_INFO_HEIGHT (in_info); attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; attribs[atti++] = fourcc; /* first plane */ { attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT; attribs[atti++] = fd[0]; attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; attribs[atti++] = offset[0]; attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; attribs[atti++] = get_egl_stride (in_info, 0); if (with_modifiers && modifier != DRM_FORMAT_MOD_INVALID) { attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; attribs[atti++] = modifier & 0xffffffff; attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; attribs[atti++] = (modifier >> 32) & 0xffffffff; } } /* second plane */ if (n_planes >= 2) { attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT; attribs[atti++] = fd[1]; attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT; attribs[atti++] = offset[1]; attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT; attribs[atti++] = get_egl_stride (in_info, 1); if (with_modifiers && modifier != DRM_FORMAT_MOD_INVALID) { attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT; attribs[atti++] = modifier & 0xffffffff; attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT; attribs[atti++] = (modifier >> 32) & 0xffffffff; } } /* third plane */ if (n_planes == 3) { attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT; attribs[atti++] = fd[2]; attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT; attribs[atti++] = offset[2]; attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT; attribs[atti++] = get_egl_stride (in_info, 2); if (with_modifiers && modifier != DRM_FORMAT_MOD_INVALID) { attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT; attribs[atti++] = modifier & 0xffffffff; attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT; attribs[atti++] = (modifier >> 32) & 0xffffffff; } } { uint32_t color_space; switch (in_info->colorimetry.matrix) { case GST_VIDEO_COLOR_MATRIX_BT601: color_space = EGL_ITU_REC601_EXT; break; case GST_VIDEO_COLOR_MATRIX_BT709: color_space = EGL_ITU_REC709_EXT; break; case GST_VIDEO_COLOR_MATRIX_BT2020: color_space = EGL_ITU_REC2020_EXT; break; default: color_space = 0; break; } if (color_space != 0) { attribs[atti++] = EGL_YUV_COLOR_SPACE_HINT_EXT; attribs[atti++] = color_space; } } { uint32_t range; switch (in_info->colorimetry.range) { case GST_VIDEO_COLOR_RANGE_0_255: range = EGL_YUV_FULL_RANGE_EXT; break; case GST_VIDEO_COLOR_RANGE_16_235: range = EGL_YUV_NARROW_RANGE_EXT; break; default: range = 0; break; } if (range != 0) { attribs[atti++] = EGL_SAMPLE_RANGE_HINT_EXT; attribs[atti++] = range; } } /* Add the EGL_NONE sentinel */ attribs[atti] = EGL_NONE; g_assert (atti <= G_N_ELEMENTS (attribs) - 1); for (i = 0; i < atti; i++) GST_LOG ("attr %i: %" G_GINTPTR_FORMAT, i, attribs[i]); img = _gst_egl_image_create (context, EGL_LINUX_DMA_BUF_EXT, NULL, attribs); if (!img) { GST_WARNING ("eglCreateImage failed: %s", gst_egl_get_error_string (eglGetError ())); return NULL; } return gst_egl_image_new_wrapped (context, img, GST_GL_RGBA, NULL, (GstEGLImageDestroyNotify) _destroy_egl_image); } /** * gst_egl_image_from_dmabuf_direct: * @context: a #GstGLContext (must be an EGL context) * @fd: Array of DMABuf file descriptors * @offset: Array of offsets, relative to the DMABuf * @in_info: the #GstVideoInfo * * Creates an EGL image that imports the dmabuf FD. The dmabuf data * is passed directly as the format described in in_info. This is * useful if the hardware is capable of performing color space conversions * internally. The appropriate DRM format is picked, and the EGL image * is created with this DRM format. * * Another notable difference to gst_egl_image_from_dmabuf() * is that this function creates one EGL image for all planes, not one for * a single plane. * * Returns: (nullable): a #GstEGLImage wrapping @dmabuf or %NULL on failure */ GstEGLImage * gst_egl_image_from_dmabuf_direct (GstGLContext * context, gint * fd, const gsize * offset, const GstVideoInfo * in_info) { return gst_egl_image_from_dmabuf_direct_target (context, fd, offset, in_info, GST_GL_TEXTURE_TARGET_2D); } gboolean gst_egl_image_export_dmabuf (GstEGLImage * image, int *fd, gint * stride, gsize * offset) { EGLBoolean (*gst_eglExportDMABUFImageQueryMESA) (EGLDisplay dpy, EGLImageKHR image, int *fourcc, int *num_planes, EGLuint64KHR * modifiers); EGLBoolean (*gst_eglExportDMABUFImageMESA) (EGLDisplay dpy, EGLImageKHR image, int *fds, EGLint * strides, EGLint * offsets); GstGLDisplayEGL *display_egl; EGLDisplay egl_display = EGL_DEFAULT_DISPLAY; int num_planes = 0; int egl_fd = 0; EGLint egl_stride = 0; EGLint egl_offset = 0; int fourcc; EGLuint64KHR modifier; gst_eglExportDMABUFImageQueryMESA = gst_gl_context_get_proc_address (image->context, "eglExportDMABUFImageQueryMESA"); gst_eglExportDMABUFImageMESA = gst_gl_context_get_proc_address (image->context, "eglExportDMABUFImageMESA"); if (!gst_eglExportDMABUFImageQueryMESA || !gst_eglExportDMABUFImageMESA) return FALSE; display_egl = (GstGLDisplayEGL *) gst_gl_display_egl_from_gl_display (image-> context->display); if (!display_egl) { GST_WARNING_OBJECT (image->context, "Failed to retrieve GstGLDisplayEGL from %" GST_PTR_FORMAT, image->context->display); return FALSE; } egl_display = (EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (display_egl)); gst_object_unref (display_egl); if (!gst_eglExportDMABUFImageQueryMESA (egl_display, image->image, &fourcc, &num_planes, &modifier)) return FALSE; /* Don't allow multi-plane dmabufs */ if (num_planes > 1) return FALSE; /* FIXME We don't support modifiers */ if (modifier != DRM_FORMAT_MOD_LINEAR) return FALSE; if (!gst_eglExportDMABUFImageMESA (egl_display, image->image, &egl_fd, &egl_stride, &egl_offset)) return FALSE; GST_DEBUG_OBJECT (image->context, "Export DMABuf with fourcc %" GST_FOURCC_FORMAT ", modififers %" G_GUINT64_FORMAT ", stride %i and offset %i", GST_FOURCC_ARGS (fourcc), modifier, egl_stride, egl_offset); *fd = egl_fd; *stride = egl_stride; *offset = egl_offset; return TRUE; } /** * gst_egl_image_can_emulate: * @context: a #GstGLContext (must be an EGL context) * @format: a #GstVideoFormat * * Checks if the given @context can emulate @format using a limited subset of * RGB texture formats. Such @format is then suitable for importing using * gst_egl_image_from_dmabuf() even when GL supports the video format as * external-only or not at all. * * Since: 1.26 * * Returns: #TRUE if @format can be emulated */ gboolean gst_egl_image_can_emulate (GstGLContext * context, GstVideoFormat format) { const GstVideoFormatInfo *info; guint i; GstGLFormat out_format; info = gst_video_format_get_info (format); for (i = 0; i != GST_VIDEO_FORMAT_INFO_N_PLANES (info); ++i) { int fourcc; fourcc = _drm_rgba_fourcc_from_format (format, i, &out_format); if (fourcc == -1) { return FALSE; } if (GST_VIDEO_FORMAT_INFO_IS_YUV (info)) { /* For YUV formats we only support linear modifier. */ if (!gst_gl_context_egl_format_supports_modifier (context, fourcc, DRM_FORMAT_MOD_LINEAR, FALSE)) { return FALSE; } } else if (GST_VIDEO_FORMAT_INFO_IS_RGB (info)) { /* For RGB formats any DMA format that is not external-only will do. */ const GArray *dma_modifiers; guint j; if (!gst_gl_context_egl_get_format_modifiers (context, fourcc, &dma_modifiers)) { return FALSE; } for (j = 0; j < dma_modifiers->len; ++j) { GstGLDmaModifier *mod = &g_array_index (dma_modifiers, GstGLDmaModifier, j); if (!mod->external_only) { break; } } if (j == dma_modifiers->len) { return FALSE; } } else { return FALSE; } } return TRUE; } #endif /* GST_GL_HAVE_DMABUF */