tee: First deactivate the pad and then remove it when releasing pads
[platform/upstream/gstreamer.git] / gst / parse / grammar.y
index 81a5e2a..1aa77ed 100644 (file)
  * Don't start the message with a capital, and don't end them with a period,
  * as they will be presented inside a sentence/error.
  */
-  
+
 #define YYERROR_VERBOSE 1
-#define YYLEX_PARAM scanner
 
-typedef void* yyscan_t;
+#define YYENABLE_NLS 0
 
-int _gst_parse_yylex (void * yylval_param , yyscan_t yyscanner);
-int _gst_parse_yylex_init (yyscan_t scanner);
-int _gst_parse_yylex_destroy (yyscan_t scanner);
-struct yy_buffer_state * _gst_parse_yy_scan_string (char* , yyscan_t);
-void _gst_parse_yypush_buffer_state (void * new_buffer ,yyscan_t yyscanner );
-void _gst_parse_yypop_buffer_state (yyscan_t yyscanner );
+#ifndef YYLTYPE_IS_TRIVIAL
+#define YYLTYPE_IS_TRIVIAL 0
+#endif
 
+/*******************************************************************************************
+*** Tracing memory leaks
+*******************************************************************************************/
 
 #ifdef __GST_PARSE_TRACE
 static guint __strings;
@@ -45,7 +44,7 @@ static guint __chains;
 gchar *
 __gst_parse_strdup (gchar *org)
 {
-  gchar *ret; 
+  gchar *ret;
   __strings++;
   ret = g_strdup (org);
   /* g_print ("ALLOCATED STR   (%3u): %p %s\n", __strings, ret, ret); */
@@ -61,7 +60,7 @@ __gst_parse_strfree (gchar *str)
     __strings--;
   }
 }
-link_t *__gst_parse_link_new ()
+link_t *__gst_parse_link_new (void)
 {
   link_t *ret;
   __links++;
@@ -80,18 +79,18 @@ __gst_parse_link_free (link_t *data)
   }
 }
 chain_t *
-__gst_parse_chain_new ()
+__gst_parse_chain_new (void)
 {
   chain_t *ret;
   __chains++;
   ret = g_slice_new0 (chain_t);
-  /* g_print ("ALLOCATED CHAIN (%3u): %p\n", __chains, ret); */
+  /* g_print ("@%p: ALLOCATED CHAIN (%3u):\n", ret, __chains); */
   return ret;
 }
 void
 __gst_parse_chain_free (chain_t *data)
 {
-  /* g_print ("FREEING CHAIN   (%3u): %p\n", __chains - 1, data); */
+  /* g_print ("@%p: FREEING CHAIN   (%3u):\n", data, __chains - 1); */
   g_slice_free (chain_t, data);
   g_return_if_fail (__chains > 0);
   __chains--;
@@ -99,23 +98,9 @@ __gst_parse_chain_free (chain_t *data)
 
 #endif /* __GST_PARSE_TRACE */
 
-typedef struct {
-  gchar *src_pad;
-  gchar *sink_pad;
-  GstElement *sink;
-  GstCaps *caps;
-  gulong signal_id;
-} DelayedLink;
-
-typedef struct {
-  GstElement *parent;
-  gchar *name;
-  gchar *value_str;
-  gulong signal_id;
-} DelayedSet;
-
-/*** define SET_ERROR macro/function */
-
+/*******************************************************************************************
+*** define SET_ERROR macro/function
+*******************************************************************************************/
 #ifdef G_HAVE_ISO_VARARGS
 
 #  define SET_ERROR(error, type, ...) \
@@ -151,7 +136,7 @@ SET_ERROR (GError **error, gint type, const char *format, ...)
       va_start (varargs, format);
       string = g_strdup_vprintf (format, varargs);
       va_end (varargs);
-      
+
       g_set_error (error, GST_PARSE_ERROR, type, string);
 
       g_free (string);
@@ -191,7 +176,7 @@ YYPRINTF(const char *format, ...)
 {
   va_list varargs;
   gchar *temp;
-  
+
   va_start (varargs, format);
   temp = g_strdup_vprintf (format, varargs);
   GST_CAT_LOG (GST_CAT_PIPELINE, "%s", temp);
@@ -203,95 +188,153 @@ YYPRINTF(const char *format, ...)
 
 #endif /* GST_DISABLE_GST_DEBUG */
 
-#define ADD_MISSING_ELEMENT(graph,name) G_STMT_START {                      \
-    if ((graph)->ctx) {                                                     \
-      (graph)->ctx->missing_elements =                                      \
-          g_list_append ((graph)->ctx->missing_elements, g_strdup (name));  \
-    } } G_STMT_END
 
-#define GST_BIN_MAKE(res, type, chainval, assign, free_string) \
-G_STMT_START { \
-  chain_t *chain = chainval; \
-  GSList *walk; \
-  GstBin *bin = (GstBin *) gst_element_factory_make (type, NULL); \
-  if (!chain) { \
-    SET_ERROR (graph->error, GST_PARSE_ERROR_EMPTY_BIN, \
-        _("specified empty bin \"%s\", not allowed"), type); \
-    g_slist_foreach (assign, (GFunc) gst_parse_strfree, NULL); \
-    g_slist_free (assign); \
-    gst_object_unref (bin); \
-    if (free_string) \
-      gst_parse_strfree (type); /* Need to clean up the string */ \
-    YYERROR; \
-  } else if (!bin) { \
-    ADD_MISSING_ELEMENT(graph, type); \
-    SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, \
-        _("no bin \"%s\", skipping"), type); \
-    g_slist_foreach (assign, (GFunc) gst_parse_strfree, NULL); \
-    g_slist_free (assign); \
-    res = chain; \
-  } else { \
-    for (walk = chain->elements; walk; walk = walk->next ) \
-      gst_bin_add (bin, GST_ELEMENT (walk->data)); \
-    g_slist_free (chain->elements); \
-    chain->elements = g_slist_prepend (NULL, bin); \
-    res = chain; \
-    /* set the properties now */ \
-    for (walk = assign; walk; walk = walk->next) \
-      gst_parse_element_set ((gchar *) walk->data, GST_ELEMENT (bin), graph); \
-    g_slist_free (assign); \
-  } \
-} G_STMT_END
+/*
+ * include headers generated by bison & flex, after defining (or not defining) YYDEBUG
+ */
+#include "grammar.tab.h"
+#include "parse_lex.h"
 
-#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
+/*******************************************************************************************
+*** report missing elements/bins/..
+*******************************************************************************************/
 
-#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); \
+
+static void  add_missing_element(graph_t *graph,gchar *name){
+  if ((graph)->ctx){
+    (graph)->ctx->missing_elements = g_list_append ((graph)->ctx->missing_elements, g_strdup (name));
+    }
+}
+
+
+/*******************************************************************************************
+*** helpers for pipeline-setup
+*******************************************************************************************/
+
+#define TRY_SETUP_LINK(l) G_STMT_START { \
+   if( (!(l)->src.element) && (!(l)->src.name) ){ \
+     SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("link has no source [sink=%s@%p]"), \
+       (l)->sink.name ? (l)->sink.name : "", \
+       (l)->sink.element); \
+     gst_parse_free_link (l); \
+   }else if( (!(l)->sink.element) && (!(l)->sink.name) ){ \
+     SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("link has no sink [source=%s@%p]"), \
+       (l)->src.name ? (l)->src.name : "", \
+       (l)->src.element); \
+     gst_parse_free_link (l); \
+   }else{ \
+     graph->links = g_slist_append (graph->links, l ); \
+   }   \
 } G_STMT_END
 
