mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-18 15:51:11 +00:00
1640 lines
47 KiB
C
1640 lines
47 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, ¶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, 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 {
|
|
res = gst_vaapi_surface_put_image (surface, image);
|
|
}
|
|
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;
|
|
}
|