autovideoconvert: Handle passing bin description instead of factories

This way we can build our own well know bins for conversion keeping the
code simple.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/899>
This commit is contained in:
Thibault Saunier 2023-03-31 13:28:09 -03:00 committed by GStreamer Marge Bot
parent 292d8453d3
commit b0fdbcec64
7 changed files with 451 additions and 362 deletions

View file

@ -43,6 +43,8 @@
#include "gstautoconvert.h"
#include <gst/pbutils/pbutils.h>
GST_DEBUG_CATEGORY (autoconvert_debug);
#define GST_CAT_DEFAULT (autoconvert_debug)
struct _GstAutoConvert
{
GstBaseAutoConvert parent;
@ -155,6 +157,9 @@ gst_auto_convert_class_init (GstAutoConvertClass * klass)
GObjectClass *gobject_class = (GObjectClass *) klass;
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GST_DEBUG_CATEGORY_INIT (autoconvert_debug, "autoconvert", 0,
"Auto convert element");
gobject_class->set_property = gst_auto_convert_set_property;
gobject_class->get_property = gst_auto_convert_get_property;
@ -187,6 +192,8 @@ gst_auto_convert_class_init (GstAutoConvertClass * klass)
"An element factory name", NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS),
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
GST_BASE_AUTO_CONVERT_CLASS (klass)->registers_filters = FALSE;
}
static void

View file

