mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-05 16:06:43 +00:00
089ef59c37
Since nobody uses it, just remove it. Thus extract_allowed_surface_formats() is refactored to attend only gst_vaapi_plugin_base_get_allowed_sinkpad_raw_caps(). Now a surface is created when the image chorma is different from the previous one. And if the driver has the quirk, it outputs all the supported image formats without trying them. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer-vaapi/-/merge_requests/381>
1607 lines
46 KiB
C
1607 lines
46 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);
|
|
gst_vaapi_display_replace (&plugin->display, NULL);
|
|
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)
|
|
{
|
|
gst_caps_replace (&plugin->allowed_raw_caps, NULL);
|
|
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_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_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, ¶ms);
|
|
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, ¶ms);
|
|
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)
|
|
{
|
|
guint i;
|
|
GArray *out_formats;
|
|
guint surface_chroma = 0;
|
|
GstVaapiSurface *surface = NULL;
|
|
|
|
/* let's assume all the formats are possible to download */
|
|
if (gst_vaapi_display_has_driver_quirks (display,
|
|
GST_VAAPI_DRIVER_QUIRK_NO_CHECK_SURFACE_PUT_IMAGE)) {
|
|
out_formats = g_array_ref (img_formats);
|
|
goto bail;
|
|
}
|
|
|
|
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;
|
|
guint img_chroma;
|
|
|
|
if (img_format == GST_VIDEO_FORMAT_UNKNOWN)
|
|
continue;
|
|
|
|
img_chroma = gst_vaapi_video_format_get_chroma_type (img_format);
|
|
if (img_chroma != surface_chroma) {
|
|
if (surface)
|
|
gst_vaapi_surface_unref (surface);
|
|
gst_video_info_set_format (&vi, img_format, 64, 64);
|
|
surface = gst_vaapi_surface_new_full (display, &vi, 0);
|
|
if (!surface)
|
|
continue;
|
|
surface_chroma = img_chroma;
|
|
}
|
|
|
|
image = gst_vaapi_image_new (display, img_format, 64, 64);
|
|
if (!image)
|
|
continue;
|
|
if (gst_vaapi_surface_put_image (surface, image))
|
|
g_array_append_val (out_formats, img_format);
|
|
gst_vaapi_image_unref (image);
|
|
}
|
|
|
|
if (surface)
|
|
gst_vaapi_surface_unref (surface);
|
|
|
|
bail:
|
|
if (out_formats->len == 0) {
|
|
g_array_unref (out_formats);
|
|
return NULL;
|
|
}
|
|
return out_formats;
|
|
}
|
|
|
|
static gboolean
|
|
ensure_allowed_raw_caps (GstVaapiPluginBase * plugin)
|
|
{
|
|
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);
|
|
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))
|
|
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;
|
|
}
|