2 * Copyright (C) 2002 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2002 Wim Taymans <wtay@chello.be>
5 * gstspider.c: element to automatically link sinks and sources
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
25 * - handle automatic removal of unneeded elements
26 * - make the spider handle and send events (esp. new media)
27 * - decide if we plug pads or elements, currently it's a mess
29 * - implement proper saving/loading from xml
30 * - implement a way to allow merging/splitting (aka tee)
31 * - find ways to define which elements to use when plugging
33 * - improve typefinding
34 * - react to errors inside the pipeline
35 * - implement more properties, change the current
36 * - emit signals (most important: "NOT PLUGGABLE")
37 * - implement something for reporting the state of the spider
38 * to allow easier debugging.
39 * (could be useful for bins in general)
48 #include "gstspider.h"
49 #include "gstspideridentity.h"
50 #include "gstsearchfuncs.h"
52 GST_DEBUG_CATEGORY (gst_spider_debug);
53 #define GST_CAT_DEFAULT gst_spider_debug
55 /* signals and args */
67 /* generic templates */
68 GST_PAD_TEMPLATE_FACTORY (spider_src_factory,
75 /* standard GObject stuff */
76 static void gst_spider_class_init (GstSpiderClass *klass);
77 static void gst_spider_init (GstSpider *spider);
78 static void gst_spider_dispose (GObject *object);
80 /* element class functions */
81 static GstPad* gst_spider_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name);
82 static void gst_spider_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
83 static void gst_spider_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
86 static GstSpiderConnection * gst_spider_link_new (GstSpiderIdentity *src);
87 static void gst_spider_link_destroy (GstSpiderConnection *conn);
88 static void gst_spider_link_reset (GstSpiderConnection *conn, GstElement *to);
89 static void gst_spider_link_add (GstSpiderConnection *conn, GstElement *element);
90 static GstSpiderConnection * gst_spider_link_find (GstSpiderIdentity *src);
91 static GstSpiderConnection * gst_spider_link_get (GstSpiderIdentity *src);
93 /* autoplugging functions */
94 static GstElement * gst_spider_find_element_to_plug (GstElement *src, GstElementFactory *fac, GstPadDirection dir);
95 static GstPadLinkReturn gst_spider_plug (GstSpiderConnection *conn);
96 static GstPadLinkReturn gst_spider_plug_from_srcpad (GstSpiderConnection *conn, GstPad *srcpad);
97 /*static GstPadLinkReturn gst_spider_plug_peers (GstSpider *spider, GstPad *srcpad, GstPad *sinkpad); */
98 static GstPadLinkReturn gst_spider_create_and_plug (GstSpiderConnection *conn, GList *plugpath);
100 /* random functions */
101 static gchar * gst_spider_unused_elementname (GstBin *bin, const gchar *startwith);
104 static void print_spider_contents (GstSpider *spider);
105 static void print_spider_link (GstSpiderConnection *conn); */
107 /* === variables === */
108 static GstElementClass * parent_class = NULL;
111 static guint gst_spider_signals[LAST_SIGNAL] = { 0 };*/
113 /* GObject and GStreamer init functions */
115 gst_spider_get_type(void)
117 static GType spider_type = 0;
120 static const GTypeInfo spider_info = {
121 sizeof(GstSpiderClass),
124 (GClassInitFunc) gst_spider_class_init,
129 (GInstanceInitFunc)gst_spider_init,
131 spider_type = g_type_register_static (GST_TYPE_BIN, "GstSpider", &spider_info, 0);
137 gst_spider_class_init (GstSpiderClass *klass)
139 GObjectClass *gobject_class;
140 GstElementClass *gstelement_class;
142 gobject_class = (GObjectClass*) klass;
143 gstelement_class = (GstElementClass*) klass;
145 parent_class = g_type_class_ref(GST_TYPE_BIN);
148 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FACTORIES,
149 g_param_spec_pointer ("factories", "allowed factories", "allowed factories for autoplugging", G_PARAM_READWRITE));
151 gobject_class->set_property = gst_spider_set_property;
152 gobject_class->get_property = gst_spider_get_property;
153 gobject_class->dispose = gst_spider_dispose;
155 gst_element_class_add_pad_template (gstelement_class, GST_PAD_TEMPLATE_GET (spider_src_factory));
157 gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR(gst_spider_request_new_pad);
160 gst_spider_init (GstSpider *spider)
162 /* use only elements which have sources and sinks and where the sinks have caps */
163 /* FIXME: How do we handle factories that are added after the spider was constructed? */
164 spider->factories = gst_autoplug_factories_filters_with_sink_caps ((GList *)
165 gst_registry_pool_feature_list (GST_TYPE_ELEMENT_FACTORY));
167 spider->links = NULL;
169 spider->sink_ident = gst_spider_identity_new_sink ("sink_ident");
170 gst_bin_add (GST_BIN (spider), GST_ELEMENT (spider->sink_ident));
171 gst_element_add_ghost_pad (GST_ELEMENT(spider), spider->sink_ident->sink, "sink");
176 gst_spider_dispose (GObject *object)
180 spider = GST_SPIDER (object);
181 g_list_free (spider->factories);
183 ((GObjectClass *) parent_class)->dispose (object);
186 gst_spider_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name)
190 GstSpiderIdentity *identity;
193 g_return_val_if_fail (templ != NULL, NULL);
194 g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL);
195 g_return_val_if_fail (GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC, NULL);
197 spider = GST_SPIDER (element);
199 /* create an identity object, so we have a pad */
200 padname = gst_spider_unused_elementname ((GstBin *)spider, "src_");
201 identity = gst_spider_identity_new_src (padname);
202 returnpad = identity->src;
204 /* FIXME: use the requested name for the pad */
206 gst_object_replace ((GstObject **) &returnpad->padtemplate, (GstObject *) templ);
208 gst_bin_add (GST_BIN (element), GST_ELEMENT (identity));
210 returnpad = gst_element_add_ghost_pad (element, returnpad, padname);
211 gst_spider_link_new (identity);
212 GST_DEBUG ("successuflly created requested pad %s:%s", GST_DEBUG_PAD_NAME (returnpad));
218 gst_spider_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
223 /* it's not null if we got it, but it might not be ours */
224 g_return_if_fail (GST_IS_SPIDER (object));
226 spider = GST_SPIDER (object);
230 list = (GList *) g_value_get_pointer (value);
233 g_return_if_fail (list->data != NULL);
234 g_return_if_fail (GST_IS_ELEMENT_FACTORY (list->data));
235 list = g_list_next (list);
237 g_list_free (spider->factories);
238 spider->factories = (GList *) g_value_get_pointer (value);
245 gst_spider_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
249 /* it's not null if we got it, but it might not be ours */
250 spider = GST_SPIDER(object);
254 g_value_set_pointer (value, spider->factories);
257 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
261 /* get a name for an element that isn't used yet */
263 gst_spider_unused_elementname (GstBin *bin, const gchar *startwith)
265 gchar * name = g_strdup_printf ("%s%d", startwith, 0);
268 for (i = 0; gst_bin_get_by_name (bin, name) != NULL; )
271 name = g_strdup_printf ("%s%d", startwith, ++i);
277 gst_spider_link_sometimes (GstElement *src, GstPad *pad, GstSpiderConnection *conn)
279 gulong signal_id = conn->signal_id;
281 /* try to autoplug the elements */
282 if (gst_spider_plug_from_srcpad (conn, pad) != GST_PAD_LINK_REFUSED) {
283 GST_DEBUG ("%s:%s was autoplugged to %s:%s, removing callback",
284 GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (conn->src->sink));
285 g_signal_handler_disconnect (src, signal_id);
289 /* create a new link from those two elements */
290 static GstSpiderConnection *
291 gst_spider_link_new (GstSpiderIdentity *src)
293 GstSpider *spider = GST_SPIDER (GST_OBJECT_PARENT (src));
295 GstSpiderConnection *conn = g_new0 (GstSpiderConnection, 1);
298 conn->current = (GstElement *) spider->sink_ident;
299 spider->links = g_list_prepend (spider->links, conn);
304 gst_spider_link_destroy (GstSpiderConnection *conn)
306 GstSpider *spider = GST_SPIDER (GST_OBJECT_PARENT (conn->src));
307 /* reset link to unplugged */
308 gst_spider_link_reset (conn, (GstElement *) spider->sink_ident);
312 gst_spider_link_reset (GstSpiderConnection *conn, GstElement *to)
314 GstSpider *spider = GST_SPIDER (GST_OBJECT_PARENT (conn->src));
315 GST_DEBUG ("resetting link from %s to %s, currently at %s to %s", GST_ELEMENT_NAME (spider->sink_ident),
316 GST_ELEMENT_NAME (conn->src), GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (to));
317 while ((conn->path != NULL) && ((GstElement *) conn->path->data != to))
319 gst_object_unref ((GstObject *) conn->path->data);
320 conn->path = g_list_delete_link (conn->path, conn->path);
322 if (conn->path == NULL)
324 conn->current = (GstElement *) spider->sink_ident;
329 /* add an element to the link */
331 gst_spider_link_add (GstSpiderConnection *conn, GstElement *element)
333 gst_object_ref ((GstObject *) element);
334 gst_object_sink ((GstObject *) element);
335 conn->path = g_list_prepend (conn->path, element);
336 conn->current = element;
338 /* find the link from those two elements */
339 static GstSpiderConnection *
340 gst_spider_link_find (GstSpiderIdentity *src)
342 GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (src);
343 GList *list = spider->links;
347 GstSpiderConnection *conn = (GstSpiderConnection *) list->data;
348 if (conn->src == src){
351 list = g_list_next(list);
355 /* get a new link from those two elements
356 * search first; if none is found, create a new one */
357 static GstSpiderConnection *
358 gst_spider_link_get (GstSpiderIdentity *src)
360 GstSpiderConnection *ret;
362 if ((ret = gst_spider_link_find (src)) != NULL)
366 return gst_spider_link_new (src);
369 gst_spider_identity_plug (GstSpiderIdentity *ident)
372 const GList *padlist;
374 GstSpiderConnection *conn;
377 g_return_if_fail (ident != NULL);
378 g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
379 spider = GST_SPIDER (GST_ELEMENT_PARENT (ident));
380 g_assert (spider != NULL);
381 g_assert (GST_IS_SPIDER (spider));
383 /* return if we're already plugged */
384 if (ident->plugged) return;
386 /* get the direction of our ident */
387 if (GST_PAD_PEER (ident->sink))
389 if (GST_PAD_PEER (ident->src))
391 /* Hey, the ident is linked on both sides */
392 g_warning ("Trying to autoplug a linked element. Aborting...");
398 if (GST_PAD_PEER (ident->src))
402 /* the ident isn't linked on either side */
403 g_warning ("Trying to autoplug an unlinked element. Aborting...");
408 /* now iterate all possible pads and link when needed */
409 padlist = gst_element_get_pad_list (GST_ELEMENT (spider));
413 GstSpiderIdentity *peer;
415 g_assert (GST_IS_PAD (padlist->data));
416 otherpad = (GstPad *) GST_GPAD_REALPAD (padlist->data);
417 peer = (GstSpiderIdentity *) GST_PAD_PARENT (otherpad);
418 /* we only want to link to the other side */
419 if (dir != GST_PAD_DIRECTION (otherpad))
421 /* we only link to plugged in elements */
422 if (peer->plugged == TRUE)
424 /* plug in the right direction */
425 if (dir == GST_PAD_SINK)
427 conn = gst_spider_link_get (peer);
429 conn = gst_spider_link_get (ident);
431 if ((GstElement *) spider->sink_ident == conn->current)
433 gst_spider_plug (conn);
437 padlist = g_list_next (padlist);
440 ident->plugged = TRUE;
443 gst_spider_identity_unplug (GstSpiderIdentity *ident)
445 GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (ident);
446 GList *list = spider->links;
450 GstSpiderConnection *conn = list->data;
452 list = g_list_next (list);
453 if (conn->src == ident)
455 g_list_delete_link (spider->links, cur);
456 gst_spider_link_destroy (conn);
459 ident->plugged = FALSE;
461 /* links src to sink using the elementfactories in plugpath
462 * plugpath will be removed afterwards */
463 static GstPadLinkReturn
464 gst_spider_create_and_plug (GstSpiderConnection *conn, GList *plugpath)
466 GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (conn->src);
467 GList *endelements = NULL, *templist = NULL;
470 /* exit if plugging is already done */
471 if ((GstElement *) conn->src == conn->current)
472 return GST_PAD_LINK_DONE;
474 /* try to shorten the list at the end and not duplicate link code */
475 if (plugpath != NULL)
477 templist = g_list_last (plugpath);
478 element = (GstElement *) conn->src;
479 while ((plugpath != NULL) && (element = gst_spider_find_element_to_plug (element, (GstElementFactory *) plugpath->data, GST_PAD_SINK)))
481 GList *cur = templist;
482 endelements = g_list_prepend (endelements, element);
483 templist = g_list_previous (templist);
484 g_list_delete_link (cur, cur);
489 while (conn->current != (GstElement *) (endelements == NULL ? conn->src : endelements->data))
491 /* get sink element to plug, src is conn->current */
492 if (plugpath == NULL)
494 element = (GstElement *) (endelements == NULL ? conn->src : endelements->data);
496 element = gst_element_factory_create ((GstElementFactory *) plugpath->data, NULL);
497 GST_DEBUG ("Adding element %s of type %s and syncing state with autoplugger",
498 GST_ELEMENT_NAME (element), GST_PLUGIN_FEATURE_NAME (plugpath->data));
499 gst_bin_add (GST_BIN (spider), element);
501 /* insert and link new element */
502 if (gst_element_link (conn->current, element)) {
503 gst_element_sync_state_with_parent (element);
505 /* check if the src has SOMETIMES templates. If so, link a callback */
506 GList *templs = gst_element_get_pad_template_list (conn->current);
508 /* remove element that couldn't be linked, if it wasn't the endpoint */
509 if (element != (GstElement *) conn->src)
510 gst_bin_remove (GST_BIN (spider), element);
513 GstPadTemplate *templ = (GstPadTemplate *) templs->data;
514 if ((GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC) && (GST_PAD_TEMPLATE_PRESENCE(templ) == GST_PAD_SOMETIMES))
516 GST_DEBUG ("adding callback to link element %s to %s", GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (conn->src));
517 conn->signal_id = g_signal_connect (G_OBJECT (conn->current), "new_pad",
518 G_CALLBACK (gst_spider_link_sometimes), conn);
519 g_list_free (plugpath);
520 return GST_PAD_LINK_DELAYED;
522 templs = g_list_next (templs);
524 GST_DEBUG ("no chance to link element %s to %s", GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (conn->src));
525 g_list_free (plugpath);
526 return GST_PAD_LINK_REFUSED;
528 GST_DEBUG ("added element %s and attached it to element %s", GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (conn->current));
529 gst_spider_link_add (conn, element);
530 if (plugpath != NULL)
531 plugpath = g_list_delete_link (plugpath, plugpath);
534 /* ref all elements at the end */
537 gst_spider_link_add (conn, endelements->data);
538 endelements = g_list_delete_link (endelements, endelements);
541 return GST_PAD_LINK_DONE;
543 /* checks, if src is already linked to an element from factory fac on direction dir */
545 gst_spider_find_element_to_plug (GstElement *src, GstElementFactory *fac, GstPadDirection dir)
547 GList *padlist = GST_ELEMENT_PADS (src);
551 GstPad *pad = (GstPad *) GST_PAD_REALIZE (padlist->data);
552 /* is the pad on the right side and is it linked? */
553 if ((GST_PAD_DIRECTION (pad) == dir) && (pad = (GstPad *) (GST_RPAD_PEER (pad))))
555 /* is the element the pad is linked to of the right type? */
556 GstElement *element = GST_PAD_PARENT (pad);
557 if (GST_ELEMENT_CLASS (G_OBJECT_GET_CLASS (element))->elementfactory == fac) {
561 padlist = g_list_next (padlist);
566 /* try to establish the link */
567 static GstPadLinkReturn
568 gst_spider_plug (GstSpiderConnection *conn)
570 GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (conn->src);
571 if ((GstElement *) conn->src == conn->current)
572 return GST_PAD_LINK_DONE;
573 if ((GstElement *) spider->sink_ident == conn->current)
574 return gst_spider_plug_from_srcpad (conn, spider->sink_ident->src);
575 g_warning ("FIXME: autoplugging only possible from GstSpiderIdentity conn->sink yet (yep, that's technical)\n");
576 return GST_PAD_LINK_REFUSED;
578 /* try to establish the link using this pad */
579 static GstPadLinkReturn
580 gst_spider_plug_from_srcpad (GstSpiderConnection *conn, GstPad *srcpad)
584 gboolean result = TRUE;
585 GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (conn->src);
586 GstElement *startelement = conn->current;
588 g_assert ((GstElement *) GST_OBJECT_PARENT (srcpad) == conn->current);
589 GST_DEBUG ("trying to plug from %s:%s to %s",
590 GST_DEBUG_PAD_NAME (srcpad), GST_ELEMENT_NAME (conn->src));
592 /* find a path from src to sink */
593 plugpath = gst_autoplug_sp (gst_pad_get_caps (srcpad), gst_pad_get_caps (conn->src->sink), spider->factories);
595 /* prints out the path that was found for plugging */
596 /* g_print ("found path from %s to %s:\n", GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (conn->src));
600 g_print("%s\n", GST_OBJECT_NAME (templist->data));
601 templist = g_list_next (templist);
604 /* if there is no way to plug: return */
605 if (plugpath == NULL) {
606 GST_DEBUG ("no chance to plug from %s to %s", GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (conn->src));
607 return GST_PAD_LINK_REFUSED;
609 GST_DEBUG ("found a link that needs %d elements", g_list_length (plugpath));
611 /* now remove non-needed elements from the beginning of the path
612 * alter src to point to the new element where we need to start
613 * plugging and alter the plugpath to represent the elements, that must be plugged
615 element = conn->current;
616 while ((plugpath != NULL) && (element = gst_spider_find_element_to_plug (element, (GstElementFactory *) plugpath->data, GST_PAD_SRC)))
618 gst_spider_link_add (conn, element);
619 plugpath = g_list_delete_link (plugpath, plugpath);
622 GST_DEBUG ("%d elements must be inserted to establish the link", g_list_length (plugpath));
623 /* create the elements and plug them */
624 result = gst_spider_create_and_plug (conn, plugpath);
626 /* reset the "current" element */
627 if (result == GST_PAD_LINK_REFUSED)
629 gst_spider_link_reset (conn, startelement);
635 GstElementDetails gst_spider_details = {
639 "Automatically link sinks and sources",
641 "Benjamin Otte <in7y118@public.uni-hamburg.de>",
646 plugin_init (GModule *module, GstPlugin *plugin)
648 GstElementFactory *factory;
650 GST_DEBUG_CATEGORY_INIT (gst_spider_debug, "spider", 0, "spider autoplugging element");
651 GST_DEBUG_CATEGORY_INIT (gst_spider_identity_debug, "spideridentity", 0, "spider autoplugging proxy element");
653 factory = gst_element_factory_new("spider", GST_TYPE_SPIDER,
654 &gst_spider_details);
655 gst_plugin_set_longname (plugin, "Spider autoplugging elements");
656 g_return_val_if_fail(factory != NULL, FALSE);
658 gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (spider_src_factory));
660 gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
662 /* add spideridentity */
663 factory = gst_element_factory_new ("spideridentity", GST_TYPE_SPIDER_IDENTITY,
664 &gst_spider_identity_details);
665 gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
670 GstPluginDesc plugin_desc = {