initial import of the spider autoplugger
authorBenjamin Otte <otte@gnome.org>
Mon, 28 Jan 2002 01:47:31 +0000 (01:47 +0000)
committerBenjamin Otte <otte@gnome.org>
Mon, 28 Jan 2002 01:47:31 +0000 (01:47 +0000)
Original commit message from CVS:
initial import of the spider autoplugger

gst/autoplug/gstsearchfuncs.c [new file with mode: 0644]
gst/autoplug/gstsearchfuncs.h [new file with mode: 0644]
gst/autoplug/gstspider.c [new file with mode: 0644]
gst/autoplug/gstspider.h [new file with mode: 0644]
gst/autoplug/gstspideridentity.c [new file with mode: 0644]
gst/autoplug/gstspideridentity.h [new file with mode: 0644]
gst/autoplug/spidertest.c [new file with mode: 0644]

diff --git a/gst/autoplug/gstsearchfuncs.c b/gst/autoplug/gstsearchfuncs.c
new file mode 100644 (file)
index 0000000..080588f
--- /dev/null
@@ -0,0 +1,391 @@
+/* GStreamer
+ * Copyright (C) 1999-2002 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2000-2002 Wim Taymans <wtay@chello.be>
+ *
+ * gstsearchfuncs.c: functions needed when doing searches while
+ *                   autoplugging
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "gstsearchfuncs.h"
+
+/* function that really misses in GLib
+ */
+void
+g_list_free_list_and_elements (GList *list)
+{
+       GList *walk = list;
+       
+       while (walk)
+       {
+               g_free (walk->data);
+               walk = g_list_next (walk);
+       }
+       g_list_free (list);
+}
+
+/**
+ * gst_autoplug_can_connect_src:
+ * @fac: factory to connect to
+ * @src: caps to check
+ *
+ * Checks if a factory's sink can connect to the given caps
+ *
+ * Return: TRUE, if a connection can be established.
+ */
+GstPadTemplate *
+gst_autoplug_can_connect_src (GstElementFactory *fac, GstCaps *src)
+{
+       GList *templs;
+       
+       templs = fac->padtemplates;
+       
+       while (templs)
+       {
+               if ((GST_PADTEMPLATE_DIRECTION (templs->data) == GST_PAD_SINK) && gst_caps_check_compatibility(src, GST_PADTEMPLATE_CAPS (templs->data)))
+               {
+                       return GST_PADTEMPLATE (templs->data);
+               }
+    templs = g_list_next (templs);
+       }
+       
+       return NULL;    
+}
+/**
+ * gst_autoplug_can_connect_sink:
+ * @fac: factory to connect to
+ * @src: caps to check
+ *
+ * Checks if a factory's src can connect to the given caps
+ *
+ * Return: TRUE, if a connection can be established.
+ */
+GstPadTemplate *
+gst_autoplug_can_connect_sink (GstElementFactory *fac, GstCaps *sink)
+{
+       GList *templs;
+       
+       templs = fac->padtemplates;
+       
+       while (templs)
+       {
+               if ((GST_PADTEMPLATE_DIRECTION (templs->data) == GST_PAD_SRC) && gst_caps_check_compatibility(GST_PADTEMPLATE_CAPS (templs->data), sink))
+               {
+                       return GST_PADTEMPLATE (templs->data);
+               }
+    templs = g_list_next (templs);
+       }
+       
+       return NULL;    
+}
+GstPadTemplate *
+gst_autoplug_can_match (GstElementFactory *src, GstElementFactory *dest)
+{
+  GList *srctemps, *desttemps;
+
+  srctemps = src->padtemplates;
+
+  while (srctemps) {
+    GstPadTemplate *srctemp = (GstPadTemplate *)srctemps->data;
+
+    desttemps = dest->padtemplates;
+
+    while (desttemps) {
+      GstPadTemplate *desttemp = (GstPadTemplate *)desttemps->data;
+
+      if (srctemp->direction == GST_PAD_SRC &&
+          desttemp->direction == GST_PAD_SINK) {
+          if (gst_caps_check_compatibility (gst_padtemplate_get_caps (srctemp), 
+              gst_padtemplate_get_caps (desttemp))) {
+            GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT,
+                       "factory \"%s\" can connect with factory \"%s\"\n", 
+                                                                      GST_OBJECT_NAME (src), GST_OBJECT_NAME (dest));
+            return desttemp;
+              }
+      }
+
+      desttemps = g_list_next (desttemps);
+    }
+    srctemps = g_list_next (srctemps);
+  }
+  GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT,
+                        "factory \"%s\" cannot connect with factory \"%s\"\n", 
+                  GST_OBJECT_NAME (src), GST_OBJECT_NAME (dest));
+  return NULL;
+}
+
+/* returns TRUE if the factory has padtemplates with the specified direction */
+gboolean
+gst_autoplug_factory_has_direction (GstElementFactory *fac, GstPadDirection dir)
+{
+       GList *templs = fac->padtemplates;
+       
+       while (templs)
+       {
+               if (GST_PADTEMPLATE_DIRECTION (templs->data) == dir)
+               {
+                       return TRUE;
+               }
+               templs = g_list_next (templs);
+       }
+       
+       return FALSE;
+}
+
+/* Decisions are based on the padtemplates. 
+ * These functions return a new list so be sure to free it.
+ */
+GList *
+gst_autoplug_factories_sinks (GList *factories)
+{
+       GList *ret = NULL;
+       
+       while (factories)
+       {
+               if (gst_autoplug_factory_has_sink (factories->data))
+                       ret = g_list_prepend (ret, factories->data);
+               factories = g_list_next (factories);
+  }
+       return ret;     
+}
+GList *
+gst_autoplug_factories_srcs (GList *factories)
+{
+       GList *ret = NULL;
+       
+       while (factories)
+       {
+               if (gst_autoplug_factory_has_src (factories->data))
+                       ret = g_list_prepend (ret, factories->data);
+               factories = g_list_next (factories);
+  }
+       return ret;     
+}
+GList *        
+gst_autoplug_factories_filters (GList *factories)
+{
+       GList *ret = NULL;
+       
+       while (factories)
+       {
+               /* if you want it faster do src/sink check at once, don't call two functions */
+               if (gst_autoplug_factory_has_src (factories->data) && gst_autoplug_factory_has_sink (factories->data))
+                       ret = g_list_prepend (ret, factories->data);
+               factories = g_list_next (factories);
+  }
+       return ret;     
+}
+
+
+/* returns all factories which have sinks with non-NULL caps and srcs with
+ * any caps.
+ */
+GList *
+gst_autoplug_factories_filters_with_sink_caps (GList *factories)
+{
+       GList *ret = NULL;
+       
+       while (factories)
+       {
+               GList *templs = ((GstElementFactory *) factories->data)->padtemplates;
+               gboolean have_src = FALSE;
+               gboolean have_sink = FALSE;
+               while (templs)
+               {
+                       if (GST_PADTEMPLATE_DIRECTION (templs->data) == GST_PAD_SRC)
+                       {
+                               have_src = TRUE;
+                       }       
+                       if ((GST_PADTEMPLATE_DIRECTION (templs->data) == GST_PAD_SINK) && (GST_PADTEMPLATE_CAPS (templs->data) != NULL))
+                       {
+                               have_sink = TRUE;
+                       }
+                       if (have_src && have_sink)
+                       {
+                               ret = g_list_prepend (ret, factories->data);
+                               break;
+                       }
+                       templs = g_list_next (templs);
+               }
+               factories = g_list_next (factories);
+       }
+       return ret;
+}
+
+
+
+/* returns all factories which have a maximum of maxtemplates GstPadTemplates in direction dir
+ */
+GList *
+gst_autoplug_factories_at_most_templates(GList *factories, GstPadDirection dir, guint maxtemplates)
+{
+       GList *ret = NULL;
+       
+       while (factories)
+       {
+               guint count = 0;
+               GList *templs = ((GstElementFactory *) factories->data)->padtemplates;
+
+               while (templs)
+               {
+                       if (GST_PADTEMPLATE_DIRECTION (templs->data) == dir)
+                       {
+                               count++;
+                       }
+                       if (count > maxtemplates)
+                               break;
+                       templs = g_list_next (templs);
+               }
+               if (count <= maxtemplates)
+                 ret = g_list_prepend (ret, factories->data);
+               
+               factories = g_list_next (factories);
+       }
+       return ret;
+}
+/*********************************************************************
+ *
+ * SHORTEST PATH ALGORITHM
+ */
+/**
+ * gst_autoplug_sp:
+ * @srccaps: Caps to plug from
+ * @sinkcaps: Caps to plug to
+ * @factories: GList containing all allowed #GstElementFactory entries
+ *
+ * main function 
+ *
+ * Returns: a GList of GstElementFactory items which have to be connected to get
+ *          the shortest path.
+ */
+GList *
+gst_autoplug_sp (GstCaps *srccaps, GstCaps *sinkcaps, GList *factories)
+{
+       GList *factory_nodes = NULL;
+       guint curcost = GST_AUTOPLUG_MAX_COST; /* below this cost, there is no path */
+       GstAutoplugNode *bestnode = NULL; /* best (unconnected) endpoint currently */
+       
+       GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT, "attempting to autoplug via shortest path");
+  GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, "trying to plug %s to %s\n", gst_caps_get_mime (srccaps), gst_caps_get_mime (sinkcaps));
+  /* g_print ("trying to plug %s to %s\n", gst_caps_get_mime (srccaps), gst_caps_get_mime (sinkcaps)); */
+       /* wrap all factories as GstAutoplugNode 
+        * initialize the cost */
+       while (factories)
+       {
+               GstAutoplugNode *node = g_new0 (GstAutoplugNode, 1);
+               node->prev = NULL;
+               node->fac = (GstElementFactory *) factories->data;
+               node->templ = gst_autoplug_can_connect_src (node->fac, srccaps);
+               node->cost = (node->templ ? gst_autoplug_get_cost (node->fac) : GST_AUTOPLUG_MAX_COST);
+               node->endpoint = gst_autoplug_can_connect_sink (node->fac, sinkcaps);
+               if ((node->endpoint != NULL) && (bestnode == NULL || (node->cost < bestnode->cost)))
+               {
+                       bestnode = node;
+               }
+               factory_nodes = g_list_prepend (factory_nodes, node);
+               /* make curcost the minimum cost of any plugin */
+               curcost = node->cost < curcost ? node->cost : curcost;
+               factories = g_list_next (factories);
+       }
+       
+       /* check if we even have possible endpoints */
+       if (bestnode == NULL)
+       {
+               GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, "no factory found that could connect to sink caps\n");
+               g_list_free_list_and_elements (factory_nodes);
+               return NULL;
+       }
+       
+       /* iterate until we found the best path */
+       while (curcost < GST_AUTOPLUG_MAX_COST)
+       {
+               GList *nodes = factory_nodes;
+               guint nextcost = GST_AUTOPLUG_MAX_COST; /* next cost to check */
+               GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, "iterating at current cost %d, bestnode %s at %d\n", curcost, GST_OBJECT_NAME (bestnode->fac), bestnode->cost);
+               /* check if we already have a valid best connection to the sink */
+               if (bestnode->cost <= curcost)
+               {
+                       GList *ret;
+                       GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, "found a way to connect via %s\n", GST_OBJECT_NAME ((GstObject *) bestnode->fac));         
+                       /* enter all factories into the return list */
+                       ret = g_list_prepend (NULL, bestnode->fac);
+                       bestnode = bestnode->prev;
+                       while (bestnode != NULL)
+                       {
+                               ret = g_list_prepend (ret, bestnode->fac);
+                               bestnode = bestnode->prev;
+                       }
+                       g_list_free_list_and_elements (factory_nodes);
+                       return ret;
+               }
+               
+               /* iterate over all factories we have
+                * if they have the current cost, calculate if this
+                * factory supplies shorter paths to other elements
+                */
+               while (nodes)
+               {
+                       if (((GstAutoplugNode *) nodes->data)->cost == curcost)
+                       {
+                               /* now check all elements if we got a shorter path */
+                               GList *sinknodes = factory_nodes;
+                               GstAutoplugNode *srcnode = (GstAutoplugNode *) nodes->data;
+                               while (sinknodes)
+                               {
+                                       GstAutoplugNode *sinknode = (GstAutoplugNode *) sinknodes->data;
+                                       GstPadTemplate *templ;
+                                       if ((sinknode->cost > srcnode->cost + gst_autoplug_get_cost (sinknode->fac)) && (templ = gst_autoplug_can_match(srcnode->fac, sinknode->fac)))
+                                       {
+                                               /* we got a shorter path
+                                                * now enter that path to that node */
+                                               sinknode->prev = srcnode;
+                                               sinknode->templ = templ;
+                                               sinknode->cost = srcnode->cost + gst_autoplug_get_cost (sinknode->fac);
+                                               /* make sure to set which cost to view next */
+                                               nextcost = (nextcost > sinknode->cost) ? sinknode->cost : nextcost;
+                                               /* did we get a new best node? */
+                                               if (sinknode->endpoint && (sinknode->cost < bestnode->cost))
+                                               {
+                                                       bestnode = sinknode;
+                                               }                       
+                                       }
+                                       sinknodes = g_list_next (sinknodes);
+                               }
+                               /* FIXME: for speed remove the item we just iterated with from the factory_nodes
+                                * but don't free it yet and don't forget to free it.
+                                */
+                       }
+                 nodes = g_list_next (nodes);
+               }
+               curcost = nextcost;
+       }
+       
+       GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, "found no path from source caps to sink caps\n");          
+       g_list_free_list_and_elements (factory_nodes);
+       return NULL;    
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gst/autoplug/gstsearchfuncs.h b/gst/autoplug/gstsearchfuncs.h
new file mode 100644 (file)
index 0000000..e7e0970
--- /dev/null
@@ -0,0 +1,63 @@
+/* GStreamer
+ * Copyright (C) 1999-2002 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2000-2002 Wim Taymans <wtay@chello.be>
+ *
+ * gstsearchfuncs.h: Header for gstsearchfuncs.c
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_SEARCHFUNCS_H__
+#define __GST_SEARCHFUNCS_H__
+
+#include <gst/gst.h>
+
+/* placeholder for maximum cost when plugging */
+#define GST_AUTOPLUG_MAX_COST 999999
+
+/* struct for a node, in the search tree */
+typedef struct _GstAutoplugNode GstAutoplugNode;
+       
+struct _GstAutoplugNode {
+       GstAutoplugNode    *prev;      /* previous node */
+       GstElementFactory  *fac;       /* factory of element to connect to */
+       GstPadTemplate     *templ;     /* template which can connect */
+       guint               cost;      /* total cost to get here */
+       GstPadTemplate     *endpoint;  /* pad template that can connect to sink caps */
+};
+
+/* helper functions */
+GstPadTemplate *                       gst_autoplug_can_connect_src            (GstElementFactory *fac, GstCaps *src);
+GstPadTemplate *                       gst_autoplug_can_connect_sink           (GstElementFactory *fac, GstCaps *sink);
+GstPadTemplate *                       gst_autoplug_can_match                  (GstElementFactory *src, GstElementFactory *dest);
+gboolean                               gst_autoplug_factory_has_direction      (GstElementFactory *fac, GstPadDirection dir);
+#define gst_autoplug_factory_has_sink(fac) gst_autoplug_factory_has_direction((fac), GST_PAD_SINK)
+#define gst_autoplug_factory_has_src(fac) gst_autoplug_factory_has_direction((fac), GST_PAD_SRC)
+
+/* cost functions */
+#define gst_autoplug_get_cost(fac) 1
+
+/* factory selections */
+GList *                                        gst_autoplug_factories_sinks            (GList *factories);
+GList *                                        gst_autoplug_factories_srcs             (GList *factories);
+GList *                                        gst_autoplug_factories_filters          (GList *factories);
+GList *                                        gst_autoplug_factories_filters_with_sink_caps(GList *factories);
+GList *                                gst_autoplug_factories_at_most_templates(GList *factories, GstPadDirection dir, guint maxtemplates);
+
+/* shortest path algorithm */
+GList *                                gst_autoplug_sp                         (GstCaps *src_caps, GstCaps *sink_caps, GList *factories);
+
+#endif /* __GST_SEARCHFUNCS_H__ */
diff --git a/gst/autoplug/gstspider.c b/gst/autoplug/gstspider.c
new file mode 100644 (file)
index 0000000..a28d9d5
--- /dev/null
@@ -0,0 +1,536 @@
+/* GStreamer
+ * Copyright (C) 2002 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2002 Wim Taymans <wtay@chello.be>
+ *
+ * gstspider.c: element to automatically connect sinks and sources
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/**
+ * TODO:
+ * - handle automatic removal of unneeded elements
+ * - make the spider handle and send events (esp. new media)
+ * - allow disconnecting
+ * - implement a way to allow merging/splitting (aka tee)
+ * - find ways to define which elements to use when plugging
+ * - remove pads
+ * - improve typefinding
+ * - react to errors inside the pipeline
+ * - implement more properties, change the current
+ * - emit signals (most important: "NOT PLUGGABLE")
+ * - implement something for reporting the state of the spider
+ *   to allow easier debugging.
+ *   (could be useful for bins in general)
+ * - fix bugs
+ * ...
+ */
+#include "gstspider.h"
+#include "gstspideridentity.h"
+#include "gstsearchfuncs.h"
+/* signals and args */
+enum {
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum {
+  ARG_0,
+  ARG_PLUGTYPE,
+  /* FILL ME TOO */
+};
+
+/* generic templates */
+GST_PADTEMPLATE_FACTORY (spider_src_factory,
+  "src_%02d",
+  GST_PAD_SRC,
+  GST_PAD_REQUEST,
+  NULL      /* no caps */
+);
+
+GST_PADTEMPLATE_FACTORY (spider_sink_factory,
+  "sink_%02d",
+  GST_PAD_SINK,
+  GST_PAD_REQUEST,
+  NULL      /* no caps */
+);
+/* standard GObject stuff */
+static void                        gst_spider_class_init                         (GstSpiderClass *klass);
+static void                        gst_spider_init                               (GstSpider *spider);
+static void                        gst_spider_dispose                            (GObject *object);
+
+/* element class functions */
+static GstPad*                     gst_spider_request_new_pad                    (GstElement *element, GstPadTemplate *templ, const gchar *name);
+static void                        gst_spider_set_property                       (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static void                        gst_spider_get_property                       (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+
+/* autoplugging functions */
+static GstElement *                gst_spider_find_element_to_plug               (GstElement *src, GstElementFactory *fac, GstPadDirection dir);
+static GstPadConnectReturn         gst_spider_plug_peers                         (GstSpider *spider, GstSpiderIdentity *src, GstSpiderIdentity *sink);
+static GstPadConnectReturn         gst_spider_create_and_plug                    (GstSpider *spider, GstElement *src, GstElement *sink, GList *plugpath);
+
+/* random functions */
+static gchar *                    gst_spider_unused_elementname                 (GstBin *bin, const gchar *startwith);
+
+/* === variables === */
+static                             GstElementClass                               *parent_class = NULL;
+
+/* no signals yet
+static guint gst_spider_signals[LAST_SIGNAL] = { 0 };*/
+
+/* GObject and GStreamer init functions */
+GType
+gst_spider_get_type(void)
+{
+  static GType spider_type = 0;
+
+  if (!spider_type) {
+    static const GTypeInfo spider_info = {
+      sizeof(GstSpiderClass),      NULL,
+      NULL,
+      (GClassInitFunc)gst_spider_class_init,
+      NULL,
+      NULL,
+      sizeof(GstSpider),
+      0,
+      (GInstanceInitFunc)gst_spider_init,
+    };
+    spider_type = g_type_register_static (GST_TYPE_BIN, "GstSpider", &spider_info, 0);
+  }
+  return spider_type;
+}
+
+static void
+gst_spider_class_init (GstSpiderClass *klass)
+{
+  GObjectClass     *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass*) klass;
+  gstelement_class = (GstElementClass*) klass;
+
+  parent_class = g_type_class_ref(GST_TYPE_BIN);
+
+  /* properties */
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PLUGTYPE,
+  g_param_spec_int ("plugtype", "plug direction", "encoding, decoding or anything",
+                      GST_SPIDER_ANY, GST_SPIDER_PLUGTYPES - 1, 0, G_PARAM_READWRITE));
+
+  gobject_class->set_property = gst_spider_set_property;
+  gobject_class->get_property = gst_spider_get_property;
+
+  gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR(gst_spider_request_new_pad);
+}
+static void 
+gst_spider_init (GstSpider *spider) 
+{
+  spider->plugtype = GST_SPIDER_ANY;
+}
+
+static GstPad *
+gst_spider_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name)
+{
+  GstPad *returnpad;
+  gchar *padname;
+  GstSpiderIdentity *identity;
+  GstSpider *spider;
+  
+  g_return_val_if_fail (templ != NULL, NULL);
+  g_return_val_if_fail (GST_IS_PADTEMPLATE (templ), NULL);
+  
+  spider = GST_SPIDER (element);
+  
+  /* create an identity object, so we have a pad */
+  switch ( GST_PADTEMPLATE_DIRECTION (templ))
+  {
+    case GST_PAD_SRC:
+      padname = gst_spider_unused_elementname ((GstBin *)spider, "src_");
+      identity = gst_spider_identity_new_src (padname);
+      break;
+    case GST_PAD_SINK:
+      padname = gst_spider_unused_elementname ((GstBin *)spider, "sink_");
+      identity = gst_spider_identity_new_sink (padname);
+      break;
+    case GST_PAD_UNKNOWN:
+    default:
+      g_warning("Spider: you must request a source or sink pad.");
+      return NULL;
+  }
+  
+  /* connect a ghost pad on the right side of the identity and set the requested template */
+  returnpad = gst_spider_identity_request_new_pad  (GST_ELEMENT (identity), templ, NULL);
+
+  /* FIXME: use the requested name for the pad */
+
+  gst_object_ref (GST_OBJECT (templ));
+  GST_PAD_PADTEMPLATE (returnpad) = templ;
+  
+  gst_bin_add (GST_BIN (element), GST_ELEMENT (identity));
+  
+  returnpad = gst_element_add_ghost_pad (element, returnpad, padname);
+  GST_DEBUG (GST_CAT_ELEMENT_PADS, "successuflly created requested pad %s:%s\n", GST_DEBUG_PAD_NAME (returnpad));
+  
+  return returnpad;
+}
+
+static void
+gst_spider_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+  GstSpider *spider;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail (GST_IS_SPIDER (object));
+
+  spider = GST_SPIDER (object);
+
+  switch (prop_id) {
+    case ARG_PLUGTYPE:
+      switch (g_value_get_int (value))
+      {
+        case GST_SPIDER_ANY:
+          spider->plugtype = GST_SPIDER_ANY;
+          GST_DEBUG (0,"spider: setting plugtype to ANY\n");
+          break;
+        case GST_SPIDER_ENCODE:
+          spider->plugtype = GST_SPIDER_ENCODE;
+          GST_DEBUG (0,"spider: setting plugtype to ENCODE\n");
+          break;
+        case GST_SPIDER_DECODE:
+          spider->plugtype = GST_SPIDER_DECODE;
+          GST_DEBUG (0,"spider: setting plugtype to DECODE\n");
+          break;
+        default:
+          GST_DEBUG (0,"spider: invalid value %d while setting plugtype\n", g_value_get_int (value));
+          break;
+      }
+      break;
+    default:
+      break;
+  }
+}
+static void
+gst_spider_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+  GstSpider *spider;
+
+  /* it's not null if we got it, but it might not be ours */
+  spider = GST_SPIDER(object);
+
+  switch (prop_id) {
+    case ARG_PLUGTYPE:
+      g_value_set_int (value, spider->plugtype);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+/* get a name for an element that isn't used yet */
+static gchar *
+gst_spider_unused_elementname (GstBin *bin, const gchar *startwith)
+{
+  gchar * name = g_strdup_printf ("%s%d", startwith, 0);
+  guint i;
+  
+  for (i = 0; gst_bin_get_by_name (bin, name) != NULL; )
+  {
+    g_free (name);
+    name = g_strdup_printf ("%s%d", startwith, ++i);
+  }
+  
+  return name;
+}
+/* callback and struct for the data supplied to the callback that is used to connect dynamic pads */
+typedef struct {
+  GstSpider *spider;
+  GstElement *sink;
+  GList *plugpath;
+  gulong signal_id;
+} GstSpiderConnectSometimes;
+static void
+gst_spider_connect_sometimes (GstElement *src, GstPad *pad, GstSpiderConnectSometimes *data)
+{
+  gboolean restart = FALSE;
+  if (gst_element_get_state ((GstElement *) data->spider) == GST_STATE_PLAYING)
+  {
+    restart = TRUE;
+    gst_element_set_state ((GstElement *) data->spider, GST_STATE_PAUSED);
+  }
+  gst_spider_create_and_plug (data->spider, src, data->sink, data->plugpath);
+  g_signal_handler_disconnect (src, data->signal_id);
+  if (restart)
+  {
+    gst_element_set_state ((GstElement *) data->spider, GST_STATE_PLAYING);
+  }
+  g_free (data);
+  
+  gst_element_interrupt (src);
+}
+/* connects newsrc to newsink using the elementfactories in plugpath */
+static GstPadConnectReturn
+gst_spider_create_and_plug (GstSpider *spider, GstElement *src, GstElement *sink, GList *plugpath)
+{
+  GstElement *element;
+  
+  /* get the next element */
+  if (plugpath == NULL)
+  {
+    element = sink;
+  } else {
+    element = gst_elementfactory_create ((GstElementFactory *) plugpath->data, 
+                             gst_spider_unused_elementname (GST_BIN (spider), GST_OBJECT_NAME (plugpath->data)));
+    gst_bin_add (GST_BIN (spider), element);
+  }
+  /* insert and connect new element */
+  if (!gst_element_connect_elements (src, element))
+  {
+    /* check if the src has SOMETIMES templates. If so, connect a callback */
+    GList *templs = gst_element_get_padtemplate_list (src);
+        
+    /* remove element that couldn't be connected, if it wasn't the endpoint */
+    if (element != sink)
+      gst_bin_remove (GST_BIN (spider), element);
+    
+    while (templs) {
+      GstPadTemplate *templ = (GstPadTemplate *) templs->data;
+      if ((GST_PADTEMPLATE_DIRECTION (templ) == GST_PAD_SRC) && (GST_PADTEMPLATE_PRESENCE(templ) == GST_PAD_SOMETIMES))
+      {
+       GstSpiderConnectSometimes *data = g_new (GstSpiderConnectSometimes, 1);
+        GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, "adding callback to connect element %s to %s\n", GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (element));
+       data->spider = spider;
+       data->sink = sink;
+       data->plugpath = plugpath;
+       data->signal_id = g_signal_connect (G_OBJECT (src), "new_pad", 
+                                           G_CALLBACK (gst_spider_connect_sometimes), data);
+       return GST_PAD_CONNECT_DELAYED;
+      }
+      templs = g_list_next (templs);
+    }
+    GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, "no chance to connect element %s to %s\n", GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (element));
+    g_list_free (plugpath);
+    return GST_PAD_CONNECT_REFUSED;
+  }
+  GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, "added element %s and attached it to element %s\n", GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (src));
+  plugpath = g_list_delete_link (plugpath, plugpath);
+  
+  /* recursively connect the rest of the elements */
+  if (element != sink) {
+    return gst_spider_create_and_plug (spider, element, sink, plugpath);
+  }
+  
+  return GST_PAD_CONNECT_DONE;
+}
+/* checks, if src is already connected to an element from factory fac on direction dir */
+static GstElement *
+gst_spider_find_element_to_plug (GstElement *src, GstElementFactory *fac, GstPadDirection dir)
+{
+  GList *padlist = GST_ELEMENT_PADS (src);
+  
+  while (padlist)
+  {
+    GstPad *pad = (GstPad *) GST_PAD_REALIZE (padlist->data);
+    /* is the pad on the right side and is it connected? */
+    if ((GST_PAD_DIRECTION (pad) == dir) && (pad = GST_PAD (GST_RPAD_PEER (pad))))
+    {
+      /* is the element the pad is connected to of the right type? */
+      GstElement *element = GST_PAD_PARENT (pad);
+      if (GST_ELEMENT_CLASS (G_OBJECT_GET_CLASS (element))->elementfactory == fac) {
+        return element;
+      }
+    }
+    padlist = g_list_next (padlist);
+  }
+  
+  return NULL;
+}
+/* plugs a pad into the autoplugger if it isn't plugged yet */
+void
+gst_spider_plug  (GstSpiderIdentity *ident)
+{
+  GstSpider *spider;
+  GList *plugto;
+  GstPad *plugpad;
+  
+  /* checks */
+  g_return_if_fail (ident != NULL);
+  g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
+  spider = GST_SPIDER (GST_ELEMENT_PARENT (ident));
+  g_assert (spider != NULL);
+  g_assert (GST_IS_SPIDER (spider));
+  
+  /* return, if we're already plugged */
+  if (ident->plugged) return;
+  
+  if (ident->sink && GST_PAD_PEER (ident->sink))
+  {
+    if (ident->src && GST_PAD_PEER (ident->src))
+    {
+      /* Hey, the ident is connected on both sides */
+      g_warning ("Trying to autoplug a connected element. Aborting...");
+      return;
+    } else {
+      plugpad = ident->sink;
+    }
+  } else {
+    if (ident->src && GST_PAD_PEER (ident->src))
+    {
+      plugpad = ident->src;
+    } else {
+      /* the ident isn't connected on either side */
+      g_warning ("Trying to autoplug an unconnected element. Aborting...");
+      return;
+    }
+  }
+
+  /* now iterate all possible pads and connect */
+  plugto = gst_element_get_pad_list (GST_ELEMENT (spider));
+  while (plugto)
+  {
+    GstPad *otherpad = (GstPad *) GST_GPAD_REALPAD (plugto->data);
+    GstSpiderIdentity *peer = (GstSpiderIdentity *) GST_PAD_PARENT (otherpad);
+    /* we only want to connect to the other side */
+    if (GST_PAD_DIRECTION (plugpad) != GST_PAD_DIRECTION (otherpad))
+    {
+      /* we only connect to plugged in elements */
+      if (peer->plugged == TRUE) 
+      {
+        /* plug in the right direction */
+        if (plugpad == ident->src)
+        {
+          gst_spider_plug_peers (spider, peer, ident);
+        } else {
+          gst_spider_plug_peers (spider, ident, peer);
+        }
+      }
+    }
+    plugto = g_list_next (plugto);
+  }
+  
+  ident->plugged = TRUE;
+}
+/* connect the src Identity element to the sink identity element
+ * Returns: DONE, if item could be plugged, DELAYED, if a callback is needed or REFUSED,
+ * if no connection was possible
+ */
+static GstPadConnectReturn
+gst_spider_plug_peers (GstSpider *spider, GstSpiderIdentity *src, GstSpiderIdentity *sink)
+{
+  GstElement *element, *newsrc, *newsink;
+  GstPad *pad, *compat;
+  GList *plugpath;
+  GList *neededfactories;
+  GList *templist;
+  gboolean result = TRUE;
+  
+  GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, "trying to plug from %s to %s\n", GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (sink));
+  
+  neededfactories = (GList *) gst_elementfactory_get_list ();
+  /* use only elements which have sources and sinks and where the sinks have caps */
+  neededfactories = gst_autoplug_factories_filters_with_sink_caps (neededfactories);
+  
+  /* use only the elements with exactly 1 sink/src when decoding/encoding */
+  if (spider->plugtype == GST_SPIDER_ENCODE)
+  {
+    templist = neededfactories;
+    neededfactories = gst_autoplug_factories_at_most_templates (neededfactories, GST_PAD_SRC, 1);
+    g_list_free (templist);
+  }
+  if (spider->plugtype == GST_SPIDER_DECODE)
+  {
+    templist = neededfactories;
+    neededfactories = gst_autoplug_factories_at_most_templates (neededfactories, GST_PAD_SINK, 1);
+    g_list_free (templist);
+  }
+  
+  /* find a path from src to sink */
+  plugpath = gst_autoplug_sp (gst_pad_get_caps ((GstPad *) GST_RPAD_PEER (src->sink)), gst_pad_get_caps ((GstPad *) GST_RPAD_PEER (sink->src)), neededfactories);
+  
+  /* if there is no way to plug: return */
+  if (plugpath == NULL) {
+    GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, "no chance to plug from %s to %s\n", GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (sink));
+    return FALSE;
+  }
+  GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, "found a connection that needs %d elements\n", g_list_length (plugpath));
+
+  /* now remove non-needed elements from the beginning of the path 
+   * alter src to point to the new element where we need to start 
+   * plugging and alter the plugpath to represent the elements, that must be plugged
+   */
+  newsrc = (GstElement *) src;
+  while (element = gst_spider_find_element_to_plug (newsrc, (GstElementFactory *) plugpath->data, GST_PAD_SRC))
+  {
+    newsrc = element;
+    plugpath = g_list_delete_link (plugpath, plugpath);
+  }
+  /* now do the same at the end */
+  newsink = (GstElement *) sink;
+  templist = g_list_last (plugpath);
+  while (element = gst_spider_find_element_to_plug (newsink, (GstElementFactory *) plugpath->data, GST_PAD_SINK))
+  {
+    GList *cur = templist;
+    newsink = element;
+    templist = g_list_previous (templist);
+    g_list_delete_link (cur, cur);    
+  }
+  
+  GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT, "%d elements must be inserted to establish the connection\n", g_list_length (plugpath));
+  /* create the elements and plug them */
+  result = gst_spider_create_and_plug (spider, newsrc, newsink, plugpath);
+
+  /* free no longer needed data */
+  g_list_free (neededfactories);
+  
+  return result;  
+}
+
+GstElementDetails gst_spider_details = {
+  "Spider",
+  "Filter/Autplug",
+  "Automatically connect sinks and sources",
+  VERSION,
+  "Benjamin Otte <in7y118@public.uni-hamburg.de>",
+  "(C) 2002",
+};
+
+static gboolean
+plugin_init (GModule *module, GstPlugin *plugin)
+{
+  GstElementFactory *factory;
+
+  factory = gst_elementfactory_new("spider", GST_TYPE_SPIDER,
+                                   &gst_spider_details);
+  g_return_val_if_fail(factory != NULL, FALSE);
+
+  gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (spider_src_factory));
+  gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (spider_sink_factory));
+
+  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
+
+  return TRUE;
+}
+
+GstPluginDesc plugin_desc = {
+  GST_VERSION_MAJOR,
+  GST_VERSION_MINOR,
+  "gstspider",
+  plugin_init
+};
+
+  
+  
\ No newline at end of file
diff --git a/gst/autoplug/gstspider.h b/gst/autoplug/gstspider.h
new file mode 100644 (file)
index 0000000..695f2b2
--- /dev/null
@@ -0,0 +1,77 @@
+/* GStreamer
+ * Copyright (C) 2002 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2002 Wim Taymans <wtay@chello.be>
+ *
+ * gstspider.h: Header for GstSpider object
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GST_SPIDER_H__
+#define __GST_SPIDER_H__
+
+#include <gst/gst.h>
+#include "gstspideridentity.h"
+
+typedef enum {
+       GST_SPIDER_ANY = 0,
+       GST_SPIDER_ENCODE = 1,
+       GST_SPIDER_DECODE = 2,
+       GST_SPIDER_PLUGTYPES
+} GstSpiderPlugtype;
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+       
+extern GstElementDetails gst_spider_details;
+
+#define GST_TYPE_SPIDER \
+  (gst_spider_get_type())
+#define GST_SPIDER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPIDER,GstSpider))
+#define GST_SPIDER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SPIDER,GstSpiderClass)) 
+#define GST_IS_SPIDER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SPIDER))
+#define GST_IS_SPIDER_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPIDER))
+       
+typedef struct _GstSpider GstSpider;
+typedef struct _GstSpiderClass GstSpiderClass;
+
+struct _GstSpider {
+  GstBin                parent;
+       
+  GstSpiderPlugtype     plugtype; /* direction to plug */              
+};
+       
+struct _GstSpiderClass {
+  GstBinClass parent_class;
+};
+
+/* default initialization stuff */
+GType          gst_spider_get_type             (void);
+
+/* private functions to be called by GstSpiderIdentity */
+void           gst_spider_plug                 (GstSpiderIdentity *ident);
+
+       
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GST_SPIDER_H__ */
diff --git a/gst/autoplug/gstspideridentity.c b/gst/autoplug/gstspideridentity.c
new file mode 100644 (file)
index 0000000..d51d157
--- /dev/null
@@ -0,0 +1,502 @@
+/* GStreamer
+ * Copyright (C) 2002 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2002 Wim Taymans <wtay@chello.be>
+ *
+ * gstspideridentity.c: IDentity element for the spider autoplugger
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include "gstspideridentity.h"
+
+#include "gstspider.h"
+
+GstElementDetails gst_spider_identity_details = {
+  "SpiderIdentity",
+  "Filter/Autoplug",
+  "connection between spider and outside elements",
+  VERSION,
+  "Benjamin Otte <in7y118@public.uni-hamburg.de>",
+  "(C) 2002",
+};
+
+
+/* generic templates 
+ * delete me when meging with spider.c
+ */
+GST_PADTEMPLATE_FACTORY (spider_src_factory,
+  "src_%02d",
+  GST_PAD_SRC,
+  GST_PAD_REQUEST,
+  NULL      /* no caps */
+);
+
+GST_PADTEMPLATE_FACTORY (spider_sink_factory,
+  "sink_%02d",
+  GST_PAD_SINK,
+  GST_PAD_REQUEST,
+  NULL      /* no caps */
+);
+
+/* SpiderIdentity signals and args */
+enum {
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum {
+  ARG_0,
+  /* FILL ME */
+};
+
+/* GObject stuff */
+static void                   gst_spider_identity_class_init            (GstSpiderIdentityClass *klass);
+static void                   gst_spider_identity_init                  (GstSpiderIdentity *spider_identity);
+
+/* functions set in pads, elements and stuff */
+static void                   gst_spider_identity_chain                  (GstPad *pad, GstBuffer *buf);
+static GstElementStateReturn  gst_spider_identity_change_state          (GstElement *element);
+static GstPadConnectReturn    gst_spider_identity_connect                (GstPad *pad, GstCaps *caps);
+static GstCaps*                gst_spider_identity_getcaps                (GstPad *pad, GstCaps *caps);
+/* loop functions */
+static void                    gst_spider_identity_dumb_loop              (GstSpiderIdentity *ident);
+static void                    gst_spider_identity_src_loop              (GstSpiderIdentity *ident);
+static void                    gst_spider_identity_sink_loop_typefinding  (GstSpiderIdentity *ident);
+static void                    gst_spider_identity_sink_loop_emptycache  (GstSpiderIdentity *ident);
+
+/* set/get functions */
+gboolean                      gst_spider_identity_is_plugged            (GstSpiderIdentity *identity);
+void                          gst_spider_identity_set_caps              (GstSpiderIdentity *identity, GstCaps *caps);
+
+/* callback */
+static void                    callback_typefind_have_type                (GstElement *typefind, GstCaps *caps, GstSpiderIdentity *identity);
+
+/* other functions */
+static void                    gst_spider_identity_start_typefinding      (GstSpiderIdentity *ident);
+
+static                        GstElementClass                           *parent_class = NULL;
+/* no signals
+static guint gst_spider_identity_signals[LAST_SIGNAL] = { 0 }; */
+
+GType
+gst_spider_identity_get_type (void) 
+{
+  static GType spider_identity_type = 0;
+
+  if (!spider_identity_type) {
+    static const GTypeInfo spider_identity_info = {
+      sizeof(GstSpiderIdentityClass),      NULL,
+      NULL,
+      (GClassInitFunc)gst_spider_identity_class_init,
+      NULL,
+      NULL,
+      sizeof(GstSpiderIdentity),
+      0,
+      (GInstanceInitFunc)gst_spider_identity_init,
+    };
+    spider_identity_type = g_type_register_static (GST_TYPE_ELEMENT, "GstSpiderIdentity", &spider_identity_info, 0);
+  }
+  return spider_identity_type;
+}
+
+static void 
+gst_spider_identity_class_init (GstSpiderIdentityClass *klass) 
+{
+  GstElementClass *gstelement_class = (GstElementClass *) klass;
+  
+  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+  
+  /* add our two pad templates */
+  gst_element_class_add_padtemplate (gstelement_class, GST_PADTEMPLATE_GET (spider_src_factory));
+  gst_element_class_add_padtemplate (gstelement_class, GST_PADTEMPLATE_GET (spider_sink_factory));
+  
+  gstelement_class->change_state = GST_DEBUG_FUNCPTR(gst_spider_identity_change_state);
+  gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR(gst_spider_identity_request_new_pad);
+}
+
+static GstBufferPool*
+gst_spider_identity_get_bufferpool (GstPad *pad)
+{
+  /* fix me */
+  GstSpiderIdentity *spider_identity;
+
+  spider_identity = GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
+
+  return gst_pad_get_bufferpool (spider_identity->src);
+}
+
+static void 
+gst_spider_identity_init (GstSpiderIdentity *spider_identity) 
+{
+  /* pads */
+  spider_identity->sink = NULL;
+  spider_identity->src = NULL;
+
+  /* variables */
+  spider_identity->plugged = FALSE;
+  
+  /* caching */
+  spider_identity->cache_start = NULL;
+  spider_identity->cache_end = NULL;
+  
+}
+
+static void 
+gst_spider_identity_chain (GstPad *pad, GstBuffer *buf) 
+{
+  GstSpiderIdentity *ident;
+  
+  /* g_print ("chaining on pad %s:%s with buffer %p\n", GST_DEBUG_PAD_NAME (pad), buf); */
+
+  g_return_if_fail (pad != NULL);
+  g_return_if_fail (GST_IS_PAD (pad));
+  if (buf == NULL) return;
+
+  ident = GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
+
+  if (GST_IS_EVENT (buf)) {
+    gst_pad_event_default (ident->sink, GST_EVENT (buf));
+  }
+
+  if ((ident->src != NULL) && (GST_PAD_PEER (ident->src) != NULL)) {
+    /* g_print("pushing buffer %p (refcount %d - buffersize %d) to pad %s:%s\n", buf, GST_BUFFER_REFCOUNT (buf), GST_BUFFER_SIZE (buf), GST_DEBUG_PAD_NAME (ident->src)); */
+    gst_pad_push (ident->src, buf);
+  } else if (GST_IS_BUFFER (buf)) {
+    gst_buffer_unref (buf);
+  }
+}
+GstSpiderIdentity*           
+gst_spider_identity_new_src (gchar *name)
+{
+  GstSpiderIdentity *ret = (GstSpiderIdentity *) g_object_new (gst_spider_identity_get_type (), NULL);
+  
+  GST_ELEMENT_NAME (ret) = name;
+  /* set the right functions */
+  gst_element_set_loop_function (GST_ELEMENT (ret), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_src_loop));
+  
+  return ret;
+}
+GstSpiderIdentity*           
+gst_spider_identity_new_sink (gchar *name)
+{
+  GstSpiderIdentity *ret = (GstSpiderIdentity *) g_object_new (gst_spider_identity_get_type (), NULL);
+  
+  GST_ELEMENT_NAME (ret) = name;
+
+  return ret;
+}
+
+/* shamelessly stolen from gstqueue.c to get proxy connections */
+static GstPadConnectReturn
+gst_spider_identity_connect (GstPad *pad, GstCaps *caps)
+{
+  GstSpiderIdentity *spider_identity = GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
+  GstPad *otherpad;
+
+  if (pad == spider_identity->src) 
+    otherpad = spider_identity->sink;
+  else
+    otherpad = spider_identity->src;
+
+  if (otherpad != NULL)
+    return gst_pad_proxy_connect (otherpad, caps);
+  
+  return GST_PAD_CONNECT_OK;
+}
+
+static GstCaps*
+gst_spider_identity_getcaps (GstPad *pad, GstCaps *caps)
+{
+  GstSpiderIdentity *spider_identity = GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
+  GstPad *otherpad;
+
+  if (pad == spider_identity->src) 
+    otherpad = spider_identity->sink;
+  else
+    otherpad = spider_identity->src;
+
+  if (otherpad != NULL)
+    return gst_pad_get_allowed_caps (otherpad);
+  return NULL;
+}
+
+GstPad*
+gst_spider_identity_request_new_pad  (GstElement *element, GstPadTemplate *templ, const gchar *name)
+{
+  GstSpiderIdentity *ident;
+  
+  /*checks */
+  g_return_val_if_fail (templ != NULL, NULL);
+  g_return_val_if_fail (GST_IS_PADTEMPLATE (templ), NULL);
+  ident = GST_SPIDER_IDENTITY (element);
+  g_return_val_if_fail (ident != NULL, NULL);
+  g_return_val_if_fail (GST_IS_SPIDER_IDENTITY (ident), NULL);
+  
+  switch (GST_PADTEMPLATE_DIRECTION (templ))
+  {
+    case GST_PAD_SINK:
+      if (ident->sink != NULL) break;
+      /* sink */
+      GST_DEBUG(0, "element %s requests new sink pad\n", GST_ELEMENT_NAME(ident));
+      ident->sink = gst_pad_new ("sink", GST_PAD_SINK);
+      gst_element_add_pad (GST_ELEMENT (ident), ident->sink);
+      gst_pad_set_connect_function (ident->sink, GST_DEBUG_FUNCPTR (gst_spider_identity_connect));
+      gst_pad_set_getcaps_function (ident->sink, GST_DEBUG_FUNCPTR (gst_spider_identity_getcaps));
+      return ident->sink;
+    case GST_PAD_SRC:
+      /* src */
+      if (ident->src != NULL) break;
+      GST_DEBUG(0, "element %s requests new src pad\n", GST_ELEMENT_NAME(ident));
+      ident->src = gst_pad_new ("src", GST_PAD_SRC);
+      gst_element_add_pad (GST_ELEMENT (ident), ident->src);
+      gst_pad_set_connect_function (ident->src, GST_DEBUG_FUNCPTR (gst_spider_identity_connect));
+      gst_pad_set_getcaps_function (ident->src, GST_DEBUG_FUNCPTR (gst_spider_identity_getcaps));
+      return ident->src;
+    default:
+      break;
+  }
+  
+  GST_DEBUG(0, "element %s requested a new pad but none could be created\n", GST_ELEMENT_NAME(ident));
+  return NULL;
+}
+
+/* this function has to
+ * - start the autoplugger
+ * - start type finding
+ * ...
+ */
+static GstElementStateReturn
+gst_spider_identity_change_state (GstElement *element)
+{
+  GstSpiderIdentity *ident;
+  GstSpider *spider;
+  GstElementStateReturn ret = GST_STATE_SUCCESS;
+  
+  /* element check */
+  ident = GST_SPIDER_IDENTITY (element);
+  g_return_val_if_fail (ident != NULL, GST_PAD_CONNECT_REFUSED);
+  g_return_val_if_fail (GST_IS_SPIDER_IDENTITY (ident), GST_PAD_CONNECT_REFUSED);
+  
+  /* autoplugger check */
+  spider = GST_SPIDER (GST_ELEMENT_PARENT (ident));
+  g_return_val_if_fail (spider != NULL, GST_PAD_CONNECT_REFUSED);
+  g_return_val_if_fail (GST_IS_SPIDER (spider), GST_PAD_CONNECT_REFUSED);
+  
+  switch (GST_STATE_TRANSITION (element)) {
+    case GST_STATE_PAUSED_TO_PLAYING:
+      /* start typefinding or plugging */
+      if ((ident->sink != NULL) && (ident->src == NULL))
+      {
+        if (gst_pad_get_caps ((GstPad *) GST_PAD_PEER (ident->sink)) == NULL)
+        {
+          gst_spider_identity_start_typefinding (ident);
+          break;
+        } else {
+          gst_spider_plug (ident);
+        }
+      }
+      /* autoplug on src */
+      if ((ident->src != NULL) && (ident->sink == NULL))
+      {
+        gst_spider_plug (ident);
+      }
+    default:
+      break;
+  }
+  
+  if ((ret != GST_STATE_FAILURE) && (GST_ELEMENT_CLASS (parent_class)->change_state))
+    ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
+  
+  return ret;
+}
+
+static void
+gst_spider_identity_start_typefinding (GstSpiderIdentity *ident)
+{
+  GstElement* typefind;
+  
+  GST_DEBUG (GST_CAT_AUTOPLUG, "element %s starts typefinding", GST_ELEMENT_NAME(ident));
+  
+  /* create and connect typefind object */
+  typefind = gst_elementfactory_make ("typefind", g_strdup_printf("%s%s", "typefind", GST_ELEMENT_NAME(ident)));
+  g_signal_connect (G_OBJECT (typefind), "have_type",
+                    G_CALLBACK (callback_typefind_have_type), ident);
+  gst_bin_add (GST_BIN (GST_ELEMENT_PARENT (ident)), typefind);
+  gst_pad_connect (gst_element_get_compatible_pad ((GstElement *) ident, gst_element_get_pad (typefind, "sink")), gst_element_get_pad (typefind, "sink"));
+  
+  gst_element_set_loop_function (GST_ELEMENT (ident), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_sink_loop_typefinding));
+}
+/* waiting for a good suggestion on where to set the caps from typefinding
+ * Caps must be cleared when pad is disconnected
+ * 
+ * Currently we are naive and set the caps on the source of the identity object 
+ * directly and hope to avoid any disturbance in the force.
+ */
+void
+gst_spider_identity_set_caps (GstSpiderIdentity *ident, GstCaps *caps)
+{
+  if (ident->src)
+  {
+    gst_pad_try_set_caps (ident->src, caps);
+  }
+}
+
+
+static void
+callback_typefind_have_type (GstElement *typefind, GstCaps *caps, GstSpiderIdentity *ident)
+{
+  gboolean restart_spider = FALSE;
+  
+  GST_INFO (GST_CAT_AUTOPLUG, "element %s has found caps", GST_ELEMENT_NAME(ident));
+  /* checks */
+  
+  /* we have to ref the typefind, because if me remove it the scheduler segfaults 
+   * FIXME: get rid of the typefinder
+   */
+  gst_object_ref (GST_OBJECT (typefind));
+  
+  g_assert (GST_IS_ELEMENT (typefind));
+  g_assert (GST_IS_SPIDER_IDENTITY (ident));
+  
+  /* pause the autoplugger */
+  if (gst_element_get_state (GST_ELEMENT (GST_ELEMENT_PARENT(ident))) == GST_STATE_PLAYING)
+  {
+    gst_element_set_state (GST_ELEMENT (GST_ELEMENT_PARENT(ident)), GST_STATE_PAUSED);
+    restart_spider = TRUE;
+  }
+
+  /* remove typefind */
+  gst_pad_disconnect (ident->src, (GstPad*) GST_RPAD_PEER (ident->src));
+  gst_bin_remove (GST_BIN (GST_ELEMENT_PARENT (ident)), typefind);
+  
+  /* set caps */
+  gst_spider_identity_set_caps (ident, caps);
+  
+  /* set new loop function, we gotta empty the cache */
+  gst_element_set_loop_function (GST_ELEMENT (ident), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_sink_loop_emptycache));
+  
+  /* autoplug this pad */
+  gst_spider_plug (ident);  
+  
+  /* restart autoplugger */
+  if (restart_spider)
+    gst_element_set_state (GST_ELEMENT (GST_ELEMENT_PARENT(ident)), GST_STATE_PLAYING);
+  
+}
+/* since we can't set the loop function to NULL if there's a cothread for us,
+ * we have to use a dumb one
+ */
+static void
+gst_spider_identity_dumb_loop  (GstSpiderIdentity *ident)
+{
+  GstBuffer *buf;
+
+  g_return_if_fail (ident != NULL);
+  g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
+  g_assert (ident->sink != NULL);
+
+  buf = gst_pad_pull (ident->sink);
+
+  gst_spider_identity_chain (ident->sink, buf);
+}
+/* do nothing until we're connected - then disable yourself
+ */
+static void
+gst_spider_identity_src_loop (GstSpiderIdentity *ident)
+{
+  /* checks - disable for speed */
+  g_return_if_fail (ident != NULL);
+  g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
+  
+  /* we don't want a loop function if we're plugged */
+  if (ident->sink && GST_PAD_PEER (ident->sink))
+  {
+    gst_element_set_loop_function (GST_ELEMENT (ident), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_dumb_loop));
+    gst_spider_identity_dumb_loop (ident);
+    return;
+  }
+  
+  /* in any case, we don't want to do anything:
+   * - if we're not plugged, we don't have buffers
+   * - if we're plugged, we wanna be chained please
+   */
+  gst_element_interrupt (GST_ELEMENT (ident));
+  return;  
+}
+/* This loop function is only needed when typefinding.
+ * It works quite simple: get a new buffer, append it to the cache
+ * and push it to the typefinder.
+ */
+static void
+gst_spider_identity_sink_loop_typefinding (GstSpiderIdentity *ident)
+{
+  GstBuffer *buf;
+  
+  /* checks - disable for speed */
+  g_return_if_fail (ident != NULL);
+  g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
+  g_assert (ident->sink != NULL);
+  
+  /* get buffer */
+  buf = gst_pad_pull (ident->sink);
+  
+  /* if it's an event... */
+  if (GST_IS_EVENT (buf)) {
+    /* handle DISCONT events, please */
+    gst_pad_event_default (ident->sink, GST_EVENT (buf));
+  } 
+
+  /* add it to the end of the cache */
+  gst_buffer_ref (buf);
+  GST_DEBUG (0, "element %s adds buffer %p (size %d) to cache\n", GST_ELEMENT_NAME(ident),  buf, GST_BUFFER_SIZE (buf));
+  ident->cache_end = g_list_prepend (ident->cache_end, buf);
+  if (ident->cache_start == NULL)
+    ident->cache_start = ident->cache_end;
+  
+  /* push the buffer */
+  gst_spider_identity_chain (ident->sink, buf);
+}
+/* this function is needed after typefinding:
+ * empty the cache and when the cache is empty - remove this function
+ */
+static void
+gst_spider_identity_sink_loop_emptycache (GstSpiderIdentity *ident)
+{
+  GstBuffer *buf;
+  
+  /* get the buffer and push it */
+  buf = GST_BUFFER (ident->cache_start->data);
+  gst_spider_identity_chain (ident->sink, buf);
+  
+  ident->cache_start = g_list_previous (ident->cache_start);
+
+  /* now check if we have more buffers to push */
+  if (ident->cache_start == NULL)
+  {
+    GST_DEBUG(0, "cache from %s is empty, changing loop function\n", GST_ELEMENT_NAME(ident));
+    /* free cache */
+    g_list_free (ident->cache_end);
+    ident->cache_end = NULL;
+    
+    /* remove loop function */
+    gst_element_set_loop_function (GST_ELEMENT (ident), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_dumb_loop));
+    gst_element_interrupt (GST_ELEMENT (ident));
+  }  
+}
+
+
+
diff --git a/gst/autoplug/gstspideridentity.h b/gst/autoplug/gstspideridentity.h
new file mode 100644 (file)
index 0000000..3c5139b
--- /dev/null
@@ -0,0 +1,90 @@
+/* GStreamer
+ * Copyright (C) 2002 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2002 Wim Taymans <wtay@chello.be>
+ *
+ * gstspideridentity.h: 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GST_SPIDER_IDENTITY_H__
+#define __GST_SPIDER_IDENTITY_H__
+
+
+#include <config.h>
+#include <gst/gst.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+GstElementDetails gst_spider_identity_details;
+
+
+#define GST_TYPE_SPIDER_IDENTITY \
+  (gst_spider_identity_get_type())
+#define GST_SPIDER_IDENTITY(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPIDER_IDENTITY,GstSpiderIdentity))
+#define GST_SPIDER_IDENTITY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SPIDER_IDENTITY,GstSpiderIdentityClass))
+#define GST_IS_SPIDER_IDENTITY(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SPIDER_IDENTITY))
+#define GST_IS_SPIDER_IDENTITY_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPIDER_IDENTITY))
+
+typedef struct _GstSpiderIdentity GstSpiderIdentity;
+typedef struct _GstSpiderIdentityClass GstSpiderIdentityClass;
+
+struct _GstSpiderIdentity {
+  GstElement element;
+
+  /* sink and source */
+  GstPad *sink;
+  GstPad *src;
+       
+  /* plugged into autoplugger yet? */
+  gboolean plugged;
+       
+  /* caching */
+  GList *cache_start;
+  GList *cache_end;
+       
+  /* Caps from typefinding */
+  GstCaps *caps;
+
+};
+
+struct _GstSpiderIdentityClass {
+  GstElementClass parent_class;
+
+};
+
+GType                          gst_spider_identity_get_type                    (void);
+
+GstSpiderIdentity*             gst_spider_identity_new_sink                    (gchar *name);
+GstSpiderIdentity*             gst_spider_identity_new_src                     (gchar *name);
+GstPad*                                gst_spider_identity_request_new_pad             (GstElement *element, GstPadTemplate *templ, const gchar *name);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GST_SPIDER_IDENTITY_H__ */
diff --git a/gst/autoplug/spidertest.c b/gst/autoplug/spidertest.c
new file mode 100644 (file)
index 0000000..c5a25ab
--- /dev/null
@@ -0,0 +1,147 @@
+#include <stdlib.h>
+#include <gst/gst.h>
+
+/* 1:1 copy of gstpropsprivate.h, needed for INFO events */
+
+#define GST_PROPS_ENTRY_IS_VARIABLE(a)  (((GstPropsEntry*)(a))->propstype > GST_PROPS_VAR_ID)
+
+typedef struct _GstPropsEntry GstPropsEntry;
+
+struct _GstPropsEntry {
+  GQuark    propid;
+  GstPropsId propstype;    
+
+  union {
+    /* flat values */
+    gboolean bool_data;
+    guint32  fourcc_data;
+    gint     int_data;
+    gfloat   float_data;
+
+    /* structured values */
+    struct {
+      GList *entries;
+    } list_data;
+    struct {
+      gchar *string;
+    } string_data;
+    struct {
+      gint min;
+      gint max;
+    } int_range_data;
+    struct {
+      gfloat min;
+      gfloat max;
+    } float_range_data;
+  } data;
+};
+
+/* end gstpropsprivate.h */
+
+/* property output, stolen from gst-launch */
+static void 
+print_props (gpointer data, gpointer user_data)
+{
+  GstPropsEntry *entry = (GstPropsEntry *)data;
+  GstElement *element = GST_ELEMENT (user_data);
+
+  g_print ("%s: %s: ", gst_element_get_name (element), 
+      g_quark_to_string (entry->propid));
+  switch (entry->propstype) {
+    case GST_PROPS_INT_ID:
+      g_print ("%d\n", entry->data.int_data);
+      break;
+    case GST_PROPS_STRING_ID:
+      g_print ("%s\n", entry->data.string_data.string);
+      break;
+    case GST_PROPS_FLOAT_ID:
+      g_print ("%f\n", entry->data.float_data);
+      break;
+    default:
+      g_print ("unknown\n");
+  }
+}
+
+static void 
+event_func (GstElement *element, GstEvent *event)
+{
+  GstProps *props;
+
+  if (event == NULL)
+    return;
+  
+  if (GST_EVENT_TYPE (event) == GST_EVENT_INFO) {
+    props = GST_EVENT_INFO_PROPS (event);
+
+    g_list_foreach (props->properties, print_props, GST_EVENT_SRC (event));
+  }
+}
+
+/**
+ * Test program for the autoplugger.
+ * Uses new API extensions (2002-01-28), too.
+ *
+ * USAGE: spidertest <mediafile>
+ * If mediafile can be recognized, xvideo and oss audio output are tried.
+ */
+int main(int argc,char *argv[]) 
+{
+  GstElement *bin, *filesrc, *decoder, *osssink, *videosink;
+  
+  if (argc != 2) {
+    g_print("usage: %s <file>\n", argv[0]);
+    exit(-1);
+  }
+
+   gst_init(&argc,&argv);
+
+  /* create a new bin to hold the elements */
+  bin = gst_pipeline_new("pipeline");
+  g_signal_connect (G_OBJECT (bin), "event", G_CALLBACK (event_func), NULL);
+  /* create a disk reader */
+  filesrc = gst_elementfactory_make("filesrc", "disk_source");
+  g_object_set(G_OBJECT(filesrc),"location", argv[1], NULL);
+
+  /* now it's time to get the decoder */
+  decoder = gst_elementfactory_make("spider", "spider");
+  if (!decoder) {
+    g_print ("could not find plugin \"spider\"\n");
+    exit (-2);
+  }
+  
+  /* only use decoding plugins */
+  g_object_set(G_OBJECT(decoder),"plugtype", 2, NULL);
+
+  /* create video and audio sink */
+  osssink = gst_elementfactory_make("osssink", "audio");
+  videosink = gst_elementfactory_make("xvideosink", "video");
+
+  if ((!osssink) || (!videosink)) {
+    g_print ("could not create output plugins\n");
+    exit (-3);
+  }
+
+  /* add objects to the main pipeline */
+  gst_bin_add(GST_BIN(bin), filesrc);
+  gst_bin_add(GST_BIN(bin), decoder);
+  gst_bin_add(GST_BIN(bin), osssink);
+  gst_bin_add(GST_BIN(bin), videosink);
+
+  /* connect objects */
+  if (!(gst_element_connect_elements(filesrc, decoder) &&
+       gst_element_connect_elements(decoder, osssink) &&
+       gst_element_connect_elements(decoder, videosink)))
+  {
+    g_print ("the pipeline could not be connected\n");
+    exit (-4);
+  }
+
+  /* start playing */
+  gst_element_set_state(bin, GST_STATE_PLAYING);
+
+  while (gst_bin_iterate(GST_BIN(bin)));
+
+  exit(0);
+}
+