gstreamer/gst/vaapi/gstvaapipluginbase.c
Víctor Manuel Jáquez Leal ee3d4c3206 libs: display: driver quirks mechanism
This mechanism comes from ffmpeg vaapi implementation, where they have
their own quirks.

A specific driver is identified by a substring present in the vendor
string. If that substring is found, a set of bitwise flags are store.
These flags can be accessed through the function
gst_vaapi_display_has_driver_quirks().

The purpose for this first quirks is to disable the put image try for
AMD Gallium driver (see [1]).

1. https://gitlab.freedesktop.org/gstreamer/gstreamer-vaapi/merge_requests/72
2020-01-24 12:21:59 +00:00

1645 lines
48 KiB
C

/*
* gstvaapipluginbase.c - Base GStreamer VA-API Plugin element
*
* Copyright (C) 2010-2011 Splitted-Desktop Systems
* Author: Gwenole Beauchesne <gwenole.beauchesne@splitted-desktop.com>
* Copyright (C) 2011-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 "gstcompat.h"
#include <gst/vaapi/gstvaapisurface_drm.h>
#include <gst/base/gstpushsrc.h>
#include "gstvaapipluginbase.h"
#include "gstvaapipluginutil.h"
#include "gstvaapivideocontext.h"
#include "gstvaapivideometa.h"
#include "gstvaapivideobufferpool.h"
#if USE_GST_GL_HELPERS
# include <gst/gl/gl.h>
#endif
GST_DEBUG_CATEGORY_STATIC (CAT_PERFORMANCE);
/* Default debug category is from the subclass */
#define GST_CAT_DEFAULT (plugin->debug_category)
#define BUFFER_POOL_SINK_MIN_BUFFERS 2
#define GST_VAAPI_PAD_PRIVATE(pad) \
(GST_VAAPI_PLUGIN_BASE_GET_CLASS(plugin)->get_vaapi_pad_private(plugin, pad))
GstVaapiPadPrivate *
gst_vaapi_pad_private_new (void)
{
GstVaapiPadPrivate *priv = g_new0 (GstVaapiPadPrivate, 1);
gst_video_info_init (&priv->info);
return priv;
}
void
gst_vaapi_pad_private_reset (GstVaapiPadPrivate * priv)
{
g_assert (priv);
gst_caps_replace (&priv->caps, NULL);
gst_video_info_init (&priv->info);
g_clear_object (&priv->buffer_pool);
g_clear_object (&priv->allocator);
priv->buffer_size = 0;
priv->caps_is_raw = FALSE;
g_clear_object (&priv->other_allocator);
}
void
gst_vaapi_pad_private_finalize (GstVaapiPadPrivate * priv)
{
gst_vaapi_pad_private_reset (priv);
g_free (priv);
}
/* GstVideoContext interface */
static void
plugin_set_display (GstVaapiPluginBase * plugin, GstVaapiDisplay * display)
{
const gchar *const display_name =
gst_vaapi_display_get_display_name (display);
if (plugin->display_name && g_strcmp0 (plugin->display_name, display_name)) {
GST_DEBUG_OBJECT (plugin, "incompatible display name '%s', requested '%s'",
display_name, plugin->display_name);
gst_vaapi_display_replace (&plugin->display, NULL);
} else {
GST_INFO_OBJECT (plugin, "set display %" GST_PTR_FORMAT, display);
gst_vaapi_display_replace (&plugin->display, display);
plugin->display_type = gst_vaapi_display_get_display_type (display);
gst_vaapi_plugin_base_set_display_name (plugin, display_name);
}
gst_object_unref (display);
}
/**
* gst_vaapi_plugin_base_set_context:
* @plugin: a #GstVaapiPluginBase instance
* @context: a #GstContext to set
*
* This is a common set_context() element's vmethod for all the
* GStreamer VA-API elements.
*
* It normally should be used through the macro
* #GST_VAAPI_PLUGIN_BASE_DEFINE_SET_CONTEXT()
**/
void
gst_vaapi_plugin_base_set_context (GstVaapiPluginBase * plugin,
GstContext * context)
{
GstVaapiDisplay *display = NULL;
/* gst.vaapi.app.Display is only attended _if_ the element is
* vaapisink and it doesn't have a display set yet */
if (gst_vaapi_video_context_get_display (context,
GST_IS_VIDEO_SINK (plugin) && !plugin->display, &display)) {
plugin_set_display (plugin, display);
}
#if USE_GST_GL_HELPERS
gst_gl_handle_set_context (GST_ELEMENT_CAST (plugin), context,
(GstGLDisplay **) & plugin->gl_display,
(GstGLContext **) & plugin->gl_other_context);
#endif
}
void
gst_vaapi_plugin_base_init_interfaces (GType g_define_type_id)
{
}
static gboolean
default_has_interface (GstVaapiPluginBase * plugin, GType type)
{
return FALSE;
}
static void
default_display_changed (GstVaapiPluginBase * plugin)
{
}
static GstVaapiSurface *
_get_cached_surface (GstBuffer * buf)
{
return gst_mini_object_get_qdata (GST_MINI_OBJECT (buf),
g_quark_from_static_string ("GstVaapiDMABufSurface"));
}
static void
_set_cached_surface (GstBuffer * buf, GstVaapiSurface * surface)
{
return gst_mini_object_set_qdata (GST_MINI_OBJECT (buf),
g_quark_from_static_string ("GstVaapiDMABufSurface"), surface,
(GDestroyNotify) gst_vaapi_surface_unref);
}
static gboolean
plugin_update_sinkpad_info_from_buffer (GstVaapiPluginBase * plugin,
GstPad * sinkpad, GstBuffer * buf)
{
GstVaapiPadPrivate *sinkpriv = GST_VAAPI_PAD_PRIVATE (sinkpad);
GstVideoInfo *const vip = &sinkpriv->info;
GstVideoMeta *vmeta;
guint i;
vmeta = gst_buffer_get_video_meta (buf);
if (!vmeta)
return TRUE;
if (GST_VIDEO_INFO_FORMAT (vip) != vmeta->format ||
GST_VIDEO_INFO_WIDTH (vip) != vmeta->width ||
GST_VIDEO_INFO_HEIGHT (vip) != vmeta->height ||
GST_VIDEO_INFO_N_PLANES (vip) != vmeta->n_planes)
return FALSE;
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (vip); ++i) {
GST_VIDEO_INFO_PLANE_OFFSET (vip, i) = vmeta->offset[i];
GST_VIDEO_INFO_PLANE_STRIDE (vip, i) = vmeta->stride[i];
}
GST_VIDEO_INFO_SIZE (vip) = gst_buffer_get_size (buf);
return TRUE;
}
static gboolean
is_dma_buffer (GstBuffer * buf)
{
GstMemory *mem;
if (gst_buffer_n_memory (buf) < 1)
return FALSE;
mem = gst_buffer_peek_memory (buf, 0);
if (!mem || !gst_is_dmabuf_memory (mem))
return FALSE;
return TRUE;
}
static gboolean
plugin_bind_dma_to_vaapi_buffer (GstVaapiPluginBase * plugin, GstPad * sinkpad,
GstBuffer * inbuf, GstBuffer * outbuf)
{
GstVaapiPadPrivate *sinkpriv = GST_VAAPI_PAD_PRIVATE (sinkpad);
GstVideoInfo *const vip = &sinkpriv->info;
GstVaapiVideoMeta *meta;
GstVaapiSurface *surface;
GstVaapiSurfaceProxy *proxy;
gint fd;
fd = gst_dmabuf_memory_get_fd (gst_buffer_peek_memory (inbuf, 0));
if (fd < 0)
return FALSE;
if (!plugin_update_sinkpad_info_from_buffer (plugin, sinkpad, inbuf))
goto error_update_sinkpad_info;
meta = gst_buffer_get_vaapi_video_meta (outbuf);
g_return_val_if_fail (meta != NULL, FALSE);
/* Check for a VASurface cached in the buffer */
surface = _get_cached_surface (inbuf);
if (!surface) {
/* otherwise create one and cache it */
surface =
gst_vaapi_surface_new_with_dma_buf_handle (plugin->display, fd, vip);
if (!surface)
goto error_create_surface;
_set_cached_surface (inbuf, surface);
}
proxy = gst_vaapi_surface_proxy_new (surface);
if (!proxy)
goto error_create_proxy;
gst_vaapi_video_meta_set_surface_proxy (meta, proxy);
gst_vaapi_surface_proxy_unref (proxy);
gst_buffer_add_parent_buffer_meta (outbuf, inbuf);
return TRUE;
/* ERRORS */
error_update_sinkpad_info:
{
GST_ERROR_OBJECT (plugin,
"failed to update sink pad video info from video meta");
return FALSE;
}
error_create_surface:
{
GST_ERROR_OBJECT (plugin,
"failed to create VA surface from dma_buf handle");
return FALSE;
}
error_create_proxy:
{
GST_ERROR_OBJECT (plugin,
"failed to create VA surface proxy from wrapped VA surface");
return FALSE;
}
}
static void
plugin_reset_texture_map (GstVaapiPluginBase * plugin)
{
if (plugin->display)
gst_vaapi_display_reset_texture_map (plugin->display);
}
static GstVaapiPadPrivate *
default_get_vaapi_pad_private (GstVaapiPluginBase * plugin, GstPad * pad)
{
if (plugin->sinkpad == pad)
return plugin->sinkpriv;
g_assert (plugin->srcpad == pad);
return plugin->srcpriv;
}
void
gst_vaapi_plugin_base_class_init (GstVaapiPluginBaseClass * klass)
{
klass->has_interface = default_has_interface;
klass->display_changed = default_display_changed;
klass->get_vaapi_pad_private = default_get_vaapi_pad_private;
}
void
gst_vaapi_plugin_base_init (GstVaapiPluginBase * plugin,
GstDebugCategory * debug_category)
{
plugin->debug_category = debug_category;
plugin->display_type = GST_VAAPI_DISPLAY_TYPE_ANY;
plugin->display_type_req = GST_VAAPI_DISPLAY_TYPE_ANY;
/* sink pad */
plugin->sinkpad = gst_element_get_static_pad (GST_ELEMENT (plugin), "sink");
if (plugin->sinkpad)
plugin->sinkpriv = gst_vaapi_pad_private_new ();
/* src pad */
if (!(GST_OBJECT_FLAGS (plugin) & GST_ELEMENT_FLAG_SINK))
plugin->srcpad = gst_element_get_static_pad (GST_ELEMENT (plugin), "src");
if (plugin->srcpad)
plugin->srcpriv = gst_vaapi_pad_private_new ();
plugin->enable_direct_rendering =
(g_getenv ("GST_VAAPI_ENABLE_DIRECT_RENDERING") != NULL);
}
void
gst_vaapi_plugin_base_finalize (GstVaapiPluginBase * plugin)
{
gst_vaapi_plugin_base_close (plugin);
g_free (plugin->display_name);
if (plugin->sinkpriv)
gst_vaapi_pad_private_finalize (plugin->sinkpriv);
if (plugin->srcpriv)
gst_vaapi_pad_private_finalize (plugin->srcpriv);
if (plugin->sinkpad)
gst_object_unref (plugin->sinkpad);
if (plugin->srcpad)
gst_object_unref (plugin->srcpad);
}
/**
* gst_vaapi_plugin_base_open:
* @plugin: a #GstVaapiPluginBase
*
* Allocates any internal resources needed for correct operation from
* the subclass.
*
* Returns: %TRUE if successful, %FALSE otherwise.
*/
gboolean
gst_vaapi_plugin_base_open (GstVaapiPluginBase * plugin)
{
return TRUE;
}
/**
* gst_vaapi_plugin_base_close:
* @plugin: a #GstVaapiPluginBase
*
* Deallocates all internal resources that were allocated so
* far. i.e. put the base plugin object into a clean state.
*/
void
gst_vaapi_plugin_base_close (GstVaapiPluginBase * plugin)
{
/* Release vaapi textures first if exist, which refs display object */
plugin_reset_texture_map (plugin);
gst_vaapi_display_replace (&plugin->display, NULL);
gst_object_replace (&plugin->gl_context, NULL);
gst_object_replace (&plugin->gl_display, NULL);
gst_object_replace (&plugin->gl_other_context, NULL);
gst_caps_replace (&plugin->allowed_raw_caps, NULL);
if (plugin->sinkpriv)
gst_vaapi_pad_private_reset (plugin->sinkpriv);
if (plugin->srcpriv)
gst_vaapi_pad_private_reset (plugin->srcpriv);
}
/**
* gst_vaapi_plugin_base_has_display_type:
* @plugin: a #GstVaapiPluginBase
* @display_type_req: the desired #GstVaapiDisplayType
*
* Checks whether the @plugin elements already has a #GstVaapiDisplay
* instance compatible with type @display_type_req.
*
* Return value: %TRUE if @plugin has a compatible display, %FALSE otherwise
*/
gboolean
gst_vaapi_plugin_base_has_display_type (GstVaapiPluginBase * plugin,
GstVaapiDisplayType display_type_req)
{
GstVaapiDisplayType display_type;
if (!plugin->display)
return FALSE;
display_type = plugin->display_type;
if (gst_vaapi_display_type_is_compatible (display_type, display_type_req))
return TRUE;
display_type = gst_vaapi_display_get_class_type (plugin->display);
if (gst_vaapi_display_type_is_compatible (display_type, display_type_req))
return TRUE;
return FALSE;
}
/**
* gst_vaapi_plugin_base_set_display_type:
* @plugin: a #GstVaapiPluginBase
* @display_type: the new request #GstVaapiDisplayType
*
* Requests a new display type. The change is effective at the next
* call to gst_vaapi_plugin_base_ensure_display().
*/
void
gst_vaapi_plugin_base_set_display_type (GstVaapiPluginBase * plugin,
GstVaapiDisplayType display_type)
{
plugin->display_type_req = display_type;
}
/**
* gst_vaapi_plugin_base_set_display_name:
* @plugin: a #GstVaapiPluginBase
* @display_name: the new display name to match
*
* Sets the name of the display to look for. The change is effective
* at the next call to gst_vaapi_plugin_base_ensure_display().
*/
void
gst_vaapi_plugin_base_set_display_name (GstVaapiPluginBase * plugin,
const gchar * display_name)
{
g_free (plugin->display_name);
plugin->display_name = g_strdup (display_name);
}
/**
* gst_vaapi_plugin_base_ensure_display:
* @plugin: a #GstVaapiPluginBase
*
* Ensures the display stored in @plugin complies with the requested
* display type constraints.
*
* Returns: %TRUE if the display was created to match the requested
* type, %FALSE otherwise.
*/
gboolean
gst_vaapi_plugin_base_ensure_display (GstVaapiPluginBase * plugin)
{
if (gst_vaapi_plugin_base_has_display_type (plugin, plugin->display_type_req))
return TRUE;
gst_vaapi_display_replace (&plugin->display, NULL);
if (!gst_vaapi_ensure_display (GST_ELEMENT (plugin),
plugin->display_type_req))
return FALSE;
plugin->display_type = gst_vaapi_display_get_display_type (plugin->display);
GST_VAAPI_PLUGIN_BASE_GET_CLASS (plugin)->display_changed (plugin);
return TRUE;
}
static gboolean
gst_vaapi_buffer_pool_caps_is_equal (GstBufferPool * pool, GstCaps * newcaps)
{
GstStructure *config;
GstCaps *caps;
gboolean ret;
caps = NULL;
ret = FALSE;
config = gst_buffer_pool_get_config (pool);
if (gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL))
ret = gst_caps_is_equal (newcaps, caps);
gst_structure_free (config);
return ret;
}
static inline gboolean
reset_allocator (GstAllocator * allocator, GstVideoInfo * vinfo)
{
const GstVideoInfo *orig_vi;
if (!allocator)
return TRUE;
orig_vi = gst_allocator_get_vaapi_video_info (allocator, NULL);
if (!gst_video_info_changed (orig_vi, vinfo))
return FALSE;
gst_object_unref (allocator);
return TRUE;
}
static gboolean
ensure_sinkpad_allocator (GstVaapiPluginBase * plugin, GstPad * sinkpad,
GstCaps * caps, guint * size)
{
GstVaapiPadPrivate *sinkpriv = GST_VAAPI_PAD_PRIVATE (sinkpad);
GstVideoInfo vinfo;
const GstVideoInfo *image_info;
GstVaapiImageUsageFlags usage_flag =
GST_VAAPI_IMAGE_USAGE_FLAG_NATIVE_FORMATS;
if (!gst_video_info_from_caps (&vinfo, caps))
goto error_invalid_caps;
if (!reset_allocator (sinkpriv->allocator, &vinfo))
goto bail;
/* enable direct upload if upstream requests raw video */
if (gst_caps_is_video_raw (caps)) {
usage_flag = GST_VAAPI_IMAGE_USAGE_FLAG_DIRECT_UPLOAD;
GST_INFO_OBJECT (plugin, "enabling direct upload in sink allocator");
}
sinkpriv->allocator =
gst_vaapi_video_allocator_new (plugin->display, &vinfo, 0, usage_flag);
bail:
if (!sinkpriv->allocator)
goto error_create_allocator;
image_info = gst_allocator_get_vaapi_video_info (sinkpriv->allocator, NULL);
g_assert (image_info); /* allocator ought set its image info */
/* update the size with the one generated by the allocator */
*size = GST_VIDEO_INFO_SIZE (image_info);
return TRUE;
/* ERRORS */
error_invalid_caps:
{
GST_ERROR_OBJECT (plugin, "invalid caps %" GST_PTR_FORMAT, caps);
return FALSE;
}
error_create_allocator:
{
GST_ERROR_OBJECT (plugin, "failed to create sink pad's allocator");
return FALSE;
}
}
static inline guint
get_dmabuf_surface_allocation_flags (void)
{
/* @FIXME: fetch the real devices ids */
/* Pair vendor/device identifies an unique physical device. */
guint va_vendor_id = 0x00;
guint va_device_id = 0x00;
guint gl_vendor_id = 0x00;
guint gl_device_id = 0x00;
/* Requires linear memory only if fd export is done on a different
* device than the device where the fd is imported. */
gboolean same_physical_device = va_vendor_id == gl_vendor_id
&& va_device_id == gl_device_id;
if (same_physical_device)
return 0;
return GST_VAAPI_SURFACE_ALLOC_FLAG_LINEAR_STORAGE;
}
static inline GstAllocator *
create_dmabuf_srcpad_allocator (GstVaapiPluginBase * plugin,
GstVideoInfo * vinfo, gboolean check_for_map)
{
GstAllocator *allocator;
if (!GST_IS_VIDEO_DECODER (plugin) && !GST_IS_BASE_TRANSFORM (plugin))
return NULL;
allocator = gst_vaapi_dmabuf_allocator_new (plugin->display, vinfo,
get_dmabuf_surface_allocation_flags (), GST_PAD_SRC);
if (!allocator || !check_for_map)
return allocator;
/* the dmabuf allocator *must* be capable to map a buffer with raw
* caps and the there's no evidence of downstream dmabuf
* importation */
if (!gst_vaapi_dmabuf_can_map (plugin->display, allocator)) {
GST_INFO_OBJECT (plugin, "dmabuf allocator generates unmappable buffers");
gst_object_replace ((GstObject **) & allocator, NULL);
}
return allocator;
}
static gboolean
ensure_srcpad_allocator (GstVaapiPluginBase * plugin, GstPad * srcpad,
GstVideoInfo * vinfo, GstCaps * caps)
{
GstVaapiPadPrivate *srcpriv = GST_VAAPI_PAD_PRIVATE (srcpad);
const GstVideoInfo *image_info;
if (!reset_allocator (srcpriv->allocator, vinfo))
goto valid_allocator;
srcpriv->allocator = NULL;
if (caps && gst_caps_is_video_raw (caps)) {
GstAllocator *allocator = create_dmabuf_srcpad_allocator (plugin, vinfo,
!srcpriv->can_dmabuf);
srcpriv->allocator = allocator;
} else if (caps && gst_vaapi_caps_feature_contains (caps,
GST_VAAPI_CAPS_FEATURE_DMABUF)) {
srcpriv->allocator = create_dmabuf_srcpad_allocator (plugin, vinfo, FALSE);
if (!srcpriv->allocator)
goto error_create_allocator;
}
if (!srcpriv->allocator) {
GstVaapiImageUsageFlags usage_flag =
GST_VAAPI_IMAGE_USAGE_FLAG_NATIVE_FORMATS;
if (plugin->enable_direct_rendering) {
usage_flag = GST_VAAPI_IMAGE_USAGE_FLAG_DIRECT_RENDER;
GST_INFO_OBJECT (plugin, "enabling direct rendering in source allocator");
}
srcpriv->allocator =
gst_vaapi_video_allocator_new (plugin->display, vinfo, 0, usage_flag);
}
if (!srcpriv->allocator)
goto error_create_allocator;
valid_allocator:
image_info = gst_allocator_get_vaapi_video_info (srcpriv->allocator, NULL);
g_assert (image_info); /* both allocators ought set its image
* info */
/* update the size with the one generated by the allocator */
GST_VIDEO_INFO_SIZE (vinfo) = GST_VIDEO_INFO_SIZE (image_info);
if (GST_IS_VIDEO_DECODER (plugin)) {
/* the received caps are the "allocation caps" which may be
* different from the "negotiation caps". In this case, we should
* indicate the allocator to store the negotiation caps since they
* are the one should be used for frame mapping with GstVideoMeta */
gboolean different_caps = srcpriv->caps &&
!gst_caps_is_strictly_equal (srcpriv->caps, caps);
const GstVideoInfo *previous_negotiated =
gst_allocator_get_vaapi_negotiated_video_info (srcpriv->allocator);
if (different_caps) {
guint i;
GstVideoInfo vi = srcpriv->info;
/* update the planes and the size with the allocator image/surface
* info, but not the resolution */
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (image_info); i++) {
GST_VIDEO_INFO_PLANE_OFFSET (&vi, i) =
GST_VIDEO_INFO_PLANE_OFFSET (image_info, i);
GST_VIDEO_INFO_PLANE_STRIDE (&vi, i) =
GST_VIDEO_INFO_PLANE_STRIDE (image_info, i);
}
GST_VIDEO_INFO_SIZE (&vi) = GST_VIDEO_INFO_SIZE (image_info);
gst_allocator_set_vaapi_negotiated_video_info (srcpriv->allocator, &vi);
} else if (previous_negotiated) {
gst_allocator_set_vaapi_negotiated_video_info (srcpriv->allocator, NULL);
}
}
return TRUE;
/* ERRORS */
error_create_allocator:
{
GST_ERROR_OBJECT (plugin, "failed to create src pad's allocator");
return FALSE;
}
}
/**
* gst_vaapi_plugin_base_create_pool:
* @plugin: a #GstVaapiPluginBase
* @caps: the initial #GstCaps for the resulting buffer pool
* @size: the size of each buffer, not including prefix and padding
* @options: a set of #GstVaapiVideoBufferPoolOption encoded as bit-wise
* @allocator: (allow-none): the #GstAllocator to use or %NULL
*
* Create an instance of #GstVaapiVideoBufferPool
*
* Returns: (transfer full): a new allocated #GstBufferPool
**/
static GstBufferPool *
gst_vaapi_plugin_base_create_pool (GstVaapiPluginBase * plugin, GstCaps * caps,
gsize size, guint min_buffers, guint max_buffers, guint options,
GstAllocator * allocator)
{
GstBufferPool *pool;
GstStructure *config;
if (!(pool = gst_vaapi_video_buffer_pool_new (plugin->display)))
goto error_create_pool;
config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_params (config, caps, size, min_buffers,
max_buffers);
gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_VAAPI_VIDEO_META);
if (options & GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_VIDEO_META) {
gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_VIDEO_META);
}
if (options & GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT) {
gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
}
#if (USE_GLX || USE_EGL)
if (options & GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_GL_TEXTURE_UPLOAD) {
gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META);
}
#endif
if (allocator)
gst_buffer_pool_config_set_allocator (config, allocator, NULL);
if (!gst_buffer_pool_set_config (pool, config)) {
config = gst_buffer_pool_get_config (pool);
if (!gst_buffer_pool_config_validate_params (config, caps, size,
min_buffers, max_buffers)) {
gst_structure_free (config);
goto error_pool_config;
}
if (!gst_buffer_pool_set_config (pool, config))
goto error_pool_config;
}
return pool;
/* ERRORS */
error_create_pool:
{
GST_ERROR_OBJECT (plugin, "failed to create buffer pool");
return NULL;
}
error_pool_config:
{
gst_object_unref (pool);
GST_ELEMENT_ERROR (plugin, RESOURCE, SETTINGS,
("Failed to configure the buffer pool"),
("Configuration is most likely invalid, please report this issue."));
return NULL;
}
}
/**
* ensure_sinkpad_buffer_pool:
* @plugin: a #GstVaapiPluginBase
* @sinkpad: the #GstPad to use for the resulting buffer pool
*
* Makes sure the sink pad video buffer pool is created with the
* appropriate caps defined in the @sinkpad.
*
* Returns: %TRUE if successful, %FALSE otherwise.
*/
static gboolean
ensure_sinkpad_buffer_pool (GstVaapiPluginBase * plugin, GstPad * sinkpad)
{
GstVaapiPadPrivate *sinkpriv = GST_VAAPI_PAD_PRIVATE (sinkpad);
GstCaps *caps = sinkpriv->caps;
GstBufferPool *pool;
guint size;
/* video decoders don't use a buffer pool in the sink pad */
if (GST_IS_VIDEO_DECODER (plugin))
return TRUE;
if (!gst_vaapi_plugin_base_ensure_display (plugin))
return FALSE;
if (sinkpriv->buffer_pool) {
if (gst_vaapi_buffer_pool_caps_is_equal (sinkpriv->buffer_pool, caps))
return TRUE;
gst_buffer_pool_set_active (sinkpriv->buffer_pool, FALSE);
g_clear_object (&sinkpriv->buffer_pool);
g_clear_object (&sinkpriv->allocator);
sinkpriv->buffer_size = 0;
}
if (!ensure_sinkpad_allocator (plugin, sinkpad, caps, &size))
return FALSE;
pool =
gst_vaapi_plugin_base_create_pool (plugin, caps, size,
BUFFER_POOL_SINK_MIN_BUFFERS, 0,
GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_VIDEO_META, sinkpriv->allocator);
if (!pool)
return FALSE;
sinkpriv->buffer_pool = pool;
sinkpriv->buffer_size = size;
return TRUE;
}
static gboolean
_set_srcpad_caps (GstVaapiPluginBase * plugin, GstPad * srcpad, GstCaps * caps)
{
GstVaapiPadPrivate *srcpriv = NULL;
if (caps) {
g_assert (srcpad);
srcpriv = GST_VAAPI_PAD_PRIVATE (srcpad);
g_assert (srcpriv);
if (caps != srcpriv->caps) {
if (!gst_video_info_from_caps (&srcpriv->info, caps))
return FALSE;
if (srcpriv->buffer_pool
&& !gst_vaapi_buffer_pool_caps_is_equal (srcpriv->buffer_pool,
caps)) {
gst_buffer_pool_set_active (srcpriv->buffer_pool, FALSE);
g_clear_object (&srcpriv->buffer_pool);
g_clear_object (&srcpriv->allocator);
plugin_reset_texture_map (plugin);
}
gst_caps_replace (&srcpriv->caps, caps);
}
}
return TRUE;
}
static gboolean
_set_sinkpad_caps (GstVaapiPluginBase * plugin, GstPad * sinkpad,
GstCaps * caps)
{
GstVaapiPadPrivate *sinkpriv = NULL;
if (caps) {
g_assert (sinkpad);
sinkpriv = GST_VAAPI_PAD_PRIVATE (sinkpad);
g_assert (sinkpriv);
if (caps != sinkpriv->caps) {
if (!gst_video_info_from_caps (&sinkpriv->info, caps))
return FALSE;
gst_caps_replace (&sinkpriv->caps, caps);
sinkpriv->caps_is_raw = !gst_caps_has_vaapi_surface (caps);
}
if (!ensure_sinkpad_buffer_pool (plugin, sinkpad))
return FALSE;
}
return TRUE;
}
/**
* gst_vaapi_plugin_base_pad_set_caps:
* @plugin: a #GstVaapiPluginBase
* @sinkpad: the sink pad to set @incaps on
* @incaps: the sink pad (input) caps
* @srcpad: the src pad to set @outcaps on
* @outcaps: the src pad (output) caps
*
* Notifies the base plugin object of the new input and output caps,
* obtained from the subclass on the requested pads.
*
* Returns: %TRUE if the update of caps was successful, %FALSE otherwise.
*/
gboolean
gst_vaapi_plugin_base_pad_set_caps (GstVaapiPluginBase * plugin,
GstPad * sinkpad, GstCaps * incaps, GstPad * srcpad, GstCaps * outcaps)
{
return _set_sinkpad_caps (plugin, sinkpad, incaps)
&& _set_srcpad_caps (plugin, srcpad, outcaps);
}
/**
* gst_vaapi_plugin_base_set_caps:
* @plugin: a #GstVaapiPluginBase
* @incaps: the sink pad (input) caps
* @outcaps: the src pad (output) caps
*
* Notifies the base plugin object of the new input and output caps,
* obtained from the subclass, on the base plugin static pads.
*
* Returns: %TRUE if the update of caps was successful, %FALSE otherwise.
*/
gboolean
gst_vaapi_plugin_base_set_caps (GstVaapiPluginBase * plugin, GstCaps * incaps,
GstCaps * outcaps)
{
return gst_vaapi_plugin_base_pad_set_caps (plugin, plugin->sinkpad, incaps,
plugin->srcpad, outcaps);
}
/**
* gst_vaapi_plugin_base_pad_propose_allocation:
* @plugin: a #GstVaapiPluginBase
* @sinkpad: the sinkpad to configure the allocation query on
* @query: the allocation query to configure
*
* Proposes allocation parameters to the upstream elements on the requested
* sinkpad.
*
* Returns: %TRUE if successful, %FALSE otherwise.
*/
gboolean
gst_vaapi_plugin_base_pad_propose_allocation (GstVaapiPluginBase * plugin,
GstPad * sinkpad, GstQuery * query)
{
GstVaapiPadPrivate *sinkpriv = GST_VAAPI_PAD_PRIVATE (sinkpad);
GstCaps *caps = NULL;
GstBufferPool *pool = NULL;
gboolean need_pool;
guint size = 0, n_allocators;
gst_query_parse_allocation (query, &caps, &need_pool);
if (!caps)
goto error_no_caps;
if (!ensure_sinkpad_allocator (plugin, sinkpad, caps, &size))
return FALSE;
if (need_pool) {
pool = gst_vaapi_plugin_base_create_pool (plugin, caps, size,
BUFFER_POOL_SINK_MIN_BUFFERS, 0,
GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_VIDEO_META, sinkpriv->allocator);
if (!pool)
return FALSE;
}
/* Set sinkpad allocator as the last allocation param.
*
* If there's none, set system's allocator first and VAAPI allocator
* second
*/
n_allocators = gst_query_get_n_allocation_params (query);
if (n_allocators == 0) {
GstAllocator *allocator;
allocator = gst_allocator_find (GST_ALLOCATOR_SYSMEM);
gst_query_add_allocation_param (query, allocator, NULL);
gst_object_unref (allocator);
}
gst_query_add_allocation_param (query, sinkpriv->allocator, NULL);
gst_query_add_allocation_pool (query, pool, size,
BUFFER_POOL_SINK_MIN_BUFFERS, 0);
if (pool)
gst_object_unref (pool);
gst_query_add_allocation_meta (query, GST_VAAPI_VIDEO_META_API_TYPE, NULL);
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
return TRUE;
/* ERRORS */
error_no_caps:
{
GST_INFO_OBJECT (plugin, "no caps specified");
return FALSE;
}
}
/**
* gst_vaapi_plugin_base_propose_allocation:
* @plugin: a #GstVaapiPluginBase
* @query: the allocation query to configure
*
* Proposes allocation parameters to the upstream elements on the base plugin
* static sinkpad.
*
* Returns: %TRUE if successful, %FALSE otherwise.
*/
gboolean
gst_vaapi_plugin_base_propose_allocation (GstVaapiPluginBase * plugin,
GstQuery * query)
{
return gst_vaapi_plugin_base_pad_propose_allocation (plugin, plugin->sinkpad,
query);
}
/**
* gst_vaapi_plugin_base_decide_allocation:
* @plugin: a #GstVaapiPluginBase
* @query: the allocation query to parse
* @feature: the desired #GstVaapiCapsFeature, or zero to find the
* preferred one
*
* Decides allocation parameters for the downstream elements on the base
* plugin static srcpad.
*
* Returns: %TRUE if successful, %FALSE otherwise.
*/
gboolean
gst_vaapi_plugin_base_decide_allocation (GstVaapiPluginBase * plugin,
GstQuery * query)
{
GstVaapiPadPrivate *srcpriv = GST_VAAPI_PAD_PRIVATE (plugin->srcpad);
GstCaps *caps = NULL;
GstBufferPool *pool;
GstVideoInfo vi;
guint i, size, min, max, pool_options, num_allocators;
gint index_allocator;
gboolean update_pool = FALSE;
#if (USE_GLX || USE_EGL)
guint idx;
#endif
gst_query_parse_allocation (query, &caps, NULL);
if (!caps)
goto error_no_caps;
pool_options = 0;
if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL))
pool_options |= GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_VIDEO_META;
#if (USE_GLX || USE_EGL)
if (gst_query_find_allocation_meta (query,
GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, &idx) &&
gst_vaapi_caps_feature_contains (caps,
GST_VAAPI_CAPS_FEATURE_GL_TEXTURE_UPLOAD_META))
pool_options |= GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_GL_TEXTURE_UPLOAD;
#if USE_GST_GL_HELPERS
if (!plugin->gl_context &&
(pool_options & GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_GL_TEXTURE_UPLOAD)) {
const GstStructure *params;
GstObject *gl_context;
gst_query_parse_nth_allocation_meta (query, idx, &params);
if (params) {
if (gst_structure_get (params, "gst.gl.GstGLContext", GST_TYPE_GL_CONTEXT,
&gl_context, NULL) && gl_context) {
gst_vaapi_plugin_base_set_gl_context (plugin, gl_context);
gst_vaapi_plugin_base_set_srcpad_can_dmabuf (plugin, gl_context);
gst_object_unref (gl_context);
}
}
}
#endif
#endif
/* Make sure the display we pass down to the buffer pool is actually
the expected one, especially when the downstream element requires
a GLX or EGL display */
if (!gst_vaapi_plugin_base_ensure_display (plugin))
goto error_ensure_display;
if (!gst_video_info_from_caps (&vi, caps))
goto error_invalid_caps;
gst_video_info_force_nv12_if_encoded (&vi);
index_allocator = -1;
num_allocators = gst_query_get_n_allocation_params (query);
for (i = 0; i < num_allocators; i++) {
GstAllocator *allocator = NULL;
GstAllocationParams params;
gst_query_parse_nth_allocation_param (query, i, &allocator, &params);
if (!allocator)
continue;
/* Let's keep the the first allocator if it is not VA-API. It
* might be used if it is required to copy the output frame to a
* new buffer */
if (i == 0
&& g_strcmp0 (allocator->mem_type, GST_VAAPI_VIDEO_MEMORY_NAME) != 0) {
if (srcpriv->other_allocator)
gst_object_unref (srcpriv->other_allocator);
srcpriv->other_allocator = allocator;
srcpriv->other_allocator_params = params;
continue;
}
if (g_strcmp0 (allocator->mem_type, GST_VAAPI_VIDEO_MEMORY_NAME) == 0) {
GST_DEBUG_OBJECT (plugin, "found vaapi allocator in query %"
GST_PTR_FORMAT, allocator);
index_allocator = i;
if (srcpriv->allocator)
gst_object_unref (srcpriv->allocator);
srcpriv->allocator = allocator;
break;
}
gst_object_unref (allocator);
}
if (gst_query_get_n_allocation_pools (query) > 0) {
gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
update_pool = TRUE;
size = MAX (size, GST_VIDEO_INFO_SIZE (&vi));
if (pool) {
/* Check whether downstream element proposed a bufferpool but did
not provide a correct propose_allocation() implementation */
if (gst_buffer_pool_has_option (pool,
GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT))
pool_options |= GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT;
/* GstVaapiVideoMeta is mandatory, and this implies VA surface memory */
if (!gst_buffer_pool_has_option (pool,
GST_BUFFER_POOL_OPTION_VAAPI_VIDEO_META)) {
GST_INFO_OBJECT (plugin, "ignoring non-VAAPI pool: %" GST_PTR_FORMAT,
pool);
g_clear_object (&pool);
}
}
} else {
pool = NULL;
size = GST_VIDEO_INFO_SIZE (&vi);
min = max = 0;
}
if (!pool) {
if (!ensure_srcpad_allocator (plugin, plugin->srcpad, &vi, caps))
goto error;
size = GST_VIDEO_INFO_SIZE (&vi); /* size might be updated by
* allocator */
pool = gst_vaapi_plugin_base_create_pool (plugin, caps, size, min, max,
pool_options, srcpriv->allocator);
if (!pool)
goto error;
}
if (update_pool)
gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
else
gst_query_add_allocation_pool (query, pool, size, min, max);
/* allocator might be updated by ensure_srcpad_allocator() */
if (srcpriv->allocator) {
if (index_allocator > 0) {
gst_query_set_nth_allocation_param (query, index_allocator,
srcpriv->allocator, NULL);
} else {
GST_DEBUG_OBJECT (plugin, "adding allocator in query %" GST_PTR_FORMAT,
srcpriv->allocator);
gst_query_add_allocation_param (query, srcpriv->allocator, NULL);
}
}
g_clear_object (&srcpriv->buffer_pool);
srcpriv->buffer_pool = pool;
/* if downstream doesn't support GstVideoMeta, and the negotiated
* caps are raw video, and the used allocator is the VA-API one, we
* should copy the VA-API frame into a dumb buffer */
plugin->copy_output_frame = gst_vaapi_video_buffer_pool_copy_buffer (pool);
return TRUE;
/* ERRORS */
error_no_caps:
{
GST_ERROR_OBJECT (plugin, "no caps specified");
return FALSE;
}
error_invalid_caps:
{
GST_ERROR_OBJECT (plugin, "invalid caps %" GST_PTR_FORMAT, caps);
return FALSE;
}
error_ensure_display:
{
GST_ERROR_OBJECT (plugin, "failed to ensure display of type %d",
plugin->display_type_req);
return FALSE;
}
error:
{
/* error message already sent */
return FALSE;
}
}
/**
* gst_vaapi_plugin_base_pad_get_input_buffer:
* @plugin: a #GstVaapiPluginBase
* @sinkpad: the sink pad to obtain input buffer on
* @inbuf: the sink pad (input) buffer
* @outbuf_ptr: the pointer to location to the VA surface backed buffer
*
* Acquires the static sink pad (input) buffer as a VA surface backed
* buffer. This is mostly useful for raw YUV buffers, as source
* buffers that are already backed as a VA surface are passed
* verbatim.
*
* Returns: #GST_FLOW_OK if the buffer could be acquired
*/
GstFlowReturn
gst_vaapi_plugin_base_pad_get_input_buffer (GstVaapiPluginBase * plugin,
GstPad * sinkpad, GstBuffer * inbuf, GstBuffer ** outbuf_ptr)
{
GstVaapiPadPrivate *sinkpriv = GST_VAAPI_PAD_PRIVATE (sinkpad);
GstVaapiVideoMeta *meta;
GstBuffer *outbuf;
GstVideoFrame src_frame, out_frame;
gboolean success;
g_return_val_if_fail (inbuf != NULL, GST_FLOW_ERROR);
g_return_val_if_fail (outbuf_ptr != NULL, GST_FLOW_ERROR);
meta = gst_buffer_get_vaapi_video_meta (inbuf);
if (meta) {
*outbuf_ptr = gst_buffer_ref (inbuf);
return GST_FLOW_OK;
}
if (!sinkpriv->caps_is_raw)
goto error_invalid_buffer;
if (!sinkpriv->buffer_pool)
goto error_no_pool;
if (!gst_buffer_pool_is_active (sinkpriv->buffer_pool) &&
!gst_buffer_pool_set_active (sinkpriv->buffer_pool, TRUE))
goto error_active_pool;
outbuf = NULL;
if (gst_buffer_pool_acquire_buffer (sinkpriv->buffer_pool,
&outbuf, NULL) != GST_FLOW_OK)
goto error_create_buffer;
if (is_dma_buffer (inbuf)) {
if (!plugin_bind_dma_to_vaapi_buffer (plugin, sinkpad, inbuf, outbuf))
goto error_bind_dma_buffer;
goto done;
}
if (!gst_video_frame_map (&src_frame, &sinkpriv->info, inbuf, GST_MAP_READ))
goto error_map_src_buffer;
if (!gst_video_frame_map (&out_frame, &sinkpriv->info, outbuf, GST_MAP_WRITE))
goto error_map_dst_buffer;
success = gst_video_frame_copy (&out_frame, &src_frame);
gst_video_frame_unmap (&out_frame);
gst_video_frame_unmap (&src_frame);
if (!success)
goto error_copy_buffer;
done:
if (!gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_FLAGS |
GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_META, 0, -1))
return GST_FLOW_ERROR;
*outbuf_ptr = outbuf;
return GST_FLOW_OK;
/* ERRORS */
error_no_pool:
{
GST_ELEMENT_ERROR (plugin, STREAM, FAILED,
("no buffer pool was negotiated"), ("no buffer pool was negotiated"));
return GST_FLOW_ERROR;
}
error_active_pool:
{
GST_ELEMENT_ERROR (plugin, STREAM, FAILED,
("failed to activate buffer pool"), ("failed to activate buffer pool"));
return GST_FLOW_ERROR;
}
error_map_dst_buffer:
{
gst_video_frame_unmap (&src_frame);
// fall-through
}
error_map_src_buffer:
{
GST_WARNING ("failed to map buffer");
gst_buffer_unref (outbuf);
return GST_FLOW_NOT_SUPPORTED;
}
/* ERRORS */
error_invalid_buffer:
{
GST_ELEMENT_ERROR (plugin, STREAM, FAILED,
("failed to validate source buffer"),
("failed to validate source buffer"));
return GST_FLOW_ERROR;
}
error_create_buffer:
{
GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("Allocation failed"),
("failed to create buffer"));
return GST_FLOW_ERROR;
}
error_bind_dma_buffer:
{
GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("Allocation failed"),
("failed to bind dma_buf to VA surface buffer"));
gst_buffer_unref (outbuf);
return GST_FLOW_ERROR;
}
error_copy_buffer:
{
GST_WARNING_OBJECT (plugin, "failed to upload buffer to VA surface");
gst_buffer_unref (outbuf);
return GST_FLOW_NOT_SUPPORTED;
}
}
/**
* gst_vaapi_plugin_base_get_input_buffer:
* @plugin: a #GstVaapiPluginBase
* @inbuf: the sink pad (input) buffer
* @outbuf_ptr: the pointer to location to the VA surface backed buffer
*
* Acquires the static sink pad (input) buffer as a VA surface backed
* buffer. This is mostly useful for raw YUV buffers, as source
* buffers that are already backed as a VA surface are passed
* verbatim.
*
* Returns: #GST_FLOW_OK if the buffer could be acquired
*/
GstFlowReturn
gst_vaapi_plugin_base_get_input_buffer (GstVaapiPluginBase * plugin,
GstBuffer * inbuf, GstBuffer ** outbuf_ptr)
{
return gst_vaapi_plugin_base_pad_get_input_buffer (plugin, plugin->sinkpad,
inbuf, outbuf_ptr);
}
/**
* gst_vaapi_plugin_base_set_gl_context:
* @plugin: a #GstVaapiPluginBase
* @object: the new GL context from downstream
*
* Registers the new GL context. The change is effective at the next
* call to gst_vaapi_plugin_base_ensure_display(), where the
* underlying display object could be re-allocated to fit the GL
* context needs
*/
void
gst_vaapi_plugin_base_set_gl_context (GstVaapiPluginBase * plugin,
GstObject * object)
{
#if USE_GST_GL_HELPERS
GstGLContext *const gl_context = GST_GL_CONTEXT (object);
GstVaapiDisplayType display_type;
if (plugin->gl_context == object)
return;
gst_object_replace (&plugin->gl_context, object);
switch (gst_gl_context_get_gl_platform (gl_context)) {
#if USE_GLX
case GST_GL_PLATFORM_GLX:
display_type = GST_VAAPI_DISPLAY_TYPE_GLX;
break;
#endif
case GST_GL_PLATFORM_EGL:
#if USE_EGL
display_type = GST_VAAPI_DISPLAY_TYPE_EGL;
break;
#endif
default:
display_type = plugin->display_type;
break;
}
GST_INFO_OBJECT (plugin, "GL context: %" GST_PTR_FORMAT, plugin->gl_context);
gst_vaapi_plugin_base_set_display_type (plugin, display_type);
#endif
}
/**
* gst_vaapi_plugin_base_create_gl_context:
* @plugin: a #GstVaapiPluginBase
*
* It queries downstream and upstream for a #GstGLDisplay and a other
* #GstGLContext. If not found, a new #GstGLDisplay and #GstGLContext
* are created, if it is possible.
*
* Returns: (transfer full) a new created #GstGLContext or %NULL
**/
GstObject *
gst_vaapi_plugin_base_create_gl_context (GstVaapiPluginBase * plugin)
{
#if USE_GST_GL_HELPERS
GstGLContext *gl_other_context = NULL, *gl_context = NULL;
GstGLDisplay *gl_display = NULL;
if (!plugin->gl_display)
return NULL;
gl_display = (GstGLDisplay *) plugin->gl_display;
if (gst_gl_display_get_handle_type (gl_display) == GST_GL_DISPLAY_TYPE_ANY)
goto no_valid_gl_display;
gl_other_context = (GstGLContext *) plugin->gl_other_context;
GST_INFO_OBJECT (plugin, "creating a new GstGL context");
GST_OBJECT_LOCK (gl_display);
do {
if (gl_context)
gst_object_unref (gl_context);
gl_context = gst_gl_display_get_gl_context_for_thread (gl_display, NULL);
if (!gl_context) {
if (!gst_gl_display_create_context (gl_display, gl_other_context,
&gl_context, NULL))
break;
}
} while (!gst_gl_display_add_context (gl_display, gl_context));
GST_OBJECT_UNLOCK (gl_display);
return GST_OBJECT_CAST (gl_context);
/* ERRORS */
no_valid_gl_display:
{
GST_INFO_OBJECT (plugin, "No valid GL display found");
gst_object_replace (&plugin->gl_display, NULL);
gst_object_replace (&plugin->gl_other_context, NULL);
return NULL;
}
#else
return NULL;
#endif
}
static GArray *
extract_allowed_surface_formats (GstVaapiDisplay * display,
GArray * img_formats, GstVideoFormat specified_format,
GstPadDirection direction)
{
guint i;
GArray *out_formats;
GstVaapiSurface *surface = NULL;
g_assert (direction == GST_PAD_SRC || direction == GST_PAD_SINK);
out_formats =
g_array_sized_new (FALSE, FALSE, sizeof (GstVideoFormat),
img_formats->len);
if (!out_formats)
return NULL;
for (i = 0; i < img_formats->len; i++) {
const GstVideoFormat img_format =
g_array_index (img_formats, GstVideoFormat, i);
GstVaapiImage *image;
GstVideoInfo vi;
GstVideoFormat surface_format;
gboolean res;
if (img_format == GST_VIDEO_FORMAT_UNKNOWN)
continue;
surface_format =
(specified_format == GST_VIDEO_FORMAT_UNKNOWN) ?
img_format : specified_format;
if (!surface) {
gst_video_info_set_format (&vi, surface_format, 64, 64);
surface = gst_vaapi_surface_new_full (display, &vi, 0);
if (!surface)
continue;
}
image = gst_vaapi_image_new (display, img_format, 64, 64);
if (!image) {
/* Just reuse the surface if the format is specified */
if (specified_format == GST_VIDEO_FORMAT_UNKNOWN)
gst_mini_object_replace ((GstMiniObject **) & surface, NULL);
continue;
}
res = FALSE;
if (direction == GST_PAD_SRC) {
res = gst_vaapi_surface_get_image (surface, image);
} else {
if (!gst_vaapi_display_has_driver_quirks (display,
GST_VAAPI_DRIVER_QUIRK_NO_CHECK_SURFACE_PUT_IMAGE))
res = gst_vaapi_surface_put_image (surface, image);
else
res = TRUE; /* Let's say it's possible to upload
* all formats */
}
if (res)
g_array_append_val (out_formats, img_format);
gst_vaapi_image_unref (image);
/* Just reuse the surface if the format is specified */
if (specified_format == GST_VIDEO_FORMAT_UNKNOWN)
gst_mini_object_replace ((GstMiniObject **) & surface, NULL);
}
if (surface)
gst_vaapi_surface_unref (surface);
if (out_formats->len == 0) {
g_array_unref (out_formats);
return NULL;
}
return out_formats;
}
static gboolean
ensure_allowed_raw_caps (GstVaapiPluginBase * plugin, GstVideoFormat format,
GstPadDirection direction)
{
GArray *formats, *out_formats;
GstVaapiDisplay *display;
GstCaps *out_caps;
gboolean ret = FALSE;
if (plugin->allowed_raw_caps)
return TRUE;
out_formats = NULL;
display = gst_object_ref (plugin->display);
formats = gst_vaapi_display_get_image_formats (display);
if (!formats)
goto bail;
out_formats =
extract_allowed_surface_formats (display, formats, format, direction);
if (!out_formats)
goto bail;
out_caps = gst_vaapi_video_format_new_template_caps_from_list (out_formats);
if (!out_caps)
goto bail;
gst_caps_replace (&plugin->allowed_raw_caps, out_caps);
gst_caps_unref (out_caps);
ret = TRUE;
bail:
if (formats)
g_array_unref (formats);
if (out_formats)
g_array_unref (out_formats);
gst_object_unref (display);
return ret;
}
/**
* gst_vaapi_plugin_base_get_allowed_sinkpad_raw_caps:
* @plugin: a #GstVaapiPluginBase
*
* Returns the raw #GstCaps allowed by the element.
*
* Returns: the allowed raw #GstCaps or %NULL
**/
GstCaps *
gst_vaapi_plugin_base_get_allowed_sinkpad_raw_caps (GstVaapiPluginBase * plugin)
{
if (!ensure_allowed_raw_caps (plugin, GST_VIDEO_FORMAT_UNKNOWN, GST_PAD_SINK))
return NULL;
return plugin->allowed_raw_caps;
}
/**
* gst_vaapi_plugin_base_get_allowed_srcpad_raw_caps:
* @plugin: a #GstVaapiPluginBase
* @format: a #GstVideoFormat, the format we need to check
*
* Returns the raw #GstCaps allowed by the element.
*
* Returns: the allowed raw #GstCaps or %NULL
**/
GstCaps *
gst_vaapi_plugin_base_get_allowed_srcpad_raw_caps (GstVaapiPluginBase *
plugin, GstVideoFormat format)
{
if (!ensure_allowed_raw_caps (plugin, format, GST_PAD_SRC))
return NULL;
return plugin->allowed_raw_caps;
}
/**
* gst_vaapi_plugin_base_set_srcpad_can_dmabuf:
* @plugin: a #GstVaapiPluginBase
* @object: the GL context from gst-gl
*
* This function will determine if @object supports dmabuf
* importing on the base plugin static srcpad.
*
* Please note that the context @object should come from downstream.
**/
void
gst_vaapi_plugin_base_set_srcpad_can_dmabuf (GstVaapiPluginBase * plugin,
GstObject * object)
{
#if USE_EGL && USE_GST_GL_HELPERS
GstVaapiPadPrivate *srcpriv = GST_VAAPI_PAD_PRIVATE (plugin->srcpad);
GstGLContext *const gl_context = GST_GL_CONTEXT (object);
srcpriv->can_dmabuf =
(!(gst_gl_context_get_gl_api (gl_context) & GST_GL_API_GLES1)
&& gst_gl_context_check_feature (gl_context,
"EGL_EXT_image_dma_buf_import"));
#endif
}
static void
_init_performance_debug (void)
{
#ifndef GST_DISABLE_GST_DEBUG
static volatile gsize _init = 0;
if (g_once_init_enter (&_init)) {
GST_DEBUG_CATEGORY_GET (CAT_PERFORMANCE, "GST_PERFORMANCE");
g_once_init_leave (&_init, 1);
}
#endif
}
/**
* gst_vaapi_plugin_copy_va_buffer:
* @plugin: a #GstVaapiPluginBase
* @inbuf: a #GstBuffer with VA memory type
* @outbuf: a #GstBuffer with system allocated memory
*
* Copy @inbuf to @outbuf. This if required when downstream doesn't
* support GstVideoMeta, and since VA memory may have custom strides a
* frame copy is required.
*
* Returns: %FALSE if the copy failed, otherwise %TRUE. Also returns
* %TRUE if it is not required to do the copy
**/
gboolean
gst_vaapi_plugin_copy_va_buffer (GstVaapiPluginBase * plugin,
GstBuffer * inbuf, GstBuffer * outbuf)
{
GstVaapiPadPrivate *srcpriv = GST_VAAPI_PAD_PRIVATE (plugin->srcpad);
GstVideoMeta *vmeta;
GstVideoFrame src_frame, dst_frame;
gboolean success;
if (!plugin->copy_output_frame)
return TRUE;
/* inbuf shall have video meta */
vmeta = gst_buffer_get_video_meta (inbuf);
if (!vmeta)
return FALSE;
_init_performance_debug ();
GST_CAT_INFO (CAT_PERFORMANCE, "copying VA buffer to system memory buffer");
if (!gst_video_frame_map (&src_frame, &srcpriv->info, inbuf, GST_MAP_READ))
return FALSE;
if (!gst_video_frame_map (&dst_frame, &srcpriv->info, outbuf, GST_MAP_WRITE)) {
gst_video_frame_unmap (&src_frame);
return FALSE;
}
success = gst_video_frame_copy (&dst_frame, &src_frame);
gst_video_frame_unmap (&dst_frame);
gst_video_frame_unmap (&src_frame);
if (success) {
gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS
| GST_BUFFER_COPY_FLAGS, 0, -1);
}
return success;
}