gst, controller: replace g_list_prepend + reverse with GQueue
[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  *                    2002 Andy Wingo <wingo@pobox.com>
5  *                    2008 Tim-Philipp Müller <tim centricular net>
6  *
7  * gstparse.c: get a pipeline from a text pipeline description
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 /**
26  * SECTION:gstparse
27  * @short_description: Get a pipeline from a text pipeline description
28  *
29  * These function allow to create a pipeline based on the syntax used in the
30  * gst-launch utility (see man-page for syntax documentation).
31  *
32  * Please note that these functions take several measures to create
33  * somewhat dynamic pipelines. Due to that such pipelines are not always
34  * reusable (set the state to NULL and back to PLAYING).
35  */
36
37 #include "gst_private.h"
38 #include <string.h>
39
40 #include "gstparse.h"
41 #include "gsterror.h"
42 #include "gstinfo.h"
43 #ifndef GST_DISABLE_PARSE
44 #include "parse/types.h"
45 #endif
46
47 static GstParseContext *
48 gst_parse_context_copy (const GstParseContext * context)
49 {
50   GstParseContext *ret = NULL;
51 #ifndef GST_DISABLE_PARSE
52
53   ret = gst_parse_context_new ();
54   if (context) {
55     GQueue missing_copy = G_QUEUE_INIT;
56     GList *l;
57
58     for (l = context->missing_elements; l != NULL; l = l->next)
59       g_queue_push_tail (&missing_copy, g_strdup ((const gchar *) l->data));
60
61     ret->missing_elements = missing_copy.head;
62   }
63 #endif
64   return ret;
65 }
66
67 GType
68 gst_parse_context_get_type (void)
69 {
70   static GType type = 0;
71
72   if (G_UNLIKELY (type == 0)) {
73     type = g_boxed_type_register_static ("GstParseContext",
74         (GBoxedCopyFunc) gst_parse_context_copy,
75         (GBoxedFreeFunc) gst_parse_context_free);
76   }
77
78   return type;
79 }
80
81 /**
82  * gst_parse_error_quark:
83  *
84  * Get the error quark used by the parsing subsystem.
85  *
86  * Returns: the quark of the parse errors.
87  */
88 GQuark
89 gst_parse_error_quark (void)
90 {
91   static GQuark quark = 0;
92
93   if (!quark)
94     quark = g_quark_from_static_string ("gst_parse_error");
95   return quark;
96 }
97
98
99 /**
100  * gst_parse_context_new:
101  *
102  * Allocates a parse context for use with gst_parse_launch_full() or
103  * gst_parse_launchv_full().
104  *
105  * Free-function: gst_parse_context_free
106  *
107  * Returns: (transfer full): a newly-allocated parse context. Free with
108  *     gst_parse_context_free() when no longer needed.
109  *
110  * Since: 0.10.20
111  */
112 GstParseContext *
113 gst_parse_context_new (void)
114 {
115 #ifndef GST_DISABLE_PARSE
116   GstParseContext *ctx;
117
118   ctx = g_slice_new (GstParseContext);
119   ctx->missing_elements = NULL;
120
121   return ctx;
122 #else
123   return NULL;
124 #endif
125 }
126
127 /**
128  * gst_parse_context_free:
129  * @context: (transfer full): a #GstParseContext
130  *
131  * Frees a parse context previously allocated with gst_parse_context_new().
132  *
133  * Since: 0.10.20
134  */
135 void
136 gst_parse_context_free (GstParseContext * context)
137 {
138 #ifndef GST_DISABLE_PARSE
139   if (context) {
140     g_list_foreach (context->missing_elements, (GFunc) g_free, NULL);
141     g_list_free (context->missing_elements);
142     g_slice_free (GstParseContext, context);
143   }
144 #endif
145 }
146
147 /**
148  * gst_parse_context_get_missing_elements:
149  * @context: a #GstParseContext
150  *
151  * Retrieve missing elements from a previous run of gst_parse_launch_full()
152  * or gst_parse_launchv_full(). Will only return results if an error code
153  * of %GST_PARSE_ERROR_NO_SUCH_ELEMENT was returned.
154  *
155  * Returns: (transfer full) (array zero-terminated=1) (element-type gchar*): a
156  *     NULL-terminated array of element factory name strings of missing
157  *     elements. Free with g_strfreev() when no longer needed.
158  *
159  * Since: 0.10.20
160  */
161 gchar **
162 gst_parse_context_get_missing_elements (GstParseContext * context)
163 {
164 #ifndef GST_DISABLE_PARSE
165   gchar **arr;
166   GList *l;
167   guint len, i;
168
169   g_return_val_if_fail (context != NULL, NULL);
170
171   len = g_list_length (context->missing_elements);
172
173   if (G_UNLIKELY (len == 0))
174     return NULL;
175
176   arr = g_new (gchar *, len + 1);
177
178   for (i = 0, l = context->missing_elements; l != NULL; l = l->next, ++i)
179     arr[i] = g_strdup (l->data);
180
181   arr[i] = NULL;
182
183   return arr;
184 #else
185   return NULL;
186 #endif
187 }
188
189 #ifndef GST_DISABLE_PARSE
190 static gchar *
191 _gst_parse_escape (const gchar * str)
192 {
193   GString *gstr = NULL;
194
195   g_return_val_if_fail (str != NULL, NULL);
196
197   gstr = g_string_sized_new (strlen (str));
198
199   while (*str) {
200     if (*str == ' ')
201       g_string_append_c (gstr, '\\');
202     g_string_append_c (gstr, *str);
203     str++;
204   }
205
206   return g_string_free (gstr, FALSE);
207 }
208 #endif /* !GST_DISABLE_PARSE */
209
210 /**
211  * gst_parse_launchv:
212  * @argv: (in) (array zero-terminated=1): null-terminated array of arguments
213  * @error: pointer to a #GError
214  *
215  * Create a new element based on command line syntax.
216  * @error will contain an error message if an erroneuos pipeline is specified.
217  * An error does not mean that the pipeline could not be constructed.
218  *
219  * Returns: (transfer full): a new element on success and %NULL on failure.
220  */
221 GstElement *
222 gst_parse_launchv (const gchar ** argv, GError ** error)
223 {
224   return gst_parse_launchv_full (argv, NULL, GST_PARSE_FLAG_NONE, error);
225 }
226
227 /**
228  * gst_parse_launchv_full:
229  * @argv: (in) (array zero-terminated=1): null-terminated array of arguments
230  * @context: (allow-none): a parse context allocated with
231  *     gst_parse_context_new(), or %NULL
232  * @flags: parsing options, or #GST_PARSE_FLAG_NONE
233  * @error: pointer to a #GError (which must be initialised to %NULL)
234  *
235  * Create a new element based on command line syntax.
236  * @error will contain an error message if an erroneous pipeline is specified.
237  * An error does not mean that the pipeline could not be constructed.
238  *
239  * Returns: (transfer full): a new element on success; on failure, either %NULL
240  *   or a partially-constructed bin or element will be returned and @error will
241  *   be set (unless you passed #GST_PARSE_FLAG_FATAL_ERRORS in @flags, then
242  *   %NULL will always be returned on failure)
243  *
244  * Since: 0.10.20
245  */
246 GstElement *
247 gst_parse_launchv_full (const gchar ** argv, GstParseContext * context,
248     GstParseFlags flags, GError ** error)
249 {
250 #ifndef GST_DISABLE_PARSE
251   GstElement *element;
252   GString *str;
253   const gchar **argvp, *arg;
254   gchar *tmp;
255
256   g_return_val_if_fail (argv != NULL, NULL);
257   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
258
259   /* let's give it a nice size. */
260   str = g_string_sized_new (1024);
261
262   argvp = argv;
263   while (*argvp) {
264     arg = *argvp;
265     tmp = _gst_parse_escape (arg);
266     g_string_append (str, tmp);
267     g_free (tmp);
268     g_string_append_c (str, ' ');
269     argvp++;
270   }
271
272   element = gst_parse_launch_full (str->str, context, flags, error);
273
274   g_string_free (str, TRUE);
275
276   return element;
277 #else
278   /* gst_parse_launch_full() will set a GST_CORE_ERROR_DISABLED error for us */
279   return gst_parse_launch_full ("", NULL, 0, error);
280 #endif
281 }
282
283 /**
284  * gst_parse_launch:
285  * @pipeline_description: the command line describing the pipeline
286  * @error: the error message in case of an erroneous pipeline.
287  *
288  * Create a new pipeline based on command line syntax.
289  * Please note that you might get a return value that is not %NULL even though
290  * the @error is set. In this case there was a recoverable parsing error and you
291  * can try to play the pipeline.
292  *
293  * Returns: (transfer full): a new element on success, %NULL on failure. If
294  *    more than one toplevel element is specified by the @pipeline_description,
295  *   all elements are put into a #GstPipeline, which than is returned.
296  */
297 GstElement *
298 gst_parse_launch (const gchar * pipeline_description, GError ** error)
299 {
300   return gst_parse_launch_full (pipeline_description, NULL, GST_PARSE_FLAG_NONE,
301       error);
302 }
303
304 /**
305  * gst_parse_launch_full:
306  * @pipeline_description: the command line describing the pipeline
307  * @context: (allow-none): a parse context allocated with
308  *      gst_parse_context_new(), or %NULL
309  * @flags: parsing options, or #GST_PARSE_FLAG_NONE
310  * @error: the error message in case of an erroneous pipeline.
311  *
312  * Create a new pipeline based on command line syntax.
313  * Please note that you might get a return value that is not %NULL even though
314  * the @error is set. In this case there was a recoverable parsing error and you
315  * can try to play the pipeline.
316  *
317  * Returns: (transfer full): a new element on success, %NULL on failure. If
318  *    more than one toplevel element is specified by the @pipeline_description,
319  *    all elements are put into a #GstPipeline, which then is returned.
320  *
321  * Since: 0.10.20
322  */
323 GstElement *
324 gst_parse_launch_full (const gchar * pipeline_description,
325     GstParseContext * context, GstParseFlags flags, GError ** error)
326 {
327 #ifndef GST_DISABLE_PARSE
328   GstElement *element;
329
330   g_return_val_if_fail (pipeline_description != NULL, NULL);
331   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
332
333   GST_CAT_INFO (GST_CAT_PIPELINE, "parsing pipeline description '%s'",
334       pipeline_description);
335
336   element = priv_gst_parse_launch (pipeline_description, error, context, flags);
337
338   /* don't return partially constructed pipeline if FATAL_ERRORS was given */
339   if (G_UNLIKELY (error != NULL && *error != NULL && element != NULL)) {
340     if ((flags & GST_PARSE_FLAG_FATAL_ERRORS)) {
341       gst_object_unref (element);
342       element = NULL;
343     }
344   }
345
346   return element;
347 #else
348   gchar *msg;
349
350   GST_WARNING ("Disabled API called");
351
352   msg = gst_error_get_message (GST_CORE_ERROR, GST_CORE_ERROR_DISABLED);
353   g_set_error (error, GST_CORE_ERROR, GST_CORE_ERROR_DISABLED, "%s", msg);
354   g_free (msg);
355
356   return NULL;
357 #endif
358 }