+typedef struct {
+  gchar *src_pad;
+  gchar *sink_pad;
+  GstElement *sink;
+  GstCaps *caps;
+  gulong pad_added_signal_id, no_more_pads_signal_id;
+  gboolean all_pads;
+} DelayedLink;
+
+typedef struct {
+  gchar *name;
+  gchar *value_str;
+  gulong signal_id;
+} DelayedSet;
+
+static int  gst_resolve_reference(reference_t *rr, GstElement *pipeline){
+  GstBin *bin;
+
+  if(rr->element) return  0;  /* already resolved! */
+  if(!rr->name)   return -2;  /* no chance! */
+
+  if (GST_IS_BIN (pipeline)){
+    bin = GST_BIN (pipeline);
+    rr->element = gst_bin_get_by_name_recurse_up (bin, rr->name);
+  } else {
+    rr->element = strcmp (GST_ELEMENT_NAME (pipeline), rr->name) == 0 ?
+               gst_object_ref(pipeline) : NULL;
+  }
+  if(rr->element) return 0; /* resolved */
+  else            return -1; /* not found */
+}
+
+static void gst_parse_free_delayed_set (DelayedSet *set)
+{
+  g_free(set->name);
+  g_free(set->value_str);
+  g_slice_free(DelayedSet, set);
+}
+
+static void gst_parse_new_child(GstChildProxy *child_proxy, GObject *object,
+    const gchar * name, gpointer data);
+
+static void gst_parse_add_delayed_set (GstElement *element, gchar *name, gchar *value_str)
+{
+  DelayedSet *data = g_slice_new0 (DelayedSet);
+
+  GST_CAT_LOG_OBJECT (GST_CAT_PIPELINE, element, "delaying property set %s to %s",
+    name, value_str);
+
+  data->name = g_strdup(name);
+  data->value_str = g_strdup(value_str);
+  data->signal_id = g_signal_connect_data(element, "child-added",
+      G_CALLBACK (gst_parse_new_child), data, (GClosureNotify)
+      gst_parse_free_delayed_set, (GConnectFlags) 0);
+
+  /* FIXME: we would need to listen on all intermediate bins too */
+  if (GST_IS_BIN (element)) {
+    gchar **names, **current;
+    GstElement *parent, *child;
+
+    current = names = g_strsplit (name, "::", -1);
+    parent = gst_bin_get_by_name (GST_BIN_CAST (element), current[0]);
+    current++;
+    while (parent && current[0]) {
+      child = gst_bin_get_by_name (GST_BIN (parent), current[0]);
+      if (!child && current[1]) {
+        char *sub_name = g_strjoinv ("::", &current[0]);
+
+        gst_parse_add_delayed_set(parent, sub_name, value_str);
+        g_free (sub_name);
+      }
+      gst_object_unref (parent);
+      parent = child;
+      current++;
+    }
+    if (parent)
+      gst_object_unref (parent);
+    g_strfreev (names);
+  }
+}
+
 static void gst_parse_new_child(GstChildProxy *child_proxy, GObject *object,
-                                gpointer data)
+    const gchar * name, gpointer data)
 {
   DelayedSet *set = (DelayedSet *) data;
   GParamSpec *pspec;
-  GValue v = { 0, }; 
-  GstObject *target = NULL;
+  GValue v = { 0, };
+  GObject *target = NULL;
   GType value_type;
 
-  if (gst_child_proxy_lookup (GST_OBJECT (set->parent), set->name, &target, &pspec)) { 
+  GST_CAT_LOG_OBJECT (GST_CAT_PIPELINE, child_proxy, "new child %s, checking property %s",
+      name, set->name);
+
+  if (gst_child_proxy_lookup (child_proxy, set->name, &target, &pspec)) {
     gboolean got_value = FALSE;
 
-    value_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
+    value_type = pspec->value_type;
 
-    GST_CAT_LOG (GST_CAT_PIPELINE, "parsing delayed property %s as a %s from %s", pspec->name,
-      g_type_name (value_type), set->value_str);
+    GST_CAT_LOG_OBJECT (GST_CAT_PIPELINE, child_proxy, "parsing delayed property %s as a %s from %s",
+      pspec->name, g_type_name (value_type), set->value_str);
     g_value_init (&v, value_type);
     if (gst_value_deserialize (&v, set->value_str))
       got_value = TRUE;
     else if (g_type_is_a (value_type, GST_TYPE_ELEMENT)) {
        GstElement *bin;
-       
-       bin = gst_parse_bin_from_description (set->value_str, TRUE, NULL);
+
+       bin = gst_parse_bin_from_description_full (set->value_str, TRUE, NULL,
+           GST_PARSE_FLAG_NO_SINGLE_ELEMENT_BINS | GST_PARSE_FLAG_PLACE_IN_BIN, NULL);
        if (bin) {
          g_value_set_object (&v, bin);
          got_value = TRUE;
@@ -300,74 +343,94 @@ static void gst_parse_new_child(GstChildProxy *child_proxy, GObject *object,
     g_signal_handler_disconnect (child_proxy, set->signal_id);
     if (!got_value)
       goto error;
-    g_object_set_property (G_OBJECT (target), pspec->name, &v);
+    g_object_set_property (target, pspec->name, &v);
+  } else {
+    const gchar *obj_name = GST_OBJECT_NAME(object);
+    gint len = strlen (obj_name);
+
+    /* do a delayed set */
+    if ((strlen (set->name) > (len + 2)) && !strncmp (set->name, obj_name, len) && !strncmp (&set->name[len], "::", 2)) {
+      gst_parse_add_delayed_set (GST_ELEMENT(child_proxy), set->name, set->value_str);
+    }
   }
 
 out:
   if (G_IS_VALUE (&v))
     g_value_unset (&v);
   if (target)
-    gst_object_unref (target);
+    g_object_unref (target);
   return;
 
 error:
-  GST_CAT_ERROR (GST_CAT_PIPELINE, "could not set property \"%s\" in element \"%s\"",
-        pspec->name, GST_ELEMENT_NAME (target));
+  GST_CAT_ERROR (GST_CAT_PIPELINE, "could not set property \"%s\" in %"
+      GST_PTR_FORMAT, pspec->name, target);
   goto out;
 }
 
-static void
-gst_parse_free_delayed_set (DelayedSet *set) {
-  g_free(set->name);
-  g_free(set->value_str);
-  g_slice_free(DelayedSet, set);
-}
-
-static void
-gst_parse_element_set (gchar *value, GstElement *element, graph_t *graph)
+static void gst_parse_element_set (gchar *value, GstElement *element, graph_t *graph)
 {
-  GParamSpec *pspec;
+  GParamSpec *pspec = NULL;
   gchar *pos = value;
-  GValue v = { 0, }; 
-  GstObject *target = NULL;
+  GValue v = { 0, };
+  GObject *target = NULL;
   GType value_type;
 
   /* do nothing if assignment is for missing element */
   if (element == NULL)
     goto out;
 
-  /* parse the string, so the property name is null-terminated an pos points
+  /* parse the string, so the property name is null-terminated and pos points
      to the beginning of the value */
-  while (!g_ascii_isspace (*pos) && (*pos != '=')) pos++; 
-  if (*pos == '=') { 
-    *pos = '\0'; 
-  } else { 
-    *pos = '\0'; 
+  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 == '"') {
+    while (g_ascii_isspace (*pos)) pos++;
+  }
+  pos++;
+  while (g_ascii_isspace (*pos)) pos++;
+  /* truncate a string if it is delimited with double quotes */
+  if (*pos == '"' && pos[strlen (pos) - 1] == '"') {
     pos++;
     pos[strlen (pos) - 1] = '\0';
   }
   gst_parse_unescape (pos);
 
-  if (gst_child_proxy_lookup (GST_OBJECT (element), value, &target, &pspec)) { 
+  if (GST_IS_CHILD_PROXY (element)) {
+    if (!gst_child_proxy_lookup (GST_CHILD_PROXY (element), value, &target, &pspec)) {
+      /* do a delayed set */
+      gst_parse_add_delayed_set (element, value, pos);
+    }
+  } else {
+    pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), value);
+    if (pspec != NULL) {
+      target = G_OBJECT (g_object_ref (element));
+      GST_CAT_LOG_OBJECT (GST_CAT_PIPELINE, target, "found %s property", value);
+    } else {
+      SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_PROPERTY, \
+          _("no property \"%s\" in element \"%s\""), value, \
+          GST_ELEMENT_NAME (element));
+    }
+  }
+
+  if (pspec != NULL && target != NULL) {
     gboolean got_value = FALSE;
 
-    value_type = G_PARAM_SPEC_VALUE_TYPE (pspec); 
+    value_type = pspec->value_type;
+
+    GST_CAT_LOG_OBJECT (GST_CAT_PIPELINE, element, "parsing property %s as a %s",
+        pspec->name, g_type_name (value_type));
 
-    GST_CAT_LOG (GST_CAT_PIPELINE, "parsing property %s as a %s", pspec->name,
-      g_type_name (value_type));
     g_value_init (&v, value_type);
     if (gst_value_deserialize (&v, pos))
       got_value = TRUE;
     else if (g_type_is_a (value_type, GST_TYPE_ELEMENT)) {
        GstElement *bin;
-       
-       bin = gst_parse_bin_from_description (pos, TRUE, NULL);
+
+       bin = gst_parse_bin_from_description_full (pos, TRUE, NULL,
+           GST_PARSE_FLAG_NO_SINGLE_ELEMENT_BINS | GST_PARSE_FLAG_PLACE_IN_BIN, NULL);
        if (bin) {
          g_value_set_object (&v, bin);
          got_value = TRUE;
@@ -375,24 +438,7 @@ gst_parse_element_set (gchar *value, GstElement *element, graph_t *graph)
     }
     if (!got_value)
       goto error;
-    g_object_set_property (G_OBJECT (target), pspec->name, &v);
-  } else { 
-    /* do a delayed set */
-    if (GST_IS_CHILD_PROXY (element)) {
-      DelayedSet *data = g_slice_new0 (DelayedSet);
-      
-      data->parent = element;
-      data->name = g_strdup(value);
-      data->value_str = g_strdup(pos);
-      data->signal_id = g_signal_connect_data(element, "child-added",
-          G_CALLBACK (gst_parse_new_child), data, (GClosureNotify)
-          gst_parse_free_delayed_set, (GConnectFlags) 0);
-    }
-    else {
-      SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_PROPERTY, \
-          _("no property \"%s\" in element \"%s\""), value, \
-          GST_ELEMENT_NAME (element));
-    }
+    g_object_set_property (target, pspec->name, &v);
   }
 
 out:
@@ -400,30 +446,44 @@ out:
   if (G_IS_VALUE (&v))
     g_value_unset (&v);
   if (target)
-    gst_object_unref (target);
+    g_object_unref (target);
   return;
-  
+
 error:
   SET_ERROR (graph->error, GST_PARSE_ERROR_COULD_NOT_SET_PROPERTY,
-         _("could not set property \"%s\" in element \"%s\" to \"%s\""), 
-        value, GST_ELEMENT_NAME (element), pos); 
+         _("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)
+
+static void gst_parse_free_reference (reference_t *rr)
 {
-  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);
+  if(rr->element) gst_object_unref(rr->element);
+  gst_parse_strfree (rr->name);
+  g_slist_foreach (rr->pads, (GFunc) gst_parse_strfree, NULL);
+  g_slist_free (rr->pads);
+}
+
+static void gst_parse_free_link (link_t *link)
+{
+  gst_parse_free_reference (&(link->src));
+  gst_parse_free_reference (&(link->sink));
   if (link->caps) gst_caps_unref (link->caps);
-  gst_parse_link_free (link);  
+  gst_parse_link_free (link);
 }
 
-static void
-gst_parse_free_delayed_link (DelayedLink *link)
+static void gst_parse_free_chain (chain_t *ch)
+{
+  GSList *walk;
+  gst_parse_free_reference (&(ch->first));
+  gst_parse_free_reference (&(ch->last));
+  for(walk=ch->elements;walk;walk=walk->next)
+    gst_object_unref (walk->data);
+  g_slist_free (ch->elements);
+  gst_parse_chain_free (ch);
+}
+
+static void gst_parse_free_delayed_link (DelayedLink *link)
 {
   g_free (link->src_pad);
   g_free (link->sink_pad);
@@ -431,46 +491,78 @@ gst_parse_free_delayed_link (DelayedLink *link)
   g_slice_free (DelayedLink, link);
 }
 
-static void
-gst_parse_found_pad (GstElement *src, GstPad *pad, gpointer data)
+#define PRETTY_PAD_NAME_FMT "%s %s of %s named %s"
+#define PRETTY_PAD_NAME_ARGS(elem, pad_name) \
+  (pad_name ? "pad " : "some"), (pad_name ? pad_name : "pad"), \
+  G_OBJECT_TYPE_NAME(elem), GST_STR_NULL (GST_ELEMENT_NAME (elem))
+
+static void gst_parse_no_more_pads (GstElement *src, gpointer data)
+{
+  DelayedLink *link = data;
+
+  /* Don't warn for all-pads links, as we expect those to
+   * still be active at no-more-pads */
+  if (!link->all_pads) {
+    GST_ELEMENT_WARNING(src, PARSE, DELAYED_LINK,
+      (_("Delayed linking failed.")),
+      ("failed delayed linking " PRETTY_PAD_NAME_FMT " to " PRETTY_PAD_NAME_FMT,
+          PRETTY_PAD_NAME_ARGS (src, link->src_pad),
+          PRETTY_PAD_NAME_ARGS (link->sink, link->sink_pad)));
+  }
+  /* we keep the handlers connected, so that in case an element still adds a pad
+   * despite no-more-pads, we will consider it for pending delayed links */
+}
+
+static void gst_parse_found_pad (GstElement *src, GstPad *pad, gpointer data)
 {
   DelayedLink *link = data;
 
-  GST_CAT_INFO (GST_CAT_PIPELINE, "trying delayed linking %s:%s to %s:%s", 
-                GST_STR_NULL (GST_ELEMENT_NAME (src)), GST_STR_NULL (link->src_pad),
-                GST_STR_NULL (GST_ELEMENT_NAME (link->sink)), GST_STR_NULL (link->sink_pad));
+  GST_CAT_INFO (GST_CAT_PIPELINE,
+                "trying delayed linking %s " PRETTY_PAD_NAME_FMT " to " PRETTY_PAD_NAME_FMT,
+                           link->all_pads ? "all pads" : "one pad",
+                PRETTY_PAD_NAME_ARGS (src, link->src_pad),
+                PRETTY_PAD_NAME_ARGS (link->sink, link->sink_pad));
 
   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_CAT_DEBUG (GST_CAT_PIPELINE, "delayed linking %s:%s to %s:%s worked", 
-                          GST_STR_NULL (GST_ELEMENT_NAME (src)), GST_STR_NULL (link->src_pad),
-                          GST_STR_NULL (GST_ELEMENT_NAME (link->sink)), GST_STR_NULL (link->sink_pad));
-    g_signal_handler_disconnect (src, link->signal_id);
+    GST_CAT_DEBUG (GST_CAT_PIPELINE,
+                   "delayed linking %s " PRETTY_PAD_NAME_FMT " to " PRETTY_PAD_NAME_FMT " worked",
+                              link->all_pads ? "all pads" : "one pad",
+                          PRETTY_PAD_NAME_ARGS (src, link->src_pad),
+                   PRETTY_PAD_NAME_ARGS (link->sink, link->sink_pad));
+    g_signal_handler_disconnect (src, link->no_more_pads_signal_id);
+    /* releases 'link' */
+    if (!link->all_pads)
+      g_signal_handler_disconnect (src, link->pad_added_signal_id);
   }
 }
+
 /* both padnames and the caps may be NULL */
 static gboolean
-gst_parse_perform_delayed_link (GstElement *src, const gchar *src_pad, 
+gst_parse_perform_delayed_link (GstElement *src, const gchar *src_pad,
                                 GstElement *sink, const gchar *sink_pad,
-                                GstCaps *caps)
+                                GstCaps *caps, gboolean all_pads)
 {
   GList *templs = gst_element_class_get_pad_template_list (
       GST_ELEMENT_GET_CLASS (src));
-        
+
   for (; templs; templs = templs->next) {
     GstPadTemplate *templ = (GstPadTemplate *) templs->data;
     if ((GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC) &&
         (GST_PAD_TEMPLATE_PRESENCE(templ) == GST_PAD_SOMETIMES))
     {
-      DelayedLink *data = g_slice_new (DelayedLink); 
-      
+      DelayedLink *data = g_slice_new (DelayedLink);
+
+      data->all_pads = all_pads;
+
       /* TODO: maybe we should check if src_pad matches this template's names */
 
-      GST_CAT_DEBUG (GST_CAT_PIPELINE, "trying delayed link %s:%s to %s:%s", 
-                     GST_STR_NULL (GST_ELEMENT_NAME (src)), GST_STR_NULL (src_pad),
-                     GST_STR_NULL (GST_ELEMENT_NAME (sink)), GST_STR_NULL (sink_pad));
+      GST_CAT_DEBUG (GST_CAT_PIPELINE,
+                     "trying delayed link " PRETTY_PAD_NAME_FMT " to " PRETTY_PAD_NAME_FMT,
+                     PRETTY_PAD_NAME_ARGS (src, src_pad),
+                     PRETTY_PAD_NAME_ARGS (sink, sink_pad));
 
       data->src_pad = g_strdup (src_pad);
       data->sink = sink;
@@ -480,14 +572,63 @@ gst_parse_perform_delayed_link (GstElement *src, const gchar *src_pad,
       } else {
        data->caps = NULL;
       }
-      data->signal_id = g_signal_connect_data (src, "pad-added",
+      data->pad_added_signal_id = g_signal_connect_data (src, "pad-added",
           G_CALLBACK (gst_parse_found_pad), data,
           (GClosureNotify) gst_parse_free_delayed_link, (GConnectFlags) 0);
+      data->no_more_pads_signal_id = g_signal_connect (src, "no-more-pads",
+          G_CALLBACK (gst_parse_no_more_pads), data);
       return TRUE;
     }
   }
   return FALSE;
 }
+
+static gboolean
+gst_parse_element_can_do_caps (GstElement * e, GstPadDirection dir,
+    GstCaps * link_caps)
+{
+  gboolean can_do = FALSE, done = FALSE;
+  GstIterator *it;
+
+  it = (dir == GST_PAD_SRC) ? gst_element_iterate_src_pads (e) : gst_element_iterate_sink_pads (e);
+
+  while (!done && !can_do) {
+    GValue v = G_VALUE_INIT;
+    GstPad *pad;
+    GstCaps *caps;
+
+    switch (gst_iterator_next (it, &v)) {
+      case GST_ITERATOR_OK:
+        pad = g_value_get_object (&v);
+
+        caps = gst_pad_get_current_caps (pad);
+        if (caps == NULL)
+          caps = gst_pad_query_caps (pad, NULL);
+
+        can_do = gst_caps_can_intersect (caps, link_caps);
+
+        GST_TRACE ("can_do: %d for %" GST_PTR_FORMAT " and %" GST_PTR_FORMAT,
+            can_do, caps, link_caps);
+
+        gst_caps_unref (caps);
+
+        g_value_unset (&v);
+        break;
+      case GST_ITERATOR_DONE:
+      case GST_ITERATOR_ERROR:
+        done = TRUE;
+        break;
+      case GST_ITERATOR_RESYNC:
+        gst_iterator_resync (it);
+        break;
+    }
+  }
+
+  gst_iterator_free (it);
+
+  return can_do;
+}
+
 /*
  * performs a link and frees the struct. src and sink elements must be given
  * return values   0 - link performed
@@ -497,35 +638,47 @@ gst_parse_perform_delayed_link (GstElement *src, const gchar *src_pad,
 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;
+  GstElement *src = link->src.element;
+  GstElement *sink = link->sink.element;
+  GSList *srcs = link->src.pads;
+  GSList *sinks = link->sink.pads;
   g_assert (GST_IS_ELEMENT (src));
   g_assert (GST_IS_ELEMENT (sink));
-  
+
   GST_CAT_INFO (GST_CAT_PIPELINE,
-      "linking %s:%s to %s:%s (%u/%u) with caps \"%" GST_PTR_FORMAT "\"", 
-      GST_ELEMENT_NAME (src), link->src_name ? link->src_name : "(any)",
-      GST_ELEMENT_NAME (sink), link->sink_name ? link->sink_name : "(any)",
+      "linking " PRETTY_PAD_NAME_FMT " to " PRETTY_PAD_NAME_FMT " (%u/%u) with caps \"%" GST_PTR_FORMAT "\"",
+      PRETTY_PAD_NAME_ARGS (src, link->src.name),
+      PRETTY_PAD_NAME_ARGS (sink, link->sink.name),
       g_slist_length (srcs), g_slist_length (sinks), link->caps);
 
   if (!srcs || !sinks) {
-    if (gst_element_link_pads_filtered (src,
+    gboolean found_one = gst_element_link_pads_filtered (src,
+        srcs ? (const gchar *) srcs->data : NULL, sink,
+        sinks ? (const gchar *) sinks->data : NULL, link->caps);
+
+    if (found_one) {
+      if (!link->all_pads)
+        goto success; /* Linked one, and not an all-pads link = we're done */
+
+      /* Try and link more available pads */
+      while (gst_element_link_pads_filtered (src,
         srcs ? (const gchar *) srcs->data : NULL, sink,
-        sinks ? (const gchar *) sinks->data : NULL, link->caps)) {
+        sinks ? (const gchar *) sinks->data : NULL, link->caps));
+    }
+
+    /* We either didn't find any static pads, or this is a all-pads link,
+     * in which case watch for future pads and link those. Not a failure
+     * in the all-pads case if there's no sometimes pads to watch */
+    if (gst_parse_perform_delayed_link (src,
+          srcs ? (const gchar *) srcs->data : NULL,
+          sink, sinks ? (const gchar *) sinks->data : NULL, link->caps,
+         link->all_pads) || link->all_pads) {
       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)) {
-       goto success;
-      } else {
-        goto error;
-      }
+      goto error;
     }
   }
