rewritten pipeline parsing lands. Have fun breaking it.
authorBenjamin Otte <otte@gnome.org>
Tue, 8 Apr 2003 21:59:44 +0000 (21:59 +0000)
committerBenjamin Otte <otte@gnome.org>
Tue, 8 Apr 2003 21:59:44 +0000 (21:59 +0000)
Original commit message from CVS:
rewritten pipeline parsing lands. Have fun breaking it.
regressions:
- No support for filtered links. If anybody needs this, please contact me and I'll add it as fast as possible.

improvements:
- The pipeline building actually works as expected.
- syntax compatible nearly everywhere but more flexible
- better property parsing (you can now set enums by name or nick)
- uses locked_state to allow for delayed pads
- can connect video pipelines without the need for queues
- allows properties on bins
- does not return a bin, but an element.

You may want to read docs/random/company/gstparse to learn how it works.

gst/gstparse.c
gst/gstparse.h
gst/parse/grammar.y
gst/parse/parse.l
gst/parse/types.h

index ec2874a..d893063 100644 (file)
 #include "gstparse.h"
 #include "gstinfo.h"
 #include "gstlog.h"
-#include "parse/types.h"
-
-typedef struct _gst_parse_delayed_pad gst_parse_delayed_pad;
-struct _gst_parse_delayed_pad
-{
-  gchar *name;
-  GstPad *peer;
-};
-
-typedef struct
-{
-  gchar *srcpadname;
-  GstPad *target_pad;
-  GstElement *target_element;
-  GstElement *pipeline;
-}
-dynamic_link_t;
 
+extern GstElement *_gst_parse_launch (const gchar *, GError **);
 
 GQuark 
 gst_parse_error_quark (void)
@@ -54,413 +38,38 @@ gst_parse_error_quark (void)
   return quark;
 }
 