@ -1,6 +1,8 @@
/* GStreamer
* Copyright 2010 ST-Ericsson SA
* @author: Benjamin Gaignard <benjamin.gaignard@stericsson.com>
* Copyright 2023 Igalia S.L.
* @author: Thibault Saunier <tsaunier@igalia.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -39,215 +41,20 @@ GST_DEBUG_CATEGORY (autovideoconvert_debug);
struct _GstAutoVideoConvert
{
GstAutoConvert parent;
GstBaseAutoConvert parent;
};
G_DEFINE_TYPE (GstAutoVideoConvert, gst_auto_video_convert,
GST_TYPE_AUTO_CONVERT);
GST_TYPE_BASE_AUTO_CONVERT);
GST_ELEMENT_REGISTER_DEFINE (autovideoconvert, "autovideoconvert",
GST_RANK_NONE, gst_auto_video_convert_get_type ());
static gboolean
gst_auto_video_convert_element_filter (GstPluginFeature * feature,
GstAutoVideoConvert * autovideoconvert)
{
const gchar *klass;
/* we only care about element factories */
if (G_UNLIKELY (!GST_IS_ELEMENT_FACTORY (feature)))
return FALSE;
if (!g_strcmp0 (GST_OBJECT_NAME (feature), "autovideoconvert"))
return FALSE;
if (gst_plugin_feature_get_rank (feature) <= GST_RANK_NONE)
return FALSE;
klass = gst_element_factory_get_metadata (GST_ELEMENT_FACTORY_CAST (feature),
GST_ELEMENT_METADATA_KLASS);
/* only select color space converter */
if (strstr (klass, "Colorspace") && strstr (klass, "Scaler") &&
strstr (klass, "Converter") && strstr (klass, "Video")) {
GST_DEBUG_OBJECT (autovideoconvert,
"gst_auto_video_convert_element_filter found %s",
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE_CAST (feature)));
return TRUE;
}
return FALSE;
}
typedef struct
{
const gchar *name;
const gchar *bindesc;
GstRank rank;
} KnownBin;
#define GST_AUTOCONVERT_WNBIN_QUARK g_quark_from_static_string("autovideoconvert-wn-bin-quark")
static void
gst_auto_convert_wn_bin_class_init (GstBinClass * class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (class);
KnownBin *b = g_type_get_qdata (G_OBJECT_CLASS_TYPE (class),
GST_AUTOCONVERT_WNBIN_QUARK);
gchar **factory_names = g_strsplit (b->bindesc, " ! ", -1);
gint nfactories = 0;
GstElementFactory *factory;
GstStaticPadTemplate *template = NULL;
for (nfactories = 0; factory_names[nfactories + 1]; nfactories++);
factory = gst_element_factory_find (factory_names[0]);
for (GList * tmp =
(GList *) gst_element_factory_get_static_pad_templates (factory); tmp;
tmp = tmp->next) {
if (((GstStaticPadTemplate *) tmp->data)->direction == GST_PAD_SINK) {
template = tmp->data;
break;
}
}
gst_element_class_add_static_pad_template (element_class, template);
template = NULL;
factory = gst_element_factory_find (factory_names[nfactories]);
for (GList * tmp =
(GList *) gst_element_factory_get_static_pad_templates (factory); tmp;
tmp = tmp->next) {
if (((GstStaticPadTemplate *) tmp->data)->direction == GST_PAD_SRC) {
template = tmp->data;
break;
}
}
g_assert (template);
gst_element_class_add_static_pad_template (element_class, template);
gst_element_class_set_metadata (element_class, b->name,
"Scaler/Colorspace/Converter/Video", b->name,
"Thibault Saunier <tsaunier@igalia.com>");
g_strfreev (factory_names);
}
static void
gst_auto_convert_wn_bin_init (GstBin * self)
{
KnownBin *b =
g_type_get_qdata (G_OBJECT_TYPE (self), GST_AUTOCONVERT_WNBIN_QUARK);
GError *err = NULL;
GstElement *subbin = gst_parse_bin_from_description (b->bindesc, TRUE, &err);
if (err)
g_error ("%s couldn't be built?!: %s", b->name, err->message);
gst_bin_add (self, subbin);
gst_element_add_pad (GST_ELEMENT (self), gst_ghost_pad_new ("sink",
subbin->sinkpads->data));
gst_element_add_pad (GST_ELEMENT (self), gst_ghost_pad_new ("src",
subbin->srcpads->data));
}
static void
register_well_known_bins (void)
{
GTypeInfo typeinfo = {
sizeof (GstBinClass),
(GBaseInitFunc) NULL,
NULL,
(GClassInitFunc) gst_auto_convert_wn_bin_class_init,
NULL,
NULL,
sizeof (GstBin),
0,
(GInstanceInitFunc) gst_auto_convert_wn_bin_init,
};
/* *INDENT-OFF* */
const KnownBin subbins[] = {
{
.name = "auto+bayer2rgbcolorconvert",
.bindesc = "bayer2rgb ! videoconvertscale ! videoflip method=automatic ! videoconvertscale",
.rank = GST_RANK_SECONDARY,
},
{
.name = "auto+colorconvertrgb2bayer",
.bindesc = "videoconvertscale ! videoflip method=automatic ! videoconvertscale ! rgb2bayer",
.rank = GST_RANK_SECONDARY,
},
{
/* Fallback to only videoconvertscale if videoflip is not available */
.name = "auto+colorconvert-fallback",
.bindesc = "videoconvertscale",
.rank = GST_RANK_MARGINAL,
},
{
.name = "auto+colorconvert",
.bindesc = "videoconvertscale ! videoflip method=automatic ! videoconvertscale",
.rank = GST_RANK_SECONDARY,
},
{
.name = "auto+glcolorconvert",
.bindesc = "glcolorconvert ! glcolorscale ! glvideoflip method=automatic ! glcolorconvert",
.rank = GST_RANK_PRIMARY,
},
};
/* *INDENT-ON* */
for (gint i = 0; i < G_N_ELEMENTS (subbins); i++) {
GType type = 0;
KnownBin *b = NULL;
GstElement *subbin = gst_parse_launch (subbins[i].bindesc, NULL);
if (!subbin) {
GST_INFO ("Ignoring possible Converting scaler: %s '%s'", subbins[i].name,
subbins[i].bindesc);
continue;
}
gst_object_unref (subbin);
type = g_type_register_static (GST_TYPE_BIN, subbins[i].name, &typeinfo, 0);
b = g_malloc0 (sizeof (KnownBin));
b->name = g_strdup (subbins[i].name);
b->bindesc = g_strdup (subbins[i].bindesc);
b->rank = subbins[i].rank;
g_type_set_qdata (type, GST_AUTOCONVERT_WNBIN_QUARK, b);
if (!gst_element_register (NULL, b->name, b->rank, type)) {
g_warning ("Failed to register %s", "autoglcolorconverter");
}
}
}
static GList *
gst_auto_video_convert_create_factory_list (GstAutoConvert * autoconvert)
{
GList *result = NULL;
static gpointer registered_well_known_bins = FALSE;
if (g_once_init_enter (&registered_well_known_bins)) {
register_well_known_bins ();
g_once_init_leave (&registered_well_known_bins, (gpointer) TRUE);
}
/* get the feature list using the filter */
result = gst_registry_feature_filter (gst_registry_get (),
(GstPluginFeatureFilter) gst_auto_video_convert_element_filter,
FALSE, autoconvert);
/* sort on rank and name */
result = g_list_sort (result, gst_plugin_feature_rank_compare_func);
return result;
}
static void
gst_auto_video_convert_class_init (GstAutoVideoConvertClass * klass)
{
GstElementClass *gstelement_class = (GstElementClass *) klass;
((GstAutoConvertClass *) klass)->load_factories =
gst_auto_video_convert_create_factory_list;
GST_DEBUG_CATEGORY_INIT (autovideoconvert_debug, "autovideoconvert", 0,
"Auto color space converter");
@ -255,10 +62,123 @@ gst_auto_video_convert_class_init (GstAutoVideoConvertClass * klass)
"Select color space converter and scalers based on caps",
"Bin/Colorspace/Scale/Video/Converter",
"Selects the right color space converter based on the caps",
"Benjamin Gaignard <benjamin.gaignard@stericsson.com>");
"Thibault Saunier <tsaunier@igalia.com>");
}
static void
gst_auto_video_convert_init (GstAutoVideoConvert * autovideoconvert)
{
/* *INDENT-OFF* */
static const GstAutoVideoFilterGenerator gen[] = {
{
.first_elements = { "bayer2rgb", NULL},
.colorspace_converters = { "videoconvertscale", NULL },
.last_elements = { NULL } ,
.filters = { NULL},
.rank = GST_RANK_SECONDARY,
},
{
.first_elements = { NULL, },
.colorspace_converters = { "videoconvertscale", NULL },
.last_elements = { "rgb2bayer", NULL },
.filters = { NULL },
.rank = GST_RANK_SECONDARY,
},
{
.first_elements = { NULL, },
.colorspace_converters = { "videoconvertscale", NULL },
.last_elements = { NULL, },
.filters = { NULL },
.rank = GST_RANK_SECONDARY,
},
{
.first_elements = { NULL, },
.colorspace_converters = { "glcolorconvert", "glcolorscale", "glcolorconvert", NULL },
.last_elements = { NULL, },
.filters = { NULL },
.rank = GST_RANK_PRIMARY,
},
{
.first_elements = { "glupload", },
.colorspace_converters = { "glcolorconvert", "glcolorscale", "glcolorconvert", NULL },
.last_elements = { NULL, },
.filters = { NULL },
.rank = GST_RANK_PRIMARY,
},
{
.first_elements = { "videoconvertscale", "glupload", NULL },
.colorspace_converters = { NULL },
.last_elements = { NULL },
.filters = { NULL },
.rank = GST_RANK_MARGINAL + 1,
},
{
.first_elements = { "glcolorconvert", "gldownload", NULL },
.colorspace_converters = { NULL },
.last_elements = { NULL },
.filters = { NULL },
.rank = GST_RANK_MARGINAL + 2,
},
{ /* Worst case we upload/download as required */
.first_elements = { "glupload", "gldownload", NULL },
.colorspace_converters = { "glcolorconvert", "glcolorscale", "glcolorconvert", NULL },
.last_elements = { "glupload", "gldownload", NULL },
.filters = { NULL },
.rank = GST_RANK_MARGINAL + 1,
},
{ /* Pure cuda is preferred */
.first_elements = { NULL },
.colorspace_converters = { "cudaconvertscale", NULL },
.last_elements = { NULL },
.filters = { NULL },
.rank = GST_RANK_PRIMARY,
},
{ /* CUDA -> GL */
.first_elements = { "capsfilter caps=video/x-raw(memory:CUDAMemory)", "cudadownload", NULL },
.colorspace_converters = { "glcolorconvert", "glcolorscale", "glcolorconvert", NULL },
.last_elements = { "glupload", "gldownload", NULL },
.filters = { NULL },
.rank = GST_RANK_SECONDARY,
},
{ /* GL memory to cuda */
.first_elements = { NULL },
.colorspace_converters = { "glcolorconvert", "glcolorscale", "glcolorconvert", NULL },
.last_elements = { "cudaupload", "capsfilter caps=video/x-raw(memory:CUDAMemory)", NULL },
.filters = { NULL },
.rank = GST_RANK_MARGINAL,
},
{ /* System memory to cuda */
.first_elements = { NULL },
.colorspace_converters = { "videoconvertscale", NULL },
.last_elements = { "cudaupload", "capsfilter caps=video/x-raw(memory:CUDAMemory)", NULL },
.filters = { NULL },
.rank = GST_RANK_MARGINAL,
},
{
.first_elements = { NULL, },
.colorspace_converters = { "d3d11convert", NULL },
.last_elements = { NULL, },
.filters = { NULL },
.rank = GST_RANK_PRIMARY,
},
{
.first_elements = { "d3d11download", "d3d11upload", NULL},
.colorspace_converters = { "glcolorconvert", "glcolorscale", "glcolorconvert", NULL },
.last_elements = { "d3d11download", "d3d11upload", NULL },
.filters = { NULL },
.rank = GST_RANK_MARGINAL,
},
{ /* Worst case we upload/download as required */
.first_elements = { NULL},
.colorspace_converters = { NULL },
.last_elements = { NULL },
.filters = { NULL },
.rank = 0,
},
};
/* *INDENT-ON* */
gst_auto_video_register_well_known_bins (GST_BASE_AUTO_CONVERT
(autovideoconvert), gen);
}