-  if (g_slist_length (link->src_pads) != g_slist_length (link->src_pads)) {
+  if (g_slist_length (link->src.pads) != g_slist_length (link->sink.pads)) {
     goto error;
   }
   while (srcs && sinks) {
@@ -539,22 +692,53 @@ gst_parse_perform_link (link_t *link, graph_t *graph)
     } else {
       if (gst_parse_perform_delayed_link (src, src_pad,
                                           sink, sink_pad,
-                                         link->caps)) {
+                                         link->caps, link->all_pads)) {
        continue;
       } else {
         goto error;
       }
     }
   }
-  
+
 success:
   gst_parse_free_link (link);
   return 0;
-  
+
 error:
-  SET_ERROR (graph->error, GST_PARSE_ERROR_LINK,
-      _("could not link %s to %s"), GST_ELEMENT_NAME (src),
-      GST_ELEMENT_NAME (sink));
+  if (link->caps != NULL) {
+    gboolean src_can_do_caps, sink_can_do_caps;
+    gchar *caps_str = gst_caps_to_string (link->caps);
+
+    src_can_do_caps =
+        gst_parse_element_can_do_caps (src, GST_PAD_SRC, link->caps);
+    sink_can_do_caps =
+        gst_parse_element_can_do_caps (sink, GST_PAD_SINK, link->caps);
+
+    if (!src_can_do_caps && sink_can_do_caps) {
+      SET_ERROR (graph->error, GST_PARSE_ERROR_LINK,
+          _("could not link %s to %s, %s can't handle caps %s"),
+          GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (sink),
+          GST_ELEMENT_NAME (src), caps_str);
+    } else if (src_can_do_caps && !sink_can_do_caps) {
+      SET_ERROR (graph->error, GST_PARSE_ERROR_LINK,
+          _("could not link %s to %s, %s can't handle caps %s"),
+          GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (sink),
+          GST_ELEMENT_NAME (sink), caps_str);
+    } else if (!src_can_do_caps && !sink_can_do_caps) {
+      SET_ERROR (graph->error, GST_PARSE_ERROR_LINK,
+          _("could not link %s to %s, neither element can handle caps %s"),
+          GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (sink), caps_str);
+    } else {
+      SET_ERROR (graph->error, GST_PARSE_ERROR_LINK,
+          _("could not link %s to %s with caps %s"),
+          GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (sink), caps_str);
+    }
+    g_free (caps_str);
+  } else {
+    SET_ERROR (graph->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;
 }
@@ -564,33 +748,52 @@ static int yyerror (void *scanner, graph_t *graph, const char *s);
 %}
 
 %union {
-    gchar *s;
-    chain_t *c;
-    link_t *l;
-    GstElement *e;
-    GSList *p;
-    graph_t *g;
+    gchar *ss;
+    chain_t *cc;
+    link_t *ll;
+    reference_t rr;
+    GstElement *ee;
+    GSList *pp;
+    graph_t *gg;
 }
 