-G_GNUC_UNUSED static void
-dynamic_link (GstElement * element, GstPad * newpad, gpointer data)
-{
-  dynamic_link_t *dc = (dynamic_link_t *) data;
-  gboolean warn = TRUE;
-
-  /* do we know the exact srcpadname? */
-  if (dc->srcpadname) {
-    GstPadTemplate *templ = gst_pad_get_pad_template (newpad);
-
-    /* see if this is the one */
-    if (strcmp (gst_pad_get_name (newpad), dc->srcpadname) && 
-        strcmp (gst_object_get_name (GST_OBJECT (templ)), dc->srcpadname)) {
-      return;
-    }
-  }
-
-  /* try to find a target pad if we don't know it yet */
-  if (!dc->target_pad) {
-    if (!GST_PAD_IS_LINKED (newpad)) {
-      dc->target_pad = gst_element_get_compatible_pad (dc->target_element, newpad);
-      warn = FALSE;
-    }
-    else {
-      return;
-    }
-  }
-  if (!GST_PAD_IS_LINKED (dc->target_pad) && !GST_PAD_IS_LINKED (newpad)) {
-    gst_element_set_state (dc->pipeline, GST_STATE_PAUSED);
-    if (!gst_pad_link (newpad, dc->target_pad) && warn) {
-      g_warning ("could not link %s:%s to %s:%s", GST_DEBUG_PAD_NAME (newpad), 
-                 GST_DEBUG_PAD_NAME (dc->target_pad));
-    }
-    gst_element_set_state (dc->pipeline, GST_STATE_PLAYING);
-  }
-}
-
-static gboolean
-make_elements (graph_t *g, GError **error) 
-{
-  GList *l = NULL;
-  gchar *bin_type;
-  element_t *e;
-  
-  if (!(g->bins || g->elements)) {
-    g_set_error (error,
-                 GST_PARSE_ERROR,
-                 GST_PARSE_ERROR_SYNTAX,
-                 "Empty bin");
-    return FALSE;
-  }
-
-  if (g->current_bin_type)
-    bin_type = g->current_bin_type;
-  else
-    bin_type = "pipeline";
-  
-  if (!(g->bin = gst_element_factory_make (bin_type, NULL))) {
-    g_set_error (error,
-                 GST_PARSE_ERROR,
-                 GST_PARSE_ERROR_NO_SUCH_ELEMENT,
-                 "No such bin type %s", bin_type);
-    return FALSE;
-  }
-  
-  l = g->elements;
-  while (l) {
-    e = (element_t*)l->data;
-    if (!(e->element = gst_element_factory_make (e->type, NULL))) {
-      g_set_error (error,
-                   GST_PARSE_ERROR,
-                   GST_PARSE_ERROR_NO_SUCH_ELEMENT,
-                   "No such element %s", e->type);
-      return FALSE;
-    }
-    gst_bin_add (GST_BIN (g->bin), e->element);
-    l = g_list_next (l);
-  }
-  
-  l = g->bins;
-  while (l) {
-    if (!make_elements ((graph_t*)l->data, error))
-      return FALSE;
-    gst_bin_add (GST_BIN (g->bin), ((graph_t*)l->data)->bin);
-    l = g_list_next (l);
-  }
-
-  return TRUE;
-}
-
-static gboolean
-set_properties (graph_t *g, GError **error)
-{
-  GList *l, *l2;
-  element_t *e;
-  property_t *p;
-  GParamSpec *pspec;
-  
-  l = g->elements;
-  while (l) {
-    e = (element_t*)l->data;
-    l2 = e->property_values;
-    while (l2) {
-      p = (property_t*)l2->data;
-      if ((pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (e->element), p->name))) {
-        g_object_set_property (G_OBJECT (e->element), p->name, p->value);
-      } else {
-        g_set_error (error,
-                     GST_PARSE_ERROR,
-                     GST_PARSE_ERROR_NO_SUCH_PROPERTY,
-                     "No such property '%s' in element '%s'",
-                     p->name, GST_OBJECT_NAME (GST_OBJECT (e->element)));
-        return FALSE;
-      }
-      l2 = g_list_next (l2);
-    }
-    l = g_list_next (l);
-  }
-  
-  l = g->bins;
-  while (l) {
-    if (!set_properties ((graph_t*)l->data, error))
-      return FALSE;
-    l = g_list_next (l);
-  }
-  
-  return TRUE;
-}
-
-static GstElement*
-find_element_by_index_recurse (graph_t *g, gint i)
+static gchar *_gst_parse_escape (const gchar *str)
 {
-  GList *l;
-  element_t *e;
-  GstElement *element;
-  
-  l = g->elements;
-  while (l) {
-    e = (element_t*)l->data;
-    if (e->index == i) {
-      return e->element;
-    }
-    l = g_list_next (l);
-  }
-  
-  l = g->bins;
-  while (l) {
-    if ((element = find_element_by_index_recurse ((graph_t*)l->data, i)))
-      return element;
-    l = g_list_next (l);
-  }
+  GString *gstr = NULL;
   
-  return NULL;
-}
-
-static GstElement*
-find_element_by_index (graph_t *g, gint i) 
-{
-  while (g->parent)
-    g = g->parent;
-
-  return find_element_by_index_recurse (g, i);
-}
-
-static gboolean
-make_links (graph_t *g, GError **error)
-{
-  GList *l, *a, *b;
-  link_t *c;
-  dynamic_link_t *dc;
-  GstElement *src, *sink;
-  GstPad *p1, *p2;
-  GstPadTemplate *pt1, *pt2;
-  GstCaps *caps;
+  g_return_val_if_fail (str != NULL, NULL);
   
-  l = g->links;
-  while (l) {
-    c = (link_t*)l->data;
-    if (c->src_name) {
-      if (!(src = gst_bin_get_by_name (GST_BIN (g->bin), c->src_name))) {
-        g_set_error (error,
-                     GST_PARSE_ERROR,
-                     GST_PARSE_ERROR_NO_SUCH_ELEMENT,
-                     "No such element '%s'",
-                     c->src_name);
-        return FALSE;
-      }
-    } else {
-      src = find_element_by_index (g, c->src_index);
-      g_assert (src);
-    }
-    if (c->sink_name) {
-      if (!(sink = gst_bin_get_by_name (GST_BIN (g->bin), c->sink_name))) {
-        g_set_error (error,
-                     GST_PARSE_ERROR,
-                     GST_PARSE_ERROR_NO_SUCH_ELEMENT,
-                     "No such element '%s'",
-                     c->sink_name);
-        return FALSE;
-      }
-    } else {
-      sink = find_element_by_index (g, c->sink_index);
-      g_assert (sink);
-    }
-    
-    a = c->src_pads;
-    b = c->sink_pads;
-    caps = c->caps;
-    gst_caps_ref (caps);
-    gst_caps_sink (caps);
-
-    gst_caps_debug (caps, "foo");
-    /* g_print ("a: %p, b: %p\n", a, b); */
-    if (a && b) {
-      /* balanced multipad link */
-      while (a && b) {
-        p1 = gst_element_get_pad (src, (gchar*)a->data);
-        p2 = gst_element_get_pad (sink, (gchar*)b->data);
-
-        if (!p2)
-          goto could_not_get_pad_b;
-        
-        if (!p1 && p2 && (pt1 = gst_element_get_pad_template (src, (gchar*)a->data)) &&
-            pt1->presence == GST_PAD_SOMETIMES) {
-          dc = g_new0 (dynamic_link_t, 1);
-          dc->srcpadname = (gchar*)a->data;
-          dc->target_pad = p2;
-          dc->target_element = sink;
-          dc->pipeline = g->bin;
-          
-          GST_DEBUG (GST_CAT_PIPELINE, "setting up dynamic link %s:%s and %s:%s",
-                     GST_OBJECT_NAME (GST_OBJECT (src)),
-                     (gchar*)a->data, GST_DEBUG_PAD_NAME (p2));
-          
-          g_signal_connect (G_OBJECT (src), "new_pad", G_CALLBACK (dynamic_link), dc);
-        } else if (!p1) {
-          goto could_not_get_pad_a;
-        } else if (!gst_pad_link_filtered (p1, p2, caps)) {
-          goto could_not_link_pads;
-        }
-        a = g_list_next (a);
-        b = g_list_next (b);
-      }
-    } else if (a) {
-      if ((pt1 = gst_element_get_pad_template (src, (gchar*)a->data))) {
-        if ((p1 = gst_element_get_pad (src, (gchar*)a->data)) || pt1->presence == GST_PAD_SOMETIMES) {
-          if (!p1) {
-            /* sigh, a hack until i fix the gstelement api... */
-            if ((pt2 = gst_element_get_compatible_pad_template (sink, pt1))) {
-              if ((p2 = gst_element_get_pad (sink, pt2->name_template))) {
-                dc = g_new0 (dynamic_link_t, 1);
-                dc->srcpadname = (gchar*)a->data;
-                dc->target_pad = p2;
-                dc->target_element = NULL;
-                dc->pipeline = g->bin;
-              
-                GST_DEBUG (GST_CAT_PIPELINE, "setting up dynamic link %s:%s and %s:%s",
-                           GST_OBJECT_NAME (GST_OBJECT (src)),
-                           (gchar*)a->data, GST_DEBUG_PAD_NAME (p2));
-              
-                g_signal_connect (G_OBJECT (src), "new_pad", G_CALLBACK (dynamic_link), dc);
-               goto next;
-              } else {
-                /* both pt1 and pt2 are sometimes templates. sheesh. */
-                goto both_templates_have_sometimes_presence;
-              }
-            } else {
-             /* if the target pad has no padtemplate we will figure out a target 
-              * pad later on */
-              dc = g_new0 (dynamic_link_t, 1);
-              dc->srcpadname = (gchar*)a->data;
-              dc->target_pad = NULL;
-              dc->target_element = sink;
-              dc->pipeline = g->bin;
-              
-              GST_DEBUG (GST_CAT_PIPELINE, "setting up dynamic link %s:%s, and some pad in %s",
-                           GST_OBJECT_NAME (GST_OBJECT (src)),
-                           (gchar*)a->data, GST_OBJECT_NAME (sink));
-              
-              g_signal_connect (G_OBJECT (src), "new_pad", G_CALLBACK (dynamic_link), dc);
-             goto next;
-            }
-          } else {
-            goto could_not_get_compatible_to_a;
-          }
-        } else {
-          goto could_not_get_pad_a;
-        }
-      } else {
-        goto could_not_get_pad_a;
-      }
-      
-      if (!gst_pad_link_filtered (p1, p2, caps)) {
-        goto could_not_link_pads;
-      }
-    } else if (b) {
-      /* we don't support dynamic links on this side yet, if ever */
-      if (!(p2 = gst_element_get_pad (sink, (gchar*)b->data))) {
-        goto could_not_get_pad_b;
-      }
-      if (!(p1 = gst_element_get_compatible_pad (src, p2))) {
-        goto could_not_get_compatible_to_b;
-      }
-      if (!gst_pad_link_filtered (p1, p2, caps)) {
-        goto could_not_link_pads;
-      }
-    } else {
-      if (!gst_element_link_filtered (src, sink, caps)) {
-        goto could_not_link_elements;
-      }
-    }
-next:
-    gst_caps_unref (caps);
-    l = g_list_next (l);
-  }
+  gstr = g_string_sized_new (strlen (str));
   
-  l = g->bins;
-  while (l) {
-    if (!make_links ((graph_t*)l->data, error))
-      return FALSE;
-    l = g_list_next (l);
+  while (*str) {
+    if (*str == ' ')
+      g_string_append_c (gstr, '\\');
+    g_string_append_c (gstr, *str);
+    str++;
   }
   
-  return TRUE;
-
-could_not_get_pad_a:
-  g_set_error (error,
-               GST_PARSE_ERROR,
-               GST_PARSE_ERROR_LINK,
-               "Could not get a pad %s from element %s",
-               (gchar*)a->data, GST_OBJECT_NAME (src));
-  return FALSE;
-could_not_get_pad_b:
-  g_set_error (error,
-               GST_PARSE_ERROR,
-               GST_PARSE_ERROR_LINK,
-               "Could not get a pad %s from element %s",
-               (gchar*)b->data, GST_OBJECT_NAME (sink));
-  return FALSE;
-could_not_get_compatible_to_a:
-  g_set_error (error,
-               GST_PARSE_ERROR,
-               GST_PARSE_ERROR_LINK,
-               "Could not find a compatible pad in element %s to for %s:%s",
-               GST_OBJECT_NAME (sink), GST_OBJECT_NAME (src), (gchar*)a->data);
-  return FALSE;
-could_not_get_compatible_to_b:
-  g_set_error (error,
-               GST_PARSE_ERROR,
-               GST_PARSE_ERROR_LINK,
-               "Could not find a compatible pad in element %s to for %s:%s",
-               GST_OBJECT_NAME (src), GST_OBJECT_NAME (sink), (gchar*)b->data);
-  return FALSE;
-both_templates_have_sometimes_presence:
-  g_set_error (error,
-               GST_PARSE_ERROR,
-               GST_PARSE_ERROR_LINK,
-               "Both %s:%s and %s:%s have GST_PAD_SOMETIMES presence, operation not supported",
-               GST_OBJECT_NAME (src), pt1->name_template, GST_OBJECT_NAME (sink), pt2->name_template);
-  return FALSE;
-could_not_link_pads:
-  g_set_error (error,
-               GST_PARSE_ERROR,
-               GST_PARSE_ERROR_LINK,
-               "Could not link %s:%s to %s:%s",
-               GST_DEBUG_PAD_NAME (p1),
-               GST_DEBUG_PAD_NAME (p2));
-  return FALSE;
-could_not_link_elements:
-  g_set_error (error,
-               GST_PARSE_ERROR,
-               GST_PARSE_ERROR_LINK,
-               "Could not link element %s to %s",
-               GST_OBJECT_NAME (src),
-               GST_OBJECT_NAME (sink));
-  return FALSE;
-}
-
-static GstBin*
-pipeline_from_graph (graph_t *g, GError **error)
-{
-  if (!make_elements (g, error))
-    return NULL;
-  
-  if (!set_properties (g, error))
-    return NULL;
-  
-  if (!make_links (g, error))
-    return NULL;
-  
-  return (GstBin*)g->bin;
+  return gstr->str;
 }
-
 /**
  * gst_parse_launchv:
  * @argv: null-terminated array of arguments
- * @error: pointer to GError
+ * @error: pointer to a #GError
  *
- * Create a new pipeline based on command line syntax.
+ * Create a new element based on command line syntax.
+ * #error will contain an error message if pipeline creation fails and can
+ * contain an error message, when a recoverable error happened.
  *
- * Returns: a new pipeline on success, NULL on failure and error
- * will contain the error message.
+ * Returns: a new element on success and NULL on failure.
  */
-GstBin *
+GstElement *
 gst_parse_launchv (const gchar **argv, GError **error)
 {
-  GstBin *pipeline;
+  GstElement *element;
   GString *str;
   const gchar **argvp, *arg;
   gchar *tmp;
@@ -480,47 +89,11 @@ gst_parse_launchv (const gchar **argv, GError **error)
     argvp++;
   }
   
-  pipeline = gst_parse_launch (str->str, error);
+  element = gst_parse_launch (str->str, error);
 
   g_string_free (str, TRUE);
 
-  return pipeline;
-}
-
-gchar *_gst_parse_escape (const gchar *str)
-{
-  GString *gstr = NULL;
-  
-  g_return_val_if_fail (str != NULL, NULL);
-  
-  gstr = g_string_sized_new (strlen (str));
-  
-  while (*str) {
-    if (*str == ' ')
-      g_string_append_c (gstr, '\\');
-    g_string_append_c (gstr, *str);
-    str++;
-  }
-  
-  return gstr->str;
-}
-
-void _gst_parse_unescape (gchar *str)
-{
-  gchar *walk;
-  
-  g_return_if_fail (str != NULL);
-  
-  walk = str;
-  
-  while (*walk) {
-    if (*walk == '\\')
-      walk++;
-    *str = *walk;
-    str++;
-    walk++;
-  }
-  *str = '\0';
+  return element;
 }
 
 /**
@@ -533,10 +106,10 @@ void _gst_parse_unescape (gchar *str)
  * Returns: a new bin on success, NULL on failure. By default the bin is
  * a GstPipeline, but it depends on the pipeline_description.
  */
