2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wtay@chello.be>
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.
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.
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.
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...)
28 #define GST_PARSE_LISTPAD(list) ((GstPad*)(list->data))
32 #include "gst_private.h"
34 #include "gstpipeline.h"
35 #include "gstthread.h"
38 typedef struct _gst_parse_priv gst_parse_priv;
39 struct _gst_parse_priv
48 typedef struct _gst_parse_delayed_pad gst_parse_delayed_pad;
49 struct _gst_parse_delayed_pad
64 dynamic_connect (GstElement * element, GstPad * newpad, gpointer data)
66 dyn_connect *connect = (dyn_connect *) data;
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);
78 gst_parse_launchv_recurse (const gchar **argv, GstBin * parent, gst_parse_priv * priv)
82 GstElement *element = NULL, *previous = NULL, *prevelement = NULL;
83 gchar closingchar = '\0';
87 gchar *sinkpadname = NULL, *srcpadname = NULL, *tempname;
89 GSList *sinkpads = NULL, *srcpads = NULL;
90 gint numsrcpads = 0, numsinkpads = 0;
92 gint elementcount = 0;
94 gboolean backref = FALSE;
97 priv = g_new0 (gst_parse_priv, 1);
102 if (GST_IS_PIPELINE (parent)) {
104 DEBUG ("in pipeline ");
105 } else if (GST_IS_THREAD (parent)) {
107 DEBUG ("in thread ");
112 DEBUG_NOPREFIX ("%s\n", GST_ELEMENT_NAME (GST_ELEMENT (parent)));
114 while ((arg = argv[++i])) {
117 DEBUG ("** ARGUMENT is '%s'\n", arg);
120 DEBUG ("random arg, FIXME\n");
123 } else if (arg[0] == closingchar) {
124 DEBUG ("exiting container %s\n", GST_ELEMENT_NAME (GST_ELEMENT (parent)));
128 } else if ((cptr = strchr (arg, '!'))) {
129 DEBUG ("attempting to connect pads together....\n");
131 /* if it starts with the ! */
134 /* if there's a sinkpad... */
136 sinkpadname = g_strdup (&arg[1]);
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]);
148 if (srcpadname && (ptr = strchr (srcpadname, '.'))) {
149 gchar *element_name = srcpadname;
152 *ptr = '\0'; /* it was a '.' before */
154 GST_DEBUG (0, "have pad for element %s\n", element_name);
155 new = gst_bin_get_by_name_recurse_up (parent, element_name);
157 GST_DEBUG (0, "element %s does not exist! trying to continue\n", element_name);
160 srcpadname = ptr + 1;
165 GST_DEBUG (0, "have srcpad %s, sinkpad %s\n", srcpadname, sinkpadname);
167 g_slist_free (srcpads);
173 if (srcpadname != NULL) {
175 /* split name at commas */
176 if ((ptr = strchr (srcpadname, ','))) {
177 tempname = srcpadname;
178 srcpadname = &ptr[1];
181 tempname = srcpadname;
184 /* look for pad with that name */
185 if ((temppad = gst_element_get_pad (previous, tempname))) {
186 srcpads = g_slist_append (srcpads, temppad);
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);
196 GST_DEBUG (0, "NO SUCH pad %s in element %s\n", tempname, GST_ELEMENT_NAME (previous));
198 GST_DEBUG (0, "have src pad %s:%s\n", GST_DEBUG_PAD_NAME (temppad));
201 /* if there is no more commas in srcpadname then we're done */
202 if (tempname == srcpadname)
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),
210 pads = gst_element_get_pad_list (previous);
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);
221 pads = g_list_next (pads);
224 GST_DEBUG (0, "error, can't find a src pad!!!\n");
226 GST_DEBUG (0, "have src pad %s:%s\n", GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (srcpads)));
228 } else if (strchr (arg, '=')) {
233 DEBUG ("have a property\n");
235 /* we have a property */
236 propname = g_strdup (arg);
237 pos = strchr (propname, '=');
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);
246 } else if (arg[0] == '[') {
247 DEBUG ("have element name\n");
249 if (arg[1] != '\0') {
250 fprintf (stderr, "error, unexpected junk after [\n");
251 return GST_PARSE_ERROR_SYNTAX;
254 if ((arg = argv[++i])) {
256 g_critical("parser error, please report");
257 return GST_PARSE_ERROR_INTERNAL;
259 gst_element_set_name (previous, arg);
261 fprintf (stderr, "error, expected element name, found end of arguments\n");
262 return GST_PARSE_ERROR_SYNTAX;
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;
271 fprintf (stderr, "error, expected ], found end of arguments\n");
272 return GST_PARSE_ERROR_SYNTAX;
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. */
279 if (strchr ("({", arg[0])) {
280 DEBUG ("have bin or thread\n");
283 /* create a bin and add it to the current parent */
285 element = gst_elementfactory_make ("bin", NULL);
287 fprintf (stderr, "Couldn't create a bin!\n");
288 return GST_PARSE_ERROR_CREATING_ELEMENT;
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 */
294 element = gst_elementfactory_make ("thread", NULL);
296 fprintf (stderr, "Couldn't create a thread!\n");
297 return GST_PARSE_ERROR_CREATING_ELEMENT;
299 GST_DEBUG (0, "CREATED thread %s\n", GST_ELEMENT_NAME (element));
301 fprintf (stderr, "Illegal argument: %s\n", arg);
302 return GST_PARSE_ERROR_CREATING_ELEMENT;
305 gst_bin_add (GST_BIN (parent), element);
307 j = gst_parse_launchv_recurse (argv + i + 1, GST_BIN (element), priv);
308 /* check for parse error */
312 /* we will get autoincremented at the while */
316 /* we have an element */
317 DEBUG ("attempting to create element '%s'\n", arg);
319 element = gst_elementfactory_make (arg, NULL);
321 #ifndef GST_DISABLE_REGISTRY
323 "Couldn't create a '%s', no such element or need to run gst-register?\n", arg);
325 fprintf (stderr, "Couldn't create a '%s', no such element or need to load plugin?\n",
328 return GST_PARSE_ERROR_NOSUCH_ELEMENT;
331 GST_DEBUG (0, "CREATED element %s\n", GST_ELEMENT_NAME (element));
332 gst_bin_add (GST_BIN (parent), element);
337 g_slist_free (sinkpads);
343 if (sinkpadname != NULL) {
345 /* split name at commas */
346 if ((ptr = strchr (sinkpadname, ','))) {
347 tempname = g_strndup (sinkpadname, (ptr - sinkpadname));
348 sinkpadname = &ptr[1];
350 tempname = sinkpadname;
353 /* look for pad with that name */
354 if ((temppad = gst_element_get_pad (element, tempname))) {
355 sinkpads = g_slist_append (sinkpads, temppad);
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);
365 GST_DEBUG (0, "NO SUCH pad %s in element %s\n", tempname, GST_ELEMENT_NAME (element));
367 GST_DEBUG (0, "have sink pad %s:%s\n", GST_DEBUG_PAD_NAME (temppad));
370 /* if there is no more commas in sinkpadname then we're done */
371 if (tempname == sinkpadname)
376 /* check through the list to find the first sink pad */
377 pads = gst_element_get_pad_list (element);
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);
390 GST_DEBUG (0, "can't find a sink pad for element\n");
392 GST_DEBUG (0, "have sink pad %s:%s\n", GST_DEBUG_PAD_NAME (GST_PARSE_LISTPAD (sinkpads)));
394 if (!srcpads && sinkpads && previous && srcpadname) {
395 dyn_connect *connect = g_malloc (sizeof (dyn_connect));
397 connect->srcpadname = srcpadname;
398 connect->target = GST_PARSE_LISTPAD (sinkpads);
399 connect->pipeline = GST_ELEMENT (parent);
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)));
405 g_signal_connect (G_OBJECT (previous), "new_pad", G_CALLBACK (dynamic_connect), connect);
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;
421 g_slist_free (srcpads);
424 g_slist_free (sinkpads);
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);
433 temppad = GST_PAD (pads->data);
434 pads = g_list_next (pads);
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));
448 if (!GST_IS_BIN (element))
449 prevelement = element;
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);
459 temppad = GST_PAD (pads->data);
460 pads = g_list_next (pads);
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));
477 DEBUG (closingchar != '\0' ? "returning IN THE WRONG PLACE\n" : "ending pipeline\n");
485 * @argv: null-terminated array of arguments
487 * Create a new pipeline based on command line syntax.
489 * Returns: a new pipeline on success, NULL on failure
492 gst_parse_launchv (const gchar **argv)
494 GstPipeline *pipeline;
497 /* defer error detection to the _recurse function */
499 pipeline = (GstPipeline*) gst_pipeline_new ("launch");
501 ret = gst_parse_launchv_recurse (argv, GST_BIN (pipeline), NULL);
505 gst_object_unref (GST_OBJECT (pipeline));
514 * @pipeline_description: the command line describing the pipeline
516 * Create a new pipeline based on command line syntax.
518 * Returns: a new GstPipeline (cast to a Bin) on success, NULL on failure
521 gst_parse_launch (const gchar * pipeline_description)
526 const gchar *cp, *start, *end;
528 GSList *string_list = NULL, *slist;
529 GstPipeline *pipeline;
531 end = pipeline_description + strlen (pipeline_description);
536 /* Extract the arguments to a gslist in reverse order */
537 for (cp = pipeline_description; cp < end;) {
538 i = strcspn (cp, "([{}]) \"\\");
541 temp = g_strconcat (temp, g_strndup (cp, i), NULL);
543 /* see if we have an escape char */
545 /* normal argument - copy and add to the list */
546 string_list = g_slist_prepend (string_list, temp);
550 temp = g_strconcat (temp, g_strndup (&cp[++i], 1), NULL);
556 while (cp < end && *cp == ' ') {
560 /* handle quoted arguments */
564 /* find matching quote */
565 while (cp < end && *cp != '"')
568 /* make sure we got it */
570 g_warning ("gst_parse_launch: Unbalanced quote in command line");
571 /* FIXME: The list leaks here */
575 /* copy the string sans quotes */
576 string_list = g_slist_prepend (string_list, g_strndup (start, cp - start));
578 cp += 2; /* skip the quote aswell */
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));
589 /* now allocate the new argv array, with room for NULL termination */
590 argvn = g_new0 (char *, newargc + 1);
592 GST_DEBUG (0, "got %d args\n", newargc);
594 /* reverse the list and put the strings in the new array */
597 for (slist = string_list; slist; slist = slist->next)
598 argvn[--i] = slist->data;
600 g_slist_free (string_list);
604 GST_DEBUG (0, "arg %d is: %s\n", i-1, argvn[i-1]);
608 pipeline = gst_parse_launchv ((const char**) argvn);
610 GST_DEBUG(0, "Finished - freeing temporary argument array");