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