-GstBin *
+GstElement *
 gst_parse_launch (const gchar * pipeline_description, GError **error)
 {
-  graph_t *graph;
+  GstElement *element;
   static GStaticMutex flex_lock = G_STATIC_MUTEX_INIT;
 
   g_return_val_if_fail (pipeline_description != NULL, NULL);
@@ -546,11 +119,8 @@ gst_parse_launch (const gchar * pipeline_description, GError **error)
 
   /* the need for the mutex will go away with flex 2.5.6 */
   g_static_mutex_lock (&flex_lock);
-  graph = _gst_parse_launch (pipeline_description, error);
+  element = _gst_parse_launch (pipeline_description, error);
   g_static_mutex_unlock (&flex_lock);
 
-  if (!graph)
-    return NULL;
-  
-  return pipeline_from_graph (graph, error);
+  return element;
 }
index a56f75c..e647bfa 100644 (file)
@@ -37,12 +37,13 @@ typedef enum
   GST_PARSE_ERROR_SYNTAX,
   GST_PARSE_ERROR_NO_SUCH_ELEMENT,
   GST_PARSE_ERROR_NO_SUCH_PROPERTY,
-  GST_PARSE_ERROR_LINK
+  GST_PARSE_ERROR_LINK,
+  GST_PARSE_ERROR_COULD_NOT_SET_PROPERTY,
 } GstParseError;
 
 
-GstBin*                gst_parse_launch        (const gchar *pipeline_description, GError **error);
-GstBin*                gst_parse_launchv       (const gchar **argv, GError **error);
+GstElement*    gst_parse_launch        (const gchar *pipeline_description, GError **error);
+GstElement*    gst_parse_launchv       (const gchar **argv, GError **error);
 
 #else /* GST_DISABLE_PARSE */
 
