gstreamer/gst/vaapi/gstvaapipluginbase.c
2015-06-17 09:53:29 +03:00

987 lines
28 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"
#include <gst/allocators/allocators.h>
/* Default debug category is from the subclass */
#define GST_CAT_DEFAULT (plugin->debug_category)
/* 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 %p", 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_vaapi_display_unref (display);
}
static void
plugin_set_context (GstElement * element, GstContext * context)
{
GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element);
GstVaapiDisplay *display = NULL;
if (gst_vaapi_video_context_get_display (context, &display))
plugin_set_display (plugin, display);
}
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 gboolean
plugin_update_sinkpad_info_from_buffer (GstVaapiPluginBase * plugin,
GstBuffer * buf)
{
GstVideoInfo *const vip = &plugin->sinkpad_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,
GstBuffer * inbuf, GstBuffer * outbuf)
{
GstVideoInfo *const vip = &plugin->sinkpad_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, inbuf))
goto error_update_sinkpad_info;
meta = gst_buffer_get_vaapi_video_meta (outbuf);
g_return_val_if_fail (meta != NULL, FALSE);
surface = gst_vaapi_surface_new_with_dma_buf_handle (plugin->display, fd,
GST_VIDEO_INFO_SIZE (vip), GST_VIDEO_INFO_FORMAT (vip),
GST_VIDEO_INFO_WIDTH (vip), GST_VIDEO_INFO_HEIGHT (vip),
vip->offset, vip->stride);
if (!surface)
goto error_create_surface;
proxy = gst_vaapi_surface_proxy_new (surface);
gst_vaapi_object_unref (surface);
if (!proxy)
goto error_create_proxy;
gst_vaapi_surface_proxy_set_destroy_notify (proxy,
(GDestroyNotify) gst_buffer_unref, (gpointer) gst_buffer_ref (inbuf));
gst_vaapi_video_meta_set_surface_proxy (meta, proxy);
gst_vaapi_surface_proxy_unref (proxy);
return TRUE;
/* ERRORS */
error_update_sinkpad_info:
GST_ERROR ("failed to update sink pad video info from video meta");
return FALSE;
error_create_surface:
GST_ERROR ("failed to create VA surface from dma_buf handle");
return FALSE;
error_create_proxy:
GST_ERROR ("failed to create VA surface proxy from wrapped VA surface");
return FALSE;
}
void
gst_vaapi_plugin_base_class_init (GstVaapiPluginBaseClass * klass)
{
klass->has_interface = default_has_interface;
klass->display_changed = default_display_changed;
GstElementClass *const element_class = GST_ELEMENT_CLASS (klass);
element_class->set_context = GST_DEBUG_FUNCPTR (plugin_set_context);
}
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");
plugin->sinkpad_query = GST_PAD_QUERYFUNC (plugin->sinkpad);
gst_video_info_init (&plugin->sinkpad_info);
/* src pad */
if (!(GST_OBJECT_FLAGS (plugin) & GST_ELEMENT_FLAG_SINK)) {
plugin->srcpad = gst_element_get_static_pad (GST_ELEMENT (plugin), "src");
plugin->srcpad_query = GST_PAD_QUERYFUNC (plugin->srcpad);
}
gst_video_info_init (&plugin->srcpad_info);
}
void
gst_vaapi_plugin_base_finalize (GstVaapiPluginBase * plugin)
{
gst_vaapi_plugin_base_close (plugin);
g_free (plugin->display_name);
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)
{
g_clear_object (&plugin->uploader);
gst_vaapi_display_replace (&plugin->display, NULL);
gst_object_replace (&plugin->gl_context, NULL);
gst_caps_replace (&plugin->sinkpad_caps, NULL);
plugin->sinkpad_caps_changed = FALSE;
gst_video_info_init (&plugin->sinkpad_info);
if (plugin->sinkpad_buffer_pool) {
gst_object_unref (plugin->sinkpad_buffer_pool);
plugin->sinkpad_buffer_pool = NULL;
}
g_clear_object (&plugin->srcpad_buffer_pool);
gst_caps_replace (&plugin->srcpad_caps, NULL);
plugin->srcpad_caps_changed = FALSE;
gst_video_info_init (&plugin->srcpad_info);
}
/**
* 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;
}
/**
* gst_vaapi_plugin_base_ensure_uploader:
* @plugin: a #GstVaapiPluginBase
*
* Makes sure the built-in #GstVaapiUploader object is created, or
* that it was successfully notified of any VA display change.
*
* Returns: %TRUE if the uploader was successfully created, %FALSE otherwise.
*/
gboolean
gst_vaapi_plugin_base_ensure_uploader (GstVaapiPluginBase * plugin)
{
if (plugin->uploader) {
if (!gst_vaapi_uploader_ensure_display (plugin->uploader, plugin->display))
return FALSE;
} else {
plugin->uploader = gst_vaapi_uploader_new (plugin->display);
if (!plugin->uploader)
return FALSE;
}
return TRUE;
}
/* Checks whether the supplied pad peer element supports DMABUF sharing */
/* XXX: this is a workaround to the absence of any proposer way to
specify DMABUF memory capsfeatures or bufferpool option to downstream */
static gboolean
has_dmabuf_capable_peer (GstVaapiPluginBase * plugin, GstPad * pad)
{
GstPad *other_pad = NULL;
GstElement *element = NULL;
gchar *element_name = NULL;
gboolean is_dmabuf_capable = FALSE;
gint v;
gst_object_ref (pad);
for (;;) {
other_pad = gst_pad_get_peer (pad);
gst_object_unref (pad);
if (!other_pad)
break;
element = gst_pad_get_parent_element (other_pad);
gst_object_unref (other_pad);
if (!element)
break;
if (GST_IS_PUSH_SRC (element)) {
element_name = gst_element_get_name (element);
if (!element_name || sscanf (element_name, "v4l2src%d", &v) != 1)
break;
v = 0;
g_object_get (element, "io-mode", &v, NULL);
is_dmabuf_capable = v == 5; /* "dmabuf-import" enum value */
break;
} else if (GST_IS_BASE_TRANSFORM (element)) {
element_name = gst_element_get_name (element);
if (!element_name || sscanf (element_name, "capsfilter%d", &v) != 1)
break;
pad = gst_element_get_static_pad (element, "sink");
if (!pad)
break;
} else
break;
g_free (element_name);
element_name = NULL;
g_clear_object (&element);
}
g_free (element_name);
g_clear_object (&element);
return is_dmabuf_capable;
}
/**
* ensure_sinkpad_buffer_pool:
* @plugin: a #GstVaapiPluginBase
* @caps: the initial #GstCaps for the resulting buffer pool
*
* Makes sure the sink pad video buffer pool is created with the
* appropriate @caps.
*
* Returns: %TRUE if successful, %FALSE otherwise.
*/
static gboolean
ensure_sinkpad_buffer_pool (GstVaapiPluginBase * plugin, GstCaps * caps)
{
GstBufferPool *pool;
GstCaps *pool_caps;
GstStructure *config;
GstVideoInfo vi;
gboolean need_pool;
if (!gst_vaapi_plugin_base_ensure_display (plugin))
return FALSE;
if (plugin->sinkpad_buffer_pool) {
config = gst_buffer_pool_get_config (plugin->sinkpad_buffer_pool);
gst_buffer_pool_config_get_params (config, &pool_caps, NULL, NULL, NULL);
need_pool = !gst_caps_is_equal (caps, pool_caps);
gst_structure_free (config);
if (!need_pool)
return TRUE;
g_clear_object (&plugin->sinkpad_buffer_pool);
plugin->sinkpad_buffer_size = 0;
}
pool = gst_vaapi_video_buffer_pool_new (plugin->display);
if (!pool)
goto error_create_pool;
gst_video_info_init (&vi);
gst_video_info_from_caps (&vi, caps);
if (GST_VIDEO_INFO_FORMAT (&vi) == GST_VIDEO_FORMAT_ENCODED) {
GST_DEBUG ("assume video buffer pool format is NV12");
gst_video_info_set_format (&vi, GST_VIDEO_FORMAT_NV12,
GST_VIDEO_INFO_WIDTH (&vi), GST_VIDEO_INFO_HEIGHT (&vi));
}
plugin->sinkpad_buffer_size = vi.size;
config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_params (config, caps,
plugin->sinkpad_buffer_size, 0, 0);
gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_VAAPI_VIDEO_META);
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
if (!gst_buffer_pool_set_config (pool, config))
goto error_pool_config;
plugin->sinkpad_buffer_pool = pool;
return TRUE;
/* ERRORS */
error_create_pool:
{
GST_ERROR ("failed to create buffer pool");
return FALSE;
}
error_pool_config:
{
GST_ERROR ("failed to reset buffer pool config");
gst_object_unref (pool);
return FALSE;
}
}
/**
* 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.
*
* Returns: %TRUE if the update of caps was successful, %FALSE otherwise.
*/
gboolean
gst_vaapi_plugin_base_set_caps (GstVaapiPluginBase * plugin, GstCaps * incaps,
GstCaps * outcaps)
{
if (incaps && incaps != plugin->sinkpad_caps) {
gst_caps_replace (&plugin->sinkpad_caps, incaps);
if (!gst_video_info_from_caps (&plugin->sinkpad_info, incaps))
return FALSE;
plugin->sinkpad_caps_changed = TRUE;
plugin->sinkpad_caps_is_raw = !gst_caps_has_vaapi_surface (incaps);
}
if (outcaps && outcaps != plugin->srcpad_caps) {
gst_caps_replace (&plugin->srcpad_caps, outcaps);
if (!gst_video_info_from_caps (&plugin->srcpad_info, outcaps))
return FALSE;
plugin->srcpad_caps_changed = TRUE;
}
if (plugin->uploader && plugin->sinkpad_caps_is_raw) {
if (!gst_vaapi_uploader_ensure_display (plugin->uploader, plugin->display))
return FALSE;
if (!gst_vaapi_uploader_ensure_caps (plugin->uploader,
plugin->sinkpad_caps, plugin->srcpad_caps))
return FALSE;
}
if (!ensure_sinkpad_buffer_pool (plugin, plugin->sinkpad_caps))
return FALSE;
return TRUE;
}
/**
* gst_vaapi_plugin_base_propose_allocation:
* @plugin: a #GstVaapiPluginBase
* @query: the allocation query to configure
*
* Proposes allocation parameters to the upstream elements.
*
* Returns: %TRUE if successful, %FALSE otherwise.
*/
gboolean
gst_vaapi_plugin_base_propose_allocation (GstVaapiPluginBase * plugin,
GstQuery * query)
{
GstCaps *caps = NULL;
gboolean need_pool;
gst_query_parse_allocation (query, &caps, &need_pool);
if (need_pool) {
if (!caps)
goto error_no_caps;
if (!ensure_sinkpad_buffer_pool (plugin, caps))
return FALSE;
gst_query_add_allocation_pool (query, plugin->sinkpad_buffer_pool,
plugin->sinkpad_buffer_size, 0, 0);
if (has_dmabuf_capable_peer (plugin, plugin->sinkpad)) {
GstStructure *const config =
gst_buffer_pool_get_config (plugin->sinkpad_buffer_pool);
gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_DMABUF_MEMORY);
if (!gst_buffer_pool_set_config (plugin->sinkpad_buffer_pool, config))
goto error_pool_config;
}
}
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_ERROR ("no caps specified");
return FALSE;
}
error_pool_config:
{
GST_ERROR ("failed to reset buffer pool config");
return FALSE;
}
}
/* XXXX: GStreamer 1.2 doesn't check, in gst_buffer_pool_set_config()
if the config option is already set */
static inline gboolean
gst_vaapi_plugin_base_set_pool_config (GstBufferPool * pool,
const gchar * option)
{
GstStructure *config;
config = gst_buffer_pool_get_config (pool);
if (!gst_buffer_pool_config_has_option (config, option)) {
gst_buffer_pool_config_add_option (config, option);
return gst_buffer_pool_set_config (pool, config);
}
return TRUE;
}
/**
* 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.
*
* Returns: %TRUE if successful, %FALSE otherwise.
*/
gboolean
gst_vaapi_plugin_base_decide_allocation (GstVaapiPluginBase * plugin,
GstQuery * query, guint feature)
{
GstCaps *caps = NULL;
GstBufferPool *pool;
GstStructure *config;
GstVideoInfo vi;
guint size, min, max;
gboolean update_pool = FALSE;
gboolean has_video_meta = FALSE;
gboolean has_video_alignment = FALSE;
#if (USE_GLX || USE_EGL)
gboolean has_texture_upload_meta = FALSE;
guint idx;
#endif
g_return_val_if_fail (plugin->display != NULL, FALSE);
gst_query_parse_allocation (query, &caps, NULL);
/* We don't need any GL context beyond this point if not requested
so explicitly through GstVideoGLTextureUploadMeta */
gst_object_replace (&plugin->gl_context, NULL);
if (!caps)
goto error_no_caps;
if (!feature)
feature =
gst_vaapi_find_preferred_caps_feature (plugin->srcpad,
GST_VIDEO_FORMAT_ENCODED, NULL);
has_video_meta = gst_query_find_allocation_meta (query,
GST_VIDEO_META_API_TYPE, NULL);
#if (USE_GLX || USE_EGL)
has_texture_upload_meta = gst_query_find_allocation_meta (query,
GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, &idx) &&
(feature == GST_VAAPI_CAPS_FEATURE_GL_TEXTURE_UPLOAD_META);
#if USE_GST_GL_HELPERS
if (has_texture_upload_meta) {
const GstStructure *params;
GstObject *gl_context;
gst_query_parse_nth_allocation_meta (query, idx, &params);
if (params) {
if (gst_structure_get (params, "gst.gl.GstGLContext", GST_GL_TYPE_CONTEXT,
&gl_context, NULL) && gl_context) {
gst_vaapi_plugin_base_set_gl_context (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;
gst_video_info_init (&vi);
gst_video_info_from_caps (&vi, caps);
if (GST_VIDEO_INFO_FORMAT (&vi) == GST_VIDEO_FORMAT_ENCODED)
gst_video_info_set_format (&vi, GST_VIDEO_FORMAT_I420,
GST_VIDEO_INFO_WIDTH (&vi), GST_VIDEO_INFO_HEIGHT (&vi));
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, vi.size);
if (pool) {
/* Check whether downstream element proposed a bufferpool but did
not provide a correct propose_allocation() implementation */
has_video_alignment = gst_buffer_pool_has_option (pool,
GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
}
} else {
pool = NULL;
size = vi.size;
min = max = 0;
}
/* GstVaapiVideoMeta is mandatory, and this implies VA surface memory */
if (!pool || !gst_buffer_pool_has_option (pool,
GST_BUFFER_POOL_OPTION_VAAPI_VIDEO_META)) {
GST_INFO_OBJECT (plugin, "%s. Making a new pool", pool == NULL ? "No pool" :
"Pool hasn't GstVaapiVideoMeta");
if (pool)
gst_object_unref (pool);
pool = gst_vaapi_video_buffer_pool_new (plugin->display);
if (!pool)
goto error_create_pool;
config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_params (config, caps, size, min, max);
gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_VAAPI_VIDEO_META);
if (!gst_buffer_pool_set_config (pool, config))
goto config_failed;
}
/* Check whether GstVideoMeta, or GstVideoAlignment, is needed (raw video) */
if (has_video_meta) {
if (!gst_vaapi_plugin_base_set_pool_config (pool,
GST_BUFFER_POOL_OPTION_VIDEO_META))
goto config_failed;
} else if (has_video_alignment) {
if (!gst_vaapi_plugin_base_set_pool_config (pool,
GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT))
goto config_failed;
}
/* GstVideoGLTextureUploadMeta (OpenGL) */
#if (USE_GLX || USE_EGL)
if (has_texture_upload_meta) {
if (!gst_vaapi_plugin_base_set_pool_config (pool,
GST_BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META))
goto config_failed;
}
#endif
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);
g_clear_object (&plugin->srcpad_buffer_pool);
plugin->srcpad_buffer_pool = pool;
return TRUE;
/* ERRORS */
error_no_caps:
{
GST_ERROR_OBJECT (plugin, "no caps specified");
return FALSE;
}
error_ensure_display:
{
GST_ERROR_OBJECT (plugin, "failed to ensure display of type %d",
plugin->display_type_req);
return FALSE;
}
error_create_pool:
{
GST_ERROR_OBJECT (plugin, "failed to create buffer pool");
return FALSE;
}
config_failed:
{
if (pool)
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 FALSE;
}
}
/**
* gst_vaapi_plugin_base_allocate_input_buffer:
* @plugin: a #GstVaapiPluginBase
* @caps: the buffer caps constraints to honour
* @outbuf_ptr: the pointer location to the newly allocated buffer
*
* Creates a buffer that holds a VA surface memory for the sink pad to
* use it as the result for buffer_alloc() impementations.
*
* Return: #GST_FLOW_OK if the buffer could be created.
*/
GstFlowReturn
gst_vaapi_plugin_base_allocate_input_buffer (GstVaapiPluginBase * plugin,
GstCaps * caps, GstBuffer ** outbuf_ptr)
{
GstBuffer *outbuf;
*outbuf_ptr = NULL;
if (!plugin->sinkpad_caps_changed) {
if (!gst_video_info_from_caps (&plugin->sinkpad_info, caps))
return GST_FLOW_NOT_SUPPORTED;
plugin->sinkpad_caps_changed = TRUE;
}
if (!plugin->sinkpad_caps_is_raw)
return GST_FLOW_OK;
if (!gst_vaapi_uploader_ensure_display (plugin->uploader, plugin->display))
return GST_FLOW_NOT_SUPPORTED;
if (!gst_vaapi_uploader_ensure_caps (plugin->uploader, caps, NULL))
return GST_FLOW_NOT_SUPPORTED;
outbuf = gst_vaapi_uploader_get_buffer (plugin->uploader);
if (!outbuf) {
GST_WARNING ("failed to allocate resources for raw YUV buffer");
return GST_FLOW_NOT_SUPPORTED;
}
*outbuf_ptr = outbuf;
return GST_FLOW_OK;
}
/**
* gst_vaapi_plugin_base_get_input_buffer:
* @plugin: a #GstVaapiPluginBase
* @incaps: the sink pad (input) buffer
* @outbuf_ptr: the pointer to location to the VA surface backed buffer
*
* Acquires the 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)
{
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 (!plugin->sinkpad_caps_is_raw)
goto error_invalid_buffer;
if (!plugin->sinkpad_buffer_pool)
goto error_no_pool;
if (!gst_buffer_pool_set_active (plugin->sinkpad_buffer_pool, TRUE))
goto error_active_pool;
outbuf = NULL;
if (gst_buffer_pool_acquire_buffer (plugin->sinkpad_buffer_pool,
&outbuf, NULL) != GST_FLOW_OK)
goto error_create_buffer;
if (is_dma_buffer (inbuf)) {
if (!plugin_bind_dma_to_vaapi_buffer (plugin, inbuf, outbuf))
goto error_bind_dma_buffer;
goto done;
}
if (!gst_video_frame_map (&src_frame, &plugin->sinkpad_info, inbuf,
GST_MAP_READ))
goto error_map_src_buffer;
if (!gst_video_frame_map (&out_frame, &plugin->sinkpad_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:
gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_FLAGS |
GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
*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 ("failed to upload buffer to VA surface");
gst_buffer_unref (outbuf);
return GST_FLOW_NOT_SUPPORTED;
}
}
/**
* 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;
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
#if USE_EGL
case GST_GL_PLATFORM_EGL:
display_type = GST_VAAPI_DISPLAY_TYPE_EGL;
break;
#endif
default:
display_type = plugin->display_type;
break;
}
gst_vaapi_plugin_base_set_display_type (plugin, display_type);
#endif
}