-%token <s> PARSE_URL
-%token <s> IDENTIFIER
-%left <s> REF PADREF BINREF
-%token <s> ASSIGNMENT
-%token <s> LINK
+/* No grammar ambiguities expected, FAIL otherwise */
+%expect 0
+
+%token <ss> PARSE_URL
+%token <ss> IDENTIFIER
+%left  <ss> REF PADREF BINREF
+%token <ss> ASSIGNMENT
+%token <ss> LINK
+%token <ss> LINK_ALL
+
+%type <ss> binopener
+%type <gg> graph
+%type <cc> chain bin chainlist openchain elementary
+%type <rr> reference
+%type <ll> link
+%type <ee> element
+%type <pp> morepads pads assignments
+
+%destructor {  gst_parse_strfree ($$);         } <ss>
+%destructor {  if($$)
+                 gst_parse_free_chain($$);     } <cc>
+%destructor {  gst_parse_free_link ($$);       } <ll>
+%destructor {  gst_parse_free_reference(&($$));} <rr>
+%destructor {  gst_object_unref ($$);          } <ee>
+%destructor {  GSList *walk;
+               for(walk=$$;walk;walk=walk->next)
+                 gst_parse_strfree (walk->data);
+               g_slist_free ($$);              } <pp>
+
 
-%type <g> graph
-%type <c> chain bin
-%type <l> reference
-%type <l> linkpart link
-%type <p> linklist
-%type <e> element 
-%type <p> padlist pads assignments
 
 %left '(' ')'
 %left ','
 %right '.'