index a86531a..370fad4 100644 (file)
 %{
+#include <glib-object.h>
 #include <glib.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include "../gstparse.h"
+#include "../gstinfo.h"
 #include "types.h"
 
-#define YYDEBUG 1
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #define YYERROR_VERBOSE 1
-#define YYPARSE_PARAM pgraph
+#define YYPARSE_PARAM graph
+
+#ifdef __GST_PARSE_TRACE
+static uint __strings;
+static uint __links;
+static uint __chains;
+gchar *
+__gst_parse_strdup (gchar *org)
+{
+  __strings++;
+  /* g_print ("ALLOCATED: %p %s\n", org, org); */
+  return g_strdup (org);
+}
+void
+__gst_parse_strfree (gchar *str)
+{
+  if (str) {
+    /* g_print ("FREEING  : %p %s\n", str, str); */
+    g_free (str);
+    g_return_if_fail (__strings > 0);
+    __strings--;
+  }
+}
+link_t *__gst_parse_link_new ()
+{
+  __links++;
+  return g_new0 (link_t, 1);
+}
+void
+__gst_parse_link_free (link_t *data)
+{
+  if (data) {
+    g_free (data);
+    g_return_if_fail (__links > 0);
+    __links--;
+  }
+}
+chain_t *
+__gst_parse_chain_new ()
+{
+  __chains++;
+  return g_new0 (chain_t, 1);
+}
+void
+__gst_parse_chain_free (chain_t *data)
+{
+  if (data) {
+    g_free (data);
+    g_return_if_fail (__chains > 0);
+    __chains--;
+  }
+}
+
+#endif /* __GST_PARSE_TRACE */
+
+typedef struct {
+  gchar *src_pad;
+  gchar *sink_pad;
+  GstElement *sink;
+  GstCaps *caps;
+  gulong signal_id;
+  /* FIXME: need to connect to "disposed" signal to clean up, but there is no such signal */
+} DelayedLink;
+
+#ifdef G_HAVE_ISO_VARARGS
+#define SET_ERROR(error, type, ...) G_STMT_START{ \
+  if (error) { \
+    if (*(error)) { \
+      g_warning (__VA_ARGS__); \
+    } else { \
+      g_set_error ((error), GST_PARSE_ERROR, (type), __VA_ARGS__); \
+    }\
+  } \
+}G_STMT_END
+#define ERROR(type, ...) SET_ERROR (((graph_t *) graph)->error, (type), __VA_ARGS__ )
+#ifdef GST_DEBUG_ENABLED
+#  define YYDEBUG 1
+#  define YYFPRINTF(a, ...) GST_DEBUG (GST_CAT_PIPELINE, __VA_ARGS__)
+#endif
+
+#elif defined(G_HAVE_GNUC_VARARGS)
+
+#define SET_ERROR(error, type, args...) G_STMT_START{ \
+  if (error) { \
+    if (*(error)) { \
+      g_warning ( ## args ); \
+    } else { \
+      g_set_error ((error), GST_PARSE_ERROR, (type), ## args ); \
+    }\
+  } \
+}G_STMT_END
+#define ERROR(type, args...) SET_ERROR (((graph_t *) graph)->error, (type), ## args )
+#ifdef GST_DEBUG_ENABLED
+#  define YYDEBUG 1
+#  define YYFPRINTF(a, args...) GST_DEBUG (GST_CAT_PIPELINE, ## args )
+#endif
+
+#else
+
+#define SET_ERROR(error, type, ...) G_STMT_START{ \
+  if (error) { \
+    if (*(error)) { \
+      g_warning ("error while parsing"); \
+    } else { \
+      g_set_error ((error), GST_PARSE_ERROR, (type), "error while parsing"); \
+    }\
+  } \
+}G_STMT_END
+#define ERROR(type, ...) SET_ERROR (((graph_t *) graph)->error, (type), "error while parsing")
+#ifdef GST_DEBUG_ENABLED
+#  define YYDEBUG 1
+#endif
+
+#endif // G_HAVE_ISO_VARARGS
+
+#define GST_BIN_MAKE(res, type, chain, assign) G_STMT_START{ \
+  GSList *walk; \
+  GstBin *bin = (GstBin *) gst_element_factory_make (type, NULL); \
+  if (!bin) { \
+    ERROR (GST_PARSE_ERROR_NO_SUCH_ELEMENT, "No bin \"%s\"", type); \
+  } else { \
+    walk = chain->elements; \
+    while (walk) { \
+      gst_bin_add (bin, GST_ELEMENT (walk->data)); \
+      walk = walk->next; \
+    } \
+    g_slist_free (chain->elements); \
+    chain->elements = g_slist_prepend (NULL, bin); \
+    res = chain; \
+    /* set the properties now */ \
+    walk = assign; \
+    while (walk) { \
+      gst_parse_element_set ((gchar *) walk->data, GST_ELEMENT (bin), graph); \
+      walk = g_slist_next (walk); \
+    } \
+    g_slist_free (assign); \
+  } \
+}G_STMT_END
+
+#define MAKE_LINK(link, _src, _src_name, _src_pads, _sink, _sink_name, _sink_pads) G_STMT_START{ \
+  link = gst_parse_link_new (); \
+  link->src = _src; \
+  link->sink = _sink; \
+  link->src_name = _src_name; \
+  link->sink_name = _sink_name; \
+  link->src_pads = _src_pads; \
+  link->sink_pads = _sink_pads; \
+  link->caps = NULL; \
+}G_STMT_END
+
+#define MAKE_REF(link, _src, _pads) G_STMT_START{ \
+  gchar *padname = _src; \
+  GSList *pads = _pads; \
+  if (padname) { \
+    while (*padname != '.') padname++; \
+    *padname = '\0'; \
+    padname++; \
+    if (*padname != '\0') \
+      pads = g_slist_prepend (pads, gst_parse_strdup (padname)); \
+  } \
+  MAKE_LINK (link, NULL, _src, pads, NULL, NULL, NULL); \
+}G_STMT_END
+
+static inline void gst_parse_unescape (gchar *str)
+{
+  gchar *walk;
+  
+  g_return_if_fail (str != NULL);
+  
+  walk = str;
+  
+  while (*walk) {
+    if (*walk == '\\')
+      walk++;
+    *str = *walk;
+    str++;
+    walk++;
+  }
+  *str = '\0';
+}
+static void
+gst_parse_element_set (gchar *value, GstElement *element, graph_t *graph)
+{
+  GParamSpec *pspec;
+  gchar *pos = value;
+  /* parse the string, so the property name is null-terminated an pos points
+     to the beginning of the value */
+  while (!g_ascii_isspace (*pos) && (*pos != '=')) pos++; 
+  if (*pos == '=') { 
+    *pos = '\0'; 
+  } else { 
+    *pos = '\0'; 
+    pos++;
+    while (g_ascii_isspace (*pos)) pos++; 
+  } 
+  pos++; 
+  while (g_ascii_isspace (*pos)) pos++; 
+  if (*pos == '"') {
+    pos++;
+    pos[strlen (pos) - 1] = '\0';
+  }
+  gst_parse_unescape (pos); 
+  if ((pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), value))) { 
+    GValue v = { 0, }; 
+    GValue v2 = { 0, };
+    g_value_init (&v, G_PARAM_SPEC_VALUE_TYPE(pspec)); 
+    switch (G_TYPE_FUNDAMENTAL (G_PARAM_SPEC_VALUE_TYPE (pspec))) {
+    case G_TYPE_STRING:
+      g_value_set_string (&v, pos);
+      break;      
+    case G_TYPE_BOOLEAN:
+      if (g_ascii_strcasecmp (pos, "true") && g_ascii_strcasecmp (pos, "yes") && g_ascii_strcasecmp (pos, "1")) {
+        g_value_set_boolean (&v, FALSE);
+      } else {
+        g_value_set_boolean (&v, TRUE);
+      }
+      break;
+    case G_TYPE_ENUM: {
+      GEnumValue *en;
+      gchar **endptr;
+      GEnumClass *klass = (GEnumClass *) g_type_class_peek (G_PARAM_SPEC_VALUE_TYPE (pspec));
+      if (klass == NULL) goto error;
+      if (!(en = g_enum_get_value_by_name (klass, pos)))
+        en = g_enum_get_value_by_nick (klass, pos);
+      if (en) {
+        g_value_set_enum (&v, en->value);
+      } else {
+        gint i = strtol (value, endptr, 0);
+       if (**endptr == '\0') {
+          g_value_set_enum (&v, i);
+       } else {
+         goto error;
+       } 
+      }        
+      break;
+    }
+    case G_TYPE_INT:
+    case G_TYPE_LONG:
+    case G_TYPE_INT64:
+      g_value_init (&v2, G_TYPE_LONG); 
+      g_value_set_long (&v2, strtol (pos, NULL, 0));
+      if (!g_value_transform (&v2, &v)) goto error;
+      break;      
+    case G_TYPE_UINT:
+    case G_TYPE_ULONG:
+    case G_TYPE_UINT64:
+      g_value_init (&v2, G_TYPE_ULONG); 
+      g_value_set_long (&v2, strtoul (pos, NULL, 0));
+      if (!g_value_transform (&v2, &v)) goto error;
+      break;
+    case G_TYPE_FLOAT:
+    case G_TYPE_DOUBLE:
+      g_value_init (&v2, G_TYPE_DOUBLE); 
+      g_value_set_double (&v2, atof (pos));
+      if (!g_value_transform (&v2, &v)) goto error;
+      break;
+    default:
+      /* add more */
+      g_warning ("property \"%s\" in element %s cannot be set", value, GST_ELEMENT_NAME (element)); 
+      goto error;
+    }
+    g_object_set_property (G_OBJECT (element), value, &v); 
+  } else { 
+    ERROR (GST_PARSE_ERROR_NO_SUCH_PROPERTY, "No property \"%s\" in element \"%s\"", value, GST_ELEMENT_NAME (element)); 
+  }
+
+out:
+  gst_parse_strfree (value);
+  return;
+  
+error:
+  ERROR (GST_PARSE_ERROR_COULD_NOT_SET_PROPERTY, "Could not set property \"%s\" in element \"%s\" to \"%s\"", value, GST_ELEMENT_NAME (element), pos); 
+  goto out;
+}
+static inline void
+gst_parse_free_link (link_t *link)
+{
+  gst_parse_strfree (link->src_name);
+  gst_parse_strfree (link->sink_name);
+  g_slist_foreach (link->src_pads, (GFunc) gst_parse_strfree, NULL);
+  g_slist_foreach (link->sink_pads, (GFunc) gst_parse_strfree, NULL);
+  g_slist_free (link->src_pads);
+  g_slist_free (link->sink_pads);
+  gst_caps_unref (link->caps);
+  gst_parse_link_free (link);  
+}
+static void
+gst_parse_element_lock (GstElement *element, gboolean lock)
+{
+  GstPad *pad;
+  GList *walk = (GList *) gst_element_get_pad_list (element);
+  gboolean unlocked_peer = FALSE;
+  
+  if (gst_element_is_state_locked (element) == lock)
+    return;
+  /* check if we have an unlocked peer */
+  while (walk) {
+    pad = (GstPad *) GST_PAD_REALIZE (walk->data);
+    walk = walk->next;
+    if (GST_PAD_IS_SINK (pad) && GST_PAD_PEER (pad) &&
+        !gst_element_is_state_locked (GST_PAD_PARENT (GST_PAD_PEER (pad)))) {
+      unlocked_peer = TRUE;
+      break;
+    }
+  }  
+  
+  if (lock && !unlocked_peer) {
+    gst_element_lock_state (element);
+  } else if (!lock) {
+    gst_element_unlock_state (element);
+  } else {
+    return;
+  }
+  
+  /* check if there are other pads to (un)lock */
+  walk = (GList *) gst_element_get_pad_list (element);
+  while (walk) {
+    pad = (GstPad *) GST_PAD_REALIZE (walk->data);
+    walk = walk->next;
+    if (GST_PAD_IS_SRC (pad) && GST_PAD_PEER (pad)) {
+      GstElement *next = GST_ELEMENT (GST_OBJECT_PARENT (GST_PAD_PEER (pad)));
+      if (gst_element_is_state_locked (next) != lock)
+        gst_parse_element_lock (next, lock);
+    }
+  }
+}
+static void
+gst_parse_found_pad (GstElement *src, GstPad *pad, gpointer data)
+{
+  DelayedLink *link = (DelayedLink *) data;
+  gboolean restart = FALSE;
+  
+  GST_INFO (GST_CAT_PIPELINE, "trying delayed linking %s:%s to %s:%s", 
+            GST_ELEMENT_NAME (src), link->src_pad,
+            GST_ELEMENT_NAME (link->sink), link->sink_pad);
+  if (gst_element_get_state (src) == GST_STATE_PLAYING) {
+    restart = TRUE;
+    gst_element_set_state (src, GST_STATE_PAUSED);
+  }
+  
+  if (gst_element_link_pads_filtered (src, link->src_pad, link->sink, link->sink_pad, link->caps)) {
+    /* do this here, we don't want to get any problems later on when unlocking states */
+    GST_DEBUG (GST_CAT_PIPELINE, "delayed linking %s:%s to %s:%s worked", 
+               GST_ELEMENT_NAME (src), link->src_pad,
+               GST_ELEMENT_NAME (link->sink), link->sink_pad);
+    if (restart) {
+      gst_element_set_state (src, GST_STATE_PLAYING);
+    }
+    g_signal_handler_disconnect (src, link->signal_id);
+    g_free (link->src_pad);
+    g_free (link->sink_pad);
+    gst_caps_unref (link->caps);
+    if (!gst_element_is_state_locked (src))
+      gst_parse_element_lock (link->sink, FALSE);
+    g_free (link);
+  } else {
+    if (restart) {
+      gst_element_set_state (src, GST_STATE_PLAYING);
+    }
+  }
+}
+/* both padnames and the caps may be NULL */
+static gboolean
+gst_parse_perform_delayed_link (GstElement *src, const gchar *src_pad, 
+                                GstElement *sink, const gchar *sink_pad, GstCaps *caps)
+{
+  GList *templs = gst_element_get_pad_template_list (src);
+        
+  while (templs) {
+    GstPadTemplate *templ = (GstPadTemplate *) templs->data;
+    if ((GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC) && (GST_PAD_TEMPLATE_PRESENCE(templ) == GST_PAD_SOMETIMES))
+    {
+      /* TODO: maybe we should check if src_pad matches this template's names */
+
+      GST_DEBUG (GST_CAT_PIPELINE, "trying delayed link %s:%s to %s:%s", 
+                 GST_ELEMENT_NAME (src), src_pad, GST_ELEMENT_NAME (sink), sink_pad);
+
+      DelayedLink *data = g_new (DelayedLink, 1);
+      data->src_pad = g_strdup (src_pad);
+      data->sink = sink;
+      data->sink_pad = g_strdup (sink_pad);
+      data->caps = gst_caps_ref (caps);
+      data->signal_id = g_signal_connect (G_OBJECT (src), "new_pad", 
+                                         G_CALLBACK (gst_parse_found_pad), data);
+      return TRUE;
+    }
+    templs = g_list_next (templs);
+  }
+  return FALSE;
+}
+/**
+ * performs a link and frees the struct. src and sink elements must be given
+ * return values:  0 - link performed
+ *                 1 - link delayed
+ *                <0 - error
+ */
+static gint
+gst_parse_perform_link (link_t *link, graph_t *graph)
+{
+  GstElement *src = link->src;
+  GstElement *sink = link->sink;
+  GSList *srcs = link->src_pads;
+  GSList *sinks = link->sink_pads;
+  g_assert (GST_IS_ELEMENT (src));
+  g_assert (GST_IS_ELEMENT (sink));
+  
+  GST_INFO (GST_CAT_PIPELINE, "linking %s(%s):%u to %s(%s):%u", 
+            GST_ELEMENT_NAME (src), link->src_name ? link->src_name : "---", g_slist_length (srcs),
+            GST_ELEMENT_NAME (sink), link->sink_name ? link->sink_name : "---", g_slist_length (sinks));
+  
+  if (!srcs || !sinks) {
+    if (gst_element_link_pads_filtered (src, srcs ? (const gchar *) srcs->data : NULL,
+                                        sink, sinks ? (const gchar *) sinks->data : NULL,
+                                       link->caps)) {
+      gst_parse_element_lock (sink, gst_element_is_state_locked (src));
+      goto success;
+    } else {
+      if (gst_parse_perform_delayed_link (src, srcs ? (const gchar *) srcs->data : NULL,
+                                          sink, sinks ? (const gchar *) sinks->data : NULL,
+                                         link->caps)) {
+        gst_parse_element_lock (sink, TRUE);
+       goto success;
+      } else {
+        goto error;
+      }
+    }
+  }
+  if (g_slist_length (link->src_pads) != g_slist_length (link->src_pads)) {
+    goto error;
+  }
+  while (srcs && sinks) {
+    const gchar *src_pad = (const gchar *) srcs->data;
+    const gchar *sink_pad = (const gchar *) sinks->data;
+    srcs = g_slist_next (srcs);
+    sinks = g_slist_next (sinks);
+    if (gst_element_link_pads_filtered (src, src_pad, sink, sink_pad, link->caps)) {
+      gst_parse_element_lock (sink, gst_element_is_state_locked (src));
+      continue;
+    } else {
+      if (gst_parse_perform_delayed_link (src, src_pad,
+                                          sink, sink_pad,
+                                         link->caps)) {
+        gst_parse_element_lock (sink, TRUE);
+       continue;
+      } else {
+        goto error;
+      }
+    }
+  }
+  
+success:
+  gst_parse_free_link (link);
+  return 0;
+  
+error:
+  ERROR (GST_PARSE_ERROR_LINK, "could not link %s to %s", GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (sink));
+  gst_parse_free_link (link);
+  return -1;
+}
+
 
 static int yylex (void *lvalp);
 static int yyerror (const char *s);
