/* * gstvaapisurface_egl.c - VA surface abstraction (EGL interop) * * Copyright (C) 2014 Intel Corporation * Author: Gwenole Beauchesne * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA */ #include "sysdeps.h" #include "gstvaapisurface_egl.h" #include "gstvaapisurface_drm.h" #include "gstvaapisurface_priv.h" #include "gstvaapiimage_priv.h" #include "gstvaapibufferproxy_priv.h" #include "gstvaapidisplay_egl_priv.h" #include "gstvaapifilter.h" typedef struct { GstVaapiDisplayEGL *display; EGLImageKHR image; GstVideoFormat format; guint width; guint height; GstVaapiSurface *surface; /* result */ } CreateSurfaceWithEGLImageArgs; static GstVaapiSurface * do_create_surface_with_egl_image_unlocked (GstVaapiDisplayEGL * display, EGLImageKHR image, GstVideoFormat format, guint width, guint height) { GstVaapiDisplay *const base_display = GST_VAAPI_DISPLAY_CAST (display); EglContext *const ctx = GST_VAAPI_DISPLAY_EGL_CONTEXT (display); EglVTable *vtable; gsize size, offset[GST_VIDEO_MAX_PLANES]; gint name, stride[GST_VIDEO_MAX_PLANES]; if (!ctx || !(vtable = egl_context_get_vtable (ctx, FALSE))) return NULL; memset (offset, 0, sizeof (offset)); memset (stride, 0, sizeof (stride)); if (!vtable->has_EGL_MESA_drm_image) goto error_mission_extension; /* EGL_MESA_drm_image extension */ if (!vtable->eglExportDRMImageMESA (ctx->display->base.handle.p, image, &name, NULL, &stride[0])) goto error_export_image_gem_buf; size = height * stride[0]; /* * XXX: The below surface creation may fail on Intel due to: * https://github.com/01org/intel-vaapi-driver/issues/222 * A permanent fix for that problem will be released in intel-vaapi-driver * version 1.8.4 and later, and also in 1.8.3-1ubuntu1. * However if you don't have that fix then a simple workaround is to * uncomment this line of code: * size = GST_ROUND_UP_32 (height) * stride[0]; */ return gst_vaapi_surface_new_with_gem_buf_handle (base_display, name, size, format, width, height, offset, stride); /* ERRORS */ error_export_image_gem_buf: { GST_ERROR ("failed to export EGL image to GEM buffer"); return NULL; } error_mission_extension: { GST_ERROR ("missing EGL_MESA_drm_image extension"); return NULL; } } static void do_create_surface_with_egl_image (CreateSurfaceWithEGLImageArgs * args) { GST_VAAPI_DISPLAY_LOCK (args->display); args->surface = do_create_surface_with_egl_image_unlocked (args->display, args->image, args->format, args->width, args->height); GST_VAAPI_DISPLAY_UNLOCK (args->display); } // Creates VA surface with EGLImage buffer as backing storage (internal) static inline GstVaapiSurface * create_surface_with_egl_image (GstVaapiDisplayEGL * display, EGLImageKHR image, GstVideoFormat format, guint width, guint height) { CreateSurfaceWithEGLImageArgs args = { display, image, format, width, height }; if (!egl_context_run (GST_VAAPI_DISPLAY_EGL_CONTEXT (display), (EglContextRunFunc) do_create_surface_with_egl_image, &args)) return NULL; return args.surface; } // Creates VA surface from an EGLImage buffer copy (internal) static GstVaapiSurface * create_surface_from_egl_image (GstVaapiDisplayEGL * display, const GstVideoInfo * vip, EGLImageKHR image, GstVideoFormat format, guint width, guint height, guint flags) { GstVaapiDisplay *const base_display = GST_VAAPI_DISPLAY_CAST (display); GstVaapiSurface *img_surface = NULL, *out_surface = NULL; gboolean use_native_format = TRUE; GstVaapiFilter *filter = NULL; GstVaapiFilterStatus filter_status; img_surface = create_surface_with_egl_image (display, image, format, width, height); if (!img_surface) return NULL; if (vip) { use_native_format = GST_VIDEO_INFO_FORMAT (vip) == GST_VIDEO_FORMAT_ENCODED || GST_VIDEO_INFO_FORMAT (vip) == GST_VIDEO_FORMAT_UNKNOWN; if (GST_VIDEO_INFO_WIDTH (vip) && GST_VIDEO_INFO_HEIGHT (vip)) { width = GST_VIDEO_INFO_WIDTH (vip); height = GST_VIDEO_INFO_HEIGHT (vip); } } if (use_native_format) { out_surface = gst_vaapi_surface_new (base_display, GST_VAAPI_CHROMA_TYPE_YUV420, width, height); } else { out_surface = gst_vaapi_surface_new_with_format (base_display, GST_VIDEO_INFO_FORMAT (vip), width, height); } if (!out_surface) goto error_create_surface; filter = gst_vaapi_filter_new (base_display); if (!filter) goto error_create_filter; filter_status = gst_vaapi_filter_process (filter, img_surface, out_surface, flags); if (filter_status != GST_VAAPI_FILTER_STATUS_SUCCESS) goto error_convert_surface; gst_vaapi_surface_unref (img_surface); gst_object_unref (filter); return out_surface; /* ERRORS */ error_create_surface: GST_ERROR ("failed to create output surface format:%s size:%dx%d", gst_vaapi_video_format_to_string (vip ? GST_VIDEO_INFO_FORMAT (vip) : GST_VIDEO_FORMAT_ENCODED), width, height); goto error_cleanup; error_convert_surface: GST_ERROR ("failed to transfer EGL image to VA surface (status = %d)", filter_status); goto error_cleanup; error_create_filter: GST_ERROR ("failed to create video processing filter"); // fall-through error_cleanup: gst_mini_object_replace ((GstMiniObject **) & img_surface, NULL); gst_mini_object_replace ((GstMiniObject **) & out_surface, NULL); gst_vaapi_filter_replace (&filter, NULL); return NULL; } /** * gst_vaapi_surface_new_from_egl_image: * @display: a #GstVaapiDisplay * @vip: the desired (optional) #GstVideoInfo constraints * @image: the EGL image to import * @format: the EGL @image format * @width: the EGL @image width in pixels * @height: the EGL @image height in pixels * @flags: postprocessing flags. See #GstVaapiSurfaceRenderFlags * * Creates a new #GstVaapiSurface with a copy of the EGL image * contents. i.e. the input EGL @image can be disposed and the * resulting VA surface would still be valid with the contents at the * time this function was called. * * If @vip is %NULL, then the resulting VA surface will be created * with the same video format and size as the original @image. If @vip * is non-%NULL and the desired format is GST_VIDEO_FORMAT_ENCODED, * then the resulting VA surface will have the best "native" HW * format, usually NV12. * * Return value: the newly allocated #GstVaapiSurface object, or %NULL * if creation from EGL image failed, or is not supported */ GstVaapiSurface * gst_vaapi_surface_new_from_egl_image (GstVaapiDisplay * base_display, const GstVideoInfo * vip, EGLImageKHR image, GstVideoFormat format, guint width, guint height, guint flags) { GstVaapiDisplayEGL *display; g_return_val_if_fail (GST_VAAPI_IS_DISPLAY_EGL (base_display), NULL); g_return_val_if_fail (image != EGL_NO_IMAGE_KHR, NULL); g_return_val_if_fail (width > 0, NULL); g_return_val_if_fail (height > 0, NULL); display = GST_VAAPI_DISPLAY_EGL (base_display); if (!display || !GST_VAAPI_IS_DISPLAY_EGL (display)) goto error_invalid_display; return create_surface_from_egl_image (display, vip, image, format, width, height, flags); /* ERRORS */ error_invalid_display: GST_ERROR ("invalid display (NULL or not of EGL class"); return NULL; } /** * gst_vaapi_surface_new_with_egl_image: * @display: a #GstVaapiDisplay * @image: the EGL image to import * @format: the EGL @image format * @width: the EGL @image width in pixels * @height: the EGL @image height in pixels * * Creates a new #GstVaapiSurface bound to an external EGL image. * * The caller maintains the lifetime of the EGL image object. In * particular, the EGL image shall not be destroyed before the last * reference to the resulting VA surface is released. * * Return value: the newly allocated #GstVaapiSurface object, or %NULL * if creation from EGL image failed, or is not supported */ GstVaapiSurface * gst_vaapi_surface_new_with_egl_image (GstVaapiDisplay * base_display, EGLImageKHR image, GstVideoFormat format, guint width, guint height) { GstVaapiDisplayEGL *display; g_return_val_if_fail (GST_VAAPI_IS_DISPLAY_EGL (base_display), NULL); g_return_val_if_fail (image != EGL_NO_IMAGE_KHR, NULL); g_return_val_if_fail (width > 0, NULL); g_return_val_if_fail (height > 0, NULL); display = GST_VAAPI_DISPLAY_EGL (base_display); if (!display || !GST_VAAPI_IS_DISPLAY_EGL (display)) goto error_invalid_display; return create_surface_with_egl_image (display, image, format, width, height); /* ERRORS */ error_invalid_display: GST_ERROR ("invalid display (NULL or not of EGL class"); return NULL; }