|
1 %{ |
|
2 |
|
3 |
|
4 #include <glib-object.h> |
|
5 #include <glib.h> |
|
6 #include <stdio.h> |
|
7 #include <string.h> |
|
8 #include <stdlib.h> |
|
9 |
|
10 #include "../gst_private.h" |
|
11 #include "../gst-i18n-lib.h" |
|
12 |
|
13 #include "../gstconfig.h" |
|
14 #include "../gstparse.h" |
|
15 #include "../gstinfo.h" |
|
16 #include "../gsterror.h" |
|
17 #include "../gsturi.h" |
|
18 #include "../gstutils.h" |
|
19 #include "../gstvalue.h" |
|
20 #include "../gstchildproxy.h" |
|
21 #include "types.h" |
|
22 |
|
23 #include <glib_global.h> |
|
24 #include <glib_object.h> |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 /* All error messages in this file are user-visible and need to be translated. |
|
30 * Don't start the message with a capital, and don't end them with a period, |
|
31 * as they will be presented inside a sentence/error. |
|
32 */ |
|
33 |
|
34 #define YYERROR_VERBOSE 1 |
|
35 #define YYLEX_PARAM scanner |
|
36 |
|
37 typedef void* yyscan_t; |
|
38 |
|
39 int _gst_parse_yylex (void * yylval_param , yyscan_t yyscanner); |
|
40 int _gst_parse_yylex_init (yyscan_t scanner); |
|
41 int _gst_parse_yylex_destroy (yyscan_t scanner); |
|
42 struct yy_buffer_state * _gst_parse_yy_scan_string (char* , yyscan_t); |
|
43 void _gst_parse_yypush_buffer_state (void * new_buffer ,yyscan_t yyscanner ); |
|
44 void _gst_parse_yypop_buffer_state (yyscan_t yyscanner ); |
|
45 |
|
46 |
|
47 #ifdef __GST_PARSE_TRACE |
|
48 static guint __strings; |
|
49 static guint __links; |
|
50 static guint __chains; |
|
51 gchar * |
|
52 __gst_parse_strdup (gchar *org) |
|
53 { |
|
54 gchar *ret; |
|
55 __strings++; |
|
56 ret = g_strdup (org); |
|
57 /* g_print ("ALLOCATED STR (%3u): %p %s\n", __strings, ret, ret); */ |
|
58 return ret; |
|
59 } |
|
60 void |
|
61 __gst_parse_strfree (gchar *str) |
|
62 { |
|
63 if (str) { |
|
64 /* g_print ("FREEING STR (%3u): %p %s\n", __strings - 1, str, str); */ |
|
65 g_free (str); |
|
66 g_return_if_fail (__strings > 0); |
|
67 __strings--; |
|
68 } |
|
69 } |
|
70 link_t *__gst_parse_link_new () |
|
71 { |
|
72 link_t *ret; |
|
73 __links++; |
|
74 ret = g_new0 (link_t, 1); |
|
75 /* g_print ("ALLOCATED LINK (%3u): %p\n", __links, ret); */ |
|
76 return ret; |
|
77 } |
|
78 void |
|
79 __gst_parse_link_free (link_t *data) |
|
80 { |
|
81 if (data) { |
|
82 /* g_print ("FREEING LINK (%3u): %p\n", __links - 1, data); */ |
|
83 g_free (data); |
|
84 g_return_if_fail (__links > 0); |
|
85 __links--; |
|
86 } |
|
87 } |
|
88 chain_t * |
|
89 __gst_parse_chain_new () |
|
90 { |
|
91 chain_t *ret; |
|
92 __chains++; |
|
93 ret = g_new0 (chain_t, 1); |
|
94 /* g_print ("ALLOCATED CHAIN (%3u): %p\n", __chains, ret); */ |
|
95 return ret; |
|
96 } |
|
97 void |
|
98 __gst_parse_chain_free (chain_t *data) |
|
99 { |
|
100 /* g_print ("FREEING CHAIN (%3u): %p\n", __chains - 1, data); */ |
|
101 g_free (data); |
|
102 g_return_if_fail (__chains > 0); |
|
103 __chains--; |
|
104 } |
|
105 |
|
106 #endif /* __GST_PARSE_TRACE */ |
|
107 |
|
108 typedef struct { |
|
109 gchar *src_pad; |
|
110 gchar *sink_pad; |
|
111 GstElement *sink; |
|
112 GstCaps *caps; |
|
113 gulong signal_id; |
|
114 /* FIXME: need to connect to "disposed" signal to clean up, |
|
115 * but there is no such signal */ |
|
116 } DelayedLink; |
|
117 |
|
118 typedef struct { |
|
119 GstElement *parent; |
|
120 gchar *name; |
|
121 gchar *value_str; |
|
122 gulong signal_id; |
|
123 } DelayedSet; |
|
124 |
|
125 /*** define SET_ERROR and ERROR macros/functions */ |
|
126 |
|
127 #ifdef G_HAVE_ISO_VARARGS |
|
128 |
|
129 # define SET_ERROR(error, type, ...) \ |
|
130 G_STMT_START { \ |
|
131 GST_CAT_ERROR (GST_CAT_PIPELINE, __VA_ARGS__); \ |
|
132 if ((error) && !*(error)) { \ |
|
133 g_set_error ((error), GST_PARSE_ERROR, (type), __VA_ARGS__); \ |
|
134 } \ |
|
135 } G_STMT_END |
|
136 |
|
137 # define ERROR(type, ...) \ |
|
138 SET_ERROR (((graph_t *) graph)->error, (type), __VA_ARGS__ ) |
|
139 |
|
140 #elif defined(G_HAVE_GNUC_VARARGS) |
|
141 |
|
142 # define SET_ERROR(error, type, args...) \ |
|
143 G_STMT_START { \ |
|
144 GST_CAT_ERROR (GST_CAT_PIPELINE, args ); \ |
|
145 if ((error) && !*(error)) { \ |
|
146 g_set_error ((error), GST_PARSE_ERROR, (type), args ); \ |
|
147 } \ |
|
148 } G_STMT_END |
|
149 |
|
150 # define ERROR(type, args...) \ |
|
151 SET_ERROR (((graph_t *) graph)->error,(type) , args ) |
|
152 |
|
153 #else |
|
154 |
|
155 static inline void |
|
156 SET_ERROR (GError **error, gint type, const char *format, ...) |
|
157 { |
|
158 if (error) { |
|
159 if (*error) { |
|
160 g_warning ("error while parsing"); |
|
161 } else { |
|
162 va_list varargs; |
|
163 char *string; |
|
164 |
|
165 va_start (varargs, format); |
|
166 string = g_strdup_vprintf (format, varargs); |
|
167 va_end (varargs); |
|
168 |
|
169 g_set_error (error, GST_PARSE_ERROR, type, string); |
|
170 |
|
171 g_free (string); |
|
172 } |
|
173 } |
|
174 } |
|
175 |
|
176 #endif /* G_HAVE_ISO_VARARGS */ |
|
177 |
|
178 /*** define YYPRINTF macro/function if we're debugging */ |
|
179 |
|
180 /* bison 1.35 calls this macro with side effects, we need to make sure the |
|
181 side effects work - crappy bison */ |
|
182 |
|
183 #ifndef GST_DISABLE_GST_DEBUG |
|
184 # define YYDEBUG 1 |
|
185 |
|
186 # ifdef G_HAVE_ISO_VARARGS |
|
187 |
|
188 /* # define YYFPRINTF(a, ...) GST_CAT_DEBUG (GST_CAT_PIPELINE, __VA_ARGS__) */ |
|
189 # define YYFPRINTF(a, ...) \ |
|
190 G_STMT_START { \ |
|
191 gchar *temp = g_strdup_printf (__VA_ARGS__); \ |
|
192 GST_CAT_LOG (GST_CAT_PIPELINE, temp); \ |
|
193 g_free (temp); \ |
|
194 } G_STMT_END |
|
195 |
|
196 # elif defined(G_HAVE_GNUC_VARARGS) |
|
197 |
|
198 # define YYFPRINTF(a, args...) \ |
|
199 G_STMT_START { \ |
|
200 gchar *temp = g_strdup_printf ( args ); \ |
|
201 GST_CAT_LOG (GST_CAT_PIPELINE, temp); \ |
|
202 g_free (temp); \ |
|
203 } G_STMT_END |
|
204 |
|
205 # else |
|
206 |
|
207 static inline void |
|
208 YYPRINTF(const char *format, ...) |
|
209 { |
|
210 va_list varargs; |
|
211 gchar *temp; |
|
212 |
|
213 va_start (varargs, format); |
|
214 temp = g_strdup_vprintf (format, varargs); |
|
215 GST_CAT_LOG (GST_CAT_PIPELINE, "%s", temp); |
|
216 g_free (temp); |
|
217 va_end (varargs); |
|
218 } |
|
219 |
|
220 # endif /* G_HAVE_ISO_VARARGS */ |
|
221 |
|
222 #endif /* GST_DISABLE_GST_DEBUG */ |
|
223 |
|
224 #define GST_BIN_MAKE(res, type, chainval, assign, free_string) \ |
|
225 G_STMT_START { \ |
|
226 chain_t *chain = chainval; \ |
|
227 GSList *walk; \ |
|
228 GstBin *bin = (GstBin *) gst_element_factory_make (type, NULL); \ |
|
229 if (!chain) { \ |
|
230 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_EMPTY_BIN, \ |
|
231 _("specified empty bin \"%s\", not allowed"), type); \ |
|
232 g_slist_foreach (assign, (GFunc) gst_parse_strfree, NULL); \ |
|
233 g_slist_free (assign); \ |
|
234 gst_object_unref (bin); \ |
|
235 if (free_string) \ |
|
236 gst_parse_strfree (type); /* Need to clean up the string */ \ |
|
237 YYERROR; \ |
|
238 } else if (!bin) { \ |
|
239 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, \ |
|
240 _("no bin \"%s\", skipping"), type); \ |
|
241 g_slist_foreach (assign, (GFunc) gst_parse_strfree, NULL); \ |
|
242 g_slist_free (assign); \ |
|
243 res = chain; \ |
|
244 } else { \ |
|
245 for (walk = chain->elements; walk; walk = walk->next ) \ |
|
246 gst_bin_add (bin, GST_ELEMENT (walk->data)); \ |
|
247 g_slist_free (chain->elements); \ |
|
248 chain->elements = g_slist_prepend (NULL, bin); \ |
|
249 res = chain; \ |
|
250 /* set the properties now */ \ |
|
251 for (walk = assign; walk; walk = walk->next) \ |
|
252 gst_parse_element_set ((gchar *) walk->data, GST_ELEMENT (bin), graph); \ |
|
253 g_slist_free (assign); \ |
|
254 } \ |
|
255 } G_STMT_END |
|
256 |
|
257 #define MAKE_LINK(link, _src, _src_name, _src_pads, _sink, _sink_name, _sink_pads) \ |
|
258 G_STMT_START { \ |
|
259 link = gst_parse_link_new (); \ |
|
260 link->src = _src; \ |
|
261 link->sink = _sink; \ |
|
262 link->src_name = _src_name; \ |
|
263 link->sink_name = _sink_name; \ |
|
264 link->src_pads = _src_pads; \ |
|
265 link->sink_pads = _sink_pads; \ |
|
266 link->caps = NULL; \ |
|
267 } G_STMT_END |
|
268 |
|
269 #define MAKE_REF(link, _src, _pads) \ |
|
270 G_STMT_START { \ |
|
271 gchar *padname = _src; \ |
|
272 GSList *pads = _pads; \ |
|
273 if (padname) { \ |
|
274 while (*padname != '.') padname++; \ |
|
275 *padname = '\0'; \ |
|
276 padname++; \ |
|
277 if (*padname != '\0') \ |
|
278 pads = g_slist_prepend (pads, gst_parse_strdup (padname)); \ |
|
279 } \ |
|
280 MAKE_LINK (link, NULL, _src, pads, NULL, NULL, NULL); \ |
|
281 } G_STMT_END |
|
282 |
|
283 static void gst_parse_new_child(GstChildProxy *child_proxy, GObject *object, |
|
284 gpointer data) |
|
285 { |
|
286 DelayedSet *set = (DelayedSet *) data; |
|
287 GParamSpec *pspec; |
|
288 GValue v = { 0, }; |
|
289 GstObject *target = NULL; |
|
290 GType value_type; |
|
291 |
|
292 if (gst_child_proxy_lookup (GST_OBJECT (set->parent), set->name, &target, &pspec)) { |
|
293 value_type = G_PARAM_SPEC_VALUE_TYPE (pspec); |
|
294 |
|
295 GST_CAT_LOG (GST_CAT_PIPELINE, "parsing delayed property %s as a %s from %s", pspec->name, |
|
296 g_type_name (value_type), set->value_str); |
|
297 g_value_init (&v, value_type); |
|
298 if (gst_value_deserialize (&v, set->value_str)) { |
|
299 g_object_set_property (G_OBJECT (target), pspec->name, &v); |
|
300 } |
|
301 g_signal_handler_disconnect (child_proxy, set->signal_id); |
|
302 g_free(set->name); |
|
303 g_free(set->value_str); |
|
304 g_free(set); |
|
305 } |
|
306 |
|
307 if (G_IS_VALUE (&v)) |
|
308 g_value_unset (&v); |
|
309 if (target) |
|
310 gst_object_unref (target); |
|
311 return; |
|
312 } |
|
313 |
|
314 |
|
315 static void |
|
316 gst_parse_element_set (gchar *value, GstElement *element, graph_t *graph) |
|
317 { |
|
318 GParamSpec *pspec; |
|
319 gchar *pos = value; |
|
320 GValue v = { 0, }; |
|
321 GstObject *target = NULL; |
|
322 GType value_type; |
|
323 |
|
324 /* parse the string, so the property name is null-terminated an pos points |
|
325 to the beginning of the value */ |
|
326 while (!g_ascii_isspace (*pos) && (*pos != '=')) pos++; |
|
327 if (*pos == '=') { |
|
328 *pos = '\0'; |
|
329 } else { |
|
330 *pos = '\0'; |
|
331 pos++; |
|
332 while (g_ascii_isspace (*pos)) pos++; |
|
333 } |
|
334 pos++; |
|
335 while (g_ascii_isspace (*pos)) pos++; |
|
336 if (*pos == '"') { |
|
337 pos++; |
|
338 pos[strlen (pos) - 1] = '\0'; |
|
339 } |
|
340 gst_parse_unescape (pos); |
|
341 |
|
342 if (gst_child_proxy_lookup (GST_OBJECT (element), value, &target, &pspec)) { |
|
343 value_type = G_PARAM_SPEC_VALUE_TYPE (pspec); |
|
344 GST_CAT_LOG (GST_CAT_PIPELINE, "parsing property %s as a %s", pspec->name, |
|
345 g_type_name (value_type)); |
|
346 g_value_init (&v, value_type); |
|
347 if (!gst_value_deserialize (&v, pos)) |
|
348 goto error; |
|
349 g_object_set_property (G_OBJECT (target), pspec->name, &v); |
|
350 } else { |
|
351 /* do a delayed set */ |
|
352 if (GST_IS_CHILD_PROXY (element)) { |
|
353 DelayedSet *data = g_new (DelayedSet, 1); |
|
354 |
|
355 data->parent = element; |
|
356 data->name = g_strdup(value); |
|
357 data->value_str = g_strdup(pos); |
|
358 data->signal_id = g_signal_connect(GST_OBJECT (element),"child-added", G_CALLBACK (gst_parse_new_child), data); |
|
359 } |
|
360 else { |
|
361 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_NO_SUCH_PROPERTY, \ |
|
362 _("no property \"%s\" in element \"%s\""), value, \ |
|
363 GST_ELEMENT_NAME (element)); |
|
364 } |
|
365 } |
|
366 |
|
367 out: |
|
368 gst_parse_strfree (value); |
|
369 if (G_IS_VALUE (&v)) |
|
370 g_value_unset (&v); |
|
371 if (target) |
|
372 gst_object_unref (target); |
|
373 return; |
|
374 |
|
375 error: |
|
376 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_COULD_NOT_SET_PROPERTY, |
|
377 _("could not set property \"%s\" in element \"%s\" to \"%s\""), |
|
378 value, GST_ELEMENT_NAME (element), pos); |
|
379 goto out; |
|
380 } |
|
381 static inline void |
|
382 gst_parse_free_link (link_t *link) |
|
383 { |
|
384 gst_parse_strfree (link->src_name); |
|
385 gst_parse_strfree (link->sink_name); |
|
386 g_slist_foreach (link->src_pads, (GFunc) gst_parse_strfree, NULL); |
|
387 g_slist_foreach (link->sink_pads, (GFunc) gst_parse_strfree, NULL); |
|
388 g_slist_free (link->src_pads); |
|
389 g_slist_free (link->sink_pads); |
|
390 if (link->caps) gst_caps_unref (link->caps); |
|
391 gst_parse_link_free (link); |
|
392 } |
|
393 |
|
394 static void |
|
395 gst_parse_found_pad (GstElement *src, GstPad *pad, gpointer data) |
|
396 { |
|
397 DelayedLink *link = (DelayedLink *) data; |
|
398 |
|
399 GST_CAT_INFO (GST_CAT_PIPELINE, "trying delayed linking %s:%s to %s:%s", |
|
400 GST_STR_NULL (GST_ELEMENT_NAME (src)), GST_STR_NULL (link->src_pad), |
|
401 GST_STR_NULL (GST_ELEMENT_NAME (link->sink)), GST_STR_NULL (link->sink_pad)); |
|
402 |
|
403 if (gst_element_link_pads_filtered (src, link->src_pad, link->sink, |
|
404 link->sink_pad, link->caps)) { |
|
405 /* do this here, we don't want to get any problems later on when |
|
406 * unlocking states */ |
|
407 GST_CAT_DEBUG (GST_CAT_PIPELINE, "delayed linking %s:%s to %s:%s worked", |
|
408 GST_STR_NULL (GST_ELEMENT_NAME (src)), GST_STR_NULL (link->src_pad), |
|
409 GST_STR_NULL (GST_ELEMENT_NAME (link->sink)), GST_STR_NULL (link->sink_pad)); |
|
410 g_signal_handler_disconnect (src, link->signal_id); |
|
411 g_free (link->src_pad); |
|
412 g_free (link->sink_pad); |
|
413 if (link->caps) gst_caps_unref (link->caps); |
|
414 g_free (link); |
|
415 } |
|
416 } |
|
417 /* both padnames and the caps may be NULL */ |
|
418 static gboolean |
|
419 gst_parse_perform_delayed_link (GstElement *src, const gchar *src_pad, |
|
420 GstElement *sink, const gchar *sink_pad, |
|
421 GstCaps *caps) |
|
422 { |
|
423 GList *templs = gst_element_class_get_pad_template_list ( |
|
424 GST_ELEMENT_GET_CLASS (src)); |
|
425 |
|
426 for (; templs; templs = templs->next) { |
|
427 GstPadTemplate *templ = (GstPadTemplate *) templs->data; |
|
428 if ((GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC) && |
|
429 (GST_PAD_TEMPLATE_PRESENCE(templ) == GST_PAD_SOMETIMES)) |
|
430 { |
|
431 DelayedLink *data = g_new (DelayedLink, 1); |
|
432 |
|
433 /* TODO: maybe we should check if src_pad matches this template's names */ |
|
434 |
|
435 GST_CAT_DEBUG (GST_CAT_PIPELINE, "trying delayed link %s:%s to %s:%s", |
|
436 GST_STR_NULL (GST_ELEMENT_NAME (src)), GST_STR_NULL (src_pad), |
|
437 GST_STR_NULL (GST_ELEMENT_NAME (sink)), GST_STR_NULL (sink_pad)); |
|
438 |
|
439 data->src_pad = g_strdup (src_pad); |
|
440 data->sink = sink; |
|
441 data->sink_pad = g_strdup (sink_pad); |
|
442 if (caps) { |
|
443 data->caps = gst_caps_copy (caps); |
|
444 } else { |
|
445 data->caps = NULL; |
|
446 } |
|
447 data->signal_id = g_signal_connect (G_OBJECT (src), "pad-added", |
|
448 G_CALLBACK (gst_parse_found_pad), data); |
|
449 return TRUE; |
|
450 } |
|
451 } |
|
452 return FALSE; |
|
453 } |
|
454 /* |
|
455 * performs a link and frees the struct. src and sink elements must be given |
|
456 * return values 0 - link performed |
|
457 * 1 - link delayed |
|
458 * <0 - error |
|
459 */ |
|
460 static gint |
|
461 gst_parse_perform_link (link_t *link, graph_t *graph) |
|
462 { |
|
463 GstElement *src = link->src; |
|
464 GstElement *sink = link->sink; |
|
465 GSList *srcs = link->src_pads; |
|
466 GSList *sinks = link->sink_pads; |
|
467 g_assert (GST_IS_ELEMENT (src)); |
|
468 g_assert (GST_IS_ELEMENT (sink)); |
|
469 |
|
470 GST_CAT_INFO (GST_CAT_PIPELINE, |
|
471 "linking %s:%s to %s:%s (%u/%u) with caps \"%" GST_PTR_FORMAT "\"", |
|
472 GST_ELEMENT_NAME (src), link->src_name ? link->src_name : "(any)", |
|
473 GST_ELEMENT_NAME (sink), link->sink_name ? link->sink_name : "(any)", |
|
474 g_slist_length (srcs), g_slist_length (sinks), link->caps); |
|
475 |
|
476 if (!srcs || !sinks) { |
|
477 if (gst_element_link_pads_filtered (src, |
|
478 srcs ? (const gchar *) srcs->data : NULL, sink, |
|
479 sinks ? (const gchar *) sinks->data : NULL, link->caps)) { |
|
480 goto success; |
|
481 } else { |
|
482 if (gst_parse_perform_delayed_link (src, |
|
483 srcs ? (const gchar *) srcs->data : NULL, |
|
484 sink, sinks ? (const gchar *) sinks->data : NULL, link->caps)) { |
|
485 goto success; |
|
486 } else { |
|
487 goto error; |
|
488 } |
|
489 } |
|
490 } |
|
491 if (g_slist_length (link->src_pads) != g_slist_length (link->src_pads)) { |
|
492 goto error; |
|
493 } |
|
494 while (srcs && sinks) { |
|
495 const gchar *src_pad = (const gchar *) srcs->data; |
|
496 const gchar *sink_pad = (const gchar *) sinks->data; |
|
497 srcs = g_slist_next (srcs); |
|
498 sinks = g_slist_next (sinks); |
|
499 if (gst_element_link_pads_filtered (src, src_pad, sink, sink_pad, |
|
500 link->caps)) { |
|
501 continue; |
|
502 } else { |
|
503 if (gst_parse_perform_delayed_link (src, src_pad, |
|
504 sink, sink_pad, |
|
505 link->caps)) { |
|
506 continue; |
|
507 } else { |
|
508 goto error; |
|
509 } |
|
510 } |
|
511 } |
|
512 |
|
513 success: |
|
514 gst_parse_free_link (link); |
|
515 return 0; |
|
516 |
|
517 error: |
|
518 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_LINK, |
|
519 _("could not link %s to %s"), GST_ELEMENT_NAME (src), |
|
520 GST_ELEMENT_NAME (sink)); |
|
521 gst_parse_free_link (link); |
|
522 return -1; |
|
523 } |
|
524 |
|
525 |
|
526 static int yyerror (void *scanner, graph_t *graph, const char *s); |
|
527 %} |
|
528 |
|
529 %union { |
|
530 gchar *s; |
|
531 chain_t *c; |
|
532 link_t *l; |
|
533 GstElement *e; |
|
534 GSList *p; |
|
535 graph_t *g; |
|
536 } |
|
537 |
|
538 %token <s> PARSE_URL |
|
539 %token <s> IDENTIFIER |
|
540 %left <s> REF PADREF BINREF |
|
541 %token <s> ASSIGNMENT |
|
542 %token <s> LINK |
|
543 |
|
544 %type <g> graph |
|
545 %type <c> chain bin |
|
546 %type <l> reference |
|
547 %type <l> linkpart link |
|
548 %type <p> linklist |
|
549 %type <e> element |
|
550 %type <p> padlist pads assignments |
|
551 |
|
552 %left '(' ')' |
|
553 %left ',' |
|
554 %right '.' |
|
555 %left '!' '=' |
|
556 |
|
557 %parse-param { void *scanner } |
|
558 %parse-param { graph_t *graph } |
|
559 %pure-parser |
|
560 |
|
561 %start graph |
|
562 %% |
|
563 |
|
564 element: IDENTIFIER { $$ = gst_element_factory_make ($1, NULL); |
|
565 if ($$ == NULL) { |
|
566 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, _("no element \"%s\""), $1); |
|
567 gst_parse_strfree ($1); |
|
568 YYERROR; |
|
569 } |
|
570 gst_parse_strfree ($1); |
|
571 } |
|
572 | element ASSIGNMENT { gst_parse_element_set ($2, $1, graph); |
|
573 $$ = $1; |
|
574 } |
|
575 ; |
|
576 assignments: /* NOP */ { $$ = NULL; } |
|
577 | assignments ASSIGNMENT { $$ = g_slist_prepend ($1, $2); } |
|
578 ; |
|
579 bin: '(' assignments chain ')' { GST_BIN_MAKE ($$, "bin", $3, $2, FALSE); } |
|
580 | BINREF assignments chain ')' { GST_BIN_MAKE ($$, $1, $3, $2, TRUE); |
|
581 gst_parse_strfree ($1); |
|
582 } |
|
583 | BINREF assignments ')' { GST_BIN_MAKE ($$, $1, NULL, $2, TRUE); |
|
584 gst_parse_strfree ($1); |
|
585 } |
|
586 | BINREF assignments error ')' { GST_BIN_MAKE ($$, $1, NULL, $2, TRUE); |
|
587 gst_parse_strfree ($1); |
|
588 } |
|
589 ; |
|
590 |
|
591 pads: PADREF { $$ = g_slist_prepend (NULL, $1); } |
|
592 | PADREF padlist { $$ = $2; |
|
593 $$ = g_slist_prepend ($$, $1); |
|
594 } |
|
595 ; |
|
596 padlist: ',' IDENTIFIER { $$ = g_slist_prepend (NULL, $2); } |
|
597 | ',' IDENTIFIER padlist { $$ = g_slist_prepend ($3, $2); } |
|
598 ; |
|
599 |
|
600 reference: REF { MAKE_REF ($$, $1, NULL); } |
|
601 | REF padlist { MAKE_REF ($$, $1, $2); } |
|
602 ; |
|
603 |
|
604 linkpart: reference { $$ = $1; } |
|
605 | pads { MAKE_REF ($$, NULL, $1); } |
|
606 | /* NOP */ { MAKE_REF ($$, NULL, NULL); } |
|
607 ; |
|
608 |
|
609 link: linkpart LINK linkpart { $$ = $1; |
|
610 if ($2) { |
|
611 $$->caps = gst_caps_from_string ($2); |
|
612 if ($$->caps == NULL) |
|
613 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_LINK, _("could not parse caps \"%s\""), $2); |
|
614 gst_parse_strfree ($2); |
|
615 } |
|
616 $$->sink_name = $3->src_name; |
|
617 $$->sink_pads = $3->src_pads; |
|
618 gst_parse_link_free ($3); |
|
619 } |
|
620 ; |
|
621 |
|
622 linklist: link { $$ = g_slist_prepend (NULL, $1); } |
|
623 | link linklist { $$ = g_slist_prepend ($2, $1); } |
|
624 | linklist error { $$ = $1; } |
|
625 ; |
|
626 |
|
627 chain: element { $$ = gst_parse_chain_new (); |
|
628 $$->first = $$->last = $1; |
|
629 $$->front = $$->back = NULL; |
|
630 $$->elements = g_slist_prepend (NULL, $1); |
|
631 } |
|
632 | bin { $$ = $1; } |
|
633 | chain chain { if ($1->back && $2->front) { |
|
634 if (!$1->back->sink_name) { |
|
635 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_LINK, _("link without source element")); |
|
636 gst_parse_free_link ($1->back); |
|
637 } else { |
|
638 ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $1->back); |
|
639 } |
|
640 if (!$2->front->src_name) { |
|
641 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_LINK, _("link without sink element")); |
|
642 gst_parse_free_link ($2->front); |
|
643 } else { |
|
644 ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $2->front); |
|
645 } |
|
646 $1->back = NULL; |
|
647 } else if ($1->back) { |
|
648 if (!$1->back->sink_name) { |
|
649 $1->back->sink = $2->first; |
|
650 } |
|
651 } else if ($2->front) { |
|
652 if (!$2->front->src_name) { |
|
653 $2->front->src = $1->last; |
|
654 } |
|
655 $1->back = $2->front; |
|
656 } |
|
657 |
|
658 if ($1->back) { |
|
659 ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $1->back); |
|
660 } |
|
661 $1->last = $2->last; |
|
662 $1->back = $2->back; |
|
663 $1->elements = g_slist_concat ($1->elements, $2->elements); |
|
664 if ($2) |
|
665 gst_parse_chain_free ($2); |
|
666 $$ = $1; |
|
667 } |
|
668 | chain linklist { GSList *walk; |
|
669 if ($1->back) { |
|
670 $2 = g_slist_prepend ($2, $1->back); |
|
671 $1->back = NULL; |
|
672 } else { |
|
673 if (!((link_t *) $2->data)->src_name) { |
|
674 ((link_t *) $2->data)->src = $1->last; |
|
675 } |
|
676 } |
|
677 for (walk = $2; walk; walk = walk->next) { |
|
678 link_t *link = (link_t *) walk->data; |
|
679 if (!link->sink_name && walk->next) { |
|
680 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_LINK, _("link without sink element")); |
|
681 gst_parse_free_link (link); |
|
682 } else if (!link->src_name && !link->src) { |
|
683 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_LINK, _("link without source element")); |
|
684 gst_parse_free_link (link); |
|
685 } else { |
|
686 if (walk->next) { |
|
687 ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, link); |
|
688 } else { |
|
689 $1->back = link; |
|
690 } |
|
691 } |
|
692 } |
|
693 g_slist_free ($2); |
|
694 $$ = $1; |
|
695 } |
|
696 | chain error { $$ = $1; } |
|
697 | link chain { if ($2->front) { |
|
698 if (!$2->front->src_name) { |
|
699 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_LINK, _("link without source element")); |
|
700 gst_parse_free_link ($2->front); |
|
701 } else { |
|
702 ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $2->front); |
|
703 } |
|
704 } |
|
705 if (!$1->sink_name) { |
|
706 $1->sink = $2->first; |
|
707 } |
|
708 $2->front = $1; |
|
709 $$ = $2; |
|
710 } |
|
711 | PARSE_URL chain { $$ = $2; |
|
712 if ($$->front) { |
|
713 GstElement *element = |
|
714 gst_element_make_from_uri (GST_URI_SRC, $1, NULL); |
|
715 if (!element) { |
|
716 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, |
|
717 _("no source element for URI \"%s\""), $1); |
|
718 } else { |
|
719 $$->front->src = element; |
|
720 ((graph_t *) graph)->links = g_slist_prepend ( |
|
721 ((graph_t *) graph)->links, $$->front); |
|
722 $$->front = NULL; |
|
723 $$->elements = g_slist_prepend ($$->elements, element); |
|
724 } |
|
725 } else { |
|
726 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_LINK, |
|
727 _("no element to link URI \"%s\" to"), $1); |
|
728 } |
|
729 g_free ($1); |
|
730 } |
|
731 | link PARSE_URL { GstElement *element = |
|
732 gst_element_make_from_uri (GST_URI_SINK, $2, NULL); |
|
733 if (!element) { |
|
734 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, |
|
735 _("no sink element for URI \"%s\""), $2); |
|
736 gst_parse_link_free ($1); |
|
737 g_free ($2); |
|
738 YYERROR; |
|
739 } else if ($1->sink_name || $1->sink_pads) { |
|
740 gst_object_unref (element); |
|
741 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_LINK, |
|
742 _("could not link sink element for URI \"%s\""), $2); |
|
743 gst_parse_link_free ($1); |
|
744 g_free ($2); |
|
745 YYERROR; |
|
746 } else { |
|
747 $$ = gst_parse_chain_new (); |
|
748 $$->first = $$->last = element; |
|
749 $$->front = $1; |
|
750 $$->front->sink = element; |
|
751 $$->elements = g_slist_prepend (NULL, element); |
|
752 } |
|
753 g_free ($2); |
|
754 } |
|
755 ; |
|
756 graph: /* NOP */ { SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_EMPTY, _("empty pipeline not allowed")); |
|
757 $$ = (graph_t *) graph; |
|
758 } |
|
759 | chain { $$ = (graph_t *) graph; |
|
760 if ($1->front) { |
|
761 if (!$1->front->src_name) { |
|
762 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_LINK, _("link without source element")); |
|
763 gst_parse_free_link ($1->front); |
|
764 } else { |
|
765 $$->links = g_slist_prepend ($$->links, $1->front); |
|
766 } |
|
767 $1->front = NULL; |
|
768 } |
|
769 if ($1->back) { |
|
770 if (!$1->back->sink_name) { |
|
771 SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_LINK, _("link without sink element")); |
|
772 gst_parse_free_link ($1->back); |
|
773 } else { |
|
774 $$->links = g_slist_prepend ($$->links, $1->back); |
|
775 } |
|
776 $1->back = NULL; |
|
777 } |
|
778 $$->chain = $1; |
|
779 } |
|
780 ; |
|
781 |
|
782 %% |
|
783 |
|
784 |
|
785 static int |
|
786 yyerror (void *scanner, graph_t *graph, const char *s) |
|
787 { |
|
788 /* FIXME: This should go into the GError somehow, but how? */ |
|
789 GST_WARNING ("Error during parsing: %s", s); |
|
790 return -1; |
|
791 } |
|
792 |
|
793 |
|
794 GstElement * |
|
795 _gst_parse_launch (const gchar *str, GError **error) |
|
796 { |
|
797 graph_t g; |
|
798 gchar *dstr; |
|
799 GSList *walk; |
|
800 GstBin *bin = NULL; |
|
801 GstElement *ret; |
|
802 yyscan_t scanner; |
|
803 |
|
804 g_return_val_if_fail (str != NULL, NULL); |
|
805 |
|
806 g.chain = NULL; |
|
807 g.links = NULL; |
|
808 g.error = error; |
|
809 |
|
810 #ifdef __GST_PARSE_TRACE |
|
811 GST_CAT_DEBUG (GST_CAT_PIPELINE, "TRACE: tracing enabled"); |
|
812 __strings = __chains = __links = 0; |
|
813 #endif /* __GST_PARSE_TRACE */ |
|
814 |
|
815 dstr = g_strdup (str); |
|
816 _gst_parse_yylex_init (&scanner); |
|
817 _gst_parse_yy_scan_string (dstr, scanner); |
|
818 |
|
819 #ifndef YYDEBUG |
|
820 yydebug = 1; |
|
821 #endif |
|
822 |
|
823 if (yyparse (scanner, &g) != 0) { |
|
824 SET_ERROR (error, GST_PARSE_ERROR_SYNTAX, |
|
825 "Unrecoverable syntax error while parsing pipeline %s", str); |
|
826 |
|
827 _gst_parse_yylex_destroy (scanner); |
|
828 g_free (dstr); |
|
829 |
|
830 goto error1; |
|
831 } |
|
832 _gst_parse_yylex_destroy (scanner); |
|
833 g_free (dstr); |
|
834 |
|
835 GST_CAT_DEBUG (GST_CAT_PIPELINE, "got %u elements and %u links", |
|
836 g.chain ? g_slist_length (g.chain->elements) : 0, |
|
837 g_slist_length (g.links)); |
|
838 |
|
839 if (!g.chain) { |
|
840 ret = NULL; |
|
841 } else if (!(((chain_t *) g.chain)->elements->next)) { |
|
842 /* only one toplevel element */ |
|
843 ret = (GstElement *) ((chain_t *) g.chain)->elements->data; |
|
844 g_slist_free (((chain_t *) g.chain)->elements); |
|
845 if (GST_IS_BIN (ret)) |
|
846 bin = GST_BIN (ret); |
|
847 gst_parse_chain_free (g.chain); |
|
848 } else { |
|
849 /* put all elements in our bin */ |
|
850 bin = GST_BIN (gst_element_factory_make ("pipeline", NULL)); |
|
851 g_assert (bin); |
|
852 |
|
853 for (walk = g.chain->elements; walk; walk = walk->next) { |
|
854 if (walk->data != NULL) |
|
855 gst_bin_add (bin, GST_ELEMENT (walk->data)); |
|
856 } |
|
857 |
|
858 g_slist_free (g.chain->elements); |
|
859 ret = GST_ELEMENT (bin); |
|
860 gst_parse_chain_free (g.chain); |
|
861 } |
|
862 |
|
863 /* remove links */ |
|
864 for (walk = g.links; walk; walk = walk->next) { |
|
865 link_t *l = (link_t *) walk->data; |
|
866 if (!l->src) { |
|
867 if (l->src_name) { |
|
868 if (bin) { |
|
869 l->src = gst_bin_get_by_name_recurse_up (bin, l->src_name); |
|
870 if (l->src) |
|
871 gst_object_unref (l->src); |
|
872 } else { |
|
873 l->src = strcmp (GST_ELEMENT_NAME (ret), l->src_name) == 0 ? ret : NULL; |
|
874 } |
|
875 } |
|
876 if (!l->src) { |
|
877 SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, |
|
878 "No element named \"%s\" - omitting link", l->src_name); |
|
879 gst_parse_free_link (l); |
|
880 continue; |
|
881 } |
|
882 } |
|
883 if (!l->sink) { |
|
884 if (l->sink_name) { |
|
885 if (bin) { |
|
886 l->sink = gst_bin_get_by_name_recurse_up (bin, l->sink_name); |
|
887 if (l->sink) |
|
888 gst_object_unref (l->sink); |
|
889 } else { |
|
890 l->sink = strcmp (GST_ELEMENT_NAME (ret), l->sink_name) == 0 ? ret : NULL; |
|
891 } |
|
892 } |
|
893 if (!l->sink) { |
|
894 SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, |
|
895 "No element named \"%s\" - omitting link", l->sink_name); |
|
896 gst_parse_free_link (l); |
|
897 continue; |
|
898 } |
|
899 } |
|
900 gst_parse_perform_link (l, &g); |
|
901 } |
|
902 g_slist_free (g.links); |
|
903 |
|
904 out: |
|
905 #ifdef __GST_PARSE_TRACE |
|
906 GST_CAT_DEBUG (GST_CAT_PIPELINE, |
|
907 "TRACE: %u strings, %u chains and %u links left", __strings, __chains, |
|
908 __links); |
|
909 if (__strings || __chains || __links) { |
|
910 g_warning ("TRACE: %u strings, %u chains and %u links left", __strings, |
|
911 __chains, __links); |
|
912 } |
|
913 #endif /* __GST_PARSE_TRACE */ |
|
914 |
|
915 return ret; |
|
916 |
|
917 error1: |
|
918 if (g.chain) { |
|
919 g_slist_foreach (g.chain->elements, (GFunc)gst_object_unref, NULL); |
|
920 g_slist_free (g.chain->elements); |
|
921 gst_parse_chain_free (g.chain); |
|
922 } |
|
923 |
|
924 g_slist_foreach (g.links, (GFunc)gst_parse_free_link, NULL); |
|
925 g_slist_free (g.links); |
|
926 |
|
927 if (error) |
|
928 g_assert (*error); |
|
929 ret = NULL; |
|
930 |
|
931 goto out; |
|
932 } |