@@ -16,113 +480,197 @@ static int yyerror (const char *s);
 
 %union {
     gchar *s;
-    GValue *v;
+    chain_t *c;
+    link_t *l;
+    GstElement *e;
+    GSList *p;
     graph_t *g;
-    link_t *c;
-    property_t *p;
-    element_t *e;
 }
 
 %token <s> IDENTIFIER
-%token <c> LINK BLINK FLINK
-%token <v> VALUE
+%left <s> REF PADREF BINREF
+%token <s> ASSIGNMENT
 
-%type <s> id
-%type <g> graph bin
+%type <g> graph
+%type <c> chain bin
+%type <l> reference
+%type <l> linkpart link
+%type <p> linklist
 %type <e> element
-%type <p> property_value value
-%type <c> link rlink
+%type <p> padlist pads assignments
+
 
 %left '{' '}' '(' ')'
-%left '!' '='
 %left ','
-%left '.'
+%right '.'
+%left '!' '='
 
 %pure_parser
 
 %start graph
 %%
 
-id:     IDENTIFIER
-        ;
-
-value:          VALUE                { $$ = g_new0 (property_t, 1); $$->value = $1; }
-        ;
-
-property_value: id '=' value         { $$ = $3; $$->name = $1; }
-        ;
-
-element:        id                   { static int i = 0; $$ = g_new0 (element_t, 1);
-                                       $$->type = $1; $$->index = ++i; }
-        ;
-
-graph:          /* empty */          { $$ = g_new0 (graph_t, 1); *((graph_t**) pgraph) = $$; }
-        |       graph element        { GList *l;
-                                       $$ = $1; l = $$->links_pending;
-                                       $$->elements = g_list_append ($$->elements, $2);
-                                       $$->current = $2;
-                                       if (!$$->first)
-                                           $$->first = $$->current;
-                                       while (l) {
-                                           ((link_t*) l->data)->sink_index = $$->current->index;
-                                           l = g_list_next (l);
-                                       }
-                                       if ($$->links_pending) {
-                                           g_list_free ($$->links_pending);
-                                           $$->links_pending = NULL;
-                                       }
-                                     }
-        |       graph bin            { GList *l; $$ = $1; l = $$->links_pending;
-                                       *((graph_t**) pgraph) = $$;
-                                       $$->bins = g_list_append ($$->bins, $2);
-                                       $2->parent = $$;
-                                       $$->current = $2->first;
-                                       if (!$$->first)
-                                           $$->first = $$->current;
-                                       while (l) {
-                                           ((link_t*) l->data)->sink_index = $$->current->index;
-                                           l = g_list_next (l);
-                                       }
-                                       if ($$->links_pending) {
-                                           g_list_free ($$->links_pending);
-                                           $$->links_pending = NULL;
-                                       }
-                                       $$->current = $2->current;
-                                     }
-        |       graph link     { $$ = $1;
-                                       $$->links = g_list_append ($$->links, $2);
-                                      if ($$->current)
-                                         $2->src_index = $$->current->index;
-                                       if (!$2->sink_name)
-                                           $$->links_pending = g_list_append ($$->links_pending, $2);
-                                     }
-        |       graph property_value { $$ = $1;
-                                       if (!$$->current) {
-                                           fprintf (stderr, "error: property value assignments must be preceded by an element definition\n");
-                                           YYABORT;
-                                       }
-                                       $$->current->property_values = g_list_append ($$->current->property_values,
-                                                                                     $2);
-                                     }
-        ;
-
-bin:            '{' graph '}'        { $$ = $2; $$->current_bin_type = "thread"; }
-        |       id '.' '(' graph ')' { $$ = $4; $$->current_bin_type = $1; }
-        ;
-
-link:     LINK
-        |       rlink
-        ;
-
-rlink:   '!'                   { $$ = g_new0 (link_t, 1); }
-        |       BLINK          { $$ = $1; }
-        |       FLINK          { $$ = $1; }
-        |       id ',' rlink ',' id 
-                                     { $$ = $3;
-                                       $$->src_pads = g_list_prepend ($$->src_pads, $1);
-                                       $$->sink_pads = g_list_append ($$->sink_pads, $5);
-                                     }
-        ;
+element:       IDENTIFIER                    { $$ = gst_element_factory_make ($1, NULL); 
+                                               if (!$$) ERROR (GST_PARSE_ERROR_NO_SUCH_ELEMENT, "No element \"%s\"", $1);
+                                               gst_parse_strfree ($1);
+                                              }
+       |       element ASSIGNMENT            { gst_parse_element_set ($2, $1, graph);
+                                               $$ = $1;
+                                             }
+       ;
+       
+assignments:   /* NOP */                     { $$ = NULL; }
+       |       assignments ASSIGNMENT        { $$ = g_slist_prepend ($1, $2); }
+                       
+bin:           '{' assignments chain '}'     { GST_BIN_MAKE ($$, "thread", $3, $2); }
+        |       '(' assignments chain ')'     { GST_BIN_MAKE ($$, "bin", $3, $2); }
+        |       BINREF assignments chain ')'  { GST_BIN_MAKE ($$, $1, $3, $2); 
+                                               gst_parse_strfree ($1);
+                                             }
+       ;
+       
+pads:          PADREF                        { $$ = g_slist_prepend (NULL, $1); }
+       |       PADREF padlist                { $$ = $2;
+                                               $$ = g_slist_prepend ($$, $1);
+                                             }
+
+padlist:       ',' IDENTIFIER                { $$ = g_slist_prepend (NULL, $2); }
+       |       ',' IDENTIFIER padlist        { $$ = g_slist_prepend ($3, $2); }
+       ;
+       
+reference:     REF                           { MAKE_REF ($$, $1, NULL); }
+       |       REF padlist                   { MAKE_REF ($$, $1, $2); }
+       ;
+
+linkpart:      reference                     { $$ = $1; }
+       |       pads                          { MAKE_REF ($$, NULL, $1); }
+       |       /* NOP */                     { MAKE_REF ($$, NULL, NULL); }
+       ;
+       
+link:          linkpart '!' linkpart         { $$ = $1;
+                                               $$->sink_name = $3->src_name;
+                                               $$->sink_pads = $3->src_pads;
+                                               gst_parse_link_free ($3);
+                                             }
+       ;
+       
+linklist:      link                          { $$ = g_slist_prepend (NULL, $1); }
+       |       link linklist                 { $$ = g_slist_prepend ($2, $1); }
+       ;       
+       
+chain:         element                       { $$ = gst_parse_chain_new ();
+                                               $$->first = $$->last = $1;
+                                               $$->front = $$->back = NULL;
+                                               $$->elements = g_slist_prepend (NULL, $1);
+                                             }
+       |       bin                           { $$ = $1; }
+       |       chain chain                   { if ($1->back && $2->front) {
+                                                 if (!$1->back->sink_name) {
+                                                   ERROR (GST_PARSE_ERROR_LINK, "link without source element");
+                                                   gst_parse_free_link ($1->back);
+                                                 } else {
+                                                   ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $1->back);
+                                                 }
+                                                 if (!$2->front->src_name) {
+                                                   ERROR (GST_PARSE_ERROR_LINK, "link without sink element");
+                                                   gst_parse_free_link ($2->front);
+                                                 } else {
+                                                   ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $2->front);
+                                                 }
+                                                 $1->back = NULL;
+                                               } else if ($1->back) {
+                                                 if (!$1->back->sink_name) {
+                                                   $1->back->sink = $2->first;
+                                                 } else {
+                                                   ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $1->back);
+                                                   $1->back = NULL;
+                                                 }
+                                               } else if ($2->front) {
+                                                 if (!$2->front->src_name) {
+                                                   $2->front->src = $1->last;
+                                                   $1->back = $2->front;
+                                                 } else {
+                                                   ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $2->front);
+                                                 }
+                                               }
+                                               
+                                               if ($1->back)
+                                                 gst_parse_perform_link ($1->back, (graph_t *) graph);
+                                               $1->last = $2->last;
+                                               $1->back = $2->back;
+                                               $1->elements = g_slist_concat ($1->elements, $2->elements);
+                                               gst_parse_chain_free ($2);
+                                               $$ = $1;
+                                             }
+       |       link chain                    { if ($2->front) {
+                                                 if (!$2->front->src_name) {
+                                                   ERROR (GST_PARSE_ERROR_LINK, "link without source element");
+                                                   gst_parse_free_link ($2->front);
+                                                 } else {
+                                                   ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $2->front);
+                                                 }
+                                               }
+                                               if (!$1->sink_name) {
+                                                 $1->sink = $2->first;
+                                               }
+                                               $2->front = $1;
+                                               $$ = $2;
+                                             }
+                                       
+       |       chain linklist                { GSList *walk;
+                                               if ($1->back) {
+                                                 g_slist_prepend ($2, $1->back);
+                                                 $1->back = NULL;
+                                               } else {
+                                                 if (!((link_t *) $2->data)->src_name) {
+                                                   ((link_t *) $2->data)->src = $1->last;
+                                                 }                                               
+                                               }
+                                               walk = $2;
+                                               while (walk) {
+                                                 link_t *link = (link_t *) walk->data;
+                                                 walk = walk->next;
+                                                 if (!link->sink_name) {
+                                                   ERROR (GST_PARSE_ERROR_LINK, "link without sink element");
+                                                   gst_parse_free_link (link);
+                                                 } else if (!link->src_name && !link->src) {
+                                                   ERROR (GST_PARSE_ERROR_LINK, "link without source element");
+                                                   gst_parse_free_link (link);
+                                                 } else {
+                                                   if (walk) {
+                                                     ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, link);
+                                                   } else {
+                                                     $1->back = link;
+                                                   }
+                                                 }
+                                               }
+                                               g_slist_free ($2);
+                                               $$ = $1;
+                                             }
+       ;
+       
+graph:         chain                         { $$ = (graph_t *) graph;
+                                               if ($1->front) {
+                                                 if (!$1->front->src_name) {
+                                                   ERROR (GST_PARSE_ERROR_LINK, "link without source element");
+                                                   gst_parse_free_link ($1->front);
+                                                 } else {
+                                                   $$->links = g_slist_prepend ($$->links, $1->front);
+                                                 }
+                                                 $1->front = NULL;
+                                               }
+                                               if ($1->back) {
+                                                 if (!$1->back->sink_name) {
+                                                   ERROR (GST_PARSE_ERROR_LINK, "link without sink element");
+                                                   gst_parse_free_link ($1->back);
+                                                 } else {
+                                                   $$->links = g_slist_prepend ($$->links, $1->back);
+                                                 }
+                                                 $1->back = NULL;
+                                               }
+                                               $$->chain = $1;
+                                             }
+       ;
 
 %%
 
