aye ladie, no more ugly // comments here, even if Taaz gets upset about it
[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       GST_DEBUG (0, "have srcpad %s, sinkpad %s\n", srcpadname, sinkpadname);
173
174       g_slist_free (srcpads);
175       srcpads = NULL;
176       numsrcpads = 0;
177       tempname = NULL;
178
179       /* find src pads */
180       if (srcpadname != NULL) {
181         while (1) {
182           /* split name at commas */
183           if ((ptr = strchr (srcpadname, ','))) {
184             tempname = g_strndup (srcpadname, (ptr - srcpadname));
185             srcpadname = &ptr[1];
186           }
187           else {
188             tempname = srcpadname;
189           }
190
191           /* look for pad with that name */
192           if ((temppad = gst_element_get_pad (previous, tempname))) {
193             srcpads = g_slist_append (srcpads, temppad);
194             numsrcpads++;
195           }
196
197           /* try to create a pad using that padtemplate name */
198           else if ((temppad = gst_element_request_pad_by_name (previous, tempname))) {
199             srcpads = g_slist_append (srcpads, temppad);
200             numsrcpads++;
201           }
202           if (!temppad) {
203             GST_DEBUG (0, "NO SUCH pad %s in element %s\n", tempname, GST_ELEMENT_NAME (previous));
204           }
205           else {
206             GST_DEBUG (0, "have src pad %s:%s\n", GST_DEBUG_PAD_NAME (temppad));
207           }
208
209           /* if there is no more commas in srcpadname then we're done */
210           if (tempname == srcpadname)
211             break;
212           g_free (tempname);
213         }
214       }
215       else {
216         /* check through the list to find the first sink pad */
217         GST_DEBUG (0, "CHECKING through element %s for pad named %s\n", GST_ELEMENT_NAME (previous),
218                    srcpadname);
219         pads = gst_element_get_pad_list (previous);
220         while (pads) {
221           temppad = GST_PARSE_LISTPAD (pads);
222           GST_DEBUG (0, "have pad %s:%s\n", GST_DEBUG_PAD_NAME (temppad));
223           if (GST_IS_GHOST_PAD (temppad))
224             GST_DEBUG (0, "it's a ghost pad\n");
225           if (gst_pad_get_direction (temppad) == GST_PAD_SRC) {
226             srcpads = g_slist_append (srcpads, temppad);
227             numsrcpads++;
228             break;
229           }
230           pads = g_list_next (pads);
231         }
232         if (!srcpads)
233           GST_DEBUG (0, "error, can't find a src pad!!!\n");
234         else
235           GST_DEBUG (0, "have src pad %s:%s\n", GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (srcpads)));
236       }
237
238       /* argument with = in it */
239     }
240     else if (strstr (arg, "=")) {
241       gchar *argname;
242       gchar *argval;
243       gchar *pos = strstr (arg, "=");
244
245       /* we have an argument */
246       argname = arg;
247       pos[0] = '\0';
248       argval = pos + 1;
249
250       GST_DEBUG (0, "attempting to set argument '%s' to '%s' on element '%s'\n",
251                  argname, argval, GST_ELEMENT_NAME (previous));
252       gst_util_set_object_arg (G_OBJECT (previous), argname, argval);
253       g_free (argname);
254
255       /* element or argument, or beginning of bin or thread */
256     }
257     else if (arg[0] == '[') {
258       /* we have the start of a name of the preceding element. */
259       /* rename previous element to next arg. */
260       if (arg[1] != '\0') {
261         fprintf (stderr, "error, unexpected junk after [\n");
262         return GST_PARSE_ERROR_SYNTAX;
263       }
264       i++;
265       if (i < argc) {
266         gst_element_set_name (previous, argv[i]);
267       }
268       else {
269         fprintf (stderr, "error, expected element name, found end of arguments\n");
270         return GST_PARSE_ERROR_SYNTAX;
271       }
272       i++;
273       if (i >= argc) {
274         fprintf (stderr, "error, expected ], found end of arguments\n");
275         return GST_PARSE_ERROR_SYNTAX;
276       }
277       else if (strcmp (argv[i], "]") != 0) {
278         fprintf (stderr, "error, expected ], found '%s'\n", argv[i]);
279         return GST_PARSE_ERROR_SYNTAX;
280       }
281     }
282     else {
283       DEBUG ("have element or bin/thread\n");
284       /* if we have a bin or thread starting */
285       if (strchr ("({", arg[0])) {
286         if (arg[0] == '(') {
287           /* create a bin and add it to the current parent */
288           element = gst_bin_new (g_strdup_printf ("bin%d", priv->bincount++));
289           if (!element) {
290             fprintf (stderr, "Couldn't create a bin!\n");
291             return GST_PARSE_ERROR_CREATING_ELEMENT;
292           }
293           GST_DEBUG (0, "CREATED bin %s\n", GST_ELEMENT_NAME (element));
294         }
295         else if (arg[0] == '{') {
296           /* create a thread and add it to the current parent */
297           element = gst_thread_new (g_strdup_printf ("thread%d", priv->threadcount++));
298           if (!element) {
299             fprintf (stderr, "Couldn't create a thread!\n");
300             return GST_PARSE_ERROR_CREATING_ELEMENT;
301           }
302           GST_DEBUG (0, "CREATED thread %s\n", GST_ELEMENT_NAME (element));
303         }
304         else {
305           DEBUG ("error in parser, unexpected symbol, FIXME\n");
306           i++;
307           continue;
308         }
309
310         j = gst_parse_launch_cmdline (argc - i, argv + i + 1, GST_BIN (element), priv);
311         /* check for parse error */
312         if (j < 0)
313           return j;
314         i += j;
315
316       }
317       else {
318         /* we have an element */
319         DEBUG ("attempting to create element '%s'\n", arg);
320         ptr = gst_parse_unique_name (arg, priv);
321         element = gst_elementfactory_make (arg, ptr);
322         g_free (ptr);
323         if (!element) {
324 #ifndef GST_DISABLE_REGISTRY
325           fprintf (stderr,
326                    "Couldn't create a '%s', no such element or need to run gstreamer-register?\n",
327                    arg);
328 #else
329           fprintf (stderr, "Couldn't create a '%s', no such element or need to load pluginn?\n",
330                    arg);
331 #endif
332           return GST_PARSE_ERROR_NOSUCH_ELEMENT;
333         }
334         GST_DEBUG (0, "CREATED element %s\n", GST_ELEMENT_NAME (element));
335       }
336
337       gst_bin_add (GST_BIN (parent), element);
338       elementcount++;
339
340       g_slist_free (sinkpads);
341       sinkpads = NULL;
342       numsinkpads = 0;
343       tempname = NULL;
344
345       /
346  find sink pads
347       if (sinkpadname != NULL) {
348         while (1) {
349           /* split name at commas */
350           if ((ptr = strchr (sinkpadname, ','))) {
351             tempname = g_strndup (sinkpadname, (ptr - sinkpadname));
352             sinkpadname = &ptr[1];
353           }
354           else {
355             tempname = sinkpadname;
356           }
357
358           /* look for pad with that name */
359           if ((temppad = gst_element_get_pad (element, tempname))) {
360             sinkpads = g_slist_append (sinkpads, temppad);
361             numsinkpads++;
362           }
363
364           /* try to create a pad using that padtemplate name */
365           else if ((temppad = gst_element_request_pad_by_name (element, tempname))) {
366             sinkpads = g_slist_append (sinkpads, temppad);
367             numsinkpads++;
368           }
369           if (!temppad) {
370             GST_DEBUG (0, "NO SUCH pad %s in element %s\n", tempname, GST_ELEMENT_NAME (element));
371           }
372           else {
373             GST_DEBUG (0, "have sink pad %s:%s\n", GST_DEBUG_PAD_NAME (temppad));
374           }
375
376           /* if there is no more commas in sinkpadname then we're done */
377           if (tempname == sinkpadname)
378             break;
379           g_free (tempname);
380         }
381       }
382       else {
383         /* check through the list to find the first sink pad */
384         pads = gst_element_get_pad_list (element);
385         while (pads) {
386           temppad = GST_PAD (pads->data);
387           pads = g_list_next (pads);
388           if (gst_pad_get_direction (temppad) == GST_PAD_SINK) {
389             sinkpads = g_slist_append (sinkpads, temppad);
390             numsinkpads++;
391             break;
392           }
393         }
394       }
395
396       if (!sinkpads)
397         GST_DEBUG (0, "can't find a sink pad for element\n");
398       else
399         GST_DEBUG (0, "have sink pad %s:%s\n", GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (sinkpads)));
400
401       if (!srcpads && sinkpads && previous) {
402         dyn_connect *connect = g_malloc (sizeof (dyn_connect));
403
404         connect->srcpadname = srcpadname;
405         connect->target = GST_PARSE_LISTPAD (sinkpads);
406         connect->pipeline = GST_ELEMENT (parent);
407
408         GST_DEBUG (0, "SETTING UP dynamic connection %s:%s and %s:%s\n",
409                    gst_element_get_name (previous),
410                    srcpadname, GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (sinkpads)));
411
412         g_signal_connect (G_OBJECT (previous), "new_pad", G_CALLBACK (dynamic_connect), connect);
413       }
414       else {
415         for (j = 0; (j < numsrcpads) && (j < numsinkpads); j++) {
416           GST_DEBUG (0, "CONNECTING %s:%s and %s:%s\n",
417                      GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (g_slist_nth (srcpads, j))),
418                      GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (g_slist_nth (sinkpads, j))));
419           gst_pad_connect (GST_PARSE_LISTPAD (g_slist_nth (srcpads, j)),
420                            GST_PARSE_LISTPAD (g_slist_nth (sinkpads, j)));
421         }
422       }
423
424       g_slist_free (srcpads);
425       srcpads = NULL;
426
427       g_slist_free (sinkpads);
428       sinkpads = NULL;
429
430       /* if we're the first element, ghost all the sinkpads */
431       if (elementcount == 1) {
432         DEBUG ("first element, ghosting all of %s's sink pads to parent %s\n",
433                GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (GST_ELEMENT (parent)));
434         pads = gst_element_get_pad_list (element);
435         while (pads) {
436           temppad = GST_PAD (pads->data);
437           pads = g_list_next (pads);
438           if (!temppad)
439             DEBUG ("much oddness, pad doesn't seem to exist\n");
440           else if (gst_pad_get_direction (temppad) == GST_PAD_SINK) {
441             gst_element_add_ghost_pad (GST_ELEMENT (parent), temppad,
442                                        g_strdup_printf ("%s-ghost", GST_PAD_NAME (temppad)));
443             GST_DEBUG (0, "GHOSTED %s:%s to %s as %s-ghost\n",
444                        GST_DEBUG_PAD_NAME (temppad), GST_ELEMENT_NAME (GST_ELEMENT (parent)),
445                        GST_PAD_NAME (temppad));
446           }
447         }
448       }
449
450       previous = element;
451       if (!GST_IS_BIN (element))
452         prevelement = element;
453     }
454
455     i++;
456   }
457
458   /* ghost all the src pads of the bin */
459   if (prevelement != NULL) {
460     DEBUG ("last element, ghosting all of %s's src pads to parent %s\n",
461            GST_ELEMENT_NAME (prevelement), GST_ELEMENT_NAME (GST_ELEMENT (parent)));
462     pads = gst_element_get_pad_list (prevelement);
463     while (pads) {
464       temppad = GST_PAD (pads->data);
465       pads = g_list_next (pads);
466       if (!temppad)
467         DEBUG ("much oddness, pad doesn't seem to exist\n");
468       else if (gst_pad_get_direction (temppad) == GST_PAD_SRC) {
469         gst_element_add_ghost_pad (GST_ELEMENT (parent), temppad,
470                                    g_strdup_printf ("%s-ghost", GST_PAD_NAME (temppad)));
471         GST_DEBUG (0, "GHOSTED %s:%s to %s as %s-ghost\n",
472                    GST_DEBUG_PAD_NAME (temppad), GST_ELEMENT_NAME (parent), GST_PAD_NAME (temppad));
473       }
474     }
475   }
476
477   priv->binlevel--;
478
479   if (retval)
480     return retval;
481
482   DEBUG (closingchar != '\0' ? "returning IN THE WRONG PLACE\n" : "ending pipeline\n");
483
484   return i + 1;
485 }
486
487 /**
488  * gst_parse_launch:
489  * @cmdline: the command line describing the pipeline
490  * @parent: the parent bin for the resulting pipeline
491  *
492  * Create a new pipeline based on command line syntax.
493  *
494  * Returns: ?
495  */
496 gint
497 gst_parse_launch (const gchar * cmdline, GstBin * parent)
498 {
499   gst_parse_priv priv;
500   gchar **argvn;
501   gint newargc;
502   gint i;
503   const gchar *cp, *start, *end;
504   gchar *temp;
505   GSList *string_list = NULL, *slist;
506
507   priv.bincount = 0;
508   priv.threadcount = 0;
509   priv.binlevel = 0;
510   priv.elementcounts = NULL;
511   priv.verbose = FALSE;
512   priv.debug = FALSE;
513
514   end = cmdline + strlen (cmdline);
515   newargc = 0;
516
517   temp = "";
518
519   /* Extract the arguments to a gslist in reverse order */
520   for (cp = cmdline; cp < end;) {
521     i = strcspn (cp, "([{}]) \"\\");
522
523     if (i > 0) {
524       temp = g_strconcat (temp, g_strndup (cp, i), NULL);
525
526       /* see if we have an escape char */
527       if (cp[i] != '\\') {
528         /* normal argument - copy and add to the list */
529         string_list = g_slist_prepend (string_list, temp);
530         newargc++;
531         temp = "";
532       }
533       else {
534         temp = g_strconcat (temp, g_strndup (&cp[++i], 1), NULL);
535       }
536       cp += i;
537     }
538
539     /* skip spaces */
540     while (cp < end && *cp == ' ') {
541       cp++;
542     }
543
544     /* handle quoted arguments */
545     if (*cp == '"') {
546       start = ++cp;
547
548       /* find matching quote */
549       while (cp < end && *cp != '"')
550         cp++;
551
552       /* make sure we got it */
553       if (cp == end) {
554         g_warning ("gst_parse_launch: Unbalanced quote in command line");
555         /* FIXME: The list leaks here */
556         return 0;
557       }
558
559       /* copy the string sans quotes */
560       string_list = g_slist_prepend (string_list, g_strndup (start, cp - start));
561       newargc++;
562       cp += 2;                  /* skip the quote aswell */
563     }
564
565     /* brackets exist in a separate argument slot */
566     if (*cp && strchr ("([{}])", *cp)) {
567       string_list = g_slist_prepend (string_list, g_strndup (cp, 1));
568       newargc++;
569       cp++;
570     }
571   }
572
573   /* now allocate the new argv array */
574   argvn = g_new0 (char *, newargc);
575
576   GST_DEBUG (0, "got %d args\n", newargc);
577
578   /* reverse the list and put the strings in the new array */
579   i = newargc;
580
581   for (slist = string_list; slist; slist = slist->next)
582     argvn[--i] = slist->data;
583
584   g_slist_free (string_list);
585
586   /* print them out */
587   for (i = 0; i < newargc; i++) {
588     GST_DEBUG (0, "arg %d is: %s\n", i, argvn[i]);
589   }
590
591   /* set up the elementcounts hash */
592   priv.elementcounts = g_hash_table_new (g_str_hash, g_str_equal);
593
594   /* do it! */
595   i = gst_parse_launch_cmdline (newargc, argvn, parent, &priv);
596
597 /*  GST_DEBUG(0, "Finished - freeing temporary argument array"); */
598 /*  g_strfreev(argvn); */
599
600   return i;
601 }