View file

@ -1,6 +1,8 @@
/* GStreamer
* Copyright 2010 ST-Ericsson SA
* @author: Benjamin Gaignard <benjamin.gaignard@stericsson.com>
* Copyright 2023 Igalia S.L.
* @author: Thibault Saunier <tsaunier@igalia.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -27,8 +29,20 @@
G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE(GstAutoVideoConvert, gst_auto_video_convert, GST, AUTO_VIDEO_CONVERT, GstAutoConvert);
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__ */

View file

@ -102,8 +102,8 @@ static gboolean gst_base_auto_convert_internal_src_event (GstPad * pad,
GstObject * parent, GstEvent * event);
static gboolean gst_base_auto_convert_internal_src_query (GstPad * pad,
GstObject * parent, GstQuery * query);
static GList *gst_base_auto_convert_get_or_load_factories (GstBaseAutoConvert *
self);
static GList *gst_base_auto_convert_get_or_load_filters_info (GstBaseAutoConvert
* self);
G_DECLARE_FINAL_TYPE (GstBaseAutoConvertPad, gst_base_auto_convert_pad, GST,
BASE_AUTO_CONVERT_PAD, GstPad);
@ -184,6 +184,112 @@ gst_base_auto_convert_element_removed (GstBin * bin, GstElement * child)
g_hash_table_remove (self->elements, child);
}
static void
gst_auto_convert_filter_info_free (GstAutoConvertFilterInfo * knwon_bin)
{
g_free (knwon_bin->name);
g_free (knwon_bin->bindesc);
g_free (knwon_bin);
}
static gint
g_auto_convert_filter_info_compare (GstAutoConvertFilterInfo * b1,
GstAutoConvertFilterInfo * b2)
{
gint diff;
diff = b2->rank - b1->rank;
if (diff != 0)
return diff;
diff = g_strcmp0 (b2->name, b1->name);
return diff;
}
static GstCaps *
gst_base_auto_convert_get_template_caps_for (GstElement * subbin,
GstPadDirection dir)
{
GstElement *element = NULL;
GstPad *pad = NULL;
GstCaps *res = NULL;
g_assert (g_list_length (subbin->sinkpads) == 1);
g_assert (g_list_length (subbin->srcpads) == 1);
if (GST_IS_BIN (subbin)) {
GstPad *ghostpad =
(dir == GST_PAD_SINK) ? subbin->sinkpads->data : subbin->srcpads->data;
GstPad *internal = gst_pad_get_single_internal_link (ghostpad);
pad = gst_pad_get_peer (internal);
gst_object_unref (internal);
} else {
pad =
(dir ==
GST_PAD_SINK) ? gst_object_ref (subbin->
sinkpads->data) : gst_object_ref (subbin->srcpads->data);
}
element = GST_ELEMENT (GST_OBJECT_PARENT (pad));
g_assert (element);
if (!g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (element)),
"capsfilter")) {
g_object_get (G_OBJECT (element), "caps", &res, NULL);
} else {
res = gst_pad_get_pad_template_caps (pad);
}
gst_object_unref (pad);
return gst_caps_make_writable (res);
}
gboolean
gst_base_auto_convert_register_filter (GstBaseAutoConvert * self, gchar * name,
gchar * bindesc, GstRank rank)
{
g_assert (name);
for (GList * tmp = self->filters_info; tmp; tmp = tmp->next) {
g_return_val_if_fail (g_strcmp0 (name,
((GstAutoConvertFilterInfo *) tmp->data)->name), FALSE);
}
GError *err = NULL;
bindesc = g_strchomp (bindesc);
GstElement *subbin = gst_parse_bin_from_description_full (bindesc, TRUE,
NULL, GST_PARSE_FLAG_NO_SINGLE_ELEMENT_BINS | GST_PARSE_FLAG_PLACE_IN_BIN,
&err);
if (!subbin) {
GST_INFO ("Could not create subbin for %s", name);
g_free (name);
g_free (bindesc);
return FALSE;
}
GstAutoConvertFilterInfo *filter_info = g_new0 (GstAutoConvertFilterInfo, 1);
filter_info->sink_caps =
gst_base_auto_convert_get_template_caps_for (subbin, GST_PAD_SINK);
filter_info->src_caps =
gst_base_auto_convert_get_template_caps_for (subbin, GST_PAD_SRC);
filter_info->name = name;
filter_info->bindesc = bindesc;
filter_info->rank = rank;
GST_OBJECT_LOCK (self);
self->filters_info =
g_list_insert_sorted (self->filters_info, filter_info,
(GCompareFunc) g_auto_convert_filter_info_compare);
GST_OBJECT_UNLOCK (self);
return TRUE;
}
static void
gst_base_auto_convert_class_init (GstBaseAutoConvertClass * klass)
{
@ -201,6 +307,8 @@ gst_base_auto_convert_class_init (GstBaseAutoConvertClass * klass)
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_base_auto_convert_finalize);
gstbin_class->element_removed = gst_base_auto_convert_element_removed;
klass->registers_filters = TRUE;
}
static void
@ -255,34 +363,12 @@ gst_base_auto_convert_finalize (GObject * object)
if (self->factories)
gst_plugin_feature_list_free (self->factories);
g_hash_table_unref (self->elements);
g_list_free_full (self->filters_info,
(GDestroyNotify) gst_auto_convert_filter_info_free);
G_OBJECT_CLASS (gst_base_auto_convert_parent_class)->finalize (object);
}
static GstElement *
gst_base_auto_convert_get_element_by_type (GstBaseAutoConvert * self,
GType type)
{
GList *item, *elements;
GstElement *element = NULL;
g_return_val_if_fail (type != 0, NULL);
GST_BASEAUTOCONVERT_LOCK (self);
elements = g_hash_table_get_keys (self->elements);
for (item = elements; item; item = item->next) {
if (G_OBJECT_TYPE (item->data) == type) {
element = gst_object_ref (item->data);
break;
}
}
GST_BASEAUTOCONVERT_UNLOCK (self);
g_list_free (elements);
return element;
}
/**
* get_pad_by_direction:
* @element: The Element
@ -401,18 +487,28 @@ gst_base_auto_convert_get_internal_srcpad (GstBaseAutoConvert * self)
static GstElement *
gst_base_auto_convert_add_element (GstBaseAutoConvert * self,
GstElementFactory * factory)
GstAutoConvertFilterInfo * filter_info)
{
GstElement *element = NULL;
InternalPads *pads;
GError *error = NULL;
GST_DEBUG_OBJECT (self, "Adding element %s to the baseautoconvert bin",
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
filter_info->name);
element = gst_parse_bin_from_description_full (filter_info->bindesc, TRUE,
NULL, GST_PARSE_FLAG_NO_SINGLE_ELEMENT_BINS | GST_PARSE_FLAG_PLACE_IN_BIN,
&error);
if (!element) {
GST_INFO_OBJECT (self, "Could not build %s: %s", filter_info->name,
error->message);
g_error_free (error);
element = gst_element_factory_create (factory, NULL);
if (!element)
return NULL;
}
gst_object_set_name (GST_OBJECT (element), filter_info->name);
pads = internal_pads_new (self, GST_OBJECT_NAME (element));
g_hash_table_insert (self->elements, element, pads);
@ -434,26 +530,17 @@ gst_base_auto_convert_add_element (GstBaseAutoConvert * self,
}
static GstElement *
gst_base_auto_convert_get_or_make_element_from_factory (GstBaseAutoConvert *
self, GstElementFactory * factory)
gst_base_auto_convert_get_or_make_element_from_filter_info (GstBaseAutoConvert *
self, GstAutoConvertFilterInfo * nb)
{
GstElement *element = NULL;
GstElementFactory *loaded_factory =
GST_ELEMENT_FACTORY (gst_plugin_feature_load (GST_PLUGIN_FEATURE
(factory)));
if (!loaded_factory)
return NULL;
element = gst_base_auto_convert_get_element_by_type (self,
gst_element_factory_get_element_type (loaded_factory));
element = gst_bin_get_by_name (GST_BIN (self), nb->name);
if (!element) {
element = gst_base_auto_convert_add_element (self, loaded_factory);
element = gst_base_auto_convert_add_element (self, nb);
}
gst_object_unref (loaded_factory);
return element;
}
@ -464,51 +551,25 @@ gst_base_auto_convert_get_or_make_element_from_factory (GstBaseAutoConvert *
*/
static gboolean
factory_can_intersect (GstBaseAutoConvert * self,
GstElementFactory * factory, GstPadDirection direction, GstCaps * caps)
filter_info_can_intersect (GstBaseAutoConvert * self,
GstAutoConvertFilterInfo * filter_info, GstPadDirection direction,
GstCaps * caps)
{
const GList *templates;
gint has_direction = FALSE;
gboolean ret = FALSE;
gboolean res;
GST_LOG_OBJECT (self, "Checking if %s (bin_desc=%s) supports %s caps:",
filter_info->name, filter_info->bindesc,
direction == GST_PAD_SINK ? "sink" : "src");
GST_LOG_OBJECT (self, "Supported caps: %" GST_PTR_FORMAT,
direction ==
GST_PAD_SINK ? filter_info->sink_caps : filter_info->src_caps);
GST_LOG_OBJECT (self, "Caps: %" GST_PTR_FORMAT, caps);
g_return_val_if_fail (factory != NULL, FALSE);
g_return_val_if_fail (caps != NULL, FALSE);
res =
gst_caps_can_intersect (direction ==
GST_PAD_SINK ? filter_info->sink_caps : filter_info->src_caps, caps);
GST_LOG_OBJECT (self, "Intersect result: %d", res);
templates = gst_element_factory_get_static_pad_templates (factory);
while (templates) {
GstStaticPadTemplate *template = (GstStaticPadTemplate *) templates->data;
if (template->direction == direction) {
GstCaps *tmpl_caps = NULL;
gboolean intersect;
/* If there is more than one pad in this direction, we return FALSE
* Only transform elements (with one sink and one source pad)
* are accepted
*/
if (has_direction) {
GST_ERROR_OBJECT (self, "Factory %p"
" has more than one static template with dir %d",
template, direction);
return FALSE;
}
has_direction = TRUE;
tmpl_caps = gst_static_caps_get (&template->static_caps);
intersect = gst_caps_can_intersect (tmpl_caps, caps);
GST_DEBUG_OBJECT (self, "Factories %" GST_PTR_FORMAT
" static caps %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT
" can%s intersect", factory, tmpl_caps, caps,
intersect ? "" : " not");
gst_caps_unref (tmpl_caps);
ret |= intersect;
}
templates = g_list_next (templates);
}
return ret;
return res;
}
static gboolean
@ -633,6 +694,9 @@ gst_base_auto_convert_activate_element (GstBaseAutoConvert * self,
GST_OBJECT_NAME (GST_OBJECT (element)));
done:
GST_DEBUG_OBJECT (element, "Activating element %s",
res ? "succeeded" : "failed");
gst_object_unref (element);
internal_pads_unref (pads);
gst_clear_object (&srcpad);
@ -677,6 +741,22 @@ gst_base_auto_convert_iterate_internal_links (GstPad * pad, GstObject * parent)
return it;
}
static GstAutoConvertFilterInfo *
gst_auto_convert_get_filter_info (GstBaseAutoConvert * self,
GstElement * element)
{
GList *tmp;
for (tmp = self->filters_info; tmp; tmp = tmp->next) {
GstAutoConvertFilterInfo *filter_info = tmp->data;
if (!g_strcmp0 (filter_info->name, GST_OBJECT_NAME (element)))
return filter_info;
}
return NULL;
}
/*
* If there is already an internal element, it will try to call set_caps on it
*
@ -689,9 +769,9 @@ static gboolean
gst_base_auto_convert_sink_setcaps (GstBaseAutoConvert * self, GstCaps * caps,
gboolean check_downstream)
{
GList *elem;
GList *tmp;
GstCaps *other_caps = NULL;
GList *factories;
GList *filters_info = NULL;
GstCaps *current_caps = NULL;
gboolean res = FALSE;
GstElement *current_subelement = NULL;
@ -709,66 +789,81 @@ gst_base_auto_convert_sink_setcaps (GstBaseAutoConvert * self, GstCaps * caps,
other_caps = gst_pad_peer_query_caps (self->srcpad, NULL);
current_subelement = gst_base_auto_convert_get_subelement (self);
GST_DEBUG_OBJECT (self,
"'%" GST_PTR_FORMAT "' Setting caps to: %" GST_PTR_FORMAT
" - other caps: %" GST_PTR_FORMAT, current_subelement, caps, other_caps);
if (current_subelement) {
if (gst_pad_peer_query_accept_caps (self->current_internal_srcpad, caps)) {
GstAutoConvertFilterInfo *filter_info =
gst_auto_convert_get_filter_info (self, current_subelement);
res = TRUE;
if (other_caps) {
GstElementFactory *factory =
gst_element_get_factory (current_subelement);
if (!factory_can_intersect (self, factory, GST_PAD_SRC, other_caps)) {
GST_DEBUG_OBJECT (self,
"Checking if known bin %s can intersect with %" GST_PTR_FORMAT,
filter_info->name, other_caps);
if (!filter_info_can_intersect (self, filter_info, GST_PAD_SRC,
other_caps)) {
GST_LOG_OBJECT (self,
"Factory %s does not accept src caps %" GST_PTR_FORMAT,
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)),
other_caps);
"filter_info %s does not accept src caps %" GST_PTR_FORMAT,
filter_info->name, other_caps);
res = FALSE;
}
GST_DEBUG_OBJECT (self, "Filter %s can intersect", filter_info->name);
}
if (res) {
/* If we can set the new caps on the current element,
* then we just get out
*/
GST_ERROR_OBJECT (self, "Could set %s:%s to %" GST_PTR_FORMAT,
GST_DEBUG_PAD_NAME (self->current_internal_srcpad), caps);
GST_DEBUG_OBJECT (self,
"Could set %s:%s to %" GST_PTR_FORMAT " reusing %s",
GST_DEBUG_PAD_NAME (self->current_internal_srcpad), caps,
filter_info->name);
goto get_out;
} else {
GST_DEBUG_OBJECT (self,
"Can't reuse currently configured subelement: %s",
filter_info->name);
}
}
}
if (!check_downstream)
other_caps = gst_pad_peer_query_caps (self->srcpad, NULL);
/* We already queries downstream caps otherwise */
/* We already queried downstream caps otherwise */
factories = gst_base_auto_convert_get_or_load_factories (self);
for (elem = factories; elem; elem = g_list_next (elem)) {
GstElementFactory *factory = GST_ELEMENT_FACTORY (elem->data);
filters_info = gst_base_auto_convert_get_or_load_filters_info (self);
for (tmp = filters_info; tmp; tmp = g_list_next (tmp)) {
GstAutoConvertFilterInfo *filter_info = tmp->data;
GstElement *element;
/* Lets first check if according to the static pad templates on the factory
GST_DEBUG_OBJECT (self, "Trying %s (bin_desc=%s)", filter_info->name,
filter_info->bindesc);
/* Lets first check if according to the static pad templates on the known bin
* these caps have any chance of success
*/
if (!factory_can_intersect (self, factory, GST_PAD_SINK, caps)) {
GST_LOG_OBJECT (self, "Factory %s does not accept sink caps %"
GST_PTR_FORMAT,
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)), caps);
if (!filter_info_can_intersect (self, filter_info, GST_PAD_SINK, caps)) {
GST_DEBUG_OBJECT (self, "Known bin %s does not accept sink caps %"
GST_PTR_FORMAT, filter_info->name, caps);
continue;
}
if (other_caps != NULL) {
if (!factory_can_intersect (self, factory, GST_PAD_SRC, other_caps)) {
GST_LOG_OBJECT (self,
"Factory %s does not accept src caps %" GST_PTR_FORMAT,
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)),
other_caps);
if (!filter_info_can_intersect (self, filter_info, GST_PAD_SRC,
other_caps)) {
GST_DEBUG_OBJECT (self,
"Known bin %s does not accept src caps %" GST_PTR_FORMAT,
filter_info->name, other_caps);
continue;
}
GST_DEBUG_OBJECT (self, "Filter %s can intersect", filter_info->name);
}
/* The element had a chance of success, lets make it */
GST_DEBUG_OBJECT (self, "Trying bin %s", filter_info->name);
element =
gst_base_auto_convert_get_or_make_element_from_factory (self, factory);
gst_base_auto_convert_get_or_make_element_from_filter_info (self,
filter_info);
if (!element)
continue;
@ -786,9 +881,10 @@ get_out:
gst_clear_caps (&other_caps);
gst_clear_caps (&current_caps);
if (!res)
if (!res) {
GST_WARNING_OBJECT (self,
"Could not find a matching element for caps: %" GST_PTR_FORMAT, caps);
}
return res;
}
@ -870,32 +966,51 @@ compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
}
static GList *
gst_base_auto_convert_get_or_load_factories (GstBaseAutoConvert * self)
gst_base_auto_convert_get_or_load_filters_info (GstBaseAutoConvert * self)
{
GList *all_factories;
GstBaseAutoConvertClass *klass = GST_BASE_AUTO_CONVERT_GET_CLASS (self);
GST_OBJECT_LOCK (self);
if (self->factories)
if (self->filters_info) {
GST_OBJECT_UNLOCK (self);
goto done;
GST_OBJECT_UNLOCK (self);
}
if (GST_BASE_AUTO_CONVERT_GET_CLASS (self)->registers_filters) {
GST_ERROR_OBJECT (self,
"Filters should have been registered but none found");
GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, ("No known filter found."),
(NULL));
goto done;
}
if (!self->factories) {
GST_OBJECT_UNLOCK (self);
if (klass->load_factories) {
all_factories = klass->load_factories (self);
} else {
all_factories =
g_list_sort (gst_registry_feature_filter (gst_registry_get (),
gst_base_auto_convert_default_filter_func, FALSE, NULL),
(GCompareFunc) compare_ranks);
GST_OBJECT_LOCK (self);
self->factories = all_factories;
}
GST_OBJECT_LOCK (self);
self->factories = all_factories;
done:
GST_OBJECT_UNLOCK (self);
return self->factories;
for (GList * tmp = self->factories; tmp; tmp = g_list_next (tmp)) {
GstElementFactory *factory = tmp->data;
gst_base_auto_convert_register_filter (self,
gst_object_get_name (GST_OBJECT (factory)),
gst_object_get_name (GST_OBJECT (factory)),
gst_plugin_feature_get_rank (GST_PLUGIN_FEATURE (factory))
);
}
done:
return self->filters_info;
}
/* In this case, we should almost always have an internal element, because
@ -1078,7 +1193,7 @@ gst_base_auto_convert_getcaps (GstBaseAutoConvert * self, GstCaps * filter,
GstPadDirection dir)
{
GstCaps *caps = NULL, *other_caps = NULL;
GList *elem, *factories;
GList *kb, *filters_info;
caps = gst_caps_new_empty ();
@ -1095,35 +1210,35 @@ gst_base_auto_convert_getcaps (GstBaseAutoConvert * self, GstCaps * filter,
goto out;
}
factories = gst_base_auto_convert_get_or_load_factories (self);
for (elem = factories; elem; elem = g_list_next (elem)) {
GstElementFactory *factory = GST_ELEMENT_FACTORY (elem->data);
filters_info = gst_base_auto_convert_get_or_load_filters_info (self);
for (kb = filters_info; kb; kb = g_list_next (kb)) {
GstAutoConvertFilterInfo *filter_info = kb->data;
GstElement *element = NULL;
GstCaps *element_caps;
InternalPads *pads;
if (filter) {
if (!factory_can_intersect (self, factory, dir, filter)) {
if (!filter_info_can_intersect (self, filter_info, dir, filter)) {
GST_LOG_OBJECT (self,
"Factory %s does not accept src caps %" GST_PTR_FORMAT,
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)),
other_caps);
"Bin %s does not accept %s caps %" GST_PTR_FORMAT,
filter_info->name, dir == GST_PAD_SRC ? "src" : "sink", other_caps);
continue;
}
}
if (other_caps != NULL) {
if (!factory_can_intersect (self, factory,
if (!filter_info_can_intersect (self, filter_info,
dir == GST_PAD_SINK ? GST_PAD_SRC : GST_PAD_SINK, other_caps)) {
GST_LOG_OBJECT (self,
"Factory %s does not accept src caps %" GST_PTR_FORMAT,
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)),
other_caps);
"%s does not accept %s caps %" GST_PTR_FORMAT,
filter_info->name,
dir == GST_PAD_SINK ? "src" : "sink", other_caps);
continue;
}
element = gst_base_auto_convert_get_or_make_element_from_factory (self,
factory);
element =
gst_base_auto_convert_get_or_make_element_from_filter_info (self,
filter_info);
if (element == NULL)
continue;
@ -1141,24 +1256,16 @@ gst_base_auto_convert_getcaps (GstBaseAutoConvert * self, GstCaps * filter,
if (gst_caps_is_any (caps))
goto out;
} else {
const GList *tmp;
GstCaps *static_caps =
dir == GST_PAD_SRC ? filter_info->src_caps : filter_info->sink_caps;
for (tmp = gst_element_factory_get_static_pad_templates (factory);
tmp; tmp = g_list_next (tmp)) {
GstStaticPadTemplate *template = tmp->data;
if (GST_PAD_TEMPLATE_DIRECTION (template) == dir) {
GstCaps *static_caps = gst_static_pad_template_get_caps (template);
if (static_caps) {
caps = gst_caps_merge (caps, static_caps);
}
/* Early out, any is absorbing */
if (gst_caps_is_any (caps))
goto out;
}
if (static_caps) {
caps = gst_caps_merge (caps, static_caps);
}
/* Early out, any is absorbing */
if (gst_caps_is_any (caps))
goto out;
}
}
@ -1191,7 +1298,7 @@ gst_base_auto_convert_src_event (GstPad * pad, GstObject * parent,
gst_object_unref (internal_sinkpad);
} else if (GST_EVENT_TYPE (event) != GST_EVENT_RECONFIGURE) {
GST_WARNING_OBJECT (self,
"Got upstream event while no element was selected," "forwarding.");
"Got upstream event while no element was selected, forwarding.");
ret = gst_pad_push_event (self->sinkpad, event);
} else
gst_event_unref (event);
@ -1339,3 +1446,14 @@ gst_base_auto_convert_internal_src_query (GstPad * pad, GstObject * parent,
return gst_pad_peer_query (self->sinkpad, query);
}
void
gst_base_auto_convert_reset_filters (GstBaseAutoConvert * self)
{
GST_OBJECT_LOCK (self);
g_list_free_full (self->filters_info,
(GDestroyNotify) gst_auto_convert_filter_info_free);
self->filters_info = NULL;
GST_OBJECT_UNLOCK (self);
}

