mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-09 19:09:41 +00:00
273 lines
9.2 KiB
C
273 lines
9.2 KiB
C
/*
|
|
* gstvaapisurface_egl.c - VA surface abstraction (EGL interop)
|
|
*
|
|
* Copyright (C) 2014 Intel Corporation
|
|
* Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
|
|
*
|
|
* 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, 0);
|
|
}
|
|
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;
|
|
}
|