-%left '!' '='
+%left '!' '=' ':'
 
+%lex-param { void *scanner }
 %parse-param { void *scanner }
 %parse-param { graph_t *graph }
 %pure-parser
@@ -598,17 +801,19 @@ static int yyerror (void *scanner, graph_t *graph, const char *s);
 %start graph
 %%
 
-element:       IDENTIFIER                    { $$ = gst_element_factory_make ($1, NULL); 
+/*************************************************************
+* Grammar explanation:
+*   _element_s are specified by an identifier of their type.
+*   a name can be give in the optional property-assignments
+*      coffeeelement
+*      fakesrc name=john
+*      identity silence=false name=frodo
+*   (cont'd)
+**************************************************************/
+element:       IDENTIFIER                    { $$ = gst_element_factory_make ($1, NULL);
                                                if ($$ == NULL) {
-                                                 ADD_MISSING_ELEMENT (graph, $1);
+                                                 add_missing_element(graph, $1);
                                                  SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, _("no element \"%s\""), $1);
-                                                 /* if FATAL_ERRORS flag is set, we don't have to worry about backwards
-                                                  * compatibility and can continue parsing and check for other missing
-                                                  * elements */
-                                                 if ((graph->flags & GST_PARSE_FLAG_FATAL_ERRORS) == 0) {
-                                                   gst_parse_strfree ($1);
-                                                   YYERROR;
-                                                 }
                                                }
                                                gst_parse_strfree ($1);
                                               }
@@ -616,209 +821,309 @@ element:        IDENTIFIER                    { $$ = gst_element_factory_make ($1, NULL);
                                                $$ = $1;
                                              }
        ;
-assignments:   /* NOP */                     { $$ = NULL; }
-       |       assignments ASSIGNMENT        { $$ = g_slist_prepend ($1, $2); }
-       ;               
-bin:           '(' assignments chain ')' { GST_BIN_MAKE ($$, "bin", $3, $2, FALSE); }
-        |       BINREF assignments chain ')'  { GST_BIN_MAKE ($$, $1, $3, $2, TRUE); 
-                                               gst_parse_strfree ($1);
-                                             }
-        |       BINREF assignments ')'       { GST_BIN_MAKE ($$, $1, NULL, $2, TRUE); 
-                                               gst_parse_strfree ($1);
-                                             }
-        |       BINREF assignments error ')'  { GST_BIN_MAKE ($$, $1, NULL, $2, TRUE); 
-                                               gst_parse_strfree ($1);
+
+/*************************************************************
+* Grammar explanation: (cont'd)
+*   a graph has (pure) _element_s, _bin_s and _link_s.
+*   since bins are special elements, bins and elements can
+*   be generalized as _elementary_.
+*   The construction of _bin_s will be discussed later.
+*   (cont'd)
+*
+**************************************************************/
+elementary:
+       element                               { $$ = gst_parse_chain_new ();
+                                               /* g_print ("@%p: CHAINing elementary\n", $$); */
+                                               $$->first.element = $1? gst_object_ref($1) : NULL;
+                                               $$->last.element = $1? gst_object_ref($1) : NULL;
+                                               $$->first.name = $$->last.name = NULL;
+                                               $$->first.pads = $$->last.pads = NULL;
+                                               $$->elements = $1 ? g_slist_prepend (NULL, $1) : NULL;
                                              }
+       | bin                                 { $$=$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); }
+
+/*************************************************************
+* Grammar explanation: (cont'd)
+*   a _chain_ is a list of _elementary_s that have _link_s in between
+*   which are represented through infix-notation.
+*
+*      fakesrc ! sometransformation ! fakesink
+*
+*   every _link_ can be augmented with _pads_.
+*
+*      coffeesrc .sound ! speakersink
+*      multisrc  .movie,ads ! .projector,smallscreen multisink
+*
+*   and every _link_ can be setup to filter media-types
+*      mediasrc ! audio/x-raw, signed=TRUE ! stereosink
+*
+* User HINT:
+*   if the lexer does not recognize your media-type it
+*   will make it an element name. that results in errors
+*   like
+*      NO SUCH ELEMENT: no element audio7x-raw
+*   '7' vs. '/' in https://en.wikipedia.org/wiki/QWERTZ
+*
+* Parsing HINT:
+*   in the parser we need to differ between chains that can
+*   be extended by more elementaries (_openchain_) and others
+*   that are syntactically closed (handled later in this file).
+*      (e.g. fakesrc ! sinkreferencename.padname)
+**************************************************************/
+chain: openchain                             { $$=$1;
+                                               if($$->last.name){
+                                                       SET_ERROR (graph->error, GST_PARSE_ERROR_SYNTAX,
+                                                       _("unexpected reference \"%s\" - ignoring"), $$->last.name);
+                                                       gst_parse_strfree($$->last.name);
+                                                       $$->last.name=NULL;
+                                               }
+                                               if($$->last.pads){
+                                                       SET_ERROR (graph->error, GST_PARSE_ERROR_SYNTAX,
+                                                       _("unexpected pad-reference \"%s\" - ignoring"), (gchar*)$$->last.pads->data);
+                                                       g_slist_foreach ($$->last.pads, (GFunc) gst_parse_strfree, NULL);
+                                                       g_slist_free ($$->last.pads);
+                                                       $$->last.pads=NULL;
+                                               }
+                                             }
        ;
 
