+2008-02-03 Sebastian Dröge <slomo@circular-chaos.org>
+
+ * 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 <slomo@circular-chaos.org>
* tools/gst-inspect.c: (print_interfaces),
gst_element_base_class_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+ GList *node, *padtemplates;
+
+ /* 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;
- memset (&element_class->details, 0, sizeof (GstElementDetails));
- element_class->padtemplates = NULL;
+ node->data = gst_pad_template_new (tmpl->name_template,
+ tmpl->direction, tmpl->presence, gst_caps_copy (tmpl->caps));
+ }
+ element_class->padtemplates = padtemplates;
}
static void
* @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;
+
+ /* 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);
+ }
- klass->padtemplates = g_list_append (klass->padtemplates,
- gst_object_ref (templ));
+ /* Not found a pad with the same name so add it to the list */
+ klass->padtemplates = g_list_append (klass->padtemplates, templ);
klass->numpadtemplates++;
}
* @details: details to set
*
* Sets the detailed information for a #GstElementClass.
- * <note>This function is for use in _base_init functions only.</note>
+ * <note>This function is for use in _class_init functions only.</note>
*
* The @details are copied.
*/
*
* Sets the detailed information for a #GstElementClass. Simpler version of
* gst_element_class_set_details() that generates less linker overhead.
- * <note>This function is for use in _base_init functions only.</note>
+ * <note>This function is for use in _class_init functions only.</note>
*
* The detail parameter strings are copied into the #GstElementDetails for
* the element 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 <foo@bar.com>");
+
+ 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 <foo@bar.com>");
+
+ 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)
{
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;
}