First THREADED backport attempt, focusing on adding locks and making sure the API...
[platform/upstream/gstreamer.git] / gst / autoplug / gstspider.c
1 /* GStreamer
2  * Copyright (C) 2002 Erik Walthinsen <omega@cse.ogi.edu>
3  *               2002 Wim Taymans <wtay@chello.be>
4  *
5  * gstspider.c: element to automatically link sinks and sources
6  *
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.
11  *
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.
16  *
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.
21  */
22
23 /*
24  * TODO:
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
28  * - allow unlinking
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
32  * - remove pads
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)
40  * - fix bugs
41  * ...
42  */
43
44 #ifdef HAVE_CONFIG_H
45 #  include "config.h"
46 #endif
47
48 #include "../gst-i18n-lib.h"
49 #include "gstspider.h"
50 #include "gstspideridentity.h"
51 #include "gstsearchfuncs.h"
52
53 static GstElementDetails gst_spider_details = GST_ELEMENT_DETAILS ("Spider",
54     "Generic",
55     "Automatically link sinks and sources",
56     "Benjamin Otte <in7y118@public.uni-hamburg.de>");
57
58 GST_DEBUG_CATEGORY (gst_spider_debug);
59 #define GST_CAT_DEFAULT gst_spider_debug
60
61 /* signals and args */
62 enum
63 {
64   /* FILL ME */
65   LAST_SIGNAL
66 };
67
68 enum
69 {
70   ARG_0,
71   ARG_FACTORIES,
72   /* FILL ME TOO */
73 };
74
75 /* generic templates */
76 static GstStaticPadTemplate spider_sink_factory =
77 GST_STATIC_PAD_TEMPLATE ("sink",
78     GST_PAD_SINK,
79     GST_PAD_ALWAYS,
80     GST_STATIC_CAPS_ANY);
81 static GstStaticPadTemplate spider_src_factory =
82 GST_STATIC_PAD_TEMPLATE ("src_%d",
83     GST_PAD_SRC,
84     GST_PAD_REQUEST,
85     GST_STATIC_CAPS_ANY);
86
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);
91
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);
99
100 /* link functions */
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);
108
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,
114     GstPad * srcpad);
115 /*static GstPadLinkReturn      gst_spider_plug_peers                    (GstSpider *spider, GstPad *srcpad, GstPad *sinkpad); */
116 static GstPadLinkReturn gst_spider_create_and_plug (GstSpiderConnection * conn,
117     GList * plugpath);
118
119 /* random functions */
120 static gchar *gst_spider_unused_elementname (GstBin * bin,
121     const gchar * startwith);
122
123 /* debugging stuff
124 static void                     print_spider_contents                   (GstSpider *spider);
125 static void                     print_spider_link                       (GstSpiderConnection *conn); */
126
127 /* === variables === */
128 static GstElementClass *parent_class = NULL;
129
130 /* no signals yet
131 static guint gst_spider_signals[LAST_SIGNAL] = { 0 };*/
132
133 /* GObject and GStreamer init functions */
134 GType
135 gst_spider_get_type (void)
136 {
137   static GType spider_type = 0;
138
139   if (!spider_type) {
140     static const GTypeInfo spider_info = {
141       sizeof (GstSpiderClass),
142       NULL,
143       NULL,
144       (GClassInitFunc) gst_spider_class_init,
145       NULL,
146       NULL,
147       sizeof (GstSpider),
148       0,
149       (GInstanceInitFunc) gst_spider_init,
150     };
151
152     spider_type =
153         g_type_register_static (GST_TYPE_BIN, "GstSpider", &spider_info, 0);
154   }
155   return spider_type;
156 }
157
158 static void
159 gst_spider_class_init (GstSpiderClass * klass)
160 {
161   GObjectClass *gobject_class;
162   GstElementClass *gstelement_class;
163
164   gobject_class = (GObjectClass *) klass;
165   gstelement_class = (GstElementClass *) klass;
166
167   parent_class = g_type_class_ref (GST_TYPE_BIN);
168
169   /* properties */
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));
173
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;
177
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);
183
184   gstelement_class->request_new_pad =
185       GST_DEBUG_FUNCPTR (gst_spider_request_new_pad);
186 }
187 static void
188 gst_spider_init (GstSpider * spider)
189 {
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);
193
194   spider->factories = gst_autoplug_factories_filters_with_sink_caps (list);
195   g_list_free (list);
196
197   spider->links = NULL;
198
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,
202       "sink");
203
204 }
205
206 static void
207 gst_spider_dispose (GObject * object)
208 {
209   GstSpider *spider;
210   GList *list;
211
212   spider = GST_SPIDER (object);
213   g_list_free (spider->factories);
214   spider->factories = NULL;
215
216   for (list = spider->links; list; list = list->next) {
217     GstSpiderConnection *conn = list->data;
218
219     g_list_free (conn->path);
220     g_free (conn);
221   }
222   g_list_free (spider->links);
223   spider->links = NULL;
224
225   ((GObjectClass *) parent_class)->dispose (object);
226 }
227 static GstPad *
228 gst_spider_request_new_pad (GstElement * element, GstPadTemplate * templ,
229     const gchar * name)
230 {
231   GstPad *returnpad;
232   gchar *padname;
233   GstSpiderIdentity *identity;
234   GstSpider *spider;
235
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,
239       NULL);
240
241   spider = GST_SPIDER (element);
242
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;
247
248   /* FIXME: use the requested name for the pad */
249
250   gst_object_replace ((GstObject **) & returnpad->padtemplate,
251       (GstObject *) templ);
252
253   gst_bin_add (GST_BIN (element), GST_ELEMENT (identity));
254
255   returnpad = gst_element_add_ghost_pad (element, returnpad, padname);
256   g_free (padname);
257   gst_spider_link_new (identity);
258   GST_DEBUG ("successfully created requested pad %s:%s",
259       GST_DEBUG_PAD_NAME (returnpad));
260
261   return returnpad;
262 }
263
264 static void
265 gst_spider_set_property (GObject * object, guint prop_id, const GValue * value,
266     GParamSpec * pspec)
267 {
268   GstSpider *spider;
269   GList *list;
270
271   /* it's not null if we got it, but it might not be ours */
272   g_return_if_fail (GST_IS_SPIDER (object));
273
274   spider = GST_SPIDER (object);
275
276   switch (prop_id) {
277     case ARG_FACTORIES:
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));
282       }
283       g_list_free (spider->factories);
284       spider->factories = (GList *) g_value_get_pointer (value);
285       break;
286     default:
287       break;
288   }
289 }
290 static void
291 gst_spider_get_property (GObject * object, guint prop_id, GValue * value,
292     GParamSpec * pspec)
293 {
294   GstSpider *spider;
295
296   /* it's not null if we got it, but it might not be ours */
297   spider = GST_SPIDER (object);
298
299   switch (prop_id) {
300     case ARG_FACTORIES:
301       g_value_set_pointer (value, spider->factories);
302       break;
303     default:
304       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
305       break;
306   }
307 }
308
309 /* get a name for an element that isn't used yet */
310 static gchar *
311 gst_spider_unused_elementname (GstBin * bin, const gchar * startwith)
312 {
313   gchar *name = g_strdup_printf ("%s%d", startwith, 0);
314   guint i;
315
316   for (i = 0; gst_bin_get_by_name (bin, name) != NULL;) {
317     g_free (name);
318     name = g_strdup_printf ("%s%d", startwith, ++i);
319   }
320
321   return name;
322 }
323 static void
324 gst_spider_link_sometimes (GstElement * src, GstPad * pad,
325     GstSpiderConnection * conn)
326 {
327   gulong signal_id = conn->signal_id;
328
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);
335     signal_id = 0;
336   }
337 }
338
339 /* create a new link from those two elements */
340 static GstSpiderConnection *
341 gst_spider_link_new (GstSpiderIdentity * src)
342 {
343   GstSpider *spider = GST_SPIDER (GST_OBJECT_PARENT (src));
344
345   GstSpiderConnection *conn = g_new0 (GstSpiderConnection, 1);
346
347   conn->src = src;
348   conn->path = NULL;
349   conn->current = (GstElement *) spider->sink_ident;
350   spider->links = g_list_prepend (spider->links, conn);
351
352   return conn;
353 }
354 static void
355 gst_spider_link_destroy (GstSpiderConnection * conn)
356 {
357   GstSpider *spider = GST_SPIDER (GST_OBJECT_PARENT (conn->src));
358
359   /* reset link to unplugged */
360   gst_spider_link_reset (conn, (GstElement *) spider->sink_ident);
361   g_free (conn);
362 }
363 static void
364 gst_spider_link_reset (GstSpiderConnection * conn, GstElement * to)
365 {
366   GstSpider *spider = GST_SPIDER (GST_OBJECT_PARENT (conn->src));
367
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);
374   }
375   if (conn->path == NULL) {
376     conn->current = (GstElement *) spider->sink_ident;
377   } else {
378     conn->current = to;
379   }
380 }
381
382 /* add an element to the link */
383 static void
384 gst_spider_link_add (GstSpiderConnection * conn, GstElement * element)
385 {
386   conn->path = g_list_prepend (conn->path, element);
387   conn->current = element;
388 }
389
390 /* find the link from those two elements */
391 static GstSpiderConnection *
392 gst_spider_link_find (GstSpiderIdentity * src)
393 {
394   GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (src);
395   GList *list;
396
397   for (list = spider->links; list; list = list->next) {
398     GstSpiderConnection *conn = (GstSpiderConnection *) list->data;
399
400     if (conn->src == src) {
401       return conn;
402     }
403   }
404   return NULL;
405 }
406
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)
411 {
412   GstSpiderConnection *ret;
413
414   if ((ret = gst_spider_link_find (src)) != NULL) {
415     return ret;
416   }
417   return gst_spider_link_new (src);
418 }
419
420 void
421 gst_spider_identity_plug (GstSpiderIdentity * ident)
422 {
423   GstSpider *spider;
424   const GList *padlist;
425   GstPadDirection dir;
426   GstSpiderConnection *conn;
427
428   /* checks */
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));
434
435   /* return if we're already plugged */
436   if (ident->plugged)
437     return;
438
439   /* check if there is at least one element factory that can handle the
440      identity's src caps */
441   {
442     GstCaps *src_caps = gst_pad_get_caps (ident->src);
443
444     if (!gst_caps_is_empty (src_caps) && !gst_caps_is_any (src_caps)) {
445       GList *factories;
446       GstPadTemplate *padtemp;
447       gboolean found = FALSE;
448
449       factories = spider->factories;
450       while (factories) {
451         if ((padtemp =
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));
456           found = TRUE;
457         }
458         factories = factories->next;
459       }
460       if (!found) {
461         const char *mime;
462
463         mime = gst_structure_get_name (gst_caps_get_structure (src_caps, 0));
464
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);
468         return;
469       }
470     }
471     gst_caps_unref (src_caps);
472   }
473
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...");
479       return;
480     } else {
481       dir = GST_PAD_SINK;
482     }
483   } else {
484     if (GST_PAD_PEER (ident->src)) {
485       dir = GST_PAD_SRC;
486     } else {
487       /* the ident isn't linked on either side */
488       g_warning ("Trying to autoplug an unlinked element. Aborting...");
489       return;
490     }
491   }
492
493   /* now iterate all possible pads and link when needed */
494   padlist = GST_ELEMENT (spider)->pads;
495   for (; padlist; padlist = padlist->next) {
496     GstPad *otherpad;
497     GstSpiderIdentity *peer;
498
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);
509         } else {
510           conn = gst_spider_link_get (ident);
511         }
512         if ((GstElement *) spider->sink_ident == conn->current) {
513           gst_spider_plug (conn);
514         }
515       }
516     }
517   }
518
519   ident->plugged = TRUE;
520 }
521
522 void
523 gst_spider_identity_unplug (GstSpiderIdentity * ident)
524 {
525   GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (ident);
526   GList *list;
527
528   for (list = spider->links; list; list = list->next) {
529     GstSpiderConnection *conn = list->data;
530
531     if (conn->src == ident) {
532       g_list_delete_link (spider->links, list);
533       gst_spider_link_destroy (conn);
534     }
535   }
536   ident->plugged = FALSE;
537 }
538
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)
543 {
544   GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (conn->src);
545   GList *endelements = NULL, *templist = NULL;
546   GstElement *element;
547
548   /* exit if plugging is already done */
549   if ((GstElement *) conn->src == conn->current)
550     return GST_PAD_LINK_DONE;
551
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)
557         && (element =
558             gst_spider_find_element_to_plug (element,
559                 (GstElementFactory *) plugpath->data, GST_PAD_SINK))) {
560       GList *cur = templist;
561
562       endelements = g_list_prepend (endelements, element);
563       templist = g_list_previous (templist);
564       g_list_delete_link (cur, cur);
565     }
566   }
567
568   /* do the linking */
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) {
573       element =
574           (GstElement *) (endelements == NULL ? conn->src : endelements->data);
575     } else {
576       element =
577           gst_element_factory_create ((GstElementFactory *) plugpath->data,
578           NULL);
579       GST_DEBUG
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);
583     }
584     /* insert and link new element */
585     if (gst_element_link (conn->current, element)) {
586       gst_element_sync_state_with_parent (element);
587     } else {
588       /* check if the src has SOMETIMES templates. If so, link a callback */
589       GList *templs = gst_element_get_pad_template_list (conn->current);
590
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);
594
595       for (; templs; templs = templs->next) {
596         GstPadTemplate *templ = (GstPadTemplate *) templs->data;
597
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));
602           conn->signal_id =
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;
607         }
608       }
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;
613     }
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);
619   }
620
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);
625   }
626
627   return GST_PAD_LINK_DONE;
628 }
629
630 /* checks, if src is already linked to an element from factory fac on direction dir */
631 static GstElement *
632 gst_spider_find_element_to_plug (GstElement * src, GstElementFactory * fac,
633     GstPadDirection dir)
634 {
635   GList *padlist = GST_ELEMENT_PADS (src);
636
637   for (; padlist; padlist = padlist->next) {
638     GstPad *pad = (GstPad *) GST_PAD_REALIZE (padlist->data);
639
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);
645
646       if (G_TYPE_FROM_INSTANCE (element) ==
647           gst_element_factory_get_element_type (fac)) {
648         return element;
649       }
650     }
651   }
652
653   return NULL;
654 }
655
656 /* try to establish the link */
657 static GstPadLinkReturn
658 gst_spider_plug (GstSpiderConnection * conn)
659 {
660   GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (conn->src);
661
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);
666   g_warning
667       ("FIXME: autoplugging only possible from GstSpiderIdentity conn->sink yet (yep, that's technical)\n");
668   return GST_PAD_LINK_REFUSED;
669 }
670
671 /* try to establish the link using this pad */
672 static GstPadLinkReturn
673 gst_spider_plug_from_srcpad (GstSpiderConnection * conn, GstPad * srcpad)
674 {
675   GstElement *element;
676   GList *plugpath;
677   gboolean result = TRUE;
678   GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (conn->src);
679   GstElement *startelement = conn->current;
680   GstCaps *caps1;
681   GstCaps *caps2;
682
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));
686
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;
694   }
695
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);
702
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));
705      templist = plugpath;
706      while (templist)
707      {
708      g_print("%s\n", GST_OBJECT_NAME (templist->data));
709      templist = g_list_next (templist);
710      } */
711
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;
717   }
718   GST_DEBUG ("found a link that needs %d elements", g_list_length (plugpath));
719
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
723    */
724   element = conn->current;
725   while ((plugpath != NULL)
726       && (element =
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);
731   }
732
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);
737
738   /* reset the "current" element */
739   if (result == GST_PAD_LINK_REFUSED) {
740     gst_spider_link_reset (conn, startelement);
741   }
742
743   return result;
744 }
745
746 static gboolean
747 plugin_init (GstPlugin * plugin)
748 {
749   GST_DEBUG_CATEGORY_INIT (gst_spider_debug, "spider", 0,
750       "spider autoplugging element");
751
752   if (!gst_element_register (plugin, "spider", GST_RANK_NONE, GST_TYPE_SPIDER))
753     return FALSE;
754   if (!gst_element_register (plugin, "spideridentity", GST_RANK_NONE,
755           GST_TYPE_SPIDER_IDENTITY))
756     return FALSE;
757
758   return TRUE;
759 }
760
761 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
762     GST_VERSION_MINOR,
763     "gstspider",
764     "a 1:n autoplugger",
765     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)