new parser that uses flex and bison
[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 #include <string.h>
29
30 #include "gst_private.h"
31 #include "gstparse.h"
32 #include "parse/types.h"
33
34 typedef struct _gst_parse_delayed_pad gst_parse_delayed_pad;
35 struct _gst_parse_delayed_pad
36 {
37   gchar *name;
38   GstPad *peer;
39 };
40
41 typedef struct
42 {
43   gchar *srcpadname;
44   GstPad *target;
45   GstElement *pipeline;
46 }
47 dyn_connect;
48
49
50 GQuark 
51 gst_parse_error_quark (void)
52 {
53   static GQuark quark = 0;
54   if (!quark)
55     quark = g_quark_from_static_string ("gst_parse_error");
56   return quark;
57 }
58
59 G_GNUC_UNUSED static void
60 dynamic_connect (GstElement * element, GstPad * newpad, gpointer data)
61 {
62   dyn_connect *connect = (dyn_connect *) data;
63
64   if (!strcmp (gst_pad_get_name (newpad), connect->srcpadname)) {
65     gst_element_set_state (connect->pipeline, GST_STATE_PAUSED);
66     if (!gst_pad_connect (newpad, connect->target))
67       g_warning ("could not connect %s:%s to %s:%s", GST_DEBUG_PAD_NAME (newpad), 
68                  GST_DEBUG_PAD_NAME (connect->target));
69     gst_element_set_state (connect->pipeline, GST_STATE_PLAYING);
70   }
71 }
72
73 static gboolean
74 make_elements (graph_t *g, GError **error) 
75 {
76   GList *l = NULL;
77   gchar *bin_type;
78   element_t *e;
79   
80   if (!(g->bins || g->elements)) {
81     g_set_error (error,
82                  GST_PARSE_ERROR,
83                  GST_PARSE_ERROR_SYNTAX,
84                  "Empty bin");
85     return FALSE;
86   }
87
88   if (g->current_bin_type)
89     bin_type = g->current_bin_type;
90   else
91     bin_type = "pipeline";
92   
93   if (!(g->bin = gst_elementfactory_make (bin_type, NULL))) {
94     g_set_error (error,
95                  GST_PARSE_ERROR,
96                  GST_PARSE_ERROR_NO_SUCH_ELEMENT,
97                  "No such bin type %s", bin_type);
98     return FALSE;
99   }
100   
101   l = g->elements;
102   while (l) {
103     e = (element_t*)l->data;
104     if (!(e->element = gst_elementfactory_make (e->type, NULL))) {
105       g_set_error (error,
106                    GST_PARSE_ERROR,
107                    GST_PARSE_ERROR_NO_SUCH_ELEMENT,
108                    "No such element %s", e->type);
109       return FALSE;
110     }
111     gst_bin_add (GST_BIN (g->bin), e->element);
112     l = g_list_next (l);
113   }
114   
115   l = g->bins;
116   while (l) {
117     if (!make_elements ((graph_t*)l->data, error))
118       return FALSE;
119     gst_bin_add (GST_BIN (g->bin), ((graph_t*)l->data)->bin);
120     l = g_list_next (l);
121   }
122
123   return TRUE;
124 }
125
126 static gboolean
127 set_properties (graph_t *g, GError **error)
128 {
129   GList *l, *l2;
130   element_t *e;
131   property_t *p;
132   GParamSpec *pspec;
133   
134   l = g->elements;
135   while (l) {
136     e = (element_t*)l->data;
137     l2 = e->property_values;
138     while (l2) {
139       p = (property_t*)l2->data;
140       if ((pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (e->element), p->name))) {
141         g_object_set_property (G_OBJECT (e->element), p->name, p->value);
142       } else {
143         g_set_error (error,
144                      GST_PARSE_ERROR,
145                      GST_PARSE_ERROR_NO_SUCH_PROPERTY,
146                      "No such property '%s' in element '%s'",
147                      p->name, GST_OBJECT_NAME (GST_OBJECT (e->element)));
148         return FALSE;
149       }
150       l2 = g_list_next (l2);
151     }
152     l = g_list_next (l);
153   }
154   
155   l = g->bins;
156   while (l) {
157     if (!set_properties ((graph_t*)l->data, error))
158       return FALSE;
159     l = g_list_next (l);
160   }
161   
162   return TRUE;
163 }
164
165 static GstElement*
166 find_element_by_index_recurse (graph_t *g, gint i)
167 {
168   GList *l;
169   element_t *e;
170   GstElement *element;
171   
172   l = g->elements;
173   while (l) {
174     e = (element_t*)l->data;
175     if (e->index == i) {
176       return e->element;
177     }
178     l = g_list_next (l);
179   }
180   
181   l = g->bins;
182   while (l) {
183     if ((element = find_element_by_index_recurse ((graph_t*)l->data, i)))
184       return element;
185     l = g_list_next (l);
186   }
187   
188   return NULL;
189 }
190
191 static GstElement*
192 find_element_by_index (graph_t *g, gint i) 
193 {
194   while (g->parent)
195     g = g->parent;
196
197   return find_element_by_index_recurse (g, i);
198 }
199
200 static gboolean
201 make_connections (graph_t *g, GError **error)
202 {
203   GList *l, *a, *b;
204   connection_t *c;
205   GstElement *src, *sink;
206   GstPad *p1, *p2;
207   
208   l = g->connections;
209   while (l) {
210     c = (connection_t*)l->data;
211     if (c->src_name) {
212       if (!(src = gst_bin_get_by_name (GST_BIN (g->bin), c->src_name))) {
213         g_set_error (error,
214                      GST_PARSE_ERROR,
215                      GST_PARSE_ERROR_NO_SUCH_ELEMENT,
216                      "No such element '%s'",
217                      c->src_name);
218         return FALSE;
219       }
220     } else {
221       src = find_element_by_index (g, c->src_index);
222       g_assert (src);
223     }
224     if (c->sink_name) {
225       if (!(sink = gst_bin_get_by_name (GST_BIN (g->bin), c->sink_name))) {
226         g_set_error (error,
227                      GST_PARSE_ERROR,
228                      GST_PARSE_ERROR_NO_SUCH_ELEMENT,
229                      "No such element '%s'",
230                      c->sink_name);
231         return FALSE;
232       }
233     } else {
234       sink = find_element_by_index (g, c->sink_index);
235       g_assert (sink);
236     }
237     
238     a = c->src_pads;
239     b = c->sink_pads;
240     if (a && b) {
241       /* balanced multipad connection */
242       while (a && b) {
243         if (!gst_element_connect_pads (src, (gchar*)a->data, sink, (gchar*)b->data)) {
244           g_set_error (error,
245                        GST_PARSE_ERROR,
246                        GST_PARSE_ERROR_CONNECT,
247                        "Could not connect %s:%s to %s:%s",
248                        GST_OBJECT_NAME (src), (gchar*)a->data,
249                        GST_OBJECT_NAME (sink), (gchar*)b->data);
250           return FALSE;
251         }
252         a = g_list_next (a);
253         b = g_list_next (b);
254       }
255     } else if (a) {
256       if (!(p1 = gst_element_get_pad (src, (gchar*)a->data))) {
257         g_set_error (error,
258                      GST_PARSE_ERROR,
259                      GST_PARSE_ERROR_CONNECT,
260                      "Could not get a pad %s from element %s",
261                      (gchar*)a->data, GST_OBJECT_NAME (src));
262         return FALSE;
263       }
264       if (!(p2 = gst_element_get_compatible_pad (sink, p1))) {
265         g_set_error (error,
266                      GST_PARSE_ERROR,
267                      GST_PARSE_ERROR_CONNECT,
268                      "Could not find a compatible pad in element %s to for %s:%s",
269                      GST_OBJECT_NAME (sink), GST_OBJECT_NAME (src), (gchar*)a->data);
270         return FALSE;
271       }
272       if (!gst_pad_connect (p1, p2)) {
273         g_set_error (error,
274                      GST_PARSE_ERROR,
275                      GST_PARSE_ERROR_CONNECT,
276                      "Could not connect %s:%s to %s:%s",
277                      GST_OBJECT_NAME (src), GST_OBJECT_NAME (p1),
278                      GST_OBJECT_NAME (sink), GST_OBJECT_NAME (p1));
279         return FALSE;
280       }
281     } else if (b) {
282       if (!(p2 = gst_element_get_pad (sink, (gchar*)b->data))) {
283         g_set_error (error,
284                      GST_PARSE_ERROR,
285                      GST_PARSE_ERROR_CONNECT,
286                      "Could not get a pad %s from element %s",
287                      (gchar*)b->data, GST_OBJECT_NAME (sink));
288         return FALSE;
289       }
290       if (!(p1 = gst_element_get_compatible_pad (src, p2))) {
291         g_set_error (error,
292                      GST_PARSE_ERROR,
293                      GST_PARSE_ERROR_CONNECT,
294                      "Could not find a compatible pad in element %s to for %s:%s",
295                      GST_OBJECT_NAME (src), GST_OBJECT_NAME (sink), (gchar*)b->data);
296         return FALSE;
297       }
298       if (!gst_pad_connect (p1, p2)) {
299         g_set_error (error,
300                      GST_PARSE_ERROR,
301                      GST_PARSE_ERROR_CONNECT,
302                      "Could not connect %s:%s to %s:%s",
303                      GST_OBJECT_NAME (src), GST_OBJECT_NAME (p1),
304                      GST_OBJECT_NAME (sink), GST_OBJECT_NAME (p1));
305         return FALSE;
306       }
307     } else {
308       if (!gst_element_connect (src, sink)) {
309         g_set_error (error,
310                      GST_PARSE_ERROR,
311                      GST_PARSE_ERROR_CONNECT,
312                      "Could not connect %s to %s",
313                      GST_OBJECT_NAME (src), GST_OBJECT_NAME (sink));
314         return FALSE;
315       }
316     }
317     l = g_list_next (l);
318   }
319   
320   l = g->bins;
321   while (l) {
322     if (!make_connections ((graph_t*)l->data, error))
323       return FALSE;
324     l = g_list_next (l);
325   }
326   
327   return TRUE;
328 }
329
330 static GstBin*
331 pipeline_from_graph (graph_t *g, GError **error)
332 {
333   if (!make_elements (g, error))
334     return NULL;
335   
336   if (!set_properties (g, error))
337     return NULL;
338   
339   if (!make_connections (g, error))
340     return NULL;
341   
342   return (GstBin*)g->bin;
343 }
344
345 /**
346  * gst_parse_launchv:
347  * @argv: null-terminated array of arguments
348  *
349  * Create a new pipeline based on command line syntax.
350  *
351  * Returns: a new pipeline on success, NULL on failure
352  */
353 GstBin *
354 gst_parse_launchv (const gchar **argv, GError **error)
355 {
356   GstBin *pipeline;
357   gchar *pipeline_description;
358
359   /* i think this cast works out ok... */
360   pipeline_description = g_strjoinv (" ", (gchar**)argv);
361   
362   pipeline = gst_parse_launch (pipeline_description, error);
363
364   return pipeline;
365 }
366
367 /**
368  * gst_parse_launch:
369  * @pipeline_description: the command line describing the pipeline
370  *
371  * Create a new pipeline based on command line syntax.
372  *
373  * Returns: a new GstPipeline (cast to a Bin) on success, NULL on failure
374  */
375 GstBin *
376 gst_parse_launch (const gchar * pipeline_description, GError **error)
377 {
378   graph_t *graph;
379   static GStaticMutex flex_lock = G_STATIC_MUTEX_INIT;
380
381   g_return_val_if_fail (pipeline_description != NULL, NULL);
382
383   GST_INFO (GST_CAT_PIPELINE, "parsing pipeline description %s",
384             pipeline_description);
385
386   /* the need for the mutex will go away with flex 2.5.6 */
387   g_static_mutex_lock (&flex_lock);
388   graph = _gst_parse_launch (pipeline_description, error);
389   g_static_mutex_unlock (&flex_lock);
390
391   if (!graph)
392     return NULL;
393   
394   return pipeline_from_graph (graph, error);
395 }