@@ -136,45 +684,141 @@ static int yylex (void *lvalp) {
 static int
 yyerror (const char *s)
 {
-  fprintf (stderr, "error: %s\n", s);
+  /* FIXME: This should go into the GError somehow, but how? */
+  g_warning ("error: %s\n", s);
   return -1;
 }
 
 int _gst_parse_yy_scan_string (char*);
-
-graph_t * _gst_parse_launch (const gchar *str, GError **error)
+GstElement *
+_gst_parse_launch (const gchar *str, GError **error)
 {
-    graph_t *g = NULL;
-    gchar *dstr;
-    
-    g_return_val_if_fail (str != NULL, NULL);
+  graph_t g;
+  gchar *dstr;
+  GSList *walk;
+  GstBin *bin = NULL;
+  GstElement *ret;
+  
+  g_return_val_if_fail (str != NULL, NULL);
 
-    dstr = g_strdup (str);
-    _gst_parse_yy_scan_string (dstr);
+  g.chain = NULL;
+  g.links = NULL;
+  g.error = error;
+  
+#ifdef __GST_PARSE_TRACE
+  GST_DEBUG (GST_CAT_PIPELINE, "TRACE: tracing enabled");
+  __strings = __chains = __links = 0;
+#endif /* __GST_PARSE_TRACE */
+
+  dstr = g_strdup (str);
+  _gst_parse_yy_scan_string (dstr);
 
 #ifdef DEBUG
-    _gst_parse_yydebug = 1;
+  yydebug = 1;
 #endif
 
-    if (yyparse (&g) != 0) {
-        g_set_error (error,
-                     GST_PARSE_ERROR,
-                     GST_PARSE_ERROR_SYNTAX,
-                     "Invalid syntax");
-        g_free (dstr);
-        return NULL;
-    }
+  if (yyparse (&g) != 0) {
+    SET_ERROR (error, GST_PARSE_ERROR_SYNTAX, "Unrecoverable syntax error while parsing pipeline");
     
-    g_assert (g != NULL);
+    goto error1;
+  }
+  g_free (dstr);
+  
+  GST_INFO (GST_CAT_PIPELINE, "got %u elements and %u links", g.chain ? g_slist_length (g.chain->elements) : 0, g_slist_length (g.links));
+  
+  if (!g.chain) {
+    ret = NULL;
+  } else if (!(((chain_t *) g.chain)->elements->next)) {
+    /* only one toplevel element */  
+    ret = (GstElement *) ((chain_t *) g.chain)->elements->data;
+    g_slist_free (((chain_t *) g.chain)->elements);
+    if (GST_IS_BIN (ret))
+      bin = GST_BIN (ret);
+  } else {  
+    /* put all elements in our bin */
+    bin = GST_BIN (gst_element_factory_make ("pipeline", NULL));
+    g_assert (bin);
+    walk = g.chain->elements;
+    while (walk) {
+      gst_bin_add (bin, GST_ELEMENT (walk->data));
+      walk = g_slist_next (walk);  
+    }
+    g_slist_free (g.chain->elements);
+    ret = GST_ELEMENT (bin);
+  }
+  gst_parse_chain_free (g.chain);
+  
+  /* remove links */
+  walk = g.links;
+  while (walk) {
+    link_t *l = (link_t *) walk->data;
+    GstElement *sink;
+    walk = g_slist_next (walk);
+    if (!l->src) {
+      if (l->src_name) {
+        if (bin) {
+          l->src = gst_bin_get_by_name_recurse_up (bin, l->src_name);
+        } else {
+          l->src = strcmp (GST_ELEMENT_NAME (ret), l->src_name) == 0 ? ret : NULL;
+        }
+      }
+      if (!l->src) {
+        SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, "No element named \"%s\" - omitting link", l->src_name);
+        gst_parse_free_link (l);
+        continue;
+      }
+    }
+    if (!l->sink) {
+      if (l->sink_name) {
+        if (bin) {
+          l->sink = gst_bin_get_by_name_recurse_up (bin, l->sink_name);
+        } else {
+          l->sink = strcmp (GST_ELEMENT_NAME (ret), l->sink_name) == 0 ? ret : NULL;
+        }
+      }
+      if (!l->sink) {
+        SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, "No element named \"%s\" - omitting link", l->sink_name);
+        gst_parse_free_link (l);
+        continue;
+      }
+    }
+    sink = l->sink;
+    gst_parse_perform_link (l, &g);
+  }
+  g_slist_free (g.links);
 
-    g_free (dstr);
+out:
+#ifdef __GST_PARSE_TRACE
+  GST_DEBUG (GST_CAT_PIPELINE, "TRACE: %u strings, %u chains and %u links left", __strings, __chains, __links);
+  if (__strings || __chains || __links) {
+    g_warning ("TRACE: %u strings, %u chains and %u links left", __strings, __chains, __links);
+  }
+#endif /* __GST_PARSE_TRACE */
 
