2 #include <glib-object.h>
7 #include "../gstparse.h"
8 #include "../gstinfo.h"
15 #define YYERROR_VERBOSE 1
16 #define YYPARSE_PARAM graph
18 #ifdef __GST_PARSE_TRACE
19 static uint __strings;
23 __gst_parse_strdup (gchar *org)
26 /* g_print ("ALLOCATED: %p %s\n", org, org); */
27 return g_strdup (org);
30 __gst_parse_strfree (gchar *str)
33 /* g_print ("FREEING : %p %s\n", str, str); */
35 g_return_if_fail (__strings > 0);
39 link_t *__gst_parse_link_new ()
42 return g_new0 (link_t, 1);
45 __gst_parse_link_free (link_t *data)
49 g_return_if_fail (__links > 0);
54 __gst_parse_chain_new ()
57 return g_new0 (chain_t, 1);
60 __gst_parse_chain_free (chain_t *data)
64 g_return_if_fail (__chains > 0);
69 #endif /* __GST_PARSE_TRACE */
77 /* FIXME: need to connect to "disposed" signal to clean up, but there is no such signal */
80 #ifdef G_HAVE_ISO_VARARGS
81 #define SET_ERROR(error, type, ...) G_STMT_START{ \
84 g_warning (__VA_ARGS__); \
86 g_set_error ((error), GST_PARSE_ERROR, (type), __VA_ARGS__); \
90 #define ERROR(type, ...) SET_ERROR (((graph_t *) graph)->error, (type), __VA_ARGS__ )
91 #ifdef GST_DEBUG_ENABLED
93 # define YYFPRINTF(a, ...) GST_DEBUG (GST_CAT_PIPELINE, __VA_ARGS__)
96 #elif defined(G_HAVE_GNUC_VARARGS)
98 #define SET_ERROR(error, type, args...) G_STMT_START{ \
101 g_warning ( ## args ); \
103 g_set_error ((error), GST_PARSE_ERROR, (type), ## args ); \
107 #define ERROR(type, args...) SET_ERROR (((graph_t *) graph)->error, (type), ## args )
108 #ifdef GST_DEBUG_ENABLED
110 # define YYFPRINTF(a, args...) GST_DEBUG (GST_CAT_PIPELINE, ## args )
115 #define SET_ERROR(error, type, ...) G_STMT_START{ \
118 g_warning ("error while parsing"); \
120 g_set_error ((error), GST_PARSE_ERROR, (type), "error while parsing"); \
124 #define ERROR(type, ...) SET_ERROR (((graph_t *) graph)->error, (type), "error while parsing")
125 #ifdef GST_DEBUG_ENABLED
129 #endif // G_HAVE_ISO_VARARGS
131 #define GST_BIN_MAKE(res, type, chain, assign) G_STMT_START{ \
133 GstBin *bin = (GstBin *) gst_element_factory_make (type, NULL); \
135 ERROR (GST_PARSE_ERROR_NO_SUCH_ELEMENT, "No bin \"%s\"", type); \
137 walk = chain->elements; \
139 gst_bin_add (bin, GST_ELEMENT (walk->data)); \
142 g_slist_free (chain->elements); \
143 chain->elements = g_slist_prepend (NULL, bin); \
145 /* set the properties now */ \
148 gst_parse_element_set ((gchar *) walk->data, GST_ELEMENT (bin), graph); \
149 walk = g_slist_next (walk); \
151 g_slist_free (assign); \
155 #define MAKE_LINK(link, _src, _src_name, _src_pads, _sink, _sink_name, _sink_pads) G_STMT_START{ \
156 link = gst_parse_link_new (); \
158 link->sink = _sink; \
159 link->src_name = _src_name; \
160 link->sink_name = _sink_name; \
161 link->src_pads = _src_pads; \
162 link->sink_pads = _sink_pads; \
166 #define MAKE_REF(link, _src, _pads) G_STMT_START{ \
167 gchar *padname = _src; \
168 GSList *pads = _pads; \
170 while (*padname != '.') padname++; \
173 if (*padname != '\0') \
174 pads = g_slist_prepend (pads, gst_parse_strdup (padname)); \
176 MAKE_LINK (link, NULL, _src, pads, NULL, NULL, NULL); \
179 static inline void gst_parse_unescape (gchar *str)
183 g_return_if_fail (str != NULL);
197 gst_parse_element_set (gchar *value, GstElement *element, graph_t *graph)
201 /* parse the string, so the property name is null-terminated an pos points
202 to the beginning of the value */
203 while (!g_ascii_isspace (*pos) && (*pos != '=')) pos++;
209 while (g_ascii_isspace (*pos)) pos++;
212 while (g_ascii_isspace (*pos)) pos++;
215 pos[strlen (pos) - 1] = '\0';
217 gst_parse_unescape (pos);
218 if ((pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), value))) {
221 g_value_init (&v, G_PARAM_SPEC_VALUE_TYPE(pspec));
222 switch (G_TYPE_FUNDAMENTAL (G_PARAM_SPEC_VALUE_TYPE (pspec))) {
224 g_value_set_string (&v, pos);
227 if (g_ascii_strcasecmp (pos, "true") && g_ascii_strcasecmp (pos, "yes") && g_ascii_strcasecmp (pos, "1")) {
228 g_value_set_boolean (&v, FALSE);
230 g_value_set_boolean (&v, TRUE);
235 gchar **endptr = NULL;
236 GEnumClass *klass = (GEnumClass *) g_type_class_peek (G_PARAM_SPEC_VALUE_TYPE (pspec));
237 if (klass == NULL) goto error;
238 if (!(en = g_enum_get_value_by_name (klass, pos)))
239 en = g_enum_get_value_by_nick (klass, pos);
241 g_value_set_enum (&v, en->value);
243 gint i = strtol (value, endptr, 0);
244 if (**endptr == '\0') {
245 g_value_set_enum (&v, i);
255 g_value_init (&v2, G_TYPE_LONG);
256 g_value_set_long (&v2, strtol (pos, NULL, 0));
257 if (!g_value_transform (&v2, &v)) goto error;
262 g_value_init (&v2, G_TYPE_ULONG);
263 g_value_set_long (&v2, strtoul (pos, NULL, 0));
264 if (!g_value_transform (&v2, &v)) goto error;
268 g_value_init (&v2, G_TYPE_DOUBLE);
269 g_value_set_double (&v2, atof (pos));
270 if (!g_value_transform (&v2, &v)) goto error;
274 g_warning ("property \"%s\" in element %s cannot be set", value, GST_ELEMENT_NAME (element));
277 g_object_set_property (G_OBJECT (element), value, &v);
279 ERROR (GST_PARSE_ERROR_NO_SUCH_PROPERTY, "No property \"%s\" in element \"%s\"", value, GST_ELEMENT_NAME (element));
283 gst_parse_strfree (value);
287 ERROR (GST_PARSE_ERROR_COULD_NOT_SET_PROPERTY, "Could not set property \"%s\" in element \"%s\" to \"%s\"", value, GST_ELEMENT_NAME (element), pos);
291 gst_parse_free_link (link_t *link)
293 gst_parse_strfree (link->src_name);
294 gst_parse_strfree (link->sink_name);
295 g_slist_foreach (link->src_pads, (GFunc) gst_parse_strfree, NULL);
296 g_slist_foreach (link->sink_pads, (GFunc) gst_parse_strfree, NULL);
297 g_slist_free (link->src_pads);
298 g_slist_free (link->sink_pads);
299 gst_caps_unref (link->caps);
300 gst_parse_link_free (link);
303 gst_parse_element_lock (GstElement *element, gboolean lock)
306 GList *walk = (GList *) gst_element_get_pad_list (element);
307 gboolean unlocked_peer = FALSE;
309 if (gst_element_is_state_locked (element) == lock)
311 /* check if we have an unlocked peer */
313 pad = (GstPad *) GST_PAD_REALIZE (walk->data);
315 if (GST_PAD_IS_SINK (pad) && GST_PAD_PEER (pad) &&
316 !gst_element_is_state_locked (GST_PAD_PARENT (GST_PAD_PEER (pad)))) {
317 unlocked_peer = TRUE;
322 if (lock && !unlocked_peer) {
323 gst_element_lock_state (element);
325 gst_element_unlock_state (element);
330 /* check if there are other pads to (un)lock */
331 walk = (GList *) gst_element_get_pad_list (element);
333 pad = (GstPad *) GST_PAD_REALIZE (walk->data);
335 if (GST_PAD_IS_SRC (pad) && GST_PAD_PEER (pad)) {
336 GstElement *next = GST_ELEMENT (GST_OBJECT_PARENT (GST_PAD_PEER (pad)));
337 if (gst_element_is_state_locked (next) != lock)
338 gst_parse_element_lock (next, lock);
343 gst_parse_found_pad (GstElement *src, GstPad *pad, gpointer data)
345 DelayedLink *link = (DelayedLink *) data;
346 gboolean restart = FALSE;
348 GST_INFO (GST_CAT_PIPELINE, "trying delayed linking %s:%s to %s:%s",
349 GST_ELEMENT_NAME (src), link->src_pad,
350 GST_ELEMENT_NAME (link->sink), link->sink_pad);
351 if (gst_element_get_state (src) == GST_STATE_PLAYING) {
353 gst_element_set_state (src, GST_STATE_PAUSED);
356 if (gst_element_link_pads_filtered (src, link->src_pad, link->sink, link->sink_pad, link->caps)) {
357 /* do this here, we don't want to get any problems later on when unlocking states */
358 GST_DEBUG (GST_CAT_PIPELINE, "delayed linking %s:%s to %s:%s worked",
359 GST_ELEMENT_NAME (src), link->src_pad,
360 GST_ELEMENT_NAME (link->sink), link->sink_pad);
362 gst_element_set_state (src, GST_STATE_PLAYING);
364 g_signal_handler_disconnect (src, link->signal_id);
365 g_free (link->src_pad);
366 g_free (link->sink_pad);
367 gst_caps_unref (link->caps);
368 if (!gst_element_is_state_locked (src))
369 gst_parse_element_lock (link->sink, FALSE);
373 gst_element_set_state (src, GST_STATE_PLAYING);
377 /* both padnames and the caps may be NULL */
379 gst_parse_perform_delayed_link (GstElement *src, const gchar *src_pad,
380 GstElement *sink, const gchar *sink_pad, GstCaps *caps)
382 GList *templs = gst_element_get_pad_template_list (src);
385 GstPadTemplate *templ = (GstPadTemplate *) templs->data;
386 if ((GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC) && (GST_PAD_TEMPLATE_PRESENCE(templ) == GST_PAD_SOMETIMES))
388 /* TODO: maybe we should check if src_pad matches this template's names */
390 GST_DEBUG (GST_CAT_PIPELINE, "trying delayed link %s:%s to %s:%s",
391 GST_ELEMENT_NAME (src), src_pad, GST_ELEMENT_NAME (sink), sink_pad);
393 DelayedLink *data = g_new (DelayedLink, 1);
394 data->src_pad = g_strdup (src_pad);
396 data->sink_pad = g_strdup (sink_pad);
397 data->caps = gst_caps_ref (caps);
398 data->signal_id = g_signal_connect (G_OBJECT (src), "new_pad",
399 G_CALLBACK (gst_parse_found_pad), data);
402 templs = g_list_next (templs);
407 * performs a link and frees the struct. src and sink elements must be given
408 * return values: 0 - link performed
413 gst_parse_perform_link (link_t *link, graph_t *graph)
415 GstElement *src = link->src;
416 GstElement *sink = link->sink;
417 GSList *srcs = link->src_pads;
418 GSList *sinks = link->sink_pads;
419 g_assert (GST_IS_ELEMENT (src));
420 g_assert (GST_IS_ELEMENT (sink));
422 GST_INFO (GST_CAT_PIPELINE, "linking %s(%s):%u to %s(%s):%u",
423 GST_ELEMENT_NAME (src), link->src_name ? link->src_name : "---", g_slist_length (srcs),
424 GST_ELEMENT_NAME (sink), link->sink_name ? link->sink_name : "---", g_slist_length (sinks));
426 if (!srcs || !sinks) {
427 if (gst_element_link_pads_filtered (src, srcs ? (const gchar *) srcs->data : NULL,
428 sink, sinks ? (const gchar *) sinks->data : NULL,
430 gst_parse_element_lock (sink, gst_element_is_state_locked (src));
433 if (gst_parse_perform_delayed_link (src, srcs ? (const gchar *) srcs->data : NULL,
434 sink, sinks ? (const gchar *) sinks->data : NULL,
436 gst_parse_element_lock (sink, TRUE);
443 if (g_slist_length (link->src_pads) != g_slist_length (link->src_pads)) {
446 while (srcs && sinks) {
447 const gchar *src_pad = (const gchar *) srcs->data;
448 const gchar *sink_pad = (const gchar *) sinks->data;
449 srcs = g_slist_next (srcs);
450 sinks = g_slist_next (sinks);
451 if (gst_element_link_pads_filtered (src, src_pad, sink, sink_pad, link->caps)) {
452 gst_parse_element_lock (sink, gst_element_is_state_locked (src));
455 if (gst_parse_perform_delayed_link (src, src_pad,
458 gst_parse_element_lock (sink, TRUE);
467 gst_parse_free_link (link);
471 ERROR (GST_PARSE_ERROR_LINK, "could not link %s to %s", GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (sink));
472 gst_parse_free_link (link);
477 static int yylex (void *lvalp);
478 static int yyerror (const char *s);
490 %token <s> IDENTIFIER
491 %left <s> REF PADREF BINREF
492 %token <s> ASSIGNMENT
497 %type <l> linkpart link
500 %type <p> padlist pads assignments
503 %left '{' '}' '(' ')'
513 element: IDENTIFIER { $$ = gst_element_factory_make ($1, NULL);
514 if (!$$) ERROR (GST_PARSE_ERROR_NO_SUCH_ELEMENT, "No element \"%s\"", $1);
515 gst_parse_strfree ($1);
517 | element ASSIGNMENT { gst_parse_element_set ($2, $1, graph);
522 assignments: /* NOP */ { $$ = NULL; }
523 | assignments ASSIGNMENT { $$ = g_slist_prepend ($1, $2); }
525 bin: '{' assignments chain '}' { GST_BIN_MAKE ($$, "thread", $3, $2); }
526 | '(' assignments chain ')' { GST_BIN_MAKE ($$, "bin", $3, $2); }
527 | BINREF assignments chain ')' { GST_BIN_MAKE ($$, $1, $3, $2);
528 gst_parse_strfree ($1);
532 pads: PADREF { $$ = g_slist_prepend (NULL, $1); }
533 | PADREF padlist { $$ = $2;
534 $$ = g_slist_prepend ($$, $1);
537 padlist: ',' IDENTIFIER { $$ = g_slist_prepend (NULL, $2); }
538 | ',' IDENTIFIER padlist { $$ = g_slist_prepend ($3, $2); }
541 reference: REF { MAKE_REF ($$, $1, NULL); }
542 | REF padlist { MAKE_REF ($$, $1, $2); }
545 linkpart: reference { $$ = $1; }
546 | pads { MAKE_REF ($$, NULL, $1); }
547 | /* NOP */ { MAKE_REF ($$, NULL, NULL); }
550 link: linkpart '!' linkpart { $$ = $1;
551 $$->sink_name = $3->src_name;
552 $$->sink_pads = $3->src_pads;
553 gst_parse_link_free ($3);
557 linklist: link { $$ = g_slist_prepend (NULL, $1); }
558 | link linklist { $$ = g_slist_prepend ($2, $1); }
561 chain: element { $$ = gst_parse_chain_new ();
562 $$->first = $$->last = $1;
563 $$->front = $$->back = NULL;
564 $$->elements = g_slist_prepend (NULL, $1);
567 | chain chain { if ($1->back && $2->front) {
568 if (!$1->back->sink_name) {
569 ERROR (GST_PARSE_ERROR_LINK, "link without source element");
570 gst_parse_free_link ($1->back);
572 ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $1->back);
574 if (!$2->front->src_name) {
575 ERROR (GST_PARSE_ERROR_LINK, "link without sink element");
576 gst_parse_free_link ($2->front);
578 ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $2->front);
581 } else if ($1->back) {
582 if (!$1->back->sink_name) {
583 $1->back->sink = $2->first;
585 ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $1->back);
588 } else if ($2->front) {
589 if (!$2->front->src_name) {
590 $2->front->src = $1->last;
591 $1->back = $2->front;
593 ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $2->front);
598 gst_parse_perform_link ($1->back, (graph_t *) graph);
601 $1->elements = g_slist_concat ($1->elements, $2->elements);
602 gst_parse_chain_free ($2);
605 | link chain { if ($2->front) {
606 if (!$2->front->src_name) {
607 ERROR (GST_PARSE_ERROR_LINK, "link without source element");
608 gst_parse_free_link ($2->front);
610 ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $2->front);
613 if (!$1->sink_name) {
614 $1->sink = $2->first;
620 | chain linklist { GSList *walk;
622 g_slist_prepend ($2, $1->back);
625 if (!((link_t *) $2->data)->src_name) {
626 ((link_t *) $2->data)->src = $1->last;
631 link_t *link = (link_t *) walk->data;
633 if (!link->sink_name) {
634 ERROR (GST_PARSE_ERROR_LINK, "link without sink element");
635 gst_parse_free_link (link);
636 } else if (!link->src_name && !link->src) {
637 ERROR (GST_PARSE_ERROR_LINK, "link without source element");
638 gst_parse_free_link (link);
641 ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, link);
652 graph: chain { $$ = (graph_t *) graph;
654 if (!$1->front->src_name) {
655 ERROR (GST_PARSE_ERROR_LINK, "link without source element");
656 gst_parse_free_link ($1->front);
658 $$->links = g_slist_prepend ($$->links, $1->front);
663 if (!$1->back->sink_name) {
664 ERROR (GST_PARSE_ERROR_LINK, "link without sink element");
665 gst_parse_free_link ($1->back);
667 $$->links = g_slist_prepend ($$->links, $1->back);
677 extern FILE *_gst_parse_yyin;
678 int _gst_parse_yylex (YYSTYPE *lvalp);
680 static int yylex (void *lvalp) {
681 return _gst_parse_yylex ((YYSTYPE*) lvalp);
685 yyerror (const char *s)
687 /* FIXME: This should go into the GError somehow, but how? */
688 g_warning ("error: %s\n", s);
692 int _gst_parse_yy_scan_string (char*);
694 _gst_parse_launch (const gchar *str, GError **error)
702 g_return_val_if_fail (str != NULL, NULL);
708 #ifdef __GST_PARSE_TRACE
709 GST_DEBUG (GST_CAT_PIPELINE, "TRACE: tracing enabled");
710 __strings = __chains = __links = 0;
711 #endif /* __GST_PARSE_TRACE */
713 dstr = g_strdup (str);
714 _gst_parse_yy_scan_string (dstr);
720 if (yyparse (&g) != 0) {
721 SET_ERROR (error, GST_PARSE_ERROR_SYNTAX, "Unrecoverable syntax error while parsing pipeline");
727 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));
731 } else if (!(((chain_t *) g.chain)->elements->next)) {
732 /* only one toplevel element */
733 ret = (GstElement *) ((chain_t *) g.chain)->elements->data;
734 g_slist_free (((chain_t *) g.chain)->elements);
735 if (GST_IS_BIN (ret))
738 /* put all elements in our bin */
739 bin = GST_BIN (gst_element_factory_make ("pipeline", NULL));
741 walk = g.chain->elements;
743 gst_bin_add (bin, GST_ELEMENT (walk->data));
744 walk = g_slist_next (walk);
746 g_slist_free (g.chain->elements);
747 ret = GST_ELEMENT (bin);
749 gst_parse_chain_free (g.chain);
754 link_t *l = (link_t *) walk->data;
756 walk = g_slist_next (walk);
760 l->src = gst_bin_get_by_name_recurse_up (bin, l->src_name);
762 l->src = strcmp (GST_ELEMENT_NAME (ret), l->src_name) == 0 ? ret : NULL;
766 SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, "No element named \"%s\" - omitting link", l->src_name);
767 gst_parse_free_link (l);
774 l->sink = gst_bin_get_by_name_recurse_up (bin, l->sink_name);
776 l->sink = strcmp (GST_ELEMENT_NAME (ret), l->sink_name) == 0 ? ret : NULL;
780 SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, "No element named \"%s\" - omitting link", l->sink_name);
781 gst_parse_free_link (l);
786 gst_parse_perform_link (l, &g);
788 g_slist_free (g.links);
791 #ifdef __GST_PARSE_TRACE
792 GST_DEBUG (GST_CAT_PIPELINE, "TRACE: %u strings, %u chains and %u links left", __strings, __chains, __links);
793 if (__strings || __chains || __links) {
794 g_warning ("TRACE: %u strings, %u chains and %u links left", __strings, __chains, __links);
796 #endif /* __GST_PARSE_TRACE */
804 walk = g.chain->elements;
806 gst_object_unref (GST_OBJECT (walk->data));
809 g_slist_free (g.chain->elements);
811 gst_parse_chain_free (g.chain);
815 gst_parse_free_link ((link_t *) walk->data);
818 g_slist_free (g.links);