View file

@ -42,6 +42,7 @@ struct _GstBaseAutoConvert
GstBin bin; /* we extend GstBin */
GList *factories;
GList *filters_info;
GstPad *sinkpad;
GstPad *srcpad;
@ -54,17 +55,36 @@ struct _GstBaseAutoConvert
GstPad *current_internal_srcpad;
GstPad *current_internal_sinkpad;
GHashTable * elements;
GHashTable *elements;
};
/* This structure is used to allow handling random bin from their description
without needing to register a factory. The data it contains is pretty similar
but is specific for filters (1sinkpad and 1 srcpad).
*/
typedef struct
{
/* Name of the filter, each instance of should have that name */
gchar *name;
gchar *bindesc;
GstRank rank;
GstCaps *sink_caps;
GstCaps *src_caps;
} GstAutoConvertFilterInfo;
struct _GstBaseAutoConvertClass
{
GstBinClass parent_class;
GList* (*load_factories)(GstBaseAutoConvert *base_autoconvert);
gboolean registers_filters;
};
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstBaseAutoConvert, gst_object_unref)
GType gst_base_auto_convert_get_type (void);
gboolean
gst_base_auto_convert_register_filter(GstBaseAutoConvert *self, gchar *name,
gchar * bindesc, GstRank rank);
void gst_base_auto_convert_reset_filters (GstBaseAutoConvert * self);
G_END_DECLS

