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 "../gst-i18n-lib.h"
49 #include "gstspider.h"
50 #include "gstspideridentity.h"
51 #include "gstsearchfuncs.h"
53 static GstElementDetails gst_spider_details = GST_ELEMENT_DETAILS ("Spider",
55 "Automatically link sinks and sources",
56 "Benjamin Otte <in7y118@public.uni-hamburg.de>");
58 GST_DEBUG_CATEGORY (gst_spider_debug);
59 #define GST_CAT_DEFAULT gst_spider_debug
61 /* signals and args */
75 /* generic templates */
76 static GstStaticPadTemplate spider_sink_factory =
77 GST_STATIC_PAD_TEMPLATE ("sink",
81 static GstStaticPadTemplate spider_src_factory =
82 GST_STATIC_PAD_TEMPLATE ("src_%d",
87 /* standard GObject stuff */
88 static void gst_spider_class_init (GstSpiderClass * klass);
89 static void gst_spider_init (GstSpider * spider);
90 static void gst_spider_dispose (GObject * object);
92 /* element class functions */
93 static GstPad *gst_spider_request_new_pad (GstElement * element,
94 GstPadTemplate * templ, const gchar * name);
95 static void gst_spider_set_property (GObject * object, guint prop_id,
96 const GValue * value, GParamSpec * pspec);
97 static void gst_spider_get_property (GObject * object, guint prop_id,
98 GValue * value, GParamSpec * pspec);
101 static GstSpiderConnection *gst_spider_link_new (GstSpiderIdentity * src);
102 static void gst_spider_link_destroy (GstSpiderConnection * conn);
103 static void gst_spider_link_reset (GstSpiderConnection * conn, GstElement * to);
104 static void gst_spider_link_add (GstSpiderConnection * conn,
105 GstElement * element);
106 static GstSpiderConnection *gst_spider_link_find (GstSpiderIdentity * src);
107 static GstSpiderConnection *gst_spider_link_get (GstSpiderIdentity * src);
109 /* autoplugging functions */
110 static GstElement *gst_spider_find_element_to_plug (GstElement * src,
111 GstElementFactory * fac, GstPadDirection dir);
112 static GstPadLinkReturn gst_spider_plug (GstSpiderConnection * conn);
113 static GstPadLinkReturn gst_spider_plug_from_srcpad (GstSpiderConnection * conn,
115 /*static GstPadLinkReturn gst_spider_plug_peers (GstSpider *spider, GstPad *srcpad, GstPad *sinkpad); */
116 static GstPadLinkReturn gst_spider_create_and_plug (GstSpiderConnection * conn,
119 /* random functions */
120 static gchar *gst_spider_unused_elementname (GstBin * bin,
121 const gchar * startwith);
124 static void print_spider_contents (GstSpider *spider);
125 static void print_spider_link (GstSpiderConnection *conn); */
127 /* === variables === */
128 static GstElementClass *parent_class = NULL;
131 static guint gst_spider_signals[LAST_SIGNAL] = { 0 };*/
133 /* GObject and GStreamer init functions */
135 gst_spider_get_type (void)
137 static GType spider_type = 0;
140 static const GTypeInfo spider_info = {
141 sizeof (GstSpiderClass),
144 (GClassInitFunc) gst_spider_class_init,
149 (GInstanceInitFunc) gst_spider_init,
153 g_type_register_static (GST_TYPE_BIN, "GstSpider", &spider_info, 0);
159 gst_spider_class_init (GstSpiderClass * klass)
161 GObjectClass *gobject_class;
162 GstElementClass *gstelement_class;
164 gobject_class = (GObjectClass *) klass;
165 gstelement_class = (GstElementClass *) klass;
167 parent_class = g_type_class_ref (GST_TYPE_BIN);
170 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FACTORIES,
171 g_param_spec_pointer ("factories", "allowed factories",
172 "allowed factories for autoplugging", G_PARAM_READWRITE));
174 gobject_class->set_property = gst_spider_set_property;
175 gobject_class->get_property = gst_spider_get_property;
176 gobject_class->dispose = gst_spider_dispose;
178 gst_element_class_add_pad_template (gstelement_class,
179 gst_static_pad_template_get (&spider_sink_factory));
180 gst_element_class_add_pad_template (gstelement_class,
181 gst_static_pad_template_get (&spider_src_factory));
182 gst_element_class_set_details (gstelement_class, &gst_spider_details);
184 gstelement_class->request_new_pad =
185 GST_DEBUG_FUNCPTR (gst_spider_request_new_pad);
188 gst_spider_init (GstSpider * spider)
190 /* use only elements which have sources and sinks and where the sinks have caps */
191 /* FIXME: How do we handle factories that are added after the spider was constructed? */
192 GList *list = gst_registry_pool_feature_list (GST_TYPE_ELEMENT_FACTORY);
194 spider->factories = gst_autoplug_factories_filters_with_sink_caps (list);
197 spider->links = NULL;
199 spider->sink_ident = gst_spider_identity_new_sink ("sink_ident");
200 gst_bin_add (GST_BIN (spider), GST_ELEMENT (spider->sink_ident));
201 gst_element_add_ghost_pad (GST_ELEMENT (spider), spider->sink_ident->sink,
207 gst_spider_dispose (GObject * object)
212 spider = GST_SPIDER (object);
213 g_list_free (spider->factories);
214 spider->factories = NULL;
216 for (list = spider->links; list; list = list->next) {
217 GstSpiderConnection *conn = list->data;
219 g_list_free (conn->path);
222 g_list_free (spider->links);
223 spider->links = NULL;
225 ((GObjectClass *) parent_class)->dispose (object);
228 gst_spider_request_new_pad (GstElement * element, GstPadTemplate * templ,
233 GstSpiderIdentity *identity;
236 g_return_val_if_fail (templ != NULL, NULL);
237 g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL);
238 g_return_val_if_fail (GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC,
241 spider = GST_SPIDER (element);
243 /* create an identity object, so we have a pad */
244 padname = gst_spider_unused_elementname ((GstBin *) spider, "src_");
245 identity = gst_spider_identity_new_src (padname);
246 returnpad = identity->src;
248 /* FIXME: use the requested name for the pad */
250 gst_object_replace ((GstObject **) & returnpad->padtemplate,
251 (GstObject *) templ);
253 gst_bin_add (GST_BIN (element), GST_ELEMENT (identity));
255 returnpad = gst_element_add_ghost_pad (element, returnpad, padname);
257 gst_spider_link_new (identity);
258 GST_DEBUG ("successfully created requested pad %s:%s",
259 GST_DEBUG_PAD_NAME (returnpad));
265 gst_spider_set_property (GObject * object, guint prop_id, const GValue * value,
271 /* it's not null if we got it, but it might not be ours */
272 g_return_if_fail (GST_IS_SPIDER (object));
274 spider = GST_SPIDER (object);
278 list = (GList *) g_value_get_pointer (value);
279 for (; list; list = list->next) {
280 g_return_if_fail (list->data != NULL);
281 g_return_if_fail (GST_IS_ELEMENT_FACTORY (list->data));
283 g_list_free (spider->factories);
284 spider->factories = (GList *) g_value_get_pointer (value);
291 gst_spider_get_property (GObject * object, guint prop_id, GValue * value,
296 /* it's not null if we got it, but it might not be ours */
297 spider = GST_SPIDER (object);
301 g_value_set_pointer (value, spider->factories);
304 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
309 /* get a name for an element that isn't used yet */
311 gst_spider_unused_elementname (GstBin * bin, const gchar * startwith)
313 gchar *name = g_strdup_printf ("%s%d", startwith, 0);
316 for (i = 0; gst_bin_get_by_name (bin, name) != NULL;) {
318 name = g_strdup_printf ("%s%d", startwith, ++i);
324 gst_spider_link_sometimes (GstElement * src, GstPad * pad,
325 GstSpiderConnection * conn)
327 gulong signal_id = conn->signal_id;
329 GST_INFO ("plugging from new sometimes pad %s:%s", GST_DEBUG_PAD_NAME (pad));
330 /* try to autoplug the elements */
331 if (gst_spider_plug_from_srcpad (conn, pad) != GST_PAD_LINK_REFUSED) {
332 GST_DEBUG ("%s:%s was autoplugged to %s:%s, removing callback",
333 GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (conn->src->sink));
334 g_signal_handler_disconnect (src, signal_id);
339 /* create a new link from those two elements */
340 static GstSpiderConnection *
341 gst_spider_link_new (GstSpiderIdentity * src)
343 GstSpider *spider = GST_SPIDER (GST_OBJECT_PARENT (src));
345 GstSpiderConnection *conn = g_new0 (GstSpiderConnection, 1);
349 conn->current = (GstElement *) spider->sink_ident;
350 spider->links = g_list_prepend (spider->links, conn);
355 gst_spider_link_destroy (GstSpiderConnection * conn)
357 GstSpider *spider = GST_SPIDER (GST_OBJECT_PARENT (conn->src));
359 /* reset link to unplugged */
360 gst_spider_link_reset (conn, (GstElement *) spider->sink_ident);
364 gst_spider_link_reset (GstSpiderConnection * conn, GstElement * to)
366 GstSpider *spider = GST_SPIDER (GST_OBJECT_PARENT (conn->src));
368 GST_DEBUG ("resetting link from %s to %s, currently at %s to %s",
369 GST_ELEMENT_NAME (spider->sink_ident), GST_ELEMENT_NAME (conn->src),
370 GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (to));
371 while ((conn->path != NULL) && ((GstElement *) conn->path->data != to)) {
372 gst_object_unref ((GstObject *) conn->path->data);
373 conn->path = g_list_delete_link (conn->path, conn->path);
375 if (conn->path == NULL) {
376 conn->current = (GstElement *) spider->sink_ident;
382 /* add an element to the link */
384 gst_spider_link_add (GstSpiderConnection * conn, GstElement * element)
386 conn->path = g_list_prepend (conn->path, element);
387 conn->current = element;
390 /* find the link from those two elements */
391 static GstSpiderConnection *
392 gst_spider_link_find (GstSpiderIdentity * src)
394 GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (src);
397 for (list = spider->links; list; list = list->next) {
398 GstSpiderConnection *conn = (GstSpiderConnection *) list->data;
400 if (conn->src == src) {
407 /* get a new link from those two elements
408 * search first; if none is found, create a new one */
409 static GstSpiderConnection *
410 gst_spider_link_get (GstSpiderIdentity * src)
412 GstSpiderConnection *ret;
414 if ((ret = gst_spider_link_find (src)) != NULL) {
417 return gst_spider_link_new (src);
421 gst_spider_identity_plug (GstSpiderIdentity * ident)
424 const GList *padlist;
426 GstSpiderConnection *conn;
429 g_return_if_fail (ident != NULL);
430 g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
431 spider = GST_SPIDER (GST_ELEMENT_PARENT (ident));
432 g_assert (spider != NULL);
433 g_assert (GST_IS_SPIDER (spider));
435 /* return if we're already plugged */
439 /* check if there is at least one element factory that can handle the
440 identity's src caps */
442 GstCaps *src_caps = gst_pad_get_caps (ident->src);
444 if (!gst_caps_is_empty (src_caps) && !gst_caps_is_any (src_caps)) {
446 GstPadTemplate *padtemp;
447 gboolean found = FALSE;
449 factories = spider->factories;
452 gst_autoplug_can_connect_src (factories->data, src_caps))) {
453 GST_DEBUG ("can connect src to %s pad template: %" GST_PTR_FORMAT,
454 GST_PLUGIN_FEATURE_NAME (factories->data),
455 gst_pad_template_get_caps (padtemp));
458 factories = factories->next;
463 mime = gst_structure_get_name (gst_caps_get_structure (src_caps, 0));
465 GST_ELEMENT_ERROR (spider, STREAM, CODEC_NOT_FOUND,
466 (_("There is no element present to handle the stream's mime type %s."), mime), (NULL));
467 gst_caps_unref (src_caps);
471 gst_caps_unref (src_caps);
474 /* get the direction of our ident */
475 if (GST_PAD_PEER (ident->sink)) {
476 if (GST_PAD_PEER (ident->src)) {
477 /* Hey, the ident is linked on both sides */
478 g_warning ("Trying to autoplug a linked element. Aborting...");
484 if (GST_PAD_PEER (ident->src)) {
487 /* the ident isn't linked on either side */
488 g_warning ("Trying to autoplug an unlinked element. Aborting...");
493 /* now iterate all possible pads and link when needed */
494 padlist = GST_ELEMENT (spider)->pads;
495 for (; padlist; padlist = padlist->next) {
497 GstSpiderIdentity *peer;
499 g_assert (GST_IS_PAD (padlist->data));
500 otherpad = (GstPad *) GST_GPAD_REALPAD (padlist->data);
501 peer = (GstSpiderIdentity *) GST_PAD_PARENT (otherpad);
502 /* we only want to link to the other side */
503 if (dir != GST_PAD_DIRECTION (otherpad)) {
504 /* we only link to plugged in elements */
505 if (peer->plugged == TRUE) {
506 /* plug in the right direction */
507 if (dir == GST_PAD_SINK) {
508 conn = gst_spider_link_get (peer);
510 conn = gst_spider_link_get (ident);
512 if ((GstElement *) spider->sink_ident == conn->current) {
513 gst_spider_plug (conn);
519 ident->plugged = TRUE;
523 gst_spider_identity_unplug (GstSpiderIdentity * ident)
525 GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (ident);
528 for (list = spider->links; list; list = list->next) {
529 GstSpiderConnection *conn = list->data;
531 if (conn->src == ident) {
532 g_list_delete_link (spider->links, list);
533 gst_spider_link_destroy (conn);
536 ident->plugged = FALSE;
539 /* links src to sink using the elementfactories in plugpath
540 * plugpath will be removed afterwards */
541 static GstPadLinkReturn
542 gst_spider_create_and_plug (GstSpiderConnection * conn, GList * plugpath)
544 GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (conn->src);
545 GList *endelements = NULL, *templist = NULL;
548 /* exit if plugging is already done */
549 if ((GstElement *) conn->src == conn->current)
550 return GST_PAD_LINK_DONE;
552 /* try to shorten the list at the end and not duplicate link code */
553 if (plugpath != NULL) {
554 templist = g_list_last (plugpath);
555 element = (GstElement *) conn->src;
556 while ((plugpath != NULL)
558 gst_spider_find_element_to_plug (element,
559 (GstElementFactory *) plugpath->data, GST_PAD_SINK))) {
560 GList *cur = templist;
562 endelements = g_list_prepend (endelements, element);
563 templist = g_list_previous (templist);
564 g_list_delete_link (cur, cur);
569 while (conn->current != (GstElement *) (endelements ==
570 NULL ? conn->src : endelements->data)) {
571 /* get sink element to plug, src is conn->current */
572 if (plugpath == NULL) {
574 (GstElement *) (endelements == NULL ? conn->src : endelements->data);
577 gst_element_factory_create ((GstElementFactory *) plugpath->data,
580 ("Adding element %s of type %s and syncing state with autoplugger",
581 GST_ELEMENT_NAME (element), GST_PLUGIN_FEATURE_NAME (plugpath->data));
582 gst_bin_add (GST_BIN (spider), element);
584 /* insert and link new element */
585 if (gst_element_link (conn->current, element)) {
586 gst_element_sync_state_with_parent (element);
588 /* check if the src has SOMETIMES templates. If so, link a callback */
589 GList *templs = gst_element_get_pad_template_list (conn->current);
591 /* remove element that couldn't be linked, if it wasn't the endpoint */
592 if (element != (GstElement *) conn->src)
593 gst_bin_remove (GST_BIN (spider), element);
595 for (; templs; templs = templs->next) {
596 GstPadTemplate *templ = (GstPadTemplate *) templs->data;
598 if ((GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC)
599 && (GST_PAD_TEMPLATE_PRESENCE (templ) == GST_PAD_SOMETIMES)) {
600 GST_DEBUG ("adding callback to link element %s to %s",
601 GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (conn->src));
603 g_signal_connect (G_OBJECT (conn->current), "new_pad",
604 G_CALLBACK (gst_spider_link_sometimes), conn);
605 g_list_free (plugpath);
606 return GST_PAD_LINK_DELAYED;
609 GST_DEBUG ("no chance to link element %s to %s",
610 GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (conn->src));
611 g_list_free (plugpath);
612 return GST_PAD_LINK_REFUSED;
614 GST_DEBUG ("coupling %s and %s",
615 GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (conn->current));
616 gst_spider_link_add (conn, element);
617 if (plugpath != NULL)
618 plugpath = g_list_delete_link (plugpath, plugpath);
621 /* ref all elements at the end */
622 while (endelements) {
623 gst_spider_link_add (conn, endelements->data);
624 endelements = g_list_delete_link (endelements, endelements);
627 return GST_PAD_LINK_DONE;
630 /* checks, if src is already linked to an element from factory fac on direction dir */
632 gst_spider_find_element_to_plug (GstElement * src, GstElementFactory * fac,
635 GList *padlist = GST_ELEMENT_PADS (src);
637 for (; padlist; padlist = padlist->next) {
638 GstPad *pad = (GstPad *) GST_PAD_REALIZE (padlist->data);
640 /* is the pad on the right side and is it linked? */
641 if ((GST_PAD_DIRECTION (pad) == dir)
642 && (pad = (GstPad *) (GST_RPAD_PEER (pad)))) {
643 /* is the element the pad is linked to of the right type? */
644 GstElement *element = GST_PAD_PARENT (pad);
646 if (G_TYPE_FROM_INSTANCE (element) ==
647 gst_element_factory_get_element_type (fac)) {
656 /* try to establish the link */
657 static GstPadLinkReturn
658 gst_spider_plug (GstSpiderConnection * conn)
660 GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (conn->src);
662 if ((GstElement *) conn->src == conn->current)
663 return GST_PAD_LINK_DONE;
664 if ((GstElement *) spider->sink_ident == conn->current)
665 return gst_spider_plug_from_srcpad (conn, spider->sink_ident->src);
667 ("FIXME: autoplugging only possible from GstSpiderIdentity conn->sink yet (yep, that's technical)\n");
668 return GST_PAD_LINK_REFUSED;
671 /* try to establish the link using this pad */
672 static GstPadLinkReturn
673 gst_spider_plug_from_srcpad (GstSpiderConnection * conn, GstPad * srcpad)
677 gboolean result = TRUE;
678 GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (conn->src);
679 GstElement *startelement = conn->current;
683 g_assert ((GstElement *) GST_OBJECT_PARENT (srcpad) == conn->current);
684 GST_DEBUG ("trying to plug from %s:%s to %s",
685 GST_DEBUG_PAD_NAME (srcpad), GST_ELEMENT_NAME (conn->src));
687 /* see if they match already */
688 if (gst_pad_link (srcpad, conn->src->sink)) {
689 GST_DEBUG ("%s:%s and %s:%s can link directly",
690 GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (conn->src->sink));
691 gst_pad_unlink (srcpad, conn->src->sink);
692 gst_spider_create_and_plug (conn, NULL);
693 return GST_PAD_LINK_OK;
696 /* find a path from src to sink */
697 caps1 = gst_pad_get_caps (srcpad);
698 caps2 = gst_pad_get_caps (conn->src->sink);
699 plugpath = gst_autoplug_sp (caps1, caps2, spider->factories);
700 gst_caps_unref (caps1);
701 gst_caps_unref (caps2);
703 /* prints out the path that was found for plugging */
704 /* g_print ("found path from %s to %s:\n", GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (conn->src));
708 g_print("%s\n", GST_OBJECT_NAME (templist->data));
709 templist = g_list_next (templist);
712 /* if there is no way to plug: return */
713 if (plugpath == NULL) {
714 GST_DEBUG ("no chance to plug from %s to %s",
715 GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (conn->src));
716 return GST_PAD_LINK_REFUSED;
718 GST_DEBUG ("found a link that needs %d elements", g_list_length (plugpath));
720 /* now remove non-needed elements from the beginning of the path
721 * alter src to point to the new element where we need to start
722 * plugging and alter the plugpath to represent the elements, that must be plugged
724 element = conn->current;
725 while ((plugpath != NULL)
727 gst_spider_find_element_to_plug (element,
728 (GstElementFactory *) plugpath->data, GST_PAD_SRC))) {
729 gst_spider_link_add (conn, element);
730 plugpath = g_list_delete_link (plugpath, plugpath);
733 GST_DEBUG ("%d elements must be inserted to establish the link",
734 g_list_length (plugpath));
735 /* create the elements and plug them */
736 result = gst_spider_create_and_plug (conn, plugpath);
738 /* reset the "current" element */
739 if (result == GST_PAD_LINK_REFUSED) {
740 gst_spider_link_reset (conn, startelement);
747 plugin_init (GstPlugin * plugin)
749 GST_DEBUG_CATEGORY_INIT (gst_spider_debug, "spider", 0,
750 "spider autoplugging element");
752 if (!gst_element_register (plugin, "spider", GST_RANK_NONE, GST_TYPE_SPIDER))
754 if (!gst_element_register (plugin, "spideridentity", GST_RANK_NONE,
755 GST_TYPE_SPIDER_IDENTITY))
761 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
765 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)