From e41e31f2ae2d7fc0e66f404a19fe17869f535ef3 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Fri, 31 Mar 2023 17:14:19 -0300 Subject: [PATCH] auto: Add a gstautodeinterlace element Which can do colorspace conversion if necessary. Part-of: --- .../gst/autoconvert/gstautodeinterlace.c | 787 ++++++++++++++++++ .../gst/autoconvert/gstautodeinterlace.h | 33 + .../gst/autoconvert/gstautovideo.c | 117 +++ .../gst/autoconvert/gstautovideo.h | 40 + .../gst/autoconvert/gstautovideoconvert.c | 1 + .../gst/autoconvert/gstautovideoconvert.h | 16 +- .../gst/autoconvert/meson.build | 2 + .../gst-plugins-bad/gst/autoconvert/plugin.c | 6 +- 8 files changed, 985 insertions(+), 17 deletions(-) create mode 100644 subprojects/gst-plugins-bad/gst/autoconvert/gstautodeinterlace.c create mode 100644 subprojects/gst-plugins-bad/gst/autoconvert/gstautodeinterlace.h create mode 100644 subprojects/gst-plugins-bad/gst/autoconvert/gstautovideo.c create mode 100644 subprojects/gst-plugins-bad/gst/autoconvert/gstautovideo.h diff --git a/subprojects/gst-plugins-bad/gst/autoconvert/gstautodeinterlace.c b/subprojects/gst-plugins-bad/gst/autoconvert/gstautodeinterlace.c new file mode 100644 index 0000000000..c6a406f372 --- /dev/null +++ b/subprojects/gst-plugins-bad/gst/autoconvert/gstautodeinterlace.c @@ -0,0 +1,787 @@ +/* GStreamer + * Copyright 2023 Igalia S.L. + * @author: Thibault Saunier + * + * 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:element-autodeinterlace + * @title: autodeinterlace + * + * The #autodeinterlace element is used to automatically select the right + * deinterlacer based on the caps of the stream. It can plug several elements + * to download/upload to/from the GPU when required/best. + * + * Note: This element guarantees that caps negotiation works but when setting + * properties that are not available on the best underlying deinterlacer some + * upload/download might be required and performance are impacted. + * + * Since: 1.24 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstautovideo.h" +#include "gstautodeinterlace.h" + +GST_DEBUG_CATEGORY (autodeinterlace_debug); +#define GST_CAT_DEFAULT (autodeinterlace_debug) + +#define DEFAULT_FIELDS GST_AUTO_DEINTERLACE_FIELDS_ALL +#define DEFAULT_LAYOUT GST_AUTO_DEINTERLACE_LAYOUT_AUTO +#define DEFAULT_MODE GST_AUTO_DEINTERLACE_MODE_AUTO +enum +{ + PROP_0, + PROP_FIELDS, + PROP_MODE, + PROP_LAYOUT, +}; + +/** + * GstAutoDeinterlaceFieldLayout: + * @GST_AUTO_DEINTERLACE_LAYOUT_AUTO: Auto detection + * @GST_AUTO_DEINTERLACE_LAYOUT_TFF: Top field first + * @GST_AUTO_DEINTERLACE_LAYOUT_BFF: Bottom field first + * + * Since: 1.24 + */ +typedef enum +{ + GST_AUTO_DEINTERLACE_LAYOUT_AUTO, + GST_AUTO_DEINTERLACE_LAYOUT_TFF, + GST_AUTO_DEINTERLACE_LAYOUT_BFF, +} GstAutoDeinterlaceFieldLayout; + +#define GST_TYPE_AUTO_DEINTERLACE_FIELD_LAYOUT (gst_auto_deinterlace_field_layout_get_type ()) +static GType +gst_auto_deinterlace_field_layout_get_type (void) +{ + static GType auto_deinterlace_field_layout_type = 0; + + static const GEnumValue field_layout_types[] = { + {GST_AUTO_DEINTERLACE_LAYOUT_AUTO, "Auto detection", "auto"}, + {GST_AUTO_DEINTERLACE_LAYOUT_TFF, "Top field first", "tff"}, + {GST_AUTO_DEINTERLACE_LAYOUT_BFF, "Bottom field first", "bff"}, + {0, NULL, NULL}, + }; + + if (!auto_deinterlace_field_layout_type) { + auto_deinterlace_field_layout_type = + g_enum_register_static ("GstAutoDeinterlaceFieldLayout", + field_layout_types); + } + return auto_deinterlace_field_layout_type; +} + +typedef enum +{ + GST_AUTO_DEINTERLACE_FIELDS_ALL, /* All (missing data is interp.) */ + GST_AUTO_DEINTERLACE_FIELDS_TOP, /* Top Fields Only */ + GST_AUTO_DEINTERLACE_FIELDS_BOTTOM, /* Bottom Fields Only */ + GST_AUTO_DEINTERLACE_FIELDS_AUTO /* Automatically detect */ +} GstAutoDeinterlaceFields; + + +#define GST_TYPE_AUTO_DEINTERLACE_FIELDS (gst_auto_deinterlace_fields_get_type ()) +static GType +gst_auto_deinterlace_fields_get_type (void) +{ + static GType deinterlace_fields_type = 0; + + static const GEnumValue fields_types[] = { + {GST_AUTO_DEINTERLACE_FIELDS_ALL, "All fields", "all"}, + {GST_AUTO_DEINTERLACE_FIELDS_TOP, "Top fields only", "top"}, + {GST_AUTO_DEINTERLACE_FIELDS_BOTTOM, "Bottom fields only", "bottom"}, + {GST_AUTO_DEINTERLACE_FIELDS_AUTO, "Automatically detect", "auto"}, + {0, NULL, NULL}, + }; + + if (!deinterlace_fields_type) { + deinterlace_fields_type = + g_enum_register_static ("GstAutoDeinterlaceFields", fields_types); + } + return deinterlace_fields_type; +} + +/** + * GstAutoDeinterlaceModes: + * @GST_AUTO_DEINTERLACE_MODE_AUTO: Auto detection (best effort) + * @GST_AUTO_DEINTERLACE_MODE_INTERLACED: Force deinterlacing + * @GST_AUTO_DEINTERLACE_MODE_DISABLED: Run in passthrough modes_types + * @GST_AUTO_DEINTERLACE_MODE_AUTO_STRICT: Auto detection (strict) + * + * Since: 1.24 + */ +typedef enum +{ + GST_AUTO_DEINTERLACE_MODE_AUTO, + GST_AUTO_DEINTERLACE_MODE_INTERLACED, + GST_AUTO_DEINTERLACE_MODE_DISABLED, + GST_AUTO_DEINTERLACE_MODE_AUTO_STRICT +} GstAutoDeinterlaceModes; + +#define GST_TYPE_AUTO_DEINTERLACE_MODES (gst_auto_deinterlace_modes_get_type ()) +static GType +gst_auto_deinterlace_modes_get_type (void) +{ + static GType deinterlace_modes_type = 0; + + static const GEnumValue modes_types[] = { + {GST_AUTO_DEINTERLACE_MODE_AUTO, "Auto detection (best effort)", "auto"}, + {GST_AUTO_DEINTERLACE_MODE_INTERLACED, "Force deinterlacing", "interlaced"}, + {GST_AUTO_DEINTERLACE_MODE_DISABLED, "Run in passthrough mode", "disabled"}, + {GST_AUTO_DEINTERLACE_MODE_AUTO_STRICT, "Auto detection (strict)", + "auto-strict"}, + {0, NULL, NULL}, + }; + + if (!deinterlace_modes_type) { + deinterlace_modes_type = + g_enum_register_static ("GstAutoDeinterlaceModes", modes_types); + } + return deinterlace_modes_type; +} + + +typedef struct +{ + const gchar *factory_name; + const gchar *our_name; + const gchar *their_name; + struct + { + gint our_value; + const gchar *their_value; + } values[5]; +} EnumMap; + +static EnumMap ENUM_MAP[] = { + { + .factory_name = "deinterlace", + .our_name = "layout", + .their_name = "tff", + .values = { + { + .our_value = GST_AUTO_DEINTERLACE_LAYOUT_AUTO, + .their_value = "auto", + }, + { + .our_value = GST_AUTO_DEINTERLACE_LAYOUT_TFF, + .their_value = "tff", + }, + { + .our_value = GST_AUTO_DEINTERLACE_LAYOUT_BFF, + .their_value = "bff", + }, + }, + }, + { + .factory_name = "deinterlace", + .our_name = "fields", + .their_name = "fields", + .values = { + { + .our_value = GST_AUTO_DEINTERLACE_FIELDS_ALL, + .their_value = "all", + }, + { + .our_value = GST_AUTO_DEINTERLACE_FIELDS_BOTTOM, + .their_value = "bottom", + }, + { + .our_value = GST_AUTO_DEINTERLACE_FIELDS_TOP, + .their_value = "top", + }, + { + .our_value = GST_AUTO_DEINTERLACE_FIELDS_AUTO, + .their_value = "auto", + }, + }, + }, + { + .factory_name = "deinterlace", + .our_name = "mode", + .their_name = "mode", + .values = { + { + .our_value = GST_AUTO_DEINTERLACE_MODE_AUTO, + .their_value = "auto", + }, + { + .our_value = GST_AUTO_DEINTERLACE_MODE_DISABLED, + .their_value = "disabled", + }, + { + .our_value = GST_AUTO_DEINTERLACE_MODE_INTERLACED, + .their_value = "interlaced", + }, + { + .our_value = GST_AUTO_DEINTERLACE_MODE_AUTO_STRICT, + .their_value = "auto-strict", + }, + }, + }, + { + .factory_name = "gldeinterlace", + .our_name = "layout", + .their_name = NULL, + }, + { + .factory_name = "gldeinterlace", + .our_name = "fields", + .their_name = NULL, + }, + { + .factory_name = "gldeinterlace", + .our_name = "mode", + .their_name = NULL, + }, + { + .factory_name = "d3d11deinterlaceelement", + .our_name = "layout", + .their_name = NULL, + }, + { + .factory_name = "d3d11deinterlaceelement", + .our_name = "fields", + .their_name = NULL, + }, + { + .factory_name = "d3d11deinterlaceelement", + .our_name = "mode", + .their_name = NULL, + } +}; + +#if !GLIB_CHECK_VERSION(2, 68, 0) +static GObject * +g_binding_dup_source (GBinding * binding) +{ + return g_object_ref (g_binding_get_source (binding)); +} + +static GObject * +g_binding_dup_target (GBinding * binding) +{ + return g_object_ref (g_binding_get_target (binding)); +} +#endif + +struct _GstAutoDeinterlace +{ + GstBaseAutoConvert parent; + + GstAutoDeinterlaceFieldLayout field_layout; + GstAutoDeinterlaceFields fields; + GstAutoDeinterlaceModes mode; + GList *bindings; +}; + +G_DEFINE_TYPE (GstAutoDeinterlace, gst_auto_deinterlace, + GST_TYPE_BASE_AUTO_CONVERT); + +GST_ELEMENT_REGISTER_DEFINE (autodeinterlace, "autodeinterlace", + GST_RANK_NONE, gst_auto_deinterlace_get_type ()); + +static void +gst_auto_deinterlace_register_filters (GstAutoDeinterlace * self) +{ + const GstAutoVideoFilterGenerator *g; + + /* Only the software deinterlacer supports all our properties so if we are using + * defaults values for all of them, use hw deinterlacer otherwise ensure to use + * our software one to respect what the user wants */ + /* *INDENT-OFF* */ + if (self->field_layout == DEFAULT_LAYOUT && self->fields == DEFAULT_FIELDS + && self->mode == DEFAULT_MODE) { + static const GstAutoVideoFilterGenerator gen[] = { + { + .first_elements = { "bayer2rgb", NULL}, + .colorspace_converters = { "videoconvert", NULL }, + .last_elements = { NULL } , + .filters = { "deinterlace", NULL}, + .rank = GST_RANK_SECONDARY, + }, + { + .first_elements = { NULL, }, + .colorspace_converters = { "videoconvert", NULL }, + .last_elements = { "rgb2bayer", NULL }, + .filters = { "deinterlace", NULL }, + .rank = GST_RANK_SECONDARY, + }, + { + .first_elements = { NULL, }, + .colorspace_converters = { "videoconvert", NULL }, + .last_elements = { NULL, }, + .filters = { "deinterlace", NULL }, + .rank = GST_RANK_SECONDARY, + }, + { + .first_elements = { NULL, }, + .colorspace_converters = { "glcolorconvert", "glcolorscale", "glcolorconvert", NULL }, + .last_elements = { NULL, }, + .filters = { "gldeinterlace", NULL }, + .rank = GST_RANK_PRIMARY, + }, + { + .first_elements = { "glupload", }, + .colorspace_converters = { "glcolorconvert", "glcolorscale", "glcolorconvert", NULL }, + .last_elements = { NULL, }, + .filters = { "gldeinterlace", NULL }, + .rank = GST_RANK_PRIMARY, + }, + { /* Worst case we upload/download as required */ + .first_elements = { "glupload", "gldownload", NULL }, + .colorspace_converters = { "glcolorconvert", "glcolorscale", "glcolorconvert", NULL }, + .last_elements = { "glupload", "gldownload", NULL }, + .filters = { "gldeinterlace", NULL }, + .rank = GST_RANK_MARGINAL, + }, + { + .first_elements = { NULL, }, + .colorspace_converters = { "d3d11convert", NULL }, + .last_elements = { NULL, }, + .filters = { "d3d11deinterlaceelement" }, + .rank = GST_RANK_PRIMARY, + }, + { + .first_elements = { NULL}, + .colorspace_converters = { "d3d11deinterlace", NULL }, + .last_elements = { NULL }, + .filters = { NULL }, + .rank = GST_RANK_MARGINAL, + }, + { /* CUDA -> GL */ + .first_elements = { "capsfilter caps=video/x-raw(memory:CUDAMemory)", "cudadownload", NULL }, + .colorspace_converters = { "glcolorconvert", "glcolorscale", "glcolorconvert", NULL }, + .last_elements = { NULL }, + .filters = { "gldeinterlace", NULL }, + .rank = GST_RANK_PRIMARY - 1, + }, + { /* CUDA -> CUDA */ + .first_elements = { "capsfilter caps=video/x-raw(memory:CUDAMemory)", "cudadownload", NULL }, + .colorspace_converters = { "glcolorconvert", "glcolorscale", "glcolorconvert", NULL }, + .last_elements = { "cudaupload", "capsfilter caps=video/x-raw(memory:CUDAMemory)", NULL }, + .filters = { "gldeinterlace", NULL }, + .rank = GST_RANK_SECONDARY - 1, + }, + { /* Software -> CUDA (uploading as soon as possible) */ + .first_elements = { "glupload", NULL }, + .colorspace_converters = { "glcoloconvert", NULL }, + .last_elements = { "cudaupload", "capsfilter caps=video/x-raw(memory:CUDAMemory)", NULL }, + .filters = { "gldeinterlace", NULL }, + .rank = GST_RANK_MARGINAL, + }, + { /* CUDA -> Software */ + .first_elements = { "capsfilter caps=video/x-raw(memory:CUDAMemory)", "cudadownload", NULL }, + .colorspace_converters = { "glcolorconvert", "glcolorscale", "glcolorconvert", NULL }, + .last_elements = { "gldownload", NULL }, + .filters = { "gldeinterlace", NULL }, + .rank = GST_RANK_MARGINAL, + }, + { + .first_elements = { NULL}, + .colorspace_converters = { NULL }, + .last_elements = { NULL }, + .filters = { NULL }, + .rank = 0, + }, + }; + + g = gen; + } else { + static const GstAutoVideoFilterGenerator gen[] = { + { + .first_elements = { "bayer2rgb", NULL}, + .colorspace_converters = { "videoconvert", NULL }, + .last_elements = { NULL } , + .filters = { "deinterlace", NULL}, + .rank = GST_RANK_SECONDARY, + }, + { + .first_elements = { NULL, }, + .colorspace_converters = { "videoconvert", NULL }, + .last_elements = { "rgb2bayer", NULL }, + .filters = { "deinterlace", NULL }, + .rank = GST_RANK_SECONDARY, + }, + { + .first_elements = { NULL, }, + .colorspace_converters = { "videoconvertscale", NULL }, + .last_elements = { NULL, }, + .filters = { "deinterlace", NULL }, + .rank = GST_RANK_SECONDARY, + }, + { /* Worst case we upload/download as required */ + .first_elements = { "gldownload", NULL }, + .colorspace_converters = { "videoconvert", NULL }, + .last_elements = { "glupload", NULL }, + .filters = { "deinterlace", NULL }, + .rank = GST_RANK_MARGINAL, + }, + { /* Cuda -> Cuda */ + .first_elements = { "capsfilter caps=video/x-raw(memory:CUDAMemory)", "cudadownload", NULL }, + .colorspace_converters = { "videoconvert", NULL }, + .last_elements = { "cudaupload", "capsfilter caps=video/x-raw(memory:CUDAMemory)", NULL }, + .filters = { "deinterlace", NULL }, + .rank = GST_RANK_SECONDARY + 1, + }, + { /* Cuda -> software */ + .first_elements = { "capsfilter caps=video/x-raw(memory:CUDAMemory)", "cudadownload", NULL }, + .colorspace_converters = { "videoconvert", NULL }, + .last_elements = { "glupload", NULL }, + .filters = { "deinterlace", NULL }, + .rank = GST_RANK_MARGINAL, + }, + { /* Cuda -> software */ + .first_elements = { "capsfilter caps=video/x-raw(memory:CUDAMemory)", "cudadownload", NULL }, + .colorspace_converters = { "videoconvert", NULL }, + .last_elements = { NULL }, + .filters = { "deinterlace", NULL }, + .rank = GST_RANK_MARGINAL, + }, + { /* Software -> cuda */ + .first_elements = { NULL }, + .colorspace_converters = { "videoconvert", NULL }, + .last_elements = { "cudaupload", "capsfilter caps=video/x-raw(memory:CUDAMemory)", NULL }, + .filters = { "deinterlace", NULL }, + .rank = GST_RANK_MARGINAL, + }, + { + .first_elements = { "d3d11upload", "d3d11download" }, + .colorspace_converters = { "videoconvert", NULL }, + .last_elements = { "d3d11upload", "d3d11download" }, + .filters = { "deinterlace" }, + .rank = GST_RANK_MARGINAL, + }, + { /* Worst case we upload/download as required */ + .first_elements = { NULL}, + .colorspace_converters = { NULL }, + .last_elements = { NULL }, + .filters = { NULL }, + .rank = 0, + }, + }; + + g = gen; + } + /* *INDENT-ON* */ + + gst_auto_video_register_well_known_bins (GST_BASE_AUTO_CONVERT (self), g); +} + +static void +gst_auto_deinterlace_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstAutoDeinterlace *autodeinterlace = GST_AUTO_DEINTERLACE (object); + + GST_OBJECT_LOCK (object); + switch (prop_id) { + case PROP_LAYOUT: + g_value_set_enum (value, autodeinterlace->field_layout); + break; + case PROP_FIELDS: + g_value_set_enum (value, autodeinterlace->fields); + break; + case PROP_MODE: + g_value_set_enum (value, autodeinterlace->mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + GST_OBJECT_UNLOCK (object); +} + +static void +gst_auto_deinterlace_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAutoDeinterlace *autodeinterlace = GST_AUTO_DEINTERLACE (object); + gint v = g_value_get_enum (value); + gboolean changed = FALSE; + + GST_OBJECT_LOCK (object); + switch (prop_id) { + case PROP_LAYOUT: + changed = (autodeinterlace->field_layout != v); + autodeinterlace->field_layout = v; + break; + case PROP_FIELDS: + changed = (autodeinterlace->fields != v); + autodeinterlace->fields = v; + break; + case PROP_MODE: + changed = (autodeinterlace->mode != v); + autodeinterlace->mode = v; + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + GST_OBJECT_UNLOCK (object); + + if (changed) { + gst_base_auto_convert_reset_filters (GST_BASE_AUTO_CONVERT (object)); + gst_auto_deinterlace_register_filters (GST_AUTO_DEINTERLACE (object)); + + /* Force a reconfigure so the new property can be taken into account if necessary */ + gst_pad_push_event (GST_BASE_AUTO_CONVERT (object)->sinkpad, + gst_event_new_reconfigure ()); + } +} + +static gboolean +element_is_handled_deinterlace (GstElement * element) +{ + GstElementFactory *factory = gst_element_get_factory (element); + + for (gint i = 0; i < G_N_ELEMENTS (ENUM_MAP); i++) { + if (g_strcmp0 (GST_OBJECT_NAME (factory), ENUM_MAP[i].factory_name) == 0) { + return TRUE; + } + } + + return FALSE; +} + +static gboolean +gst_auto_deinterlace_transform_to (GBinding * binding, const GValue * from, + GValue * to_value, gpointer _udata) +{ + const gchar *value = NULL; + EnumMap *map = NULL; + gboolean res = FALSE; + GstAutoDeinterlace *self = + GST_AUTO_DEINTERLACE (g_binding_dup_source (binding)); + GstElement *target = GST_ELEMENT (g_binding_dup_target (binding)); + GstElementFactory *factory = gst_element_get_factory (target); + + for (gint i = 0; i < G_N_ELEMENTS (ENUM_MAP); i++) { + if (g_strcmp0 (GST_OBJECT_NAME (factory), ENUM_MAP[i].factory_name) == 0 && + g_strcmp0 (g_binding_get_source_property (binding), + ENUM_MAP[i].our_name) == 0) { + map = &ENUM_MAP[i]; + break; + } + } + + if (!map) { + GST_WARNING_OBJECT (self, "Could not find mapping for %s" + " property won't be set on the deinterlacing element", + gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)) + ); + goto done; + } + + + for (gint j = 0; map->values[j].their_value; j++) { + if (map->values[j].our_value == g_value_get_enum (from)) { + value = map->values[j].their_value; + break; + } + } + + if (value) { + GParamSpec *pspec = + g_object_class_find_property (G_OBJECT_GET_CLASS (target), + g_binding_get_target_property (binding)); + GST_ERROR ("Setting %s - %s to %s", map->our_name, + g_binding_get_source_property (binding), value); + res = gst_value_deserialize_with_pspec (to_value, value, pspec); + } else { + GST_WARNING_OBJECT (self, "Could not transfer value for property %s to %s", + g_binding_get_source_property (binding), + g_binding_get_target_property (binding) + ); + } + +done: + gst_clear_object (&self); + gst_clear_object (&target); + + return res; +} + +static void +gst_auto_deinterlace_deep_element_added (GstBin * bin, GstBin * sub_bin, + GstElement * element) +{ + GstAutoDeinterlace *self = GST_AUTO_DEINTERLACE (bin); + GList *new_bindings = NULL; + if (!element_is_handled_deinterlace (element)) + goto done; + + GST_OBJECT_LOCK (bin); + for (GList * tmp = self->bindings; tmp; tmp = tmp->next) { + GBinding *binding = tmp->data; + GObject *target = g_binding_dup_target (binding); + + if (GST_ELEMENT (target) == element) { + GST_INFO_OBJECT (self, "Newly added element %s already bound", + GST_OBJECT_NAME (gst_element_get_factory (element))); + GST_OBJECT_UNLOCK (bin); + gst_object_unref (target); + goto done; + } + gst_object_unref (target); + } + GST_OBJECT_UNLOCK (bin); + + for (gint i = 0; i < G_N_ELEMENTS (ENUM_MAP); i++) { + if (g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (element)), + ENUM_MAP[i].factory_name)) + continue; + + if (!ENUM_MAP[i].their_name) { + GST_WARNING_OBJECT (self, "No mapping for our property %s on %s", + ENUM_MAP[i].our_name, + GST_OBJECT_NAME (gst_element_get_factory (element))); + continue; + } + + new_bindings = g_list_prepend (new_bindings, + g_object_bind_property_full (bin, ENUM_MAP[i].our_name, + element, ENUM_MAP[i].their_name, + G_BINDING_SYNC_CREATE | G_BINDING_DEFAULT, + gst_auto_deinterlace_transform_to, NULL, NULL, NULL) + ); + } + GST_OBJECT_LOCK (bin); + self->bindings = g_list_concat (self->bindings, new_bindings); + GST_OBJECT_UNLOCK (bin); + +done: + GST_BIN_CLASS (gst_auto_deinterlace_parent_class)->deep_element_added (bin, + sub_bin, element); +} + +static void +gst_auto_deinterlace_deep_element_removed (GstBin * bin, GstBin * sub_bin, + GstElement * element) +{ + GList *bindings = NULL; + GstAutoDeinterlace *self = GST_AUTO_DEINTERLACE (bin); + + if (!element_is_handled_deinterlace (element)) + goto done; + + GST_OBJECT_LOCK (bin); + for (GList * tmp = self->bindings; tmp; tmp = tmp->next) { + GBinding *binding = tmp->data; + GObject *target = g_binding_dup_target (binding); + + if (GST_ELEMENT (target) == element) { + GList *node = tmp; + + bindings = g_list_prepend (bindings, binding); + tmp = tmp->prev; + + self->bindings = g_list_delete_link (self->bindings, node); + if (!tmp) + break; + } + gst_object_unref (target); + } + GST_OBJECT_UNLOCK (bin); + +done: + GST_BIN_CLASS (gst_auto_deinterlace_parent_class)->deep_element_removed (bin, + sub_bin, element); +} + +static void +gst_auto_deinterlace_class_init (GstAutoDeinterlaceClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + GstBinClass *gstbin_class = (GstBinClass *) klass; + + GST_DEBUG_CATEGORY_INIT (autodeinterlace_debug, "autodeinterlace", 0, + "Auto color space converter"); + + gobject_class->set_property = gst_auto_deinterlace_set_property; + gobject_class->get_property = gst_auto_deinterlace_get_property; + + /** + * GstAutoDeinterlace:layout: + * + * This selects which fields is the first in time. + * + */ + g_object_class_install_property (gobject_class, PROP_LAYOUT, + g_param_spec_enum ("layout", + "layout", + "Layout to use" + " Note that if the underlying implementation doesn't support the property it will be ignored.", + GST_TYPE_AUTO_DEINTERLACE_FIELD_LAYOUT, + DEFAULT_LAYOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + + /** + * GstAutoDeinterlace:fields: + * + * This selects which fields should be output. If "all" is selected + * the output framerate will be double. + */ + g_object_class_install_property (gobject_class, PROP_FIELDS, + g_param_spec_enum ("fields", + "fields", + "Fields to use for deinterlacing." + " Note that if the underlying implementation doesn't support the property it will be ignored.", + GST_TYPE_AUTO_DEINTERLACE_FIELDS, + DEFAULT_FIELDS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + + /** + * GstAutoDeinterlace:mode: + * + * This selects whether the deinterlacing methods should + * always be applied or if they should only be applied + * on content that has the "interlaced" flag on the caps. + */ + g_object_class_install_property (gobject_class, PROP_MODE, + g_param_spec_enum ("mode", + "Mode", + "Deinterlace Mode", + GST_TYPE_AUTO_DEINTERLACE_MODES, + DEFAULT_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + + gst_type_mark_as_plugin_api (GST_TYPE_AUTO_DEINTERLACE_FIELD_LAYOUT, 0); + + gst_element_class_set_static_metadata (gstelement_class, + "Select deinterlacer, and converters based on caps", + "Bin/Colorspace/Scale/Video/Converter/Deinterlace", + "Selects the right deinterlacer based on caps", + "Thibault Saunier "); + + gstbin_class->deep_element_added = gst_auto_deinterlace_deep_element_added; + gstbin_class->deep_element_removed = + gst_auto_deinterlace_deep_element_removed; +} + +static void +gst_auto_deinterlace_init (GstAutoDeinterlace * self) +{ + gst_auto_deinterlace_register_filters (self); +} diff --git a/subprojects/gst-plugins-bad/gst/autoconvert/gstautodeinterlace.h b/subprojects/gst-plugins-bad/gst/autoconvert/gstautodeinterlace.h new file mode 100644 index 0000000000..4b4b47722e --- /dev/null +++ b/subprojects/gst-plugins-bad/gst/autoconvert/gstautodeinterlace.h @@ -0,0 +1,33 @@ +/* GStreamer + * Copyright 2023 Igalia S.L. + * @author: Thibault Saunier + * + * 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 +#include "gstautoconvert.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(GstAutoDeinterlace, gst_auto_deinterlace, GST, AUTO_DEINTERLACE, GstBaseAutoConvert); +GST_ELEMENT_REGISTER_DECLARE (autodeinterlace); + +G_END_DECLS + diff --git a/subprojects/gst-plugins-bad/gst/autoconvert/gstautovideo.c b/subprojects/gst-plugins-bad/gst/autoconvert/gstautovideo.c new file mode 100644 index 0000000000..a40f519101 --- /dev/null +++ b/subprojects/gst-plugins-bad/gst/autoconvert/gstautovideo.c @@ -0,0 +1,117 @@ +/* GStreamer + * Copyright 2023 Igalia S.L. + * @author: Thibault Saunier + * + * 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 + +#include "gstautovideo.h" + +GST_DEBUG_CATEGORY (autovideo_debug); +#define GST_CAT_DEFAULT (autovideo_debug) + +static gboolean +append_elements (GString * name, GString * bindesc, + const gchar * const *elements) +{ + if (!elements) { + /* Nothing to do. */ + return TRUE; + } + + for (gint i = 0; elements[i]; i++) { + gchar **e = g_strsplit (elements[i], " ", 2); + GstElementFactory *factory = gst_element_factory_find (g_strchomp (e[0])); + + if (!factory) { + GST_DEBUG_OBJECT (NULL, "Factory %s not found: %s", e[0], elements[i]); + + g_strfreev (e); + return FALSE; + } + + if (name) { + g_string_append (name, e[0]); + } + g_strfreev (e); + + if (bindesc->len > 0) { + g_string_append (bindesc, " ! "); + } + g_string_append (bindesc, elements[i]); + } + + return TRUE; +} + +static void +register_known_bin (GstBaseAutoConvert * self, + const gchar * const *first_elements, + const gchar * const *colorspace_converters, + const gchar * const *last_elements, + const gchar * const *filters, GstRank rank) +{ + GString *name = g_string_new ("autovideoconvert-"); + GString *bindesc = g_string_new (""); + + if (!append_elements (name, bindesc, first_elements)) + goto abort; + if (!append_elements (name, bindesc, colorspace_converters)) + goto abort; + + for (gint i = 0; filters && filters[i]; i++) { + const gchar *tmp[2] = { filters[i], NULL }; + if (!append_elements (name, bindesc, tmp)) + goto abort; + if (!append_elements (NULL, bindesc, colorspace_converters)) + goto abort; + } + if (!append_elements (name, bindesc, last_elements)) + goto abort; + + gst_base_auto_convert_register_filter (GST_BASE_AUTO_CONVERT (self), + g_string_free (name, FALSE), g_string_free (bindesc, FALSE), rank); + return; + +abort: + g_string_free (name, TRUE); + g_string_free (bindesc, TRUE); +} + + +void +gst_auto_video_register_well_known_bins (GstBaseAutoConvert * self, + const GstAutoVideoFilterGenerator * gen) +{ + static gsize gst_auto_video_register_well_known_bins_once = 0; + + if (g_once_init_enter (&gst_auto_video_register_well_known_bins_once)) { + GST_DEBUG_CATEGORY_INIT (autovideo_debug, "autovideo", 0, "Auto video"); + g_once_init_leave (&gst_auto_video_register_well_known_bins_once, TRUE); + } + + for (gint i = 0; gen[i].colorspace_converters[0] || gen[i].first_elements[0] + || gen[i].last_elements[0] || gen[i].filters[0]; i++) { + register_known_bin (self, gen[i].first_elements, + gen[i].colorspace_converters, gen[i].last_elements, gen[i].filters, + gen[i].rank); + } +} diff --git a/subprojects/gst-plugins-bad/gst/autoconvert/gstautovideo.h b/subprojects/gst-plugins-bad/gst/autoconvert/gstautovideo.h new file mode 100644 index 0000000000..de2f6e0313 --- /dev/null +++ b/subprojects/gst-plugins-bad/gst/autoconvert/gstautovideo.h @@ -0,0 +1,40 @@ +/* GStreamer + * Copyright 2023 Igalia S.L. + * @author: Thibault Saunier + * + * 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 "gstautoconvert.h" + +G_BEGIN_DECLS + +typedef struct +{ + const gchar *first_elements[4]; + const gchar *colorspace_converters[4]; + const gchar *last_elements[4]; + + const gchar *filters[4]; + GstRank rank; +} GstAutoVideoFilterGenerator; + +void gst_auto_video_register_well_known_bins (GstBaseAutoConvert *self, const GstAutoVideoFilterGenerator *gen); + +G_END_DECLS diff --git a/subprojects/gst-plugins-bad/gst/autoconvert/gstautovideoconvert.c b/subprojects/gst-plugins-bad/gst/autoconvert/gstautovideoconvert.c index e91861cf3e..f4bdbe0f6b 100644 --- a/subprojects/gst-plugins-bad/gst/autoconvert/gstautovideoconvert.c +++ b/subprojects/gst-plugins-bad/gst/autoconvert/gstautovideoconvert.c @@ -35,6 +35,7 @@ #include #include "gstautovideoconvert.h" +#include "gstautovideo.h" GST_DEBUG_CATEGORY (autovideoconvert_debug); #define GST_CAT_DEFAULT (autovideoconvert_debug) diff --git a/subprojects/gst-plugins-bad/gst/autoconvert/gstautovideoconvert.h b/subprojects/gst-plugins-bad/gst/autoconvert/gstautovideoconvert.h index 5920094b46..daa8f455b2 100644 --- a/subprojects/gst-plugins-bad/gst/autoconvert/gstautovideoconvert.h +++ b/subprojects/gst-plugins-bad/gst/autoconvert/gstautovideoconvert.h @@ -20,8 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __GST_AUTO_VIDEO_CONVERT_H__ -#define __GST_AUTO_VIDEO_CONVERT_H__ +#pragma once #include #include @@ -32,17 +31,4 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE(GstAutoVideoConvert, gst_auto_video_convert, GST, AUTO_VIDEO_CONVERT, GstBaseAutoConvert); GST_ELEMENT_REGISTER_DECLARE (autovideoconvert); -typedef struct -{ - const gchar *first_elements[4]; - const gchar *colorspace_converters[4]; - const gchar *last_elements[4]; - - const gchar *possible_filters[4]; - GstRank rank; -} GstAutoVideoConvertFilterBinsGenerator; - -void gst_auto_video_register_well_known_bins (GstAutoVideoConvert *self, GstAutoVideoConvertFilterBinsGenerator *gen); - G_END_DECLS -#endif /* __GST_AUTO_VIDEO_CONVERT_H__ */ diff --git a/subprojects/gst-plugins-bad/gst/autoconvert/meson.build b/subprojects/gst-plugins-bad/gst/autoconvert/meson.build index a25cf71f1c..e268e3b34d 100644 --- a/subprojects/gst-plugins-bad/gst/autoconvert/meson.build +++ b/subprojects/gst-plugins-bad/gst/autoconvert/meson.build @@ -1,7 +1,9 @@ autocon_sources = [ 'gstbaseautoconvert.c', 'gstautoconvert.c', + 'gstautovideo.c', 'gstautovideoconvert.c', + 'gstautodeinterlace.c', 'plugin.c', ] diff --git a/subprojects/gst-plugins-bad/gst/autoconvert/plugin.c b/subprojects/gst-plugins-bad/gst/autoconvert/plugin.c index fd6d39ceb5..e548a4bbb9 100644 --- a/subprojects/gst-plugins-bad/gst/autoconvert/plugin.c +++ b/subprojects/gst-plugins-bad/gst/autoconvert/plugin.c @@ -1,7 +1,7 @@ /* GStreamer - * Copyright 2010 ST-Ericsson SA + * Copyright 2010 ST-Ericsson SA * @author: Benjamin Gaignard - * + * * 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 @@ -24,6 +24,7 @@ #include "gstautoconvert.h" #include "gstautovideoconvert.h" +#include "gstautodeinterlace.h" static gboolean plugin_init (GstPlugin * plugin) @@ -32,6 +33,7 @@ plugin_init (GstPlugin * plugin) ret |= GST_ELEMENT_REGISTER (autoconvert, plugin); ret |= GST_ELEMENT_REGISTER (autovideoconvert, plugin); + ret |= GST_ELEMENT_REGISTER (autodeinterlace, plugin); return ret; }