-linkpart:      reference                     { $$ = $1; }
-       |       pads                          { MAKE_REF ($$, NULL, $1); }
-       |       /* NOP */                     { MAKE_REF ($$, NULL, NULL); }
+openchain:
+       elementary pads                       { $$=$1;
+                                               $$->last.pads = g_slist_concat ($$->last.pads, $2);
+                                               /* g_print ("@%p@%p: FKI elementary pads\n", $1, $$->last.pads); */
+                                             }
+       | openchain link pads elementary pads
+                                             {
+                                               $2->src  = $1->last;
+                                               $2->sink = $4->first;
+                                               $2->sink.pads = g_slist_concat ($3, $2->sink.pads);
+                                               TRY_SETUP_LINK($2);
+                                               $4->first = $1->first;
+                                               $4->elements = g_slist_concat ($1->elements, $4->elements);
+                                               gst_parse_chain_free($1);
+                                               $$ = $4;
+                                               $$->last.pads = g_slist_concat ($$->last.pads, $5);
+                                             }
        ;
-       
-link:          linkpart LINK linkpart        { $$ = $1;
-                                               if ($2) {
-                                                 $$->caps = gst_caps_from_string ($2);
+
+link:  LINK                                  { $$ = gst_parse_link_new ();
+                                               $$->all_pads = FALSE;
+                                               if ($1) {
+                                                 $$->caps = gst_caps_from_string ($1);
+                                                 if ($$->caps == NULL)
+                                                   SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("could not parse caps \"%s\""), $1);
+                                                 gst_parse_strfree ($1);
+                                               }
+                                             }
+       | LINK_ALL                            { $$ = gst_parse_link_new ();
+                                               $$->all_pads = TRUE;
+                                               if ($1) {
+                                                 $$->caps = gst_caps_from_string ($1);
                                                  if ($$->caps == NULL)
-                                                   SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("could not parse caps \"%s\""), $2);
-                                                 gst_parse_strfree ($2);
+                                                   SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("could not parse caps \"%s\""), $1);
+                                                 gst_parse_strfree ($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); }
-       |       linklist error                { $$ = $1; }
-       ;       
-
-chain:         element                       { $$ = gst_parse_chain_new ();
-                                               $$->first = $$->last = $1;
-                                               $$->front = $$->back = NULL;
-                                               $$->elements = g_slist_prepend (NULL, $1);
+pads:          /* NOP */                     { $$ = NULL; }
+       |       PADREF morepads               { $$ = $2;
+                                               $$ = g_slist_prepend ($$, $1);
                                              }
-       |       bin                           { $$ = $1; }
-       |       chain chain                   { if ($1->back && $2->front) {
-                                                 if (!$1->back->sink_name) {
-                                                   SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("link without source element"));
-                                                   gst_parse_free_link ($1->back);
-                                                 } else {
-                                                   graph->links = g_slist_prepend (graph->links, $1->back);
-                                                 }
-                                                 if (!$2->front->src_name) {
-                                                   SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("link without sink element"));
-                                                   gst_parse_free_link ($2->front);
-                                                 } else {
-                                                   graph->links = g_slist_prepend (graph->links, $2->front);
-                                                 }
-                                                 $1->back = NULL;
-                                               } else if ($1->back) {
-                                                 if (!$1->back->sink_name) {
-                                                   $1->back->sink = $2->first;
-                                                 }
-                                               } else if ($2->front) {
-                                                 if (!$2->front->src_name) {
-                                                   $2->front->src = $1->last;
-                                                 }
-                                                 $1->back = $2->front;
-                                               }
-                                               
-                                               if ($1->back) {
-                                                 graph->links = g_slist_prepend (graph->links, $1->back);
+       ;
+morepads:      /* NOP */                     { $$ = NULL; }
+       |       ',' IDENTIFIER morepads       { $$ = g_slist_prepend ($3, $2); }
+       ;
+
+/*************************************************************
+* Grammar explanation: (cont'd)
+*   the first and last elements of a _chain_ can be give
+*   as URL. This creates special elements that fit the URL.
+*
+*      fakesrc ! http://fake-sink.org
+*       http://somesource.org ! fakesink
+**************************************************************/
+
+chain: openchain link PARSE_URL              { GstElement *element =
+                                                         gst_element_make_from_uri (GST_URI_SINK, $3, NULL, NULL);
+                                               /* FIXME: get and parse error properly */
+                                               if (!element) {
+                                                 SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT,
+                                                         _("no sink element for URI \"%s\""), $3);
                                                }
-                                               $1->last = $2->last;
-                                               $1->back = $2->back;
-                                               $1->elements = g_slist_concat ($1->elements, $2->elements);
-                                               if ($2)
-                                                 gst_parse_chain_free ($2);
                                                $$ = $1;
+                                               $2->sink.element = element?gst_object_ref(element):NULL;
+                                               $2->src = $1->last;
+                                               TRY_SETUP_LINK($2);
+                                               $$->last.element = NULL;
+                                               $$->last.name = NULL;
+                                               $$->last.pads = NULL;
+                                               if(element) $$->elements = g_slist_append ($$->elements, element);
+                                               g_free ($3);
                                              }
-       |       chain linklist                { GSList *walk;
-                                               if ($1->back) {
-                                                 $2 = g_slist_prepend ($2, $1->back);
-                                                 $1->back = NULL;
-                                               } else {
-                                                 if (!((link_t *) $2->data)->src_name) {
-                                                   ((link_t *) $2->data)->src = $1->last;
-                                                 }                                               
-                                               }
-                                               for (walk = $2; walk; walk = walk->next) {
-                                                 link_t *link = (link_t *) walk->data;
-                                                 if (!link->sink_name && walk->next) {
-                                                   SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("link without sink element"));
-                                                   gst_parse_free_link (link);
-                                                 } else if (!link->src_name && !link->src) {
-                                                   SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("link without source element"));
-                                                   gst_parse_free_link (link);
-                                                 } else {
-                                                   if (walk->next) {
-                                                     graph->links = g_slist_prepend (graph->links, link);
-                                                   } else {
-                                                     $1->back = link;
-                                                   }
-                                                 }
+       ;
+openchain:
+       PARSE_URL                             { GstElement *element =
+                                                         gst_element_make_from_uri (GST_URI_SRC, $1, NULL, NULL);
+                                               /* FIXME: get and parse error properly */
+                                               if (!element) {
+                                                 SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT,
+                                                   _("no source element for URI \"%s\""), $1);
                                                }
