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