-    /* if the toplevel only contains one bin, make that bin top-level */
-    if (g->elements == NULL && g->bins && g->bins->next == NULL) {
-        g = (graph_t*)g->bins->data;
-        g_free (g->parent);
-        g->parent = NULL;
+  return ret;
+  
+error1:
+  g_free (dstr);
+  
+  if (g.chain) {
+    walk = g.chain->elements;
+    while (walk) {
+      gst_object_unref (GST_OBJECT (walk->data));
+      walk = walk->next;
     }
-
-    return g;
+    g_slist_free (g.chain->elements);
+  }
+  gst_parse_chain_free (g.chain);
+  
+  walk = g.links;
+  while (walk) {
+    gst_parse_free_link ((link_t *) walk->data);
+    walk = walk->next;
+  }
+  g_slist_free (g.links);
+  
+  g_assert (*error);
+  ret = NULL;
+  
+  goto out;
 }
index 327bde1..7523729 100644 (file)
 #include <ctype.h>
 #include <string.h>
 #include "types.h"
-#include <grammar.tab.h>
+#include "../gstinfo.h"
+#include "grammar.tab.h"
 
-#ifdef G_HAVE_ISO_VARARGS
-
-#ifdef DEBUG
-# define PRINT(...) printf(__VAR_ARGS__)
-#else
-#define PRINT(...)
+#ifdef HAVE_CONFIG_H
+#include <config.h>
 #endif
 
+#ifdef G_HAVE_ISO_VARARGS
+#  ifdef GST_DEBUG_ENABLED
+#    define PRINT(...) GST_DEBUG (GST_CAT_PIPELINE, "flex: "__VA_ARGS__)
+#  endif
 #elif defined(G_HAVE_GNUC_VARARGS)
-
-#ifdef DEBUG
-# define PRINT(a...) printf(##a)
+#  ifdef GST_DEBUG_ENABLED
+#    define PRINT(...) GST_DEBUG (GST_CAT_PIPELINE, "flex: "##args)
+#  endif
 #else
-#define PRINT(a...)
-#endif
-
-#endif
-
-#define CHAR(x) PRINT ("char: %c\n", *yytext); return *yytext;
+#  ifdef GST_DEBUG_ENABLED
+#    define PRINT(a...) GST_DEBUG (GST_CAT_PIPELINE, "flex: "##a)
+#  endif
+#endif // G_HAVE_ISO_VARARGS
 
 #define YY_DECL int _gst_parse_yylex (YYSTYPE *lvalp)
 #define YY_NO_UNPUT
 %}
 
