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