-                                               g_slist_free ($2);
-                                               $$ = $1;
+                                               $$ = gst_parse_chain_new ();
+                                               /* g_print ("@%p: CHAINing srcURL\n", $$); */
+                                               $$->first.element = NULL;
+                                               $$->first.name = NULL;
+                                               $$->first.pads = NULL;
+                                               $$->last.element = element ? gst_object_ref(element):NULL;
+                                               $$->last.name = NULL;
+                                               $$->last.pads = NULL;
+                                               $$->elements = element ? g_slist_prepend (NULL, element)  : NULL;
+                                               g_free($1);
+                                             }
+       ;
+
+
+/*************************************************************
+* Grammar explanation: (cont'd)
+*   the first and last elements of a _chain_ can be linked
+*   to a named _reference_ (with optional pads).
+*
+*      fakesrc ! nameOfSinkElement.
+*      fakesrc ! nameOfSinkElement.Padname
+*      fakesrc ! nameOfSinkElement.Padname, anotherPad
+*      nameOfSource.Padname ! fakesink
+**************************************************************/
+
+chain: openchain link reference              { $$ = $1;
+                                               $2->sink= $3;
+                                               $2->src = $1->last;
+                                               TRY_SETUP_LINK($2);
+                                               $$->last.element = NULL;
+                                               $$->last.name = NULL;
+                                               $$->last.pads = NULL;
                                              }
-       |       chain error                   { $$ = $1; }
-       |       link chain                    { if ($2->front) {
-                                                 if (!$2->front->src_name) {
-                                                   SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("link without source element"));
-                                                   gst_parse_free_link ($2->front);
-                                                 } else {
-                                                   graph->links = g_slist_prepend (graph->links, $2->front);
-                                                 }
+       ;
+
+
+openchain:
+       reference                             { $$ = gst_parse_chain_new ();
+                                               $$->last=$1;
+                                               $$->first.element = NULL;
+                                               $$->first.name = NULL;
+                                               $$->first.pads = NULL;
+                                               $$->elements = NULL;
+                                             }
+       ;
+reference:     REF morepads                  {
+                                               gchar *padname = $1;
+                                               GSList *pads = $2;
+                                               if (padname) {
+                                                 while (*padname != '.') padname++;
+                                                 *padname = '\0';
+                                                 padname++;
+                                                 if (*padname != '\0')
+                                                   pads = g_slist_prepend (pads, gst_parse_strdup (padname));
                                                }
-                                               if (!$1->sink_name) {
-                                                 $1->sink = $2->first;
+                                               $$.element=NULL;
+                                               $$.name=$1;
+                                               $$.pads=pads;
+                                             }
+       ;
+
+
+/*************************************************************
+* Grammar explanation: (cont'd)
+*   a _chainlist_ is just a list of _chain_s.
+*
+*   You can specify _link_s with named
+*   _reference_ on each side. That
+*   works already after the explanations above.
+*      someSourceName.Pad ! someSinkName.
+*      someSourceName.Pad,anotherPad ! someSinkName.Apad,Bpad
+*
+*   If a syntax error occurs, the already finished _chain_s
+*   and _links_ are kept intact.
+*************************************************************/
+
+chainlist: /* NOP */                         { $$ = NULL; }
+       | chainlist chain                     { if ($1){
+                                                 gst_parse_free_reference(&($1->last));
+                                                 gst_parse_free_reference(&($2->first));
+                                                 $2->first = $1->first;
+                                                 $2->elements = g_slist_concat ($1->elements, $2->elements);
+                                                 gst_parse_chain_free ($1);
                                                }
-                                               $2->front = $1;
                                                $$ = $2;
                                              }
-       |       PARSE_URL chain               { $$ = $2;
-                                               if ($$->front) {
-                                                 GstElement *element = 
-                                                         gst_element_make_from_uri (GST_URI_SRC, $1, NULL);
-                                                 if (!element) {
-                                                   SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, 
-                                                           _("no source element for URI \"%s\""), $1);
-                                                 } else {
-                                                   $$->front->src = element;
-                                                   graph->links = g_slist_prepend (
-                                                           graph->links, $$->front);
-                                                   $$->front = NULL;
-                                                   $$->elements = g_slist_prepend ($$->elements, element);
-                                                 }
-                                               } else {
-                                                 SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, 
-                                                         _("no element to link URI \"%s\" to"), $1);
-                                               }
-                                               g_free ($1);
+       | chainlist error                     { $$=$1;
+                                               GST_CAT_DEBUG (GST_CAT_PIPELINE,"trying to recover from syntax error");
+                                               SET_ERROR (graph->error, GST_PARSE_ERROR_SYNTAX, _("syntax error"));
                                              }
-       |       link PARSE_URL                { GstElement *element =
-                                                         gst_element_make_from_uri (GST_URI_SINK, $2, NULL);
-                                               if (!element) {
-                                                 SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, 
-                                                         _("no sink element for URI \"%s\""), $2);
-                                                 gst_parse_link_free ($1);
-                                                 g_free ($2);
-                                                 YYERROR;
-                                               } else if ($1->sink_name || $1->sink_pads) {
-                                                  gst_object_unref (element);
-                                                 SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, 
-                                                         _("could not link sink element for URI \"%s\""), $2);
-                                                 gst_parse_link_free ($1);
-                                                 g_free ($2);
-                                                 YYERROR;
+       ;
+
+/*************************************************************
+* Grammar explanation: (cont'd)
+*   _bins_
+*************************************************************/
+
+
+assignments:   /* NOP */                     { $$ = NULL; }
+       |       ASSIGNMENT assignments        { $$ = g_slist_prepend ($2, $1); }
+       ;
+
+binopener:     '('                           { $$ = gst_parse_strdup("bin"); }
+       |       BINREF                        { $$ = $1; }
+       ;
+bin:   binopener assignments chainlist ')'   {
+                                               chain_t *chain = $3;
+                                               GSList *walk;
+                                               GstBin *bin = (GstBin *) gst_element_factory_make ($1, NULL);
+                                               if (!chain) {
+                                                 SET_ERROR (graph->error, GST_PARSE_ERROR_EMPTY_BIN,
+                                                   _("specified empty bin \"%s\", not allowed"), $1);
+                                                 chain = gst_parse_chain_new ();
+                                                 chain->first.element = chain->last.element = NULL;
+                                                 chain->first.name    = chain->last.name    = NULL;
+                                                 chain->first.pads    = chain->last.pads    = NULL;
+                                                 chain->elements = NULL;
+                                               }
+                                               if (!bin) {
+                                                 add_missing_element(graph, $1);
+                                                 SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT,
+                                                   _("no bin \"%s\", unpacking elements"), $1);
+                                                 /* clear property-list */
+                                                 g_slist_foreach ($2, (GFunc) gst_parse_strfree, NULL);
+                                                 g_slist_free ($2);
+                                                 $2 = NULL;
                                                } else {
-                                                 $$ = gst_parse_chain_new ();
-                                                 $$->first = $$->last = element;
-                                                 $$->front = $1;
-                                                 $$->front->sink = element;
-                                                 $$->elements = g_slist_prepend (NULL, element);
+                                                 for (walk = chain->elements; walk; walk = walk->next )
+                                                   gst_bin_add (bin, GST_ELEMENT (walk->data));
+                                                 g_slist_free (chain->elements);
+                                                 chain->elements = g_slist_prepend (NULL, bin);
                                                }
-                                               g_free ($2);
+                                               $$ = chain;
+                                               /* set the properties now
+                                                * HINT: property-list cleared above, if bin==NULL
+                                                */
+                                               for (walk = $2; walk; walk = walk->next)
+                                                 gst_parse_element_set ((gchar *) walk->data,
+                                                       GST_ELEMENT (bin), graph);
+                                               g_slist_free ($2);
+                                               gst_parse_strfree ($1);
                                              }
        ;
