fix some of uraeus's commenting fixes can someone also put {} around GST_DEBUG statem...
[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       /* find sink pads */
346       if (sinkpadname != NULL) {
347         while (1) {
348           /* split name at commas */
349           if ((ptr = strchr (sinkpadname, ','))) {
350             tempname = g_strndup (sinkpadname, (ptr - sinkpadname));
351             sinkpadname = &ptr[1];
352           }
353           else {
354             tempname = sinkpadname;
355           }
356
357           /* look for pad with that name */
358           if ((temppad = gst_element_get_pad (element, tempname))) {
359             sinkpads = g_slist_append (sinkpads, temppad);
360             numsinkpads++;
361           }
362
363           /* try to create a pad using that padtemplate name */
364           else if ((temppad = gst_element_request_pad_by_name (element, tempname))) {
365             sinkpads = g_slist_append (sinkpads, temppad);
366             numsinkpads++;
367           }
368           if (!temppad) {
369             GST_DEBUG (0, "NO SUCH pad %s in element %s\n", tempname, GST_ELEMENT_NAME (element));
370           }
371           else {
372             GST_DEBUG (0, "have sink pad %s:%s\n", GST_DEBUG_PAD_NAME (temppad));
373           }
374
375           /* if there is no more commas in sinkpadname then we're done */
376           if (tempname == sinkpadname)
377             break;
378           g_free (tempname);
379         }
380       }
381       else {
382         /* check through the list to find the first sink pad */
383         pads = gst_element_get_pad_list (element);
384         while (pads) {
385           temppad = GST_PAD (pads->data);
386           pads = g_list_next (pads);
387           if (gst_pad_get_direction (temppad) == GST_PAD_SINK) {
388             sinkpads = g_slist_append (sinkpads, temppad);
389             numsinkpads++;
390             break;
391           }
392         }
393       }
394
395       if (!sinkpads)
396         GST_DEBUG (0, "can't find a sink pad for element\n");
397       else
398         GST_DEBUG (0, "have sink pad %s:%s\n", GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (sinkpads)));
399
400       if (!srcpads && sinkpads && previous) {
401         dyn_connect *connect = g_malloc (sizeof (dyn_connect));
402
403         connect->srcpadname = srcpadname;
404         connect->target = GST_PARSE_LISTPAD (sinkpads);
405         connect->pipeline = GST_ELEMENT (parent);
406
407         GST_DEBUG (0, "SETTING UP dynamic connection %s:%s and %s:%s\n",
408                    gst_element_get_name (previous),
409                    srcpadname, GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (sinkpads)));
410
411         g_signal_connect (G_OBJECT (previous), "new_pad", G_CALLBACK (dynamic_connect), connect);
412       }
413       else {
414         for (j = 0; (j < numsrcpads) && (j < numsinkpads); j++) {
415           GST_DEBUG (0, "CONNECTING %s:%s and %s:%s\n",
416                      GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (g_slist_nth (srcpads, j))),
417                      GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (g_slist_nth (sinkpads, j))));
418           gst_pad_connect (GST_PARSE_LISTPAD (g_slist_nth (srcpads, j)),
419                            GST_PARSE_LISTPAD (g_slist_nth (sinkpads, j)));
420         }
421       }
422
423       g_slist_free (srcpads);
424       srcpads = NULL;
425
426       g_slist_free (sinkpads);
427       sinkpads = NULL;
428
429       /* if we're the first element, ghost all the sinkpads */
430       if (elementcount == 1) {
431         DEBUG ("first element, ghosting all of %s's sink pads to parent %s\n",
432                GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (GST_ELEMENT (parent)));
433         pads = gst_element_get_pad_list (element);
434         while (pads) {
435           temppad = GST_PAD (pads->data);
436           pads = g_list_next (pads);
437           if (!temppad)
438             DEBUG ("much oddness, pad doesn't seem to exist\n");
439           else if (gst_pad_get_direction (temppad) == GST_PAD_SINK) {
440             gst_element_add_ghost_pad (GST_ELEMENT (parent), temppad,
441                                        g_strdup_printf ("%s-ghost", GST_PAD_NAME (temppad)));
442             GST_DEBUG (0, "GHOSTED %s:%s to %s as %s-ghost\n",
443                        GST_DEBUG_PAD_NAME (temppad), GST_ELEMENT_NAME (GST_ELEMENT (parent)),
444                        GST_PAD_NAME (temppad));
445           }
446         }
447       }
448
449       previous = element;
450       if (!GST_IS_BIN (element))
451         prevelement = element;
452     }
453
454     i++;
455   }
456
457   /* ghost all the src pads of the bin */
458   if (prevelement != NULL) {
459     DEBUG ("last element, ghosting all of %s's src pads to parent %s\n",
460            GST_ELEMENT_NAME (prevelement), GST_ELEMENT_NAME (GST_ELEMENT (parent)));
461     pads = gst_element_get_pad_list (prevelement);
462     while (pads) {
463       temppad = GST_PAD (pads->data);
464       pads = g_list_next (pads);
465       if (!temppad)
466         DEBUG ("much oddness, pad doesn't seem to exist\n");
467       else if (gst_pad_get_direction (temppad) == GST_PAD_SRC) {
468         gst_element_add_ghost_pad (GST_ELEMENT (parent), temppad,
469                                    g_strdup_printf ("%s-ghost", GST_PAD_NAME (temppad)));
470         GST_DEBUG (0, "GHOSTED %s:%s to %s as %s-ghost\n",
471                    GST_DEBUG_PAD_NAME (temppad), GST_ELEMENT_NAME (parent), GST_PAD_NAME (temppad));
472       }
473     }
474   }
475
476   priv->binlevel--;
477
478   if (retval)
479     return retval;
480
481   DEBUG (closingchar != '\0' ? "returning IN THE WRONG PLACE\n" : "ending pipeline\n");
482
483   return i + 1;
484 }
485
486 /**
487  * gst_parse_launch:
488  * @cmdline: the command line describing the pipeline
489  * @parent: the parent bin for the resulting pipeline
490  *
491  * Create a new pipeline based on command line syntax.
492  *
493  * Returns: ?
494  */
495 gint
496 gst_parse_launch (const gchar * cmdline, GstBin * parent)
497 {
498   gst_parse_priv priv;
499   gchar **argvn;
500   gint newargc;
501   gint i;
502   const gchar *cp, *start, *end;
503   gchar *temp;
504   GSList *string_list = NULL, *slist;
505
506   priv.bincount = 0;
507   priv.threadcount = 0;
508   priv.binlevel = 0;
509   priv.elementcounts = NULL;
510   priv.verbose = FALSE;
511   priv.debug = FALSE;
512
513   end = cmdline + strlen (cmdline);
514   newargc = 0;
515
516   temp = "";
517
518   /* Extract the arguments to a gslist in reverse order */
519   for (cp = cmdline; cp < end;) {
520     i = strcspn (cp, "([{}]) \"\\");
521
522     if (i > 0) {
523       temp = g_strconcat (temp, g_strndup (cp, i), NULL);
524
525       /* see if we have an escape char */
526       if (cp[i] != '\\') {
527         /* normal argument - copy and add to the list */
528         string_list = g_slist_prepend (string_list, temp);
529         newargc++;
530         temp = "";
531       }
532       else {
533         temp = g_strconcat (temp, g_strndup (&cp[++i], 1), NULL);
534       }
535       cp += i;
536     }
537
538     /* skip spaces */
539     while (cp < end && *cp == ' ') {
540       cp++;
541     }
542
543     /* handle quoted arguments */
544     if (*cp == '"') {
545       start = ++cp;
546
547       /* find matching quote */
548       while (cp < end && *cp != '"')
549         cp++;
550
551       /* make sure we got it */
552       if (cp == end) {
553         g_warning ("gst_parse_launch: Unbalanced quote in command line");
554         /* FIXME: The list leaks here */
555         return 0;
556       }
557
558       /* copy the string sans quotes */
559       string_list = g_slist_prepend (string_list, g_strndup (start, cp - start));
560       newargc++;
561       cp += 2;                  /* skip the quote aswell */
562     }
563
564     /* brackets exist in a separate argument slot */
565     if (*cp && strchr ("([{}])", *cp)) {
566       string_list = g_slist_prepend (string_list, g_strndup (cp, 1));
567       newargc++;
568       cp++;
569     }
570   }
571
572   /* now allocate the new argv array */
573   argvn = g_new0 (char *, newargc);
574
575   GST_DEBUG (0, "got %d args\n", newargc);
576
577   /* reverse the list and put the strings in the new array */
578   i = newargc;
579
580   for (slist = string_list; slist; slist = slist->next)
581     argvn[--i] = slist->data;
582
583   g_slist_free (string_list);
584
585   /* print them out */
586   for (i = 0; i < newargc; i++) {
587     GST_DEBUG (0, "arg %d is: %s\n", i, argvn[i]);
588   }
589
590   /* set up the elementcounts hash */
591   priv.elementcounts = g_hash_table_new (g_str_hash, g_str_equal);
592
593   /* do it! */
594   i = gst_parse_launch_cmdline (newargc, argvn, parent, &priv);
595
596 /*  GST_DEBUG(0, "Finished - freeing temporary argument array"); */
597 /*  g_strfreev(argvn); */
598
599   return i;
600 }