From 977a8f3b0148745299af1e30b5c61d8a09bf689c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Manuel=20J=C3=A1quez=20Leal?= Date: Wed, 17 Feb 2021 17:15:22 +0100 Subject: va: Add base transform class. This base transform class is a derivable class for VA-based filters, for example vapostproc right now, but it will be used also for future elements such as vadeinterlace. Part-of: --- sys/va/gstvabasetransform.c | 807 ++++++++++++++++++++++++++++++++++++++++++++ sys/va/gstvabasetransform.h | 90 +++++ sys/va/meson.build | 1 + 3 files changed, 898 insertions(+) create mode 100644 sys/va/gstvabasetransform.c create mode 100644 sys/va/gstvabasetransform.h (limited to 'sys') diff --git a/sys/va/gstvabasetransform.c b/sys/va/gstvabasetransform.c new file mode 100644 index 000000000..6d6ea97c5 --- /dev/null +++ b/sys/va/gstvabasetransform.c @@ -0,0 +1,807 @@ +/* GStreamer + * Copyright (C) 2021 Igalia, S.L. + * Author: Víctor Jáquez + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstvabasetransform.h" + +#include "gstvaallocator.h" +#include "gstvacaps.h" +#include "gstvapool.h" +#include "gstvautils.h" + +#define GST_CAT_DEFAULT gst_va_base_transform_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +struct _GstVaBaseTransformPrivate +{ + GstVideoInfo srcpad_info; + + GstBufferPool *other_pool; + + GstCaps *sinkpad_caps; + GstVideoInfo sinkpad_info; + GstBufferPool *sinkpad_pool; +}; + +#define gst_va_base_transform_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstVaBaseTransform, gst_va_base_transform, + GST_TYPE_BASE_TRANSFORM, G_ADD_PRIVATE (GstVaBaseTransform) + GST_DEBUG_CATEGORY_INIT (gst_va_base_transform_debug, + "vabasetransform", 0, "vabasetransform element"); + ); + +extern GRecMutex GST_VA_SHARED_LOCK; + +static void +gst_va_base_transform_dispose (GObject * object) +{ + GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (object); + + if (self->priv->other_pool) { + gst_buffer_pool_set_active (self->priv->other_pool, FALSE); + gst_clear_object (&self->priv->other_pool); + } + + gst_clear_caps (&self->out_caps); + gst_clear_caps (&self->in_caps); + + gst_clear_object (&self->filter); + gst_clear_object (&self->display); + + if (self->priv->sinkpad_pool) { + gst_buffer_pool_set_active (self->priv->sinkpad_pool, FALSE); + gst_clear_object (&self->priv->sinkpad_pool); + } + + gst_clear_caps (&self->priv->sinkpad_caps); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_va_base_transform_init (GstVaBaseTransform * self) +{ + gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (self), TRUE); + + self->priv = gst_va_base_transform_get_instance_private (self); +} + +static gboolean +gst_va_base_transform_query (GstBaseTransform * trans, + GstPadDirection direction, GstQuery * query) +{ + GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (trans); + gboolean ret = FALSE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONTEXT: + { + GstVaDisplay *display = NULL; + + gst_object_replace ((GstObject **) & display, + (GstObject *) self->display); + ret = gst_va_handle_context_query (GST_ELEMENT_CAST (self), query, + display); + gst_clear_object (&display); + break; + } + default: + ret = GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction, + query); + break; + } + + return ret; +} + +static gboolean +gst_va_base_transform_set_caps (GstBaseTransform * trans, GstCaps * incaps, + GstCaps * outcaps) +{ + GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (trans); + GstVaBaseTransformClass *fclass; + GstVideoInfo in_info, out_info; + gboolean res; + + /* input caps */ + if (!gst_video_info_from_caps (&in_info, incaps)) + goto invalid_caps; + + /* output caps */ + if (!gst_video_info_from_caps (&out_info, outcaps)) + goto invalid_caps; + + fclass = GST_VA_BASE_TRANSFORM_GET_CLASS (self); + if (fclass->set_info) + res = fclass->set_info (self, incaps, &in_info, outcaps, &out_info); + else + res = TRUE; + + self->negotiated = res; + + if (res) { + gst_caps_replace (&self->in_caps, incaps); + gst_caps_replace (&self->out_caps, outcaps); + + self->in_info = in_info; + self->out_info = out_info; + } + + if (self->priv->sinkpad_pool) { + gst_buffer_pool_set_active (self->priv->sinkpad_pool, FALSE); + gst_clear_object (&self->priv->sinkpad_pool); + } + + if (self->priv->other_pool) { + gst_buffer_pool_set_active (self->priv->other_pool, FALSE); + gst_clear_object (&self->priv->other_pool); + } + + return res; + + /* ERRORS */ +invalid_caps: + { + GST_ERROR_OBJECT (self, "invalid caps"); + self->negotiated = FALSE; + return FALSE; + } +} + +/* Answer upstream allocation query. */ +static gboolean +gst_va_base_transform_propose_allocation (GstBaseTransform * trans, + GstQuery * decide_query, GstQuery * query) +{ + GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (trans); + GstAllocator *allocator = NULL; + GstAllocationParams params = { 0, }; + GstBufferPool *pool; + GstCaps *caps; + GstVideoInfo info; + gboolean update_allocator = FALSE; + guint size, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC; /* it migth be + * used by a va + * decoder */ + + gst_clear_caps (&self->priv->sinkpad_caps); + + if (!GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans, + decide_query, query)) + return FALSE; + + /* passthrough, we're done */ + if (!decide_query) + return TRUE; + + if (gst_query_get_n_allocation_pools (query) > 0) + return TRUE; + + gst_query_parse_allocation (query, &caps, NULL); + if (!caps) + return FALSE; + if (!gst_video_info_from_caps (&info, caps)) { + GST_ERROR_OBJECT (self, "Cannot parse caps %" GST_PTR_FORMAT, caps); + return FALSE; + } + + size = GST_VIDEO_INFO_SIZE (&info); + + if (gst_query_get_n_allocation_params (query) > 0) { + gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms); + if (!GST_IS_VA_DMABUF_ALLOCATOR (allocator) + && !GST_IS_VA_ALLOCATOR (allocator)) + gst_clear_object (&allocator); + update_allocator = TRUE; + } else { + gst_allocation_params_init (¶ms); + } + + if (!allocator) { + if (!(allocator = gst_va_base_transform_allocator_from_caps (self, caps))) + return FALSE; + } + + pool = gst_va_pool_new_with_config (caps, size, 1 + self->extra_min_buffers, + 0, usage_hint, allocator, ¶ms); + if (!pool) { + gst_object_unref (allocator); + goto config_failed; + } + + if (update_allocator) + gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms); + else + gst_query_add_allocation_param (query, allocator, ¶ms); + + gst_query_add_allocation_pool (query, pool, size, 1 + self->extra_min_buffers, + 0); + + GST_DEBUG_OBJECT (self, + "proposing %" GST_PTR_FORMAT " with allocator %" GST_PTR_FORMAT, + pool, allocator); + + gst_object_unref (allocator); + gst_object_unref (pool); + + gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); + + self->priv->sinkpad_caps = gst_caps_ref (caps); + + return TRUE; + + /* ERRORS */ +config_failed: + { + GST_ERROR_OBJECT (self, "failed to set config"); + return FALSE; + } +} + +static GstBufferPool * +_create_other_pool (GstAllocator * allocator, + GstAllocationParams * params, GstCaps * caps, guint size) +{ + GstBufferPool *pool = NULL; + GstStructure *config; + + pool = gst_video_buffer_pool_new (); + config = gst_buffer_pool_get_config (pool); + + gst_buffer_pool_config_set_params (config, caps, size, 0, 0); + gst_buffer_pool_config_set_allocator (config, allocator, params); + if (!gst_buffer_pool_set_config (pool, config)) { + gst_clear_object (&pool); + } + + return pool; +} + +/* configure the allocation query that was answered downstream, we can + * configure some properties on it. Only it's called when not in + * passthrough mode. */ +static gboolean +gst_va_base_transform_decide_allocation (GstBaseTransform * trans, + GstQuery * query) +{ + GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (trans); + GstAllocator *allocator = NULL, *other_allocator = NULL; + GstAllocationParams params, other_params; + GstBufferPool *pool = NULL, *other_pool = NULL; + GstCaps *outcaps = NULL; + GstStructure *config; + GstVideoInfo vinfo; + guint min, max, size = 0, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE; + gboolean update_pool, update_allocator, has_videometa, copy_frames; + + gst_query_parse_allocation (query, &outcaps, NULL); + + gst_allocation_params_init (&other_params); + gst_allocation_params_init (¶ms); + + if (!gst_video_info_from_caps (&vinfo, outcaps)) { + GST_ERROR_OBJECT (self, "Cannot parse caps %" GST_PTR_FORMAT, outcaps); + return FALSE; + } + + if (gst_query_get_n_allocation_params (query) > 0) { + gst_query_parse_nth_allocation_param (query, 0, &allocator, &other_params); + if (allocator && !(GST_IS_VA_DMABUF_ALLOCATOR (allocator) + || GST_IS_VA_ALLOCATOR (allocator))) { + /* save the allocator for the other pool */ + other_allocator = allocator; + allocator = NULL; + } + update_allocator = TRUE; + } else { + update_allocator = FALSE; + } + + if (gst_query_get_n_allocation_pools (query) > 0) { + gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); + + if (pool) { + if (!GST_IS_VA_POOL (pool)) { + GST_DEBUG_OBJECT (self, + "may need other pool for copy frames %" GST_PTR_FORMAT, pool); + other_pool = pool; + pool = NULL; + } + } + + update_pool = TRUE; + } else { + size = GST_VIDEO_INFO_SIZE (&vinfo); + min = 1; + max = 0; + update_pool = FALSE; + } + + if (!allocator) { + /* XXX(victor): USAGE_HINT_VPP_WRITE creates tiled dmabuf frames + * in iHD */ + if (gst_caps_is_dmabuf (outcaps) && GST_VIDEO_INFO_IS_RGB (&vinfo)) + usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC; + if (!(allocator = + gst_va_base_transform_allocator_from_caps (self, outcaps))) + return FALSE; + } + + if (!pool) + pool = gst_va_pool_new (); + + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_set_allocator (config, allocator, ¶ms); + gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); + gst_buffer_pool_config_set_params (config, outcaps, size, min, max); + gst_buffer_pool_config_set_va_allocation_params (config, usage_hint); + if (!gst_buffer_pool_set_config (pool, config)) { + gst_object_unref (allocator); + gst_object_unref (pool); + return FALSE; + } + + if (GST_IS_VA_DMABUF_ALLOCATOR (allocator)) { + gst_va_dmabuf_allocator_get_format (allocator, &self->priv->srcpad_info, + NULL); + } else if (GST_IS_VA_ALLOCATOR (allocator)) { + gst_va_allocator_get_format (allocator, &self->priv->srcpad_info, NULL); + } + + if (update_allocator) + gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms); + else + gst_query_add_allocation_param (query, allocator, ¶ms); + + 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); + + has_videometa = gst_query_find_allocation_meta (query, + GST_VIDEO_META_API_TYPE, NULL); + + copy_frames = (!has_videometa && gst_va_pool_requires_video_meta (pool) + && gst_caps_is_raw (outcaps)); + if (copy_frames) { + if (other_pool) { + gst_object_replace ((GstObject **) & self->priv->other_pool, + (GstObject *) other_pool); + } else { + self->priv->other_pool = + _create_other_pool (other_allocator, &other_params, outcaps, size); + } + GST_DEBUG_OBJECT (self, "Use the other pool for copy %" GST_PTR_FORMAT, + self->priv->other_pool); + } else { + gst_clear_object (&self->priv->other_pool); + } + + GST_DEBUG_OBJECT (self, + "decided pool %" GST_PTR_FORMAT " with allocator %" GST_PTR_FORMAT, + pool, allocator); + + gst_object_unref (allocator); + gst_object_unref (pool); + gst_clear_object (&other_allocator); + gst_clear_object (&other_pool); + + /* removes allocation metas */ + return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans, + query); + +} + +/* output buffers must be from our VA-based pool, they cannot be + * system-allocated */ +static gboolean +gst_va_base_transform_transform_size (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, gsize size, + GstCaps * othercaps, gsize * othersize) +{ + return FALSE; +} + +static GstFlowReturn +gst_va_base_transform_generate_output (GstBaseTransform * trans, + GstBuffer ** outbuf) +{ + GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (trans); + GstVideoFrame src_frame; + GstVideoFrame dest_frame; + GstBuffer *buffer = NULL; + GstFlowReturn ret; + + ret = GST_BASE_TRANSFORM_CLASS (parent_class)->generate_output (trans, + outbuf); + + if (ret != GST_FLOW_OK || *outbuf == NULL) + return ret; + + if (!self->priv->other_pool) + return GST_FLOW_OK; + + /* Now need to copy the output buffer */ + ret = GST_FLOW_ERROR; + + if (!gst_buffer_pool_set_active (self->priv->other_pool, TRUE)) { + GST_WARNING_OBJECT (self, "failed to active the other pool %" + GST_PTR_FORMAT, self->priv->other_pool); + goto out; + } + + ret = gst_buffer_pool_acquire_buffer (self->priv->other_pool, &buffer, NULL); + if (ret != GST_FLOW_OK) + goto out; + + if (!gst_video_frame_map (&src_frame, &self->priv->srcpad_info, *outbuf, + GST_MAP_READ)) + goto out; + + if (!gst_video_frame_map (&dest_frame, &self->out_info, buffer, + GST_MAP_WRITE)) { + gst_video_frame_unmap (&src_frame); + goto out; + } + + if (!gst_video_frame_copy (&dest_frame, &src_frame)) { + gst_video_frame_unmap (&src_frame); + gst_video_frame_unmap (&dest_frame); + goto out; + } + + gst_video_frame_unmap (&src_frame); + gst_video_frame_unmap (&dest_frame); + + gst_buffer_replace (outbuf, buffer); + ret = GST_FLOW_OK; + +out: + gst_clear_buffer (&buffer); + return ret; +} + +static GstStateChangeReturn +gst_va_base_transform_change_state (GstElement * element, + GstStateChange transition) +{ + GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (element); + GstVaBaseTransformClass *klass = GST_VA_BASE_TRANSFORM_GET_CLASS (element); + GstStateChangeReturn ret; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!gst_va_ensure_element_data (element, klass->render_device_path, + &self->display)) + goto open_failed; + gst_clear_object (&self->filter); + self->filter = gst_va_filter_new (self->display); + if (!gst_va_filter_open (self->filter)) + goto open_failed; + if (klass->update_properties) + klass->update_properties (self); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_va_filter_close (self->filter); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + gst_clear_object (&self->filter); + gst_clear_object (&self->display); + break; + default: + break; + } + + return ret; + + /* Errors */ +open_failed: + { + GST_ELEMENT_ERROR (self, LIBRARY, INIT, (NULL), ("Failed to open VPP")); + return GST_STATE_CHANGE_FAILURE; + } +} + +static void +gst_va_base_transform_set_context (GstElement * element, GstContext * context) +{ + GstVaDisplay *old_display, *new_display; + GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (element); + GstVaBaseTransformClass *klass = GST_VA_BASE_TRANSFORM_GET_CLASS (self); + gboolean ret; + + old_display = self->display ? gst_object_ref (self->display) : NULL; + ret = gst_va_handle_set_context (element, context, klass->render_device_path, + &self->display); + new_display = self->display ? gst_object_ref (self->display) : NULL; + + if (!ret + || (old_display && new_display && old_display != new_display + && self->filter)) { + GST_ELEMENT_WARNING (element, RESOURCE, BUSY, + ("Can't replace VA display while operating"), (NULL)); + } + + gst_clear_object (&old_display); + gst_clear_object (&new_display); + + GST_ELEMENT_CLASS (parent_class)->set_context (element, context); +} + +static void +gst_va_base_transform_class_init (GstVaBaseTransformClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + GstBaseTransformClass *trans_class; + + gobject_class = G_OBJECT_CLASS (klass); + element_class = GST_ELEMENT_CLASS (klass); + trans_class = GST_BASE_TRANSFORM_CLASS (klass); + + gobject_class->dispose = gst_va_base_transform_dispose; + + trans_class->query = GST_DEBUG_FUNCPTR (gst_va_base_transform_query); + trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_va_base_transform_set_caps); + trans_class->propose_allocation = + GST_DEBUG_FUNCPTR (gst_va_base_transform_propose_allocation); + trans_class->decide_allocation = + GST_DEBUG_FUNCPTR (gst_va_base_transform_decide_allocation); + trans_class->transform_size = + GST_DEBUG_FUNCPTR (gst_va_base_transform_transform_size); + trans_class->generate_output = + GST_DEBUG_FUNCPTR (gst_va_base_transform_generate_output); + + element_class->set_context = + GST_DEBUG_FUNCPTR (gst_va_base_transform_set_context); + element_class->change_state = + GST_DEBUG_FUNCPTR (gst_va_base_transform_change_state); +} + +GstAllocator * +gst_va_base_transform_allocator_from_caps (GstVaBaseTransform * self, + GstCaps * caps) +{ + GstAllocator *allocator = NULL; + + if (gst_caps_is_dmabuf (caps)) { + allocator = gst_va_dmabuf_allocator_new (self->display); + } else { + GArray *surface_formats = gst_va_filter_get_surface_formats (self->filter); + allocator = gst_va_allocator_new (self->display, surface_formats); + } + + return allocator; +} + +static inline gsize +_get_plane_data_size (GstVideoInfo * info, guint plane) +{ + gint height, padded_height; + + height = GST_VIDEO_INFO_HEIGHT (info); + padded_height = + GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, plane, height); + + return GST_VIDEO_INFO_PLANE_STRIDE (info, plane) * padded_height; +} + +static gboolean +_try_import_dmabuf_unlocked (GstVaBaseTransform * self, GstBuffer * inbuf) +{ + GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self); + GstVideoMeta *meta; + GstVideoInfo in_info = btrans->in_info; + GstMemory *mems[GST_VIDEO_MAX_PLANES]; + guint i, n_mem, n_planes; + gsize offset[GST_VIDEO_MAX_PLANES]; + uintptr_t fd[GST_VIDEO_MAX_PLANES]; + + n_planes = GST_VIDEO_INFO_N_PLANES (&in_info); + n_mem = gst_buffer_n_memory (inbuf); + meta = gst_buffer_get_video_meta (inbuf); + + /* This will eliminate most non-dmabuf out there */ + if (!gst_is_dmabuf_memory (gst_buffer_peek_memory (inbuf, 0))) + return FALSE; + + /* We cannot have multiple dmabuf per plane */ + if (n_mem > n_planes) + return FALSE; + + /* Update video info based on video meta */ + if (meta) { + GST_VIDEO_INFO_WIDTH (&in_info) = meta->width; + GST_VIDEO_INFO_HEIGHT (&in_info) = meta->height; + + for (i = 0; i < meta->n_planes; i++) { + GST_VIDEO_INFO_PLANE_OFFSET (&in_info, i) = meta->offset[i]; + GST_VIDEO_INFO_PLANE_STRIDE (&in_info, i) = meta->stride[i]; + } + } + + /* Find and validate all memories */ + for (i = 0; i < n_planes; i++) { + guint plane_size; + guint length; + guint mem_idx; + gsize mem_skip; + + plane_size = _get_plane_data_size (&in_info, i); + + if (!gst_buffer_find_memory (inbuf, in_info.offset[i], plane_size, + &mem_idx, &length, &mem_skip)) + return FALSE; + + /* We can't have more then one dmabuf per plane */ + if (length != 1) + return FALSE; + + mems[i] = gst_buffer_peek_memory (inbuf, mem_idx); + + /* And all memory found must be dmabuf */ + if (!gst_is_dmabuf_memory (mems[i])) + return FALSE; + + offset[i] = mems[i]->offset + mem_skip; + fd[i] = gst_dmabuf_memory_get_fd (mems[i]); + } + + /* Now create a VASurfaceID for the buffer */ + return gst_va_dmabuf_memories_setup (btrans->display, &in_info, n_planes, + mems, fd, offset, VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ); +} + +static GstBufferPool * +_get_sinkpad_pool (GstVaBaseTransform * self) +{ + GstAllocator *allocator; + GstAllocationParams params = { 0, }; + GstCaps *caps; + GstVideoInfo in_info; + guint size, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ; + + if (self->priv->sinkpad_pool) + return self->priv->sinkpad_pool; + + gst_allocation_params_init (¶ms); + + if (self->priv->sinkpad_caps) { + caps = self->priv->sinkpad_caps; + gst_video_info_from_caps (&in_info, caps); + } else { + caps = self->in_caps; + in_info = self->in_info; + } + + size = GST_VIDEO_INFO_SIZE (&in_info); + + allocator = gst_va_base_transform_allocator_from_caps (self, caps); + self->priv->sinkpad_pool = gst_va_pool_new_with_config (caps, size, 1, 0, + usage_hint, allocator, ¶ms); + if (!self->priv->sinkpad_pool) { + gst_object_unref (allocator); + return NULL; + } + + if (GST_IS_VA_DMABUF_ALLOCATOR (allocator)) { + gst_va_dmabuf_allocator_get_format (allocator, &self->priv->sinkpad_info, + NULL); + } else if (GST_IS_VA_ALLOCATOR (allocator)) { + gst_va_allocator_get_format (allocator, &self->priv->sinkpad_info, NULL); + } + + gst_object_unref (allocator); + + gst_buffer_pool_set_active (self->priv->sinkpad_pool, TRUE); + + return self->priv->sinkpad_pool; +} + +static gboolean +_try_import_buffer (GstVaBaseTransform * self, GstBuffer * inbuf) +{ + VASurfaceID surface; + gboolean ret; + + surface = gst_va_buffer_get_surface (inbuf); + if (surface != VA_INVALID_ID) + return TRUE; + + g_rec_mutex_lock (&GST_VA_SHARED_LOCK); + ret = _try_import_dmabuf_unlocked (self, inbuf); + g_rec_mutex_unlock (&GST_VA_SHARED_LOCK); + + return ret; +} + +GstFlowReturn +gst_va_base_transform_import_buffer (GstVaBaseTransform * self, + GstBuffer * inbuf, GstBuffer ** buf) +{ + GstBuffer *buffer = NULL; + GstBufferPool *pool; + GstFlowReturn ret; + GstVideoFrame in_frame, out_frame; + gboolean imported, copied; + + imported = _try_import_buffer (self, inbuf); + if (imported) { + *buf = gst_buffer_ref (inbuf); + return GST_FLOW_OK; + } + + /* input buffer doesn't come from a vapool, thus it is required to + * have a pool, grab from it a new buffer and copy the input + * buffer to the new one */ + if (!(pool = _get_sinkpad_pool (self))) + return GST_FLOW_ERROR; + + ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL); + if (ret != GST_FLOW_OK) + return ret; + + GST_LOG_OBJECT (self, "copying input frame"); + + if (!gst_video_frame_map (&in_frame, &self->in_info, inbuf, GST_MAP_READ)) + goto invalid_buffer; + + if (!gst_video_frame_map (&out_frame, &self->priv->sinkpad_info, buffer, + GST_MAP_WRITE)) { + gst_video_frame_unmap (&in_frame); + goto invalid_buffer; + } + + copied = gst_video_frame_copy (&out_frame, &in_frame); + + gst_video_frame_unmap (&out_frame); + gst_video_frame_unmap (&in_frame); + + if (!copied) + goto invalid_buffer; + + /* strictly speaking this is not needed but let's play safe */ + if (!gst_buffer_copy_into (buffer, inbuf, GST_BUFFER_COPY_FLAGS | + GST_BUFFER_COPY_TIMESTAMPS, 0, -1)) + return GST_FLOW_ERROR; + + *buf = buffer; + + return GST_FLOW_OK; + +invalid_buffer: + { + GST_ELEMENT_WARNING (self, CORE, NOT_IMPLEMENTED, (NULL), + ("invalid video buffer received")); + if (buffer) + gst_buffer_unref (buffer); + return GST_FLOW_OK; + } +} diff --git a/sys/va/gstvabasetransform.h b/sys/va/gstvabasetransform.h new file mode 100644 index 000000000..3dcc0a5cb --- /dev/null +++ b/sys/va/gstvabasetransform.h @@ -0,0 +1,90 @@ +/* GStreamer + * Copyright (C) 2021 Igalia, S.L. + * Author: Víctor Jáquez + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include + +#include "gstvafilter.h" + +G_BEGIN_DECLS + +#define GST_TYPE_VA_BASE_TRANSFORM (gst_va_base_transform_get_type()) +#define GST_VA_BASE_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_VA_BASE_TRANSFORM, GstVaBaseTransform)) +#define GST_IS_VA_BASE_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_VA_BASE_TRANSFORM)) +#define GST_VA_BASE_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_VA_BASE_TRANSFORM, GstVaBaseTransformClass)) +#define GST_IS_VA_BASE_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_VA_BASE_TRANSFORM)) +#define GST_VA_BASE_TRANSFORM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_VA_BASE_TRANSFORM, GstVaBaseTransformClass)) + +typedef struct _GstVaBaseTransform GstVaBaseTransform; +typedef struct _GstVaBaseTransformClass GstVaBaseTransformClass; +typedef struct _GstVaBaseTransformPrivate GstVaBaseTransformPrivate; + +struct _GstVaBaseTransform +{ + GstBaseTransform parent; + + /*< public >*/ + GstVaDisplay *display; + GstVaFilter *filter; + + GstCaps *in_caps; + GstCaps *out_caps; + GstVideoInfo in_info; + GstVideoInfo out_info; + + gboolean negotiated; + + guint extra_min_buffers; + + /*< private >*/ + GstVaBaseTransformPrivate *priv; + + gpointer _padding[GST_PADDING]; +}; + +struct _GstVaBaseTransformClass +{ + GstBaseTransformClass parent_class; + + /*< public >*/ + gboolean (*set_info) (GstVaBaseTransform *self, + GstCaps *incaps, GstVideoInfo *in_info, + GstCaps *outcaps, GstVideoInfo *out_info); + + void (*update_properties) (GstVaBaseTransform *self); + + /*< private >*/ + gchar *render_device_path; + + gpointer _padding[GST_PADDING]; +}; + +GType gst_va_base_transform_get_type (void); + +GstAllocator * gst_va_base_transform_allocator_from_caps + (GstVaBaseTransform * self, + GstCaps * caps); + +GstFlowReturn gst_va_base_transform_import_buffer (GstVaBaseTransform * self, + GstBuffer * inbuf, + GstBuffer ** buf); + +G_END_DECLS diff --git a/sys/va/meson.build b/sys/va/meson.build index 55b664beb..cdc4697fe 100644 --- a/sys/va/meson.build +++ b/sys/va/meson.build @@ -2,6 +2,7 @@ va_sources = [ 'plugin.c', 'gstvaallocator.c', 'gstvabasedec.c', + 'gstvabasetransform.c', 'gstvacaps.c', 'gstvadecoder.c', 'gstvadevice.c', -- cgit v1.2.1