-graph:         /* NOP */                     { SET_ERROR (graph->error, GST_PARSE_ERROR_EMPTY, _("empty pipeline not allowed"));
-                                               $$ = graph;
-                                             }
-       |       chain                         { $$ = graph;
-                                               if ($1->front) {
-                                                 if (!$1->front->src_name) {
-                                                   SET_ERROR (graph->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) {
-                                                   SET_ERROR (graph->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;
-                                               }
+
+/*************************************************************
+* Grammar explanation: (cont'd)
+*   _graph_
+*************************************************************/
+
+graph: chainlist                             { $$ = graph;
                                                $$->chain = $1;
+                                               if(!$1) {
+                                                 SET_ERROR (graph->error, GST_PARSE_ERROR_EMPTY, _("empty pipeline not allowed"));
+                                               }
                                              }
        ;
 
@@ -835,13 +1140,12 @@ yyerror (void *scanner, graph_t *graph, const char *s)
 
 
 GstElement *
-_gst_parse_launch (const gchar *str, GError **error, GstParseContext *ctx,
+priv_gst_parse_launch (const gchar *str, GError **error, GstParseContext *ctx,
     GstParseFlags flags)
 {
   graph_t g;
   gchar *dstr;
   GSList *walk;
-  GstBin *bin = NULL;
   GstElement *ret;
   yyscan_t scanner;
 
@@ -853,104 +1157,109 @@ _gst_parse_launch (const gchar *str, GError **error, GstParseContext *ctx,
   g.error = error;
   g.ctx = ctx;
   g.flags = flags;
-  
+
 #ifdef __GST_PARSE_TRACE
   GST_CAT_DEBUG (GST_CAT_PIPELINE, "TRACE: tracing enabled");
   __strings = __chains = __links = 0;
 #endif /* __GST_PARSE_TRACE */
 
+  /* g_print("Now scanning: %s\n", str); */
+
   dstr = g_strdup (str);
-  _gst_parse_yylex_init (&scanner);
-  _gst_parse_yy_scan_string (dstr, scanner);
+  priv_gst_parse_yylex_init (&scanner);
+  priv_gst_parse_yy_scan_string (dstr, scanner);
 
-#ifndef YYDEBUG
+#if YYDEBUG
   yydebug = 1;
 #endif
 
   if (yyparse (scanner, &g) != 0) {
     SET_ERROR (error, GST_PARSE_ERROR_SYNTAX,
         "Unrecoverable syntax error while parsing pipeline %s", str);
-    
-    _gst_parse_yylex_destroy (scanner);
+
+    priv_gst_parse_yylex_destroy (scanner);
     g_free (dstr);
-  
+
     goto error1;
   }
-  _gst_parse_yylex_destroy (scanner);
+  priv_gst_parse_yylex_destroy (scanner);
   g_free (dstr);
-  
+
   GST_CAT_DEBUG (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 (!g.chain->elements->next) {
-    /* only one toplevel element */  
-    ret = (GstElement *) g.chain->elements->data;
-    g_slist_free (g.chain->elements);
-    if (GST_IS_BIN (ret))
-      bin = GST_BIN (ret);
-    gst_parse_chain_free (g.chain);
-  } else {  
-    /* put all elements in our bin */
-    bin = GST_BIN (gst_element_factory_make ("pipeline", NULL));
+
+  /* ensure chain is not NULL */
+  if (!g.chain){
+    g.chain=gst_parse_chain_new ();
+    g.chain->elements=NULL;
+    g.chain->first.element=NULL;
+    g.chain->first.name=NULL;
+    g.chain->first.pads=NULL;
+    g.chain->last.element=NULL;
+    g.chain->last.name=NULL;
+    g.chain->last.pads=NULL;
+  };
+
+  /* ensure elements is not empty */
+  if(!g.chain->elements){
+    g.chain->elements= g_slist_prepend (NULL, NULL);
+  };
+
+  /* put all elements in our bin if necessary */
+  if(g.chain->elements->next){
+    GstBin *bin;
+    if (flags & GST_PARSE_FLAG_PLACE_IN_BIN)
+      bin = GST_BIN (gst_element_factory_make ("bin", NULL));
+    else
+      bin = GST_BIN (gst_element_factory_make ("pipeline", NULL));
     g_assert (bin);
-    
+
     for (walk = g.chain->elements; walk; walk = walk->next) {
       if (walk->data != NULL)
         gst_bin_add (bin, GST_ELEMENT (walk->data));
     }
-    
     g_slist_free (g.chain->elements);
-    ret = GST_ELEMENT (bin);
-    gst_parse_chain_free (g.chain);
+    g.chain->elements = g_slist_prepend (NULL, bin);
   }
-  
-  /* remove links */
+
+  ret = (GstElement *) g.chain->elements->data;
+  g_slist_free (g.chain->elements);
+  g.chain->elements=NULL;
+  gst_parse_free_chain (g.chain);
+  g.chain = NULL;
+
+
+  /* resolve and perform links */
   for (walk = g.links; walk; walk = walk->next) {
     link_t *l = (link_t *) walk->data;
-    if (!l->src) {
-      if (l->src_name) {
-        if (bin) {
-          l->src = gst_bin_get_by_name_recurse_up (bin, l->src_name);
-          if (l->src)
-            gst_object_unref (l->src);
-        } else {
-          l->src = strcmp (GST_ELEMENT_NAME (ret), l->src_name) == 0 ? ret : NULL;
-        }
-      }
-      if (!l->src) {
-        if (l->src_name) {
+    int err;
+    err=gst_resolve_reference( &(l->src), ret);
+    if (err) {
+       if(-1==err){
           SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT,
-              "No element named \"%s\" - omitting link", l->src_name);
-        } else {
+              "No src-element named \"%s\" - omitting link", l->src.name);
+       }else{
           /* probably a missing element which we've handled already */
-        }
-        gst_parse_free_link (l);
-        continue;
-      }
+          SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT,
+              "No src-element found - omitting link");
+       }
+       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);
-          if (l->sink)
-            gst_object_unref (l->sink);
-        } else {
-          l->sink = strcmp (GST_ELEMENT_NAME (ret), l->sink_name) == 0 ? ret : NULL;
-        }
-      }
-      if (!l->sink) {
-        if (l->sink_name) {
+
+    err=gst_resolve_reference( &(l->sink), ret);
+    if (err) {
+       if(-1==err){
           SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT,
-              "No element named \"%s\" - omitting link", l->sink_name);
-        } else {
+              "No sink-element named \"%s\" - omitting link", l->src.name);
+       }else{
           /* probably a missing element which we've handled already */
-        }
-        gst_parse_free_link (l);
-        continue;
-      }
+          SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT,
+              "No sink-element found - omitting link");
+       }
+       gst_parse_free_link (l);
+       continue;
     }
     gst_parse_perform_link (l, &g);
   }
@@ -968,20 +1277,19 @@ out:
 #endif /* __GST_PARSE_TRACE */
 
   return ret;
-  
+
 error1:
   if (g.chain) {
-    g_slist_foreach (g.chain->elements, (GFunc)gst_object_unref, NULL);
-    g_slist_free (g.chain->elements);
-    gst_parse_chain_free (g.chain);
+    gst_parse_free_chain (g.chain);
+    g.chain=NULL;
   }
 
   g_slist_foreach (g.links, (GFunc)gst_parse_free_link, NULL);
   g_slist_free (g.links);
-  
+
   if (error)
     g_assert (*error);
   ret = NULL;
-  
+
   goto out;
 }