Need to check the returned value of gst_buffer_pool_set_active() when setting the active status of buffer pool. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1131>
		
			
				
	
	
		
			821 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			821 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* GStreamer
 | |
|  * Copyright (C) 2021 Igalia, S.L.
 | |
|  *     Author: Víctor Jáquez <vjaquez@igalia.com>
 | |
|  *
 | |
|  * 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;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * GstVaBaseTransform:
 | |
|  *
 | |
|  * A base class implementation for VA-API filters.
 | |
|  *
 | |
|  * Since: 1.20
 | |
|  */
 | |
| #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);
 | |
| 
 | |
|   gst_type_mark_as_plugin_api (GST_TYPE_VA_BASE_TRANSFORM, 0);
 | |
| }
 | |
| 
 | |
| 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);
 | |
| 
 | |
|   if (!gst_buffer_pool_set_active (self->priv->sinkpad_pool, TRUE)) {
 | |
|     GST_WARNING_OBJECT (self, "failed to active the sinkpad pool %"
 | |
|         GST_PTR_FORMAT, self->priv->sinkpad_pool);
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   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;
 | |
| 
 | |
|   /* copy metadata, default implemenation of baseclass will copy everything
 | |
|    * what we need */
 | |
|   GST_BASE_TRANSFORM_CLASS (parent_class)->copy_metadata
 | |
|       (GST_BASE_TRANSFORM_CAST (self), inbuf, buffer);
 | |
| 
 | |
|   *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;
 | |
|   }
 | |
| }
 |