cleaning up duplicated code in gstbin.c added some sanity checks in gstpad.c added...
[platform/upstream/gstreamer.git] / gst / gstparse.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * :
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /* #define DEBUG(format,args...) g_print (format, ##args) */
24 #define DEBUG(format,args...)
25 #define DEBUG_NOPREFIX(format,args...)
26 #define VERBOSE(format,args...)
27
28 #define GST_PARSE_LISTPAD(list)   ((GstPad*)(list->data))
29
30 #include <string.h>
31
32 #include "gst_private.h"
33 #include "gstparse.h"
34 #include "gstpipeline.h"
35 #include "gstthread.h"
36 #include "gstutils.h"
37
38 typedef struct _gst_parse_priv gst_parse_priv;
39 struct _gst_parse_priv
40 {
41   guint bincount;
42   guint threadcount;
43   gint binlevel;
44   GHashTable *elementcounts;
45   gboolean verbose;
46   gboolean debug;
47 };
48
49 typedef struct _gst_parse_delayed_pad gst_parse_delayed_pad;
50 struct _gst_parse_delayed_pad
51 {
52   gchar *name;
53   GstPad *peer;
54 };
55
56 typedef struct
57 {
58   gchar *srcpadname;
59   GstPad *target;
60   GstElement *pipeline;
61 }
62 dyn_connect;
63
64 static void
65 dynamic_connect (GstElement * element, GstPad * newpad, gpointer data)
66 {
67   dyn_connect *connect = (dyn_connect *) data;
68
69   if (!strcmp (gst_pad_get_name (newpad), connect->srcpadname)) {
70     gst_element_set_state (connect->pipeline, GST_STATE_PAUSED);
71     if (!gst_pad_connect (newpad, connect->target))
72       g_warning ("could not connect %s:%s to %s:%s", GST_DEBUG_PAD_NAME (newpad), 
73                  GST_DEBUG_PAD_NAME (connect->target));
74     gst_element_set_state (connect->pipeline, GST_STATE_PLAYING);
75   }
76 }
77
78 static gchar *
79 gst_parse_unique_name (const gchar * type, gst_parse_priv * priv)
80 {
81   gpointer tmp;
82   gint count;
83
84   tmp = g_hash_table_lookup (priv->elementcounts, type);
85   count = GPOINTER_TO_INT (tmp);
86   count++;
87   g_hash_table_insert (priv->elementcounts, g_strdup (type), GINT_TO_POINTER (count));
88
89   return g_strdup_printf ("%s%d", type, count - 1);
90 }
91
92 static gint
93 gst_parse_launchv_recurse (const gchar **argv, GstBin * parent, gst_parse_priv * priv)
94 {
95   gint i = -1, j = 0;
96   const gchar *arg;
97   GstElement *element = NULL, *previous = NULL, *prevelement = NULL;
98   gchar closingchar = '\0';
99   gint len;
100   const gchar *cptr;
101   gchar *ptr;
102   gchar *sinkpadname = NULL, *srcpadname = NULL, *tempname;
103   GstPad *temppad;
104   GSList *sinkpads = NULL, *srcpads = NULL;
105   gint numsrcpads = 0, numsinkpads = 0;
106   GList *pads;
107   gint elementcount = 0;
108   gint retval = 0;
109   gboolean backref = FALSE;
110
111   if (!priv) {
112     priv = g_new0 (gst_parse_priv, 1);
113     priv->elementcounts = g_hash_table_new (g_str_hash, g_str_equal);
114   }
115
116   priv->binlevel++;
117
118   if (GST_IS_PIPELINE (parent)) {
119     closingchar = '\0';
120     DEBUG ("in pipeline ");
121   } else if (GST_IS_THREAD (parent)) {
122     closingchar = '}';
123     DEBUG ("in thread ");
124   } else {
125     closingchar = ')';
126     DEBUG ("in bin ");
127   }
128   DEBUG_NOPREFIX ("%s\n", GST_ELEMENT_NAME (GST_ELEMENT (parent)));
129
130   while ((arg = argv[++i])) {
131     len = strlen (arg);
132     element = NULL;
133     DEBUG ("** ARGUMENT is '%s'\n", arg);
134
135     if (len == 0) {
136       DEBUG ("random arg, FIXME\n");
137
138       continue;
139     } else if (arg[0] == closingchar) {
140       DEBUG ("exiting container %s\n", GST_ELEMENT_NAME (GST_ELEMENT (parent)));
141
142       retval = i + 1;
143       break;
144     } else if ((cptr = strchr (arg, '!'))) {
145       DEBUG ("attempting to connect pads together....\n");
146
147       /* if it starts with the ! */
148       if (arg[0] == '!') {
149         srcpadname = NULL;
150         /* if there's a sinkpad... */
151         if (len > 1)
152           sinkpadname = g_strdup (&arg[1]);
153         else
154           sinkpadname = NULL;
155       } else {
156         srcpadname = g_strndup (arg, (cptr - arg));
157         /* if there's a sinkpad */
158         if (len > (cptr - arg) + 1)
159           sinkpadname = g_strdup(&cptr[1]);
160         else
161           sinkpadname = NULL;
162       }
163
164       if (srcpadname && (ptr = strchr (srcpadname, '.'))) {
165         gchar *element_name = srcpadname;
166         GstElement *new;
167
168         *ptr = '\0'; /* it was a '.' before */
169         
170         GST_DEBUG (0, "have pad for element %s\n", element_name);
171         new = gst_bin_get_by_name_recurse_up (parent, element_name);
172         if (!new) {
173           GST_DEBUG (0, "element %s does not exist! trying to continue\n", element_name);
174         } else {
175           previous = new;
176           srcpadname = ptr + 1;
177           backref = TRUE;
178         }
179       }
180
181       GST_DEBUG (0, "have srcpad %s, sinkpad %s\n", srcpadname, sinkpadname);
182
183       g_slist_free (srcpads);
184       srcpads = NULL;
185       numsrcpads = 0;
186       tempname = NULL;
187
188       /* find src pads */
189       if (srcpadname != NULL) {
190         while (1) {
191           /* split name at commas */
192           if ((ptr = strchr (srcpadname, ','))) {
193             tempname = srcpadname;
194             srcpadname = &ptr[1];
195             *ptr = '\0';
196           } else {
197             tempname = srcpadname;
198           }
199
200           /* look for pad with that name */
201           if ((temppad = gst_element_get_pad (previous, tempname))) {
202             srcpads = g_slist_append (srcpads, temppad);
203             numsrcpads++;
204           }
205
206           /* try to create a pad using that padtemplate name */
207           else if ((temppad = gst_element_request_pad_by_name (previous, tempname))) {
208             srcpads = g_slist_append (srcpads, temppad);
209             numsrcpads++;
210           }
211           if (!temppad) {
212             GST_DEBUG (0, "NO SUCH pad %s in element %s\n", tempname, GST_ELEMENT_NAME (previous));
213           } else {
214             GST_DEBUG (0, "have src pad %s:%s\n", GST_DEBUG_PAD_NAME (temppad));
215           }
216
217           /* if there is no more commas in srcpadname then we're done */
218           if (tempname == srcpadname)
219             break;
220           g_free (tempname);
221         }
222       } else {
223         /* check through the list to find the first src pad */
224         GST_DEBUG (0, "CHECKING element %s for pad named %s\n", GST_ELEMENT_NAME (previous),
225                    srcpadname);
226         pads = gst_element_get_pad_list (previous);
227         while (pads) {
228           temppad = GST_PARSE_LISTPAD (pads);
229           GST_DEBUG (0, "have pad %s:%s\n", GST_DEBUG_PAD_NAME (temppad));
230           if (GST_IS_GHOST_PAD (temppad))
231             GST_DEBUG (0, "it's a ghost pad\n");
232           if (gst_pad_get_direction (temppad) == GST_PAD_SRC) {
233             srcpads = g_slist_append (srcpads, temppad);
234             numsrcpads++;
235             break;
236           }
237           pads = g_list_next (pads);
238         }
239         if (!srcpads)
240           GST_DEBUG (0, "error, can't find a src pad!!!\n");
241         else
242           GST_DEBUG (0, "have src pad %s:%s\n", GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (srcpads)));
243       }
244     } else if (strchr (arg, '=')) {
245       gchar *propname;
246       gchar *propval;
247       gchar *pos;
248       
249       DEBUG ("have a property\n");
250       
251       /* we have a property */
252       propname = g_strdup (arg);
253       pos = strchr (propname, '=');
254       *pos = '\0';
255       propval = pos + 1;
256       
257       /* use g_object_set in the future when gst_util_{set|get} go away */
258       GST_DEBUG (0, "attempting to set property '%s' to '%s' on element '%s'\n",
259                  propname, propval, GST_ELEMENT_NAME (previous));
260       gst_util_set_object_arg (G_OBJECT (previous), propname, propval);
261       g_free (propname);
262     } else if (arg[0] == '[') {
263       DEBUG ("have element name\n");
264       
265       if (arg[1] != '\0') {
266         fprintf (stderr, "error, unexpected junk after [\n");
267         return GST_PARSE_ERROR_SYNTAX;
268       }
269       
270       if ((arg = argv[++i])) {
271         if (!previous) {
272           g_critical("parser error, please report");
273           return GST_PARSE_ERROR_INTERNAL;
274         }
275         gst_element_set_name (previous, arg);
276       } else {
277         fprintf (stderr, "error, expected element name, found end of arguments\n");
278         return GST_PARSE_ERROR_SYNTAX;
279       }
280       
281       if ((arg = argv[++i])) {
282         if (strcmp (arg, "]") != 0) {
283           fprintf (stderr, "error, expected ], found '%s'\n", arg);
284           return GST_PARSE_ERROR_SYNTAX;
285         }
286       } else {
287         fprintf (stderr, "error, expected ], found end of arguments\n");
288         return GST_PARSE_ERROR_SYNTAX;
289       }
290     } else {
291       /* we have an element, a bin, or a thread. we treat these together because
292        * once we rech them we have to resolve any dangling connections from
293        * previous array arguments. */
294       
295       if (strchr ("({", arg[0])) {
296         DEBUG ("have bin or thread\n");
297         
298         if (arg[0] == '(') {
299           /* create a bin and add it to the current parent */
300           element = gst_bin_new (g_strdup_printf ("bin%d", priv->bincount++));
301           if (!element) {
302             fprintf (stderr, "Couldn't create a bin!\n");
303             return GST_PARSE_ERROR_CREATING_ELEMENT;
304           }
305           GST_DEBUG (0, "CREATED bin %s\n", GST_ELEMENT_NAME (element));
306         } else if (arg[0] == '{') {
307           /* create a thread and add it to the current parent */
308           element = gst_thread_new (g_strdup_printf ("thread%d", priv->threadcount++));
309           if (!element) {
310             fprintf (stderr, "Couldn't create a thread!\n");
311             return GST_PARSE_ERROR_CREATING_ELEMENT;
312           }
313           GST_DEBUG (0, "CREATED thread %s\n", GST_ELEMENT_NAME (element));
314         } else {
315           fprintf (stderr, "Illegal argument: %s\n", arg);
316           return GST_PARSE_ERROR_CREATING_ELEMENT;
317         }
318         
319         gst_bin_add (GST_BIN (parent), element);
320         
321         j = gst_parse_launchv_recurse (argv + i + 1, GST_BIN (element), priv);
322         /* check for parse error */
323         if (j < 0)
324           return j;
325         
326         /* we will get autoincremented at the while */
327         i += j;
328         
329       } else {
330         /* we have an element */
331         DEBUG ("attempting to create element '%s'\n", arg);
332         
333         ptr = gst_parse_unique_name (arg, priv);
334         element = gst_elementfactory_make (arg, ptr);
335         g_free (ptr);
336         if (!element) {
337 #ifndef GST_DISABLE_REGISTRY
338           fprintf (stderr,
339                    "Couldn't create a '%s', no such element or need to run gst-register?\n", arg);
340 #else
341           fprintf (stderr, "Couldn't create a '%s', no such element or need to load plugin?\n",
342                    arg);
343 #endif
344           return GST_PARSE_ERROR_NOSUCH_ELEMENT;
345         }
346         
347         GST_DEBUG (0, "CREATED element %s\n", GST_ELEMENT_NAME (element));
348         gst_bin_add (GST_BIN (parent), element);
349       }
350       
351       elementcount++;
352       
353       g_slist_free (sinkpads);
354       sinkpads = NULL;
355       numsinkpads = 0;
356       tempname = NULL;
357       
358       /* find sink pads */
359       if (sinkpadname != NULL) {
360         while (1) {
361           /* split name at commas */
362           if ((ptr = strchr (sinkpadname, ','))) {
363             tempname = g_strndup (sinkpadname, (ptr - sinkpadname));
364             sinkpadname = &ptr[1];
365           } else {
366             tempname = sinkpadname;
367           }
368           
369           /* look for pad with that name */
370           if ((temppad = gst_element_get_pad (element, tempname))) {
371             sinkpads = g_slist_append (sinkpads, temppad);
372             numsinkpads++;
373           }
374           
375           /* try to create a pad using that padtemplate name */
376           else if ((temppad = gst_element_request_pad_by_name (element, tempname))) {
377             sinkpads = g_slist_append (sinkpads, temppad);
378             numsinkpads++;
379           }
380           if (!temppad) {
381             GST_DEBUG (0, "NO SUCH pad %s in element %s\n", tempname, GST_ELEMENT_NAME (element));
382           } else {
383             GST_DEBUG (0, "have sink pad %s:%s\n", GST_DEBUG_PAD_NAME (temppad));
384           }
385           
386           /* if there is no more commas in sinkpadname then we're done */
387           if (tempname == sinkpadname)
388             break;
389           g_free (tempname);
390         }
391       } else {
392         /* check through the list to find the first sink pad */
393         pads = gst_element_get_pad_list (element);
394         while (pads) {
395           temppad = GST_PAD (pads->data);
396           pads = g_list_next (pads);
397           if (gst_pad_get_direction (temppad) == GST_PAD_SINK) {
398             sinkpads = g_slist_append (sinkpads, temppad);
399             numsinkpads++;
400             break;
401           }
402         }
403       }
404       
405       if (!sinkpads)
406         GST_DEBUG (0, "can't find a sink pad for element\n");
407       else
408         GST_DEBUG (0, "have sink pad %s:%s\n", GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (sinkpads)));
409       
410       if (!srcpads && sinkpads && previous && srcpadname) {
411         dyn_connect *connect = g_malloc (sizeof (dyn_connect));
412         
413         connect->srcpadname = srcpadname;
414         connect->target = GST_PARSE_LISTPAD (sinkpads);
415         connect->pipeline = GST_ELEMENT (parent);
416         
417         GST_DEBUG (0, "SETTING UP dynamic connection %s:%s and %s:%s\n",
418                    gst_element_get_name (previous),
419                    srcpadname, GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (sinkpads)));
420         
421         g_signal_connect (G_OBJECT (previous), "new_pad", G_CALLBACK (dynamic_connect), connect);
422       } else {
423         for (j = 0; (j < numsrcpads) && (j < numsinkpads); j++) {
424           GST_DEBUG (0, "CONNECTING %s:%s and %s:%s\n",
425                      GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (g_slist_nth (srcpads, j))),
426                      GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (g_slist_nth (sinkpads, j))));
427           if (!gst_pad_connect (GST_PARSE_LISTPAD (g_slist_nth (srcpads, j)),
428                                 GST_PARSE_LISTPAD (g_slist_nth (sinkpads, j)))) {
429             g_warning ("could not connect %s:%s to %s:%s",
430                        GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (g_slist_nth (srcpads, j))), 
431                        GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (g_slist_nth (sinkpads, j))));
432             return GST_PARSE_ERROR_CONNECT;
433           }
434         }
435       }
436       
437       g_slist_free (srcpads);
438       srcpads = NULL;
439       
440       g_slist_free (sinkpads);
441       sinkpads = NULL;
442       
443       /* if we're the first element, ghost all the sinkpads */
444       if (elementcount == 1 && !backref) {
445         DEBUG ("first element, ghosting all of %s's sink pads to parent %s\n",
446                GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (GST_ELEMENT (parent)));
447         pads = gst_element_get_pad_list (element);
448         while (pads) {
449           temppad = GST_PAD (pads->data);
450           pads = g_list_next (pads);
451           if (!temppad)
452             DEBUG ("much oddness, pad doesn't seem to exist\n");
453           else if (gst_pad_get_direction (temppad) == GST_PAD_SINK) {
454             gst_element_add_ghost_pad (GST_ELEMENT (parent), temppad,
455                                        g_strdup_printf ("%s-ghost", GST_PAD_NAME (temppad)));
456             GST_DEBUG (0, "GHOSTED %s:%s to %s as %s-ghost\n",
457                        GST_DEBUG_PAD_NAME (temppad), GST_ELEMENT_NAME (GST_ELEMENT (parent)),
458                        GST_PAD_NAME (temppad));
459           }
460         }
461       }
462       
463       previous = element;
464       if (!GST_IS_BIN (element))
465         prevelement = element;
466     }
467   }
468   
469   /* ghost all the src pads of the bin */
470   if (prevelement != NULL) {
471     DEBUG ("last element, ghosting all of %s's src pads to parent %s\n",
472            GST_ELEMENT_NAME (prevelement), GST_ELEMENT_NAME (GST_ELEMENT (parent)));
473     pads = gst_element_get_pad_list (prevelement);
474     while (pads) {
475       temppad = GST_PAD (pads->data);
476       pads = g_list_next (pads);
477       if (!temppad)
478         DEBUG ("much oddness, pad doesn't seem to exist\n");
479       else if (gst_pad_get_direction (temppad) == GST_PAD_SRC) {
480         gst_element_add_ghost_pad (GST_ELEMENT (parent), temppad,
481                                    g_strdup_printf ("%s-ghost", GST_PAD_NAME (temppad)));
482         GST_DEBUG (0, "GHOSTED %s:%s to %s as %s-ghost\n",
483                    GST_DEBUG_PAD_NAME (temppad), GST_ELEMENT_NAME (parent), GST_PAD_NAME (temppad));
484       }
485     }
486   }
487   
488   priv->binlevel--;
489   
490   if (retval)
491     return retval;
492   
493   DEBUG (closingchar != '\0' ? "returning IN THE WRONG PLACE\n" : "ending pipeline\n");
494   
495   return i + 1;
496 }
497
498
499 /**
500  * gst_parse_launchv:
501  * @argv: null-terminated array of arguments
502  *
503  * Create a new pipeline based on command line syntax.
504  *
505  * Returns: a new pipeline on success, NULL on failure
506  */
507 GstPipeline *
508 gst_parse_launchv (const gchar **argv)
509 {
510   GstPipeline *pipeline;
511   gint ret;
512
513   /* defer error detection to the _recurse function */
514
515   pipeline = (GstPipeline*) gst_pipeline_new ("launch");
516
517   ret = gst_parse_launchv_recurse (argv, GST_BIN (pipeline), NULL);
518
519   if (ret <= 0) {
520     // print an error
521     gst_object_unref (GST_OBJECT (pipeline));
522     return NULL;
523   } else {
524     return pipeline;
525   }
526 }
527
528 /**
529  * gst_parse_launch:
530  * @pipeline_description: the command line describing the pipeline
531  *
532  * Create a new pipeline based on command line syntax.
533  *
534  * Returns: a new GstPipeline (cast to a Bin) on success, NULL on failure
535  */
536 GstPipeline *
537 gst_parse_launch (const gchar * pipeline_description)
538 {
539   gchar **argvn;
540   gint newargc;
541   gint i;
542   const gchar *cp, *start, *end;
543   gchar *temp;
544   GSList *string_list = NULL, *slist;
545   GstPipeline *pipeline;
546
547   end = pipeline_description + strlen (pipeline_description);
548   newargc = 0;
549
550   temp = "";
551
552   /* Extract the arguments to a gslist in reverse order */
553   for (cp = pipeline_description; cp < end;) {
554     i = strcspn (cp, "([{}]) \"\\");
555
556     if (i > 0) {
557       temp = g_strconcat (temp, g_strndup (cp, i), NULL);
558
559       /* see if we have an escape char */
560       if (cp[i] != '\\') {
561         /* normal argument - copy and add to the list */
562         string_list = g_slist_prepend (string_list, temp);
563         newargc++;
564         temp = "";
565       } else {
566         temp = g_strconcat (temp, g_strndup (&cp[++i], 1), NULL);
567       }
568       cp += i;
569     }
570
571     /* skip spaces */
572     while (cp < end && *cp == ' ') {
573       cp++;
574     }
575
576     /* handle quoted arguments */
577     if (*cp == '"') {
578       start = ++cp;
579
580       /* find matching quote */
581       while (cp < end && *cp != '"')
582         cp++;
583
584       /* make sure we got it */
585       if (cp == end) {
586         g_warning ("gst_parse_launch: Unbalanced quote in command line");
587         /* FIXME: The list leaks here */
588         return 0;
589       }
590
591       /* copy the string sans quotes */
592       string_list = g_slist_prepend (string_list, g_strndup (start, cp - start));
593       newargc++;
594       cp += 2;                  /* skip the quote aswell */
595     }
596
597     /* brackets exist in a separate argument slot */
598     if (*cp && strchr ("([{}])", *cp)) {
599       string_list = g_slist_prepend (string_list, g_strndup (cp, 1));
600       newargc++;
601       cp++;
602     }
603   }
604
605   /* now allocate the new argv array, with room for NULL termination */
606   argvn = g_new0 (char *, newargc + 1);
607
608   GST_DEBUG (0, "got %d args\n", newargc);
609
610   /* reverse the list and put the strings in the new array */
611   i = newargc;
612
613   for (slist = string_list; slist; slist = slist->next)
614     argvn[--i] = slist->data;
615
616   g_slist_free (string_list);
617
618   /* print them out */
619   while (argvn[i]) {
620     GST_DEBUG (0, "arg %d is: %s\n", i, argvn[i++]);
621   }
622
623   /* do it! */
624   pipeline = gst_parse_launchv (argvn);
625
626   GST_DEBUG(0, "Finished - freeing temporary argument array");
627   g_strfreev(argvn);
628
629   return pipeline;
630 }