Some code cleanups.
[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 }