diff --git a/ChangeLog b/ChangeLog index c00963622f..edf640f82c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +2008-02-03 Sebastian Dröge + + * gst/gstelement.c: (gst_element_base_class_init), + (gst_element_class_add_pad_template): + * gst/gstpadtemplate.c: + Make it possible (and recommended) to set element details and add + pad templates in the class_init functions by copying the details/pad + templates in GstElement's base_init. + + Also make it possible to replace existing pad templates by adding + a new one with the same name. This was done in a hackish fashion + in same elements before already. + + Don't reference pad templates that are added a second time. A + new pad template has a refcount of one and is not floating anymore + and to be owned by the element's class. Make this more explicit by + mentioning it in the docs of gst_element_class_add_pad_template(). + + These changes are backwards compatible. Fixes bug #491501. + + * tests/check/gst/gstelement.c: + Add unit test for setting element details, adding pad templates and + replacing them in a subclass. + 2008-02-02 Sebastian Dröge * tools/gst-inspect.c: (print_interfaces), diff --git a/common b/common index 571dce3335..3c5473161c 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 571dce3335f9be76978009b3842c050dbb900e6f +Subproject commit 3c5473161ce19a3530bad279b842d542895b1500 diff --git a/gst/gstelement.c b/gst/gstelement.c index 9e97fde479..a2131c53cf 100644 --- a/gst/gstelement.c +++ b/gst/gstelement.c @@ -238,9 +238,34 @@ static void gst_element_base_class_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GList *node, *padtemplates; - memset (&element_class->details, 0, sizeof (GstElementDetails)); - element_class->padtemplates = NULL; + /* Copy the element details here so elements can inherit the + * details from their base class and classes only need to set + * the details in class_init instead of base_init */ + /* FIXME: We probably need something like for copying + * the details at a central place */ + element_class->details.longname = g_strdup (element_class->details.longname); + element_class->details.klass = g_strdup (element_class->details.klass); + element_class->details.description = + g_strdup (element_class->details.description); + element_class->details.author = g_strdup (element_class->details.author); + + /* Copy the pad templates so elements inherit them + * from their base class but elements can add pad templates in class_init + * instead of base_init. + */ + /* FIXME: Do we consider GstPadTemplates as immutable? If so we can + * simply ref them instead of copying. + */ + padtemplates = g_list_copy (element_class->padtemplates); + for (node = padtemplates; node != NULL; node = node->next) { + GstPadTemplate *tmpl = (GstPadTemplate *) node->data; + + node->data = gst_pad_template_new (tmpl->name_template, + tmpl->direction, tmpl->presence, gst_caps_copy (tmpl->caps)); + } + element_class->padtemplates = padtemplates; } static void @@ -1102,22 +1127,38 @@ gst_element_iterate_sink_pads (GstElement * element) * @klass: the #GstElementClass to add the pad template to. * @templ: a #GstPadTemplate to add to the element class. * - * Adds a padtemplate to an element class. This is mainly used in the _base_init - * functions of classes. + * Adds a padtemplate to an element class. This is mainly used in the + * _class_init functions of classes. If a pad template with the same + * name as an already existing one is added the old one is replaced + * by the new one. + * + * This function takes the ownership of the #GstPadTemplate. */ void gst_element_class_add_pad_template (GstElementClass * klass, GstPadTemplate * templ) { + GList *template_list = klass->padtemplates; + g_return_if_fail (GST_IS_ELEMENT_CLASS (klass)); g_return_if_fail (GST_IS_PAD_TEMPLATE (templ)); - /* avoid registering pad templates with the same name */ - g_return_if_fail (gst_element_class_get_pad_template (klass, - templ->name_template) == NULL); + /* If we already have a pad template with the same name replace the + * old one. */ + while (template_list) { + GstPadTemplate *padtempl = (GstPadTemplate *) template_list->data; - klass->padtemplates = g_list_append (klass->padtemplates, - gst_object_ref (templ)); + /* Found pad with the same name, replace and return */ + if (strcmp (templ->name_template, padtempl->name_template) == 0) { + gst_object_unref (padtempl); + template_list->data = templ; + return; + } + template_list = g_list_next (template_list); + } + + /* Not found a pad with the same name so add it to the list */ + klass->padtemplates = g_list_append (klass->padtemplates, templ); klass->numpadtemplates++; } @@ -1127,7 +1168,7 @@ gst_element_class_add_pad_template (GstElementClass * klass, * @details: details to set * * Sets the detailed information for a #GstElementClass. - * This function is for use in _base_init functions only. + * This function is for use in _class_init functions only. * * The @details are copied. */ @@ -1155,7 +1196,7 @@ gst_element_class_set_details (GstElementClass * klass, * * Sets the detailed information for a #GstElementClass. Simpler version of * gst_element_class_set_details() that generates less linker overhead. - * This function is for use in _base_init functions only. + * This function is for use in _class_init functions only. * * The detail parameter strings are copied into the #GstElementDetails for * the element class. diff --git a/gst/gstpadtemplate.c b/gst/gstpadtemplate.c index 2f12f1c5d6..fb1be49567 100644 --- a/gst/gstpadtemplate.c +++ b/gst/gstpadtemplate.c @@ -78,13 +78,13 @@ * * * The following example shows you how to add the padtemplate to an - * element class, this is usually done in the base_init of the class: + * element class, this is usually done in the class_init of the class: * * * static void - * my_element_base_init (gpointer g_class) + * my_element_class_init (GstMyElementClass *klass) * { - * GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); + * GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); * * gst_element_class_add_pad_template (gstelement_class, * gst_static_pad_template_get (&my_template)); diff --git a/tests/check/gst/gstelement.c b/tests/check/gst/gstelement.c index 22de7dd546..c7877fc5f6 100644 --- a/tests/check/gst/gstelement.c +++ b/tests/check/gst/gstelement.c @@ -205,6 +205,189 @@ GST_START_TEST (test_class) GST_END_TEST; +typedef struct _GstTestElement +{ + GstElement parent; + +} GstTestElement; + +typedef struct _GstTestElementClass +{ + GstElementClass parent; + +} GstTestElementClass; + +static void +gst_test_element_class_init (GstTestElementClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstPadTemplate *templ; + + gst_element_class_set_details_simple (element_class, "Test element", + "Element", "Does nothing", "Foo Bar "); + + fail_unless_equals_int (g_list_length (gst_element_class_get_pad_template_list + (element_class)), 0); + + fail_unless (gst_element_class_get_pad_template (element_class, + "test") == NULL); + + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("test", GST_PAD_SRC, GST_PAD_ALWAYS, GST_CAPS_ANY)); + + fail_unless_equals_int (g_list_length (gst_element_class_get_pad_template_list + (element_class)), 1); + + fail_unless ((templ = + gst_element_class_get_pad_template (element_class, "test")) != NULL); + fail_unless (gst_caps_is_any (templ->caps)); + + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("test2", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_CAPS_ANY)); + + fail_unless_equals_int (g_list_length (gst_element_class_get_pad_template_list + (element_class)), 2); + + fail_unless ((templ = + gst_element_class_get_pad_template (element_class, "test2")) != NULL); + fail_unless (gst_caps_is_any (templ->caps)); + + /* Add "test" again, with NONE caps this time */ + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("test", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_CAPS_NONE)); + + fail_unless_equals_int (g_list_length (gst_element_class_get_pad_template_list + (element_class)), 2); + + fail_unless ((templ = + gst_element_class_get_pad_template (element_class, "test")) != NULL); + fail_unless (gst_caps_is_empty (templ->caps)); +} + +GType +gst_test_element_get_type (void) +{ + static GType gst_test_element_type = G_TYPE_NONE; + + if (gst_test_element_type == G_TYPE_NONE) { + static const GTypeInfo gst_test_element_info = { + sizeof (GstTestElementClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gst_test_element_class_init, + NULL, + NULL, + sizeof (GstTestElement), + 0, + NULL, /* instance_init */ + NULL + }; + + gst_test_element_type = g_type_register_static (GST_TYPE_ELEMENT, + "GstTestElement", &gst_test_element_info, 0); + } + return gst_test_element_type; +} + +typedef struct _GstTestElement2 +{ + GstTestElement parent; + +} GstTestElement2; + +typedef struct _GstTestElement2Class +{ + GstTestElementClass parent; + +} GstTestElement2Class; + +static void +gst_test_element2_class_init (GstTestElement2Class * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstPadTemplate *templ; + + gst_element_class_set_details_simple (element_class, "Test element 2", + "Element", "Does nothing", "Foo Bar "); + + fail_unless_equals_int (g_list_length (gst_element_class_get_pad_template_list + (element_class)), 2); + + fail_unless ((templ = + gst_element_class_get_pad_template (element_class, "test")) != NULL); + fail_unless (gst_caps_is_empty (templ->caps)); + + fail_unless ((templ = + gst_element_class_get_pad_template (element_class, "test2")) != NULL); + fail_unless (gst_caps_is_any (templ->caps)); + + /* Add "test" pad with ANY caps, should have "test" pad with EMPTY caps before */ + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("test", GST_PAD_SRC, GST_PAD_ALWAYS, GST_CAPS_ANY)); + + fail_unless_equals_int (g_list_length (gst_element_class_get_pad_template_list + (element_class)), 2); + + fail_unless ((templ = + gst_element_class_get_pad_template (element_class, "test")) != NULL); + fail_unless (gst_caps_is_any (templ->caps)); + + + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("test4", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_CAPS_ANY)); + + fail_unless_equals_int (g_list_length (gst_element_class_get_pad_template_list + (element_class)), 3); + + fail_unless ((templ = + gst_element_class_get_pad_template (element_class, "test4")) != NULL); + fail_unless (gst_caps_is_any (templ->caps)); +} + +GType +gst_test_element2_get_type (void) +{ + static GType gst_test_element2_type = G_TYPE_NONE; + + if (gst_test_element2_type == G_TYPE_NONE) { + static const GTypeInfo gst_test_element2_info = { + sizeof (GstTestElement2Class), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gst_test_element2_class_init, + NULL, + NULL, + sizeof (GstTestElement2), + 0, + NULL, /* instance_init */ + NULL + }; + + gst_test_element2_type = + g_type_register_static (gst_test_element_get_type (), "GstTestElement2", + &gst_test_element2_info, 0); + } + return gst_test_element2_type; +} + + +GST_START_TEST (test_pad_templates) +{ + GstTestElement *test; + GstTestElement2 *test2; + + test = g_object_new (gst_test_element_get_type (), NULL); + test2 = g_object_new (gst_test_element2_get_type (), NULL); + + g_object_unref (test); + g_object_unref (test2); +} + +GST_END_TEST; + Suite * gst_element_suite (void) { @@ -218,6 +401,7 @@ gst_element_suite (void) tcase_add_test (tc_chain, test_link); tcase_add_test (tc_chain, test_link_no_pads); tcase_add_test (tc_chain, test_class); + tcase_add_test (tc_chain, test_pad_templates); return s; }