-_integer [-+]?[[:digit:]]+
-_double [-+]?[[:digit:]]+"."*[[:digit:]]*
-_number {_integer}|{_double}
-_boolean "true"|"false"|"TRUE"|"FALSE"
-_identifier [[:alpha:]][[:alnum:]\-_%:]*
-_char ([^[:space:]])|("\\".)
+_operators [(){}.:!,=]
+_identifier [[:alpha:]][[:alnum:]\-_%]*
+
+_char ("\\".)|([^[:space:]])
 _string {_char}+|("\""([^\"]|"\\\"")*"\"")
+
 _comma [[:space:]]*","[[:space:]]*
 _assign [[:space:]]*"="[[:space:]]*
-_caps_type_string "fourcc"|"string"
-_caps_type_double "float"
-_caps_string {_string}{_assign}{_caps_type_string}[[:space:]]+{_string}
-_caps_int {_string}{_assign}"int"[[:space:]]+{_integer}
-_caps_double {_string}{_assign}{_caps_type_double}[[:space:]]+{_double}
-_caps_boolean {_string}{_assign}"boolean"[[:space:]]+{_boolean}
-_caps_pair {_caps_string}|{_caps_int}|{_caps_double}|{_caps_boolean}
-_caps {_string}({_comma}{_caps_pair})*
-_llink ({_identifier}\.)?{_identifier}!
-_rlink !({_identifier}\.)?{_identifier}
-_blink ({_identifier}\.)?{_identifier}!({_identifier}\.)?{_identifier}
-_flink ({_identifier}\.)?{_identifier}!{_caps}!({_identifier}\.)?{_identifier}
+
+/* we must do this here, because nearly everything matches a {_string} */ 
+_assignment {_identifier}{_assign}{_string}
+
+/* get pad/element references and stuff with dots right */
+_padref "."{_identifier}
+_ref {_identifier}"."{_identifier}?
+_binref {_identifier}[[:space:]]*"."[[:space:]]*"("
 
 %x value
 %option noyywrap
 %%
 
-<value>{
-    {_integer} {
-        PRINT ("An integer: %s (%d)\n", yytext,
-               atoi (yytext));
-        lvalp->v = g_new0 (GValue, 1);
-        g_value_init (lvalp->v, G_TYPE_INT);
-        g_value_set_int (lvalp->v, atoi (yytext));
-        BEGIN (INITIAL);
-        return VALUE;
-    }
-    
-    {_double}  {
-        PRINT ("A double: %s (%g)\n", yytext, atof (yytext));
-        lvalp->v = g_new0 (GValue, 1);
-        g_value_init (lvalp->v, G_TYPE_DOUBLE);
-        g_value_set_double (lvalp->v, atof (yytext));
-        BEGIN (INITIAL);
-        return VALUE;
-    }
-    
-    {_boolean} {
-        PRINT ("A boolean: %s (%d)\n", yytext, tolower (*yytext) == 't' ? 1 : 0);
-        lvalp->v = g_new0 (GValue, 1);
-        g_value_init (lvalp->v, G_TYPE_BOOLEAN);
-        g_value_set_boolean (lvalp->v, tolower (*yytext) == 't' ? TRUE : FALSE);
-        BEGIN (INITIAL);
-        return VALUE;
-    }
-    
-    {_string} {
-        if (*yytext == '"') {
-            yytext++;
-            *(yytext + strlen (yytext) - 1) = '\0';
-        }
-        _gst_parse_unescape (yytext);
-        PRINT ("A string: \"%s\"\n", yytext);
-        lvalp->v = g_new0 (GValue, 1);
-        g_value_init (lvalp->v, G_TYPE_STRING);
-        g_value_set_string (lvalp->v, yytext);
-        BEGIN (INITIAL);
-        return VALUE;
-    }
-    
-    [[:space:]]+ { /* PRINT ("space: [%s]\n", yytext); */ }
+{_assignment} {
+    /* "=" */
+    PRINT ("ASSIGNMENT: %s\n", yytext);
+    lvalp->s = gst_parse_strdup (yytext);
+    BEGIN (INITIAL);
+    return ASSIGNMENT;
 }
 
-{_llink} {
-    gchar *d1, *q;
-    lvalp->c = g_new0 (link_t, 1);
-    PRINT ("An link: %s\n", yytext);
-    q = strchr (yytext, '!');
-    d1 = strchr (yytext, '.');
-    if (d1) {
-        lvalp->c->src_name = g_strndup (yytext, d1 - yytext);
-        lvalp->c->src_pads = g_list_append (lvalp->c->src_pads, g_strndup (d1 + 1, q - d1 - 1));
-    } else {
-        lvalp->c->src_pads = g_list_append (lvalp->c->src_pads, g_strndup (yytext, q - yytext));
-    }
-    
-    return LINK;
+{_padref} {
+    yytext++;
+    PRINT ("PADREF: %s\n", yytext);
+    lvalp->s = gst_parse_strdup (yytext);
+    BEGIN (INITIAL);
+    return PADREF;
 }
 
-{_rlink} {
-    gchar *d2;
-    lvalp->c = g_new0 (link_t, 1);
-    PRINT ("An rlink: %s\n", yytext);
-    d2 = strchr (yytext, '.');
-    if (d2) {
-        lvalp->c->sink_name = g_strndup (yytext + 1, d2 - yytext - 1);
-        lvalp->c->sink_pads = g_list_append (lvalp->c->sink_pads, g_strdup (d2 + 1));
-    } else {
-        lvalp->c->sink_pads = g_list_append (lvalp->c->sink_pads, g_strdup (yytext + 1));
-    }
-
-    return LINK;
+{_ref} {
+    PRINT ("REF: %s\n", yytext);
+    lvalp->s = gst_parse_strdup (yytext);
+    BEGIN (INITIAL);
+    return REF;
 }
 
-{_blink} {
-    gchar *d1, *d2, *q;
-    lvalp->c = g_new0 (link_t, 1);
-    PRINT ("A blink: %s\n", yytext);
-    q = strchr (yytext, '!');
-    d1 = strchr (yytext, '.');
-    d2 = strchr (q, '.');
-    if (d1 && d1 < q) {
-        lvalp->c->src_name = g_strndup (yytext, d1 - yytext);
-        lvalp->c->src_pads = g_list_append (lvalp->c->src_pads, g_strndup (d1 + 1, q - d1 - 1));
-    } else {
-        lvalp->c->src_pads = g_list_append (lvalp->c->src_pads, g_strndup (yytext, q - yytext));
-    }
-    
-    if (d2) {
-        lvalp->c->sink_name = g_strndup (q + 1, d2 - q - 1);
-        lvalp->c->sink_pads = g_list_append (lvalp->c->sink_pads, g_strdup (d2 + 1));
-    } else {
-        lvalp->c->sink_pads = g_list_append (lvalp->c->sink_pads, g_strdup (q + 1));
-    }
-
-    return BLINK;
-}
-
-{_flink} {
-    gchar *d1, *d2, *q1, *q2, *a1, *m1;
-    gchar *mime;
-    GstProps *props;
-
-    lvalp->c = g_new0 (link_t, 1);
-    PRINT ("An flink: %s\n", yytext);
-    q1 = strchr (yytext, '!');
-    d1 = strchr (yytext, '.');
-    q2 = strchr (q1+1, '!');
-    d2 = strchr (q2, '.');
-    if (d1 && d1 < q1) {
-        lvalp->c->src_name = g_strndup (yytext, d1 - yytext);
-        lvalp->c->src_pads = g_list_append (lvalp->c->src_pads, g_strndup (d1 + 1, q1 - d1 - 1));
-    } else {
-        lvalp->c->src_pads = g_list_append (lvalp->c->src_pads, g_strndup (yytext, q1 - yytext));
-    }
-    
-    if (d2) {
-        lvalp->c->sink_name = g_strndup (q2 + 1, d2 - q2 - 1);
-        lvalp->c->sink_pads = g_list_append (lvalp->c->sink_pads, g_strdup (d2 + 1));
-    } else {
-        lvalp->c->sink_pads = g_list_append (lvalp->c->sink_pads, g_strdup (q2 + 1));
-    }
-    /* parse mime type */
-    m1 = strchr (q1 + 1, ',');
-    mime = g_strndup (q1 + 1, m1 - q1 - 1);
-
-    props = gst_props_empty_new ();
-
-    a1 = strchr (m1 + 1, ',');
-    if (a1 == NULL)
-      a1 = q2;
-
-    while (a1 && a1 <= q2) {
-      gchar *key, *t, *v;
-      gchar *k1, *k2;
-      GstPropsEntry *entry = NULL;
-
-      k1 = strchr (m1, '=');
-      key = g_strstrip (g_strndup (m1 + 1, k1 - m1 -1));
-
-      k1++;
-
-      while (g_ascii_isspace (*k1)) k1++;
-
-      k2 = strchr (k1, ' ');
-      t = g_strstrip (g_strndup (k1, k2 - k1));
-
-      while (g_ascii_isspace (*k2)) k2++;
-
-      v = g_strstrip (g_strndup (k2, a1 - k2));
-
-      if (!strcmp (t, "string")) {
-        entry = gst_props_entry_new (key, GST_PROPS_STRING (v));
-      }
-      else if (!strcmp (t, "fourcc")) {
-        entry = gst_props_entry_new (key, GST_PROPS_FOURCC (GST_STR_FOURCC(v)));
-      }
-      else if (!strcmp (t, "float")) {
-        gfloat f;
-       sscanf (v, "%f", &f);
-        entry = gst_props_entry_new (key, GST_PROPS_FLOAT (f));
-      }
-      else if (!strcmp (t, "int")) {
-        gint i;
-       sscanf (v, "%d", &i);
-        entry = gst_props_entry_new (key, GST_PROPS_INT (i));
-      }
-      else if (!strcmp (t, "boolean")) {
-        gboolean b;
-        b = (!strcmp (v, "true") || ! strcmp (v, "TRUE"));
-        entry = gst_props_entry_new (key, GST_PROPS_BOOLEAN (b));
-      }
-      gst_props_add_entry (props, entry);
-      
-      m1 = a1;
-      if (a1 < q2) {
-        a1 = strchr (m1 + 1, ',');
-        if (a1 == NULL) 
-          a1 = q2;
-      }
-      else
-        break;
-    }
-    lvalp->c->caps = gst_caps_new ("parse_caps", mime, props);
-
-    return FLINK;
+{_binref} {
+    gchar *pos = yytext;
+    while (!g_ascii_isspace (*pos) && (*pos != '.')) pos++;
+    *pos = '\0';
+    PRINT ("BINREF: %s\n", yytext);
+    lvalp->s = gst_parse_strdup (yytext);
+    BEGIN (INITIAL);
+    return BINREF;
 }
 
 {_identifier} {
-    PRINT ("An identifier: %s\n", yytext);
-    lvalp->s = g_strdup (yytext);
+    PRINT ("IDENTIFIER: %s\n", yytext);
+    lvalp->s = gst_parse_strdup (yytext);
+    BEGIN (INITIAL);
     return IDENTIFIER;
 }
 
-"=" { BEGIN (value); CHAR ('='); }
-"@" { CHAR ('@'); }
-"." { CHAR ('.'); }
-"," { CHAR (','); }
-"{" { CHAR ('{'); }
-"}" { CHAR ('}'); }
-"[" { CHAR ('['); }
-"]" { CHAR (']'); }
-"(" { CHAR ('('); }
-")" { CHAR (')'); }
-"!" { CHAR ('!'); }
-"+" { CHAR ('+'); }
+{_operators} { PRINT ("OPERATOR: [%s]\n", yytext); return *yytext; }
 
-[[:space:]]+ { PRINT ("space: [%s]\n", yytext); }
+[[:space:]]+ { PRINT ("SPACE: [%s]\n", yytext); }
 
 . {
-    printf ("unknown: %s\n", yytext);
+    printf ("???: %s\n", yytext);
     return *yytext;
 }
 
index d9e8183..f992198 100644 (file)
@@ -1,46 +1,69 @@
+#ifndef __GST_PARSE_TYPES_H__
+#define __GST_PARSE_TYPES_H__
+
 #include <glib-object.h>
 #include "../gstelement.h"
 
-typedef struct {
-    gchar *type;
-    gint index;
-    GList *property_values;
-    GstElement *element;
-} element_t;
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
 
 typedef struct {
-    gchar *name;
-    GValue *value;
-} property_t;
+  GstElement *src;
+  GstElement *sink;
+  gchar *src_name;
+  gchar *sink_name;
+  GSList *src_pads;
+  GSList *sink_pads;
+  GstCaps *caps;
+} link_t;
 
 typedef struct {
-    /* if the names are present, upon link we'll search out the pads of the
-       proper name and use those. otherwise, we'll search for elements of
-       src_index and sink_index. */
-    char *src_name;
-    char *sink_name;
-    int src_index;
-    int sink_index;
-    GList *src_pads;
-    GList *sink_pads;
-    GstCaps *caps;
-} link_t;
+  GSList *elements;
+  GstElement *first;
+  GstElement *last;
+  link_t *front;
+  link_t *back;
+} chain_t;
 
 typedef struct _graph_t graph_t;
-
 struct _graph_t {
-    element_t *first;
-    element_t *current;
-    graph_t *parent;
-    gchar *current_bin_type;
-    GList *elements;
-    GList *links;
-    GList *links_pending;
-    GList *bins;
-    GstElement *bin;
+  chain_t *chain; /* links are supposed to be done now */
+  GSList *links;
+  GError **error;
 };
 
-graph_t * _gst_parse_launch (const gchar *str, GError **error);
 
-gchar *_gst_parse_escape (const gchar *str);
-void _gst_parse_unescape (gchar *str);
+/** 
+ * Memory checking. Should probably be done with gsttrace stuff, but that 
+ * doesn't really work.
+ * This is not safe from reentrance issues, but that doesn't matter as long as
+ * we lock a mutex before parsing anyway.
+ */
+#ifdef GST_DEBUG_ENABLED
+#  define __GST_PARSE_TRACE
+#endif
+
+#ifdef __GST_PARSE_TRACE
+gchar  *__gst_parse_strdup (gchar *org);
+void   __gst_parse_strfree (gchar *str);
+link_t *__gst_parse_link_new ();
+void   __gst_parse_link_free (link_t *data);
+chain_t *__gst_parse_chain_new ();
+void   __gst_parse_chain_free (chain_t *data);
+#  define gst_parse_strdup __gst_parse_strdup
+#  define gst_parse_strfree __gst_parse_strfree
+#  define gst_parse_link_new __gst_parse_link_new
+#  define gst_parse_link_free __gst_parse_link_free
+#  define gst_parse_chain_new __gst_parse_chain_new
+#  define gst_parse_chain_free __gst_parse_chain_free
+#else /* __GST_PARSE_TRACE */
+#  define gst_parse_strdup g_strdup
+#  define gst_parse_strfree g_free
+#  define gst_parse_link_new() g_new0 (link_t, 1)
+#  define gst_parse_link_free g_free
+#  define gst_parse_chain_new() g_new0 (chain_t, 1)
+#  define gst_parse_chain_free g_free
+#endif /* __GST_PARSE_TRACE */
+
+#endif /* __GST_PARSE_TYPES_H__ */