/* * GStreamer * Copyright (C) 2019 Matthew Waters * * 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. */ /** * SECTION:vulkanvideofilter * @title: GstVulkanVideoFilter * @short_description: Vulkan filter base class * @see_also: #GstVulkanInstance, #GstVulkanDevice * * #GstVulkanVideoFilter is a helper base class for retrieving and holding the * #GstVulkanInstance, #GstVulkanDevice and #GstVulkanQueue used by an element. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include GST_DEBUG_CATEGORY (gst_debug_vulkan_video_filter); #define GST_CAT_DEFAULT gst_debug_vulkan_video_filter static void gst_vulkan_video_filter_finalize (GObject * object); static gboolean gst_vulkan_video_filter_query (GstBaseTransform * bt, GstPadDirection direction, GstQuery * query); static void gst_vulkan_video_filter_set_context (GstElement * element, GstContext * context); static gboolean gst_vulkan_video_filter_start (GstBaseTransform * bt); static gboolean gst_vulkan_video_filter_stop (GstBaseTransform * bt); static gboolean gst_vulkan_video_filter_set_caps (GstBaseTransform * bt, GstCaps * in_caps, GstCaps * out_caps); static GstCaps *gst_vulkan_video_filter_transform_caps (GstBaseTransform * bt, GstPadDirection direction, GstCaps * caps, GstCaps * filter); static gboolean gst_vulkan_video_filter_propose_allocation (GstBaseTransform * bt, GstQuery * decide_query, GstQuery * query); static gboolean gst_vulkan_video_filter_decide_allocation (GstBaseTransform * bt, GstQuery * query); enum { PROP_0, }; enum { SIGNAL_0, LAST_SIGNAL }; /* static guint gst_vulkan_video_filter_signals[LAST_SIGNAL] = { 0 }; */ #define gst_vulkan_video_filter_parent_class parent_class G_DEFINE_TYPE_WITH_CODE (GstVulkanVideoFilter, gst_vulkan_video_filter, GST_TYPE_BASE_TRANSFORM, GST_DEBUG_CATEGORY_INIT (gst_debug_vulkan_video_filter, "vulkanvideofilter", 0, "Vulkan Video Filter")); static void gst_vulkan_video_filter_class_init (GstVulkanVideoFilterClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; GstBaseTransformClass *gstbasetransform_class; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; gstbasetransform_class = (GstBaseTransformClass *) klass; gobject_class->finalize = gst_vulkan_video_filter_finalize; gstelement_class->set_context = gst_vulkan_video_filter_set_context; gstbasetransform_class->start = GST_DEBUG_FUNCPTR (gst_vulkan_video_filter_start); gstbasetransform_class->stop = GST_DEBUG_FUNCPTR (gst_vulkan_video_filter_stop); gstbasetransform_class->query = GST_DEBUG_FUNCPTR (gst_vulkan_video_filter_query); gstbasetransform_class->set_caps = gst_vulkan_video_filter_set_caps; gstbasetransform_class->transform_caps = gst_vulkan_video_filter_transform_caps; gstbasetransform_class->propose_allocation = gst_vulkan_video_filter_propose_allocation; gstbasetransform_class->decide_allocation = gst_vulkan_video_filter_decide_allocation; } static void gst_vulkan_video_filter_init (GstVulkanVideoFilter * render) { } static void gst_vulkan_video_filter_finalize (GObject * object) { GstVulkanVideoFilter *render = GST_VULKAN_VIDEO_FILTER (object); gst_caps_replace (&render->in_caps, NULL); gst_caps_replace (&render->out_caps, NULL); G_OBJECT_CLASS (parent_class)->finalize (object); } static gboolean gst_vulkan_video_filter_query (GstBaseTransform * bt, GstPadDirection direction, GstQuery * query) { GstVulkanVideoFilter *render = GST_VULKAN_VIDEO_FILTER (bt); switch (GST_QUERY_TYPE (query)) { case GST_QUERY_CONTEXT:{ if (gst_vulkan_handle_context_query (GST_ELEMENT (render), query, NULL, render->instance, render->device)) return TRUE; if (gst_vulkan_queue_handle_context_query (GST_ELEMENT (render), query, render->queue)) return TRUE; break; } default: break; } return GST_BASE_TRANSFORM_CLASS (parent_class)->query (bt, direction, query); } static void gst_vulkan_video_filter_set_context (GstElement * element, GstContext * context) { GstVulkanVideoFilter *render = GST_VULKAN_VIDEO_FILTER (element); gst_vulkan_handle_set_context (element, context, NULL, &render->instance); GST_ELEMENT_CLASS (parent_class)->set_context (element, context); } struct choose_data { GstVulkanVideoFilter *filter; GstVulkanQueue *queue; }; static gboolean _choose_queue (GstVulkanDevice * device, GstVulkanQueue * queue, struct choose_data *data) { guint flags = device->physical_device->queue_family_props[queue->family].queueFlags; if ((flags & VK_QUEUE_GRAPHICS_BIT) != 0) { if (data->queue) gst_object_unref (data->queue); data->queue = gst_object_ref (queue); return FALSE; } return TRUE; } static GstVulkanQueue * _find_graphics_queue (GstVulkanVideoFilter * filter) { struct choose_data data; data.filter = filter; data.queue = NULL; gst_vulkan_device_foreach_queue (filter->device, (GstVulkanDeviceForEachQueueFunc) _choose_queue, &data); return data.queue; } static GstCaps * gst_vulkan_video_filter_transform_caps (GstBaseTransform * bt, GstPadDirection direction, GstCaps * caps, GstCaps * filter) { GstCaps *result, *tmp; tmp = gst_caps_copy (caps); if (filter) { result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST); gst_caps_unref (tmp); } else { result = tmp; } return result; } static gboolean gst_vulkan_video_filter_set_caps (GstBaseTransform * bt, GstCaps * in_caps, GstCaps * out_caps) { GstVulkanVideoFilter *render = GST_VULKAN_VIDEO_FILTER (bt); if (!gst_video_info_from_caps (&render->in_info, in_caps)) return FALSE; if (!gst_video_info_from_caps (&render->out_info, out_caps)) return FALSE; gst_caps_replace (&render->in_caps, in_caps); gst_caps_replace (&render->out_caps, out_caps); GST_DEBUG_OBJECT (bt, "set caps: %" GST_PTR_FORMAT, in_caps); return TRUE; } static gboolean gst_vulkan_video_filter_propose_allocation (GstBaseTransform * bt, GstQuery * decide_query, GstQuery * query) { /* FIXME: */ return FALSE; } static gboolean gst_vulkan_video_filter_decide_allocation (GstBaseTransform * bt, GstQuery * query) { GstVulkanVideoFilter *render = GST_VULKAN_VIDEO_FILTER (bt); GstBufferPool *pool = NULL; GstStructure *config; GstCaps *caps; guint min, max, size; gboolean update_pool; gst_query_parse_allocation (query, &caps, NULL); if (!caps) return FALSE; if (gst_query_get_n_allocation_pools (query) > 0) { gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); update_pool = TRUE; } else { GstVideoInfo vinfo; gst_video_info_init (&vinfo); gst_video_info_from_caps (&vinfo, caps); size = vinfo.size; min = max = 0; update_pool = FALSE; } if (!pool || !GST_IS_VULKAN_IMAGE_BUFFER_POOL (pool)) { if (pool) gst_object_unref (pool); pool = gst_vulkan_image_buffer_pool_new (render->device); } 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_VIDEO_META); gst_buffer_pool_set_config (pool, config); 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); gst_object_unref (pool); return TRUE; } static gboolean gst_vulkan_video_filter_start (GstBaseTransform * bt) { GstVulkanVideoFilter *render = GST_VULKAN_VIDEO_FILTER (bt); if (!gst_vulkan_ensure_element_data (GST_ELEMENT (bt), NULL, &render->instance)) { GST_ELEMENT_ERROR (render, RESOURCE, NOT_FOUND, ("Failed to retrieve vulkan instance"), (NULL)); return FALSE; } if (!gst_vulkan_device_run_context_query (GST_ELEMENT (render), &render->device)) { GError *error = NULL; GST_DEBUG_OBJECT (render, "No device retrieved from peer elements"); if (!(render->device = gst_vulkan_instance_create_device (render->instance, &error))) { GST_ELEMENT_ERROR (render, RESOURCE, NOT_FOUND, ("Failed to create vulkan device"), ("%s", error->message)); g_clear_error (&error); return FALSE; } } if (!gst_vulkan_queue_run_context_query (GST_ELEMENT (render), &render->queue)) { GST_DEBUG_OBJECT (render, "No queue retrieved from peer elements"); render->queue = _find_graphics_queue (render); } if (!render->queue) return FALSE; return TRUE; } static gboolean gst_vulkan_video_filter_stop (GstBaseTransform * bt) { GstVulkanVideoFilter *render = GST_VULKAN_VIDEO_FILTER (bt); gst_clear_object (&render->device); gst_clear_object (&render->queue); gst_clear_object (&render->instance); return TRUE; }