View file

@ -3,7 +3,7 @@ meta,
"gltestsrc ! gldownload ! autovideoconvert name=convert ! capsfilter name=capsfilter caps=\"video/x-raw(memory:GLMemory)\" ! fakevideosink name=sink",
},
configs = {
"$(validateflow), pad=convert:src, record-buffers=true, ignored-fields=\"stream-start={stream-id,group-id,stream},buffer={meta}\"",
"$(validateflow), pad=\"^convert:src$\", record-buffers=true, ignored-fields=\"stream-start={stream-id,group-id,stream},buffer={meta}\"",
}
crank-clock, repeat=2
@ -15,6 +15,7 @@ foreach,
"video/x-raw",
>,
actions = {
[checkpoint, text="Setup capsfilter caps=$(caps)"],
[set-properties, capsfilter::caps="$(caps)"],
[crank-clock, repeat=2],
[wait, on-clock=true],

View file

@ -4,12 +4,21 @@ event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, s
buffer: pts=0:00:00.000000000, dur=0:00:00.033333333, flags=discont
buffer: pts=0:00:00.033333333, dur=0:00:00.033333333
buffer: pts=0:00:00.066666666, dur=0:00:00.033333334
event caps: video/x-raw, format=(string)RGBA, framerate=(fraction)30/1, height=(int)240, texture-target=(string)2D, width=(int)320;
CHECKPOINT: Setup capsfilter caps=video/x-raw
event caps: video/x-raw, format=(string)RGBA, framerate=(fraction)30/1, height=(int)240, width=(int)320;
buffer: pts=0:00:00.100000000, dur=0:00:00.033333333
buffer: pts=0:00:00.133333333, dur=0:00:00.033333333
CHECKPOINT: Setup capsfilter caps=video/x-raw(memory:GLMemory)
event caps: video/x-raw(memory:GLMemory), format=(string)RGBA, framerate=(fraction)30/1, height=(int)240, texture-target=(string)2D, width=(int)320;
buffer: pts=0:00:00.166666666, dur=0:00:00.033333334
buffer: pts=0:00:00.200000000, dur=0:00:00.033333333
event caps: video/x-raw, format=(string)RGBA, framerate=(fraction)30/1, height=(int)240, texture-target=(string)2D, width=(int)320;
CHECKPOINT: Setup capsfilter caps=video/x-raw
event caps: video/x-raw, format=(string)RGBA, framerate=(fraction)30/1, height=(int)240, width=(int)320;
buffer: pts=0:00:00.233333333, dur=0:00:00.033333333
buffer: pts=0:00:00.266666666, dur=0:00:00.033333334