add error symbol start translating gst-inspect
[platform/upstream/gstreamer.git] / tools / gst-launch.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *               2000 Wim Taymans <wtay@chello.be>
4  *               2004 Thomas Vander Stichele <thomas@apestaart.org>
5  *
6  * gst-launch.c: tool to launch GStreamer pipelines from the command line
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #include <string.h>
29 #include <stdlib.h>
30 #include <signal.h>
31 #include <unistd.h>
32 #include <sys/wait.h>
33 #include <locale.h> /* for LC_ALL */
34 #include "gst/gst-i18n-app.h"
35
36 #include <gst/gst.h>
37
38 /* FIXME: This is just a temporary hack.  We should have a better
39  * check for siginfo handling. */
40 #ifdef SA_SIGINFO
41 #define USE_SIGINFO
42 #endif
43
44 extern volatile gboolean glib_on_error_halt;
45 static void fault_restore (void);
46 static void fault_spin (void);
47 static void sigint_restore (void);
48
49 static gint max_iterations = 0;
50 static guint64 iterations = 0;
51 static guint64 sum = 0;
52 static guint64 min = G_MAXINT64;
53 static guint64 max = 0;
54 static GstClock *s_clock;
55 static GstElement *pipeline;
56 gboolean caught_intr = FALSE;
57
58 gboolean
59 idle_func (gpointer data)
60 {
61   gboolean busy;
62   GTimeVal tfthen, tfnow;
63   GstClockTimeDiff diff;
64
65   g_get_current_time (&tfthen);
66   busy = gst_bin_iterate (GST_BIN (data));
67   iterations++;
68   g_get_current_time (&tfnow);
69
70   diff = GST_TIMEVAL_TO_TIME (tfnow) -
71          GST_TIMEVAL_TO_TIME (tfthen);
72
73   sum += diff; 
74   min = MIN (min, diff);
75   max = MAX (max, diff);
76
77   if (!busy || caught_intr || (max_iterations>0 && iterations>=max_iterations)) {
78     gst_main_quit ();
79     g_print (_("Execution ended after %" G_GUINT64_FORMAT " iterations (sum %" G_GUINT64_FORMAT " ns, average %" G_GUINT64_FORMAT " ns, min %" G_GUINT64_FORMAT " ns, max %" G_GUINT64_FORMAT " ns).\n"),
80                     iterations, sum, sum/iterations, min, max);
81   }
82
83   return busy;
84 }
85
86 #ifndef GST_DISABLE_LOADSAVE
87 static GstElement*
88 xmllaunch_parse_cmdline (const gchar **argv)
89 {
90   GstElement *pipeline = NULL, *e;
91   GstXML *xml;
92   gboolean err;
93   const gchar *arg;
94   gchar *element, *property, *value;
95   GList *l;
96   gint i = 0;
97   
98   if (!(arg = argv[0])) {
99     g_print (_("Usage: gst-xmllaunch <file.xml> [ element.property=value ... ]\n"));
100     exit (1);
101   }
102   
103   xml = gst_xml_new ();
104   err = gst_xml_parse_file(xml, arg, NULL);
105   
106   if (err != TRUE) {
107     fprintf (stderr, _("ERROR: parse of xml file '%s' failed.\n"), arg);
108     exit (1);
109   }
110   
111   l = gst_xml_get_topelements (xml);
112   if (!l) {
113     fprintf (stderr, _("ERROR: no toplevel pipeline element in file '%s'.\n"), arg);
114     exit (1);
115   }
116     
117   if (l->next)
118     fprintf (stderr,  _("WARNING: only one toplevel element is supported at this time."));
119   
120   pipeline = GST_ELEMENT (l->data);
121   
122   while ((arg = argv[++i])) {
123     element = g_strdup (arg);
124     property = strchr (element, '.');
125     value = strchr (element, '=');
126     
127     if (!(element < property && property < value)) {
128       fprintf (stderr, _("ERROR: could not parse command line argument %d: %s.\n"), i, element);
129       g_free (element);
130       exit (1);
131     }
132     
133     *property++ = '\0';
134     *value++ = '\0';
135     
136     e = gst_bin_get_by_name (GST_BIN (pipeline), element);
137     if (!e) {
138       fprintf (stderr, _("WARNING: element named '%s' not found.\n"), element);
139     } else {
140       gst_util_set_object_arg (G_OBJECT (e), property, value);
141     }
142     g_free (element);
143   }
144   
145   if (!l)
146     return NULL;
147   else
148     return l->data;
149 }
150 #endif
151
152 #ifndef USE_SIGINFO
153 static void 
154 fault_handler_sighandler (int signum)
155 {
156   fault_restore ();
157
158   switch (signum) {
159     case SIGSEGV:
160       g_print ("Caught SIGSEGV\n");
161       break;
162     case SIGQUIT:
163       g_print ("Caught SIGQUIT\n");
164       break;
165     default:
166       g_print ("signo:  %d\n", signum);
167       break;
168   }
169
170   fault_spin();
171 }
172
173 #else
174
175 static void 
176 fault_handler_sigaction (int signum, siginfo_t *si, void *misc)
177 {
178   fault_restore ();
179
180   switch (si->si_signo) {
181     case SIGSEGV:
182       g_print ("Caught SIGSEGV accessing address %p\n", si->si_addr);
183       break;
184     case SIGQUIT:
185       g_print ("Caught SIGQUIT\n");
186       break;
187     default:
188       g_print ("signo:  %d\n", si->si_signo);
189       g_print ("errno:  %d\n", si->si_errno);
190       g_print ("code:   %d\n", si->si_code);
191       break;
192   }
193
194   fault_spin();
195 }
196 #endif
197
198 static void
199 fault_spin (void)
200 {
201   int spinning = TRUE;
202
203   glib_on_error_halt = FALSE;
204   g_on_error_stack_trace ("gst-launch");
205
206   wait (NULL);
207
208   /* FIXME how do we know if we were run by libtool? */
209   g_print ("Spinning.  Please run 'gdb gst-launch %d' to continue debugging, "
210            "Ctrl-C to quit, or Ctrl-\\ to dump core.\n",
211            (gint) getpid ());
212   while (spinning) g_usleep (1000000);
213 }
214
215 static void 
216 fault_restore (void)
217 {
218   struct sigaction action;
219
220   memset (&action, 0, sizeof (action));
221   action.sa_handler = SIG_DFL;
222
223   sigaction (SIGSEGV, &action, NULL);
224   sigaction (SIGQUIT, &action, NULL);
225 }
226
227 static void 
228 fault_setup (void)
229 {
230   struct sigaction action;
231
232   memset (&action, 0, sizeof (action));
233 #ifdef USE_SIGINFO
234   action.sa_sigaction = fault_handler_sigaction;
235   action.sa_flags = SA_SIGINFO;
236 #else
237   action.sa_handler = fault_handler_sighandler;
238 #endif
239
240   sigaction (SIGSEGV, &action, NULL);
241   sigaction (SIGQUIT, &action, NULL);
242 }
243
244 static void
245 print_tag (const GstTagList *list, const gchar *tag, gpointer unused)
246 {
247   gint i, count;
248
249   count = gst_tag_list_get_tag_size (list, tag);
250
251   for (i = 0; i < count; i++) {
252     gchar *str;
253     
254     if (gst_tag_get_type (tag) == G_TYPE_STRING) {
255       g_assert (gst_tag_list_get_string_index (list, tag, i, &str));
256     } else {
257       str = g_strdup_value_contents (
258               gst_tag_list_get_value_index (list, tag, i));
259     }
260   
261     if (i == 0) {
262       g_print ("%15s: %s\n", gst_tag_get_nick (tag), str);
263     } else {
264       g_print ("               : %s\n", str);
265     }
266
267     g_free (str);
268   }
269 }
270 static void
271 found_tag (GObject *pipeline, GstElement *source, GstTagList *tags)
272 {
273   g_print (_("FOUND TAG      : found by element \"%s\".\n"),
274            GST_STR_NULL (GST_ELEMENT_NAME (source)));
275   gst_tag_list_foreach (tags, print_tag, NULL);
276 }
277
278 /* we only use sighandler here because the registers are not important */
279 static void
280 sigint_handler_sighandler (int signum)
281 {
282   g_print ("Caught interrupt.\n");
283
284   sigint_restore();
285
286   caught_intr = TRUE;
287 }
288
289 static void
290 sigint_setup (void)
291 {
292   struct sigaction action;
293
294   memset (&action, 0, sizeof (action));
295   action.sa_handler = sigint_handler_sighandler;
296
297   sigaction (SIGINT, &action, NULL);
298 }
299
300 static void 
301 sigint_restore (void)
302 {
303   struct sigaction action;
304
305   memset (&action, 0, sizeof (action));
306   action.sa_handler = SIG_DFL;
307
308   sigaction (SIGINT, &action, NULL);
309 }
310
311 static void
312 play_handler (int signum)
313 {
314   switch (signum) {
315     case SIGUSR1:
316       g_print ("Caught SIGUSR1 - Play request.\n");
317       gst_element_set_state (pipeline, GST_STATE_PLAYING);
318       break;
319     case SIGUSR2:
320       g_print ("Caught SIGUSR2 - Stop request.\n");
321       gst_element_set_state (pipeline, GST_STATE_NULL);
322       break;
323   }
324 }
325
326 static void
327 play_signal_setup(void)
328 {
329   struct sigaction action;
330
331   memset (&action, 0, sizeof (action));
332   action.sa_handler = play_handler;
333   sigaction (SIGUSR1, &action, NULL);
334   sigaction (SIGUSR2, &action, NULL);
335 }
336
337 int
338 main(int argc, char *argv[])
339 {
340   gint i, j;
341   /* options */
342   gboolean verbose = FALSE;
343   gboolean tags = FALSE;
344   gboolean no_fault = FALSE;
345   gboolean trace = FALSE;
346   gchar *savefile = NULL;
347   gchar *exclude_args = NULL;
348   struct poptOption options[] = {
349     {"tags",    't',  POPT_ARG_NONE|POPT_ARGFLAG_STRIP,   &tags,   0,
350      N_("Output tags (also known as metadata)"), NULL},
351     {"verbose", 'v',  POPT_ARG_NONE|POPT_ARGFLAG_STRIP,   &verbose,   0,
352      N_("Output status information and property notifications"), NULL},
353     {"exclude", 'X',  POPT_ARG_STRING|POPT_ARGFLAG_STRIP, &exclude_args,  0,
354      N_("Do not output status information of TYPE"), N_("TYPE1,TYPE2,...")},
355 #ifndef GST_DISABLE_LOADSAVE
356     {"output",  'o',  POPT_ARG_STRING|POPT_ARGFLAG_STRIP, &savefile, 0,
357      N_("Save xml representation of pipeline to FILE and exit"), N_("FILE")},
358 #endif
359     {"no-fault", 'f', POPT_ARG_NONE|POPT_ARGFLAG_STRIP,   &no_fault,   0,
360      N_("Do not install a fault handler"), NULL},
361     {"trace",   'T',  POPT_ARG_NONE|POPT_ARGFLAG_STRIP,   &trace,   0,
362      N_("Print alloc trace (if enabled at compile time)"), NULL},
363     {"iterations",'i',POPT_ARG_INT|POPT_ARGFLAG_STRIP,    &max_iterations,   0,
364      N_("Number of times to iterate pipeline"), NULL},
365     POPT_TABLEEND
366   };
367
368   gchar **argvn;
369   GError *error = NULL;
370   gint res = 0;
371
372   free (malloc (8)); /* -lefence */
373
374   setlocale(LC_ALL, "");
375   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
376   textdomain (GETTEXT_PACKAGE);
377
378   gst_alloc_trace_set_flags_all (GST_ALLOC_TRACE_LIVE);
379
380   gst_init_with_popt_table (&argc, &argv, options);
381
382   /* FIXpopt: strip short args, too. We do it ourselves for now */
383   j = 1;
384   for (i = 1; i < argc; i++) {
385     if (*(argv[i]) == '-') {
386       if (strlen (argv[i]) == 2) {
387         gchar *c = argv[i];
388         c++;
389         if (*c == 'X' || *c == 'o') {
390           i++;
391         }
392       }
393     } else {
394       argv[j] = argv[i];
395       j++;
396     }
397   }
398   argc = j;
399
400   if (!no_fault)
401     fault_setup();
402
403   sigint_setup();
404   play_signal_setup();
405   
406   if (trace) {
407     if (!gst_alloc_trace_available()) {
408       g_warning ("Trace not available (recompile with trace enabled).");
409     }
410     gst_alloc_trace_print_all ();
411   }
412
413   /* make a null-terminated version of argv */
414   argvn = g_new0 (char*, argc);
415   memcpy (argvn, argv+1, sizeof (char*) * (argc-1));
416 #ifndef GST_DISABLE_LOADSAVE
417   if (strstr (argv[0], "gst-xmllaunch")) {
418     pipeline = xmllaunch_parse_cmdline ((const gchar**)argvn);
419   } 
420   else 
421 #endif
422   {
423     pipeline = (GstElement*) gst_parse_launchv ((const gchar**)argvn, &error);
424   }
425   g_free (argvn);
426
427   if (!pipeline) {
428     if (error) {
429       fprintf(stderr, _("ERROR: pipeline could not be constructed: %s.\n"),
430               error->message);
431       g_error_free (error);
432     } else {
433       fprintf(stderr, _("ERROR: pipeline could not be constructed.\n"));
434     }
435     exit(1);
436   } else if (error) {
437     fprintf(stderr, _("WARNING: erroneous pipeline: %s\n"), error->message);
438     fprintf(stderr, _("         Trying to run anyway.\n"));
439     g_error_free (error);
440   }
441   
442   if (verbose) {
443     gchar **exclude_list = exclude_args ? g_strsplit (exclude_args, ",", 0) : NULL;
444     g_signal_connect (pipeline, "deep_notify", G_CALLBACK (gst_element_default_deep_notify), exclude_list);
445   }
446   if (tags) {
447     g_signal_connect (pipeline, "found-tag", G_CALLBACK (found_tag), NULL);
448   }
449   g_signal_connect (pipeline, "error", G_CALLBACK (gst_element_default_error), NULL);
450   
451 #ifndef GST_DISABLE_LOADSAVE
452   if (savefile) {
453     gst_xml_write_file (GST_ELEMENT (pipeline), fopen (savefile, "w"));
454   }
455 #endif
456   
457   if (!savefile) {
458   
459     if (!GST_IS_BIN (pipeline)) {
460       GstElement *real_pipeline = gst_element_factory_make ("pipeline", NULL);
461       if (real_pipeline == NULL) {
462         fprintf(stderr, _("ERROR: the 'pipeline' element wasn't found.\n"));
463         exit(1);
464       }
465       gst_bin_add (GST_BIN (real_pipeline), pipeline);
466       pipeline = real_pipeline;
467     }
468
469     fprintf(stderr, _("RUNNING pipeline ...\n"));
470     if (gst_element_set_state (pipeline, GST_STATE_PLAYING) == GST_STATE_FAILURE) {
471       fprintf(stderr, _("ERROR: pipeline doesn't want to play.\n"));
472       res = -1;
473       goto end;
474     }
475
476     s_clock = gst_bin_get_clock (GST_BIN (pipeline));
477
478     if (!GST_FLAG_IS_SET (GST_OBJECT (pipeline), GST_BIN_SELF_SCHEDULABLE)) {
479         g_idle_add (idle_func, pipeline);
480         gst_main ();
481     } else {
482         g_print ("Waiting for the state change... ");
483         gst_element_wait_state_change (pipeline);
484         g_print ("got the state change.\n");
485     }
486
487     gst_element_set_state (pipeline, GST_STATE_NULL);
488   }
489
490 end:
491
492   gst_object_unref (GST_OBJECT (pipeline));
493
494   if (trace)
495     gst_alloc_trace_print_all ();
496
497   return res;
498 }