diff --git a/gst/gstparse.c b/gst/gstparse.c index ec2874ac22..d893063b61 100644 --- a/gst/gstparse.c +++ b/gst/gstparse.c @@ -26,24 +26,8 @@ #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) +static gchar *_gst_parse_escape (const gchar *str) { - 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); + 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; } - -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) -{ - 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); - } - - 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; - - 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); - } - - l = g->bins; - while (l) { - if (!make_links ((graph_t*)l->data, error)) - return FALSE; - l = g_list_next (l); - } - - 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; -} - /** * 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; } diff --git a/gst/gstparse.h b/gst/gstparse.h index a56f75c164..e647bfa70b 100644 --- a/gst/gstparse.h +++ b/gst/gstparse.h @@ -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 */ diff --git a/gst/parse/grammar.y b/gst/parse/grammar.y index a86531ac51..370fad411f 100644 --- a/gst/parse/grammar.y +++ b/gst/parse/grammar.y @@ -1,14 +1,478 @@ %{ +#include #include #include #include #include #include "../gstparse.h" +#include "../gstinfo.h" #include "types.h" -#define YYDEBUG 1 +#ifdef HAVE_CONFIG_H +#include +#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 IDENTIFIER -%token LINK BLINK FLINK -%token VALUE +%left REF PADREF BINREF +%token ASSIGNMENT -%type id -%type graph bin +%type graph +%type chain bin +%type reference +%type linkpart link +%type

linklist %type element -%type

property_value value -%type link rlink +%type

padlist pads assignments + %left '{' '}' '(' ')' -%left '!' '=' %left ',' -%left '.' +%right '.' +%left '!' '=' %pure_parser %start graph %% -id: IDENTIFIER - ; +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); + } -value: VALUE { $$ = g_new0 (property_t, 1); $$->value = $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); } + ; -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); - } - ; +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); - - g_free (dstr); - - /* 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; + 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); - return g; +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 */ + + 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; + } + 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; } diff --git a/gst/parse/parse.l b/gst/parse/parse.l index 327bde1673..75237296bf 100644 --- a/gst/parse/parse.l +++ b/gst/parse/parse.l @@ -3,272 +3,98 @@ #include #include #include "types.h" -#include +#include "../gstinfo.h" +#include "grammar.tab.h" + +#ifdef HAVE_CONFIG_H +#include +#endif #ifdef G_HAVE_ISO_VARARGS - -#ifdef DEBUG -# define PRINT(...) printf(__VAR_ARGS__) -#else -#define PRINT(...) -#endif - +# 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 %% -{ - {_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; } diff --git a/gst/parse/types.h b/gst/parse/types.h index d9e8183cb3..f992198795 100644 --- a/gst/parse/types.h +++ b/gst/parse/types.h @@ -1,46 +1,69 @@ +#ifndef __GST_PARSE_TYPES_H__ +#define __GST_PARSE_TYPES_H__ + #include #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; - -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; + GstElement *src; + GstElement *sink; + gchar *src_name; + gchar *sink_name; + GSList *src_pads; + GSList *sink_pads; + GstCaps *caps; } link_t; -typedef struct _graph_t graph_t